'분류 전체보기'에 해당되는 글 56건
- 2018.07.24
- 2018.07.19
- 2018.07.17
- 2018.07.17
- 2018.07.11
Apache와 Tomcat AJP Protocol을 이용하여 연동하는 경우 Request Entity Too Large 오류가 발생할 때 다음과 같이 조치한다.
1. httpd.conf 파일 추가
LimitRequestLine 65536 LimitRequestBody 0 LimitRequestFieldSize 65536 LimitRequestFields 10000 |
2. works.properties 파일 변경
worker.list=jkstatus,lb_dev worker.template.type=ajp13 worker.template.lbfactor=1 worker.template.socket_timeout=300 worker.template.socket_connect_timeout=5000 worker.template.socket_keepalive=true worker.template.connect_timeout=30000 worker.template.connection_pool_size=128 worker.template.connection_pool_minsize=32 worker.template.connection_pool_timeout=20 ######################################### ## Worker ######################################### worker.dev.reference=worker.template worker.dev.host=10.255.116.84 worker.dev.port=8009 worker.dev.max_packet_size=65536 ######################################### ## Load Balancer ######################################### worker.lb_dev.type=lb worker.lb_dev.balance_workers=dev worker.lb_dev.sticky_session=true worker.jkstatus.type=status #worker.jasper.max_packet_size=65536 |
Tomcat과 연동하는 workerdml max_packet_size를 65536으로 변경한다.
3. Tomcat의 server.xml 파일 수정
#JAVA_OPTS=" ${JAVA_OPTS} -Dorg.apache.coyote.ajp.MAX_PACKET_SIZE=65536"
|
MAX_PAKCET_SIZE를 JVM option에 추가하거나
<Connector URIEncoding="UTF-8" packetSize="65536" acceptCount="10" connectionTimeout="60000" enableLookups="false" maxPostSize="-1" maxThreads="256" tcpNoDelay="true" maxHttpHeaderSize="30000" port="8009" protocol="AJP/1.3" redirectPort="8443" />
|
server.xml 파일에 AJP Connector에 packetSize와 maxHttpHeaderSize를 설정한다.
Tomcat 서버 다중화 시 Session Clustering을 통해 한 서버의 장애 시에도 지속적인 서비스가 가능하도록 구성한다.
Tomcat의 Session Clustering은 Multicast를 이용하여 서버간에 Session을 동기화 하는데
AWS와 같은 Cloud platform에서는 Muticast가 지원되지 않는다.
이 때 사용할 수 있는 방법이 Redis를 이용하여 Session 동기화가 필요한다.
Spring Framework에서는 Redis를 통해 Session Clustering을 지원하고 있다.
■ Dependency
spring-aop-4.3.1.RELEASE.jar spring-beans-4.3.1.RELEASE.jar spring-context-4.1.2.RELEASE.jar spring-context-support-4.1.2.RELEASE.jar spring-core-4.3.1.RELEASE.jar spring-data-commons-1.12.10.RELEASE.jar spring-data-keyvalue-1.1.10.RELEASE.jar spring-data-redis-1.7.10.RELEASE.jar spring-expression-4.3.1.RELEASE.jar spring-jdbc-4.1.2.RELEASE.jar spring-modules-validation-0.9.jar spring-orm-4.1.2.RELEASE.jar spring-security-config-4.1.2.RELEASE.jar spring-security-core-4.1.2.RELEASE.jar spring-security-web-4.1.2.RELEASE.jar spring-session-1.3.1.RELEASE.jar spring-session-data-redis-1.3.1.RELEASE.jar spring-tx-4.1.2.RELEASE.jar spring-web-4.1.2.RELEASE.jar spring-webmvc-4.1.2.RELEASE.jar spring-ws-core-2.4.0.RELEASE.jar
|
먼저 Session Clustering을 이용하기 위해 XML을 이용하여 설정한다.
■ context-redis.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- This should not be removed --> <context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<bean id="lettuceConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory" destroy-method="destroy"> <property name="hostName" value="#{project['session.redis.url']}" /> <property name="port" value="#{project['session.redis.port']}" /> </bean>
<!-- Do not remove --> <util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
<!-- bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="int-bus-mgmt-eh-dev.hez7xm.ng.0001.euw1.cache.amazonaws.com" /> <property name="port" value="6379" /> <property name="usePool" value="true" /> </bean-->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="lettuceConnectionFactory" /> </bean>
</beans>
|
<context:annotation-config>를 통해 Context내의 Annotation을 활성화 한다.
RedisHttpSessionConfiguration을 bean으로 설정한다.
LettuceConnectionFactory에 Redis hostname과 port를 정의한다.
AWS에서 제공하는 Redis를 사용할 경우에는 ConfigureREsisAction.NO_OP를 적용해야 오류가 발생하지 않는다.
설정이 완료되었으면 web.xml에 springSessionFilter를 추가해야 한다.
■ web.xml Filter 추가
<filter> <filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> |
이상으로 Spring Framework와 Redis의 연동은 완료되었다.
Redis에 Session이 저장되어 있는지 확인하기 위해 Redis Client를 설치하여 확인이 가능하다.
Redis client에 접속하기 위해 redis-cli -h "hostname" -p "port"로 접속이 가능하다.
keys * command를 이용하여 모든 키 정보 출력이 가능하다.
[Spring Boot] ReloadableResourceBundleMessageSource를 이용한 다국어 처리 (0) | 2018.08.13 |
---|---|
[Spring Boot] Schedule Task (0) | 2018.08.13 |
Spring framework RequestMapping을 XML 파일로 생성하기 (0) | 2018.07.17 |
Spring framewok Redis session 관리 (0) | 2018.06.08 |
Spring framework Future와 AsyncResult 를 이용한 Async 서비스 호출 (0) | 2018.04.23 |
Spring framework에서 Restful을 이용한 web service를 작성 시 어떤 서비스들이 Publishing 되어 있는지 확인이 필요한 경우가 있다.
Spring framework는 RequestMappingHandlerMapping을 통해서 Controller에 Request mapping 정보를 찾을 수 있다.
RequestMappingHandlerMapping을 이용하여 Mehtod Name, media type, parameter 등을 이용하여 XML로 생성하여 브라우저에 출력한다.
■ 소스코드
import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.stream.XMLStreamWriter; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.condition.MediaTypeExpression; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; @Controller public class WadlCrt extends BaseController {
@Resource(name = "requestMappingHandler") private RequestMappingHandlerMapping requestMappingHandler;
@RequestMapping(value="wadl", produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Map<String, Object> generateWsdl(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, Object> resultMap = new HashMap<String, Object>(); Map<RequestMappingInfo, HandlerMethod> mappings = requestMappingHandler.getHandlerMethods(); Set<RequestMappingInfo> keys = mappings.keySet(); Iterator<RequestMappingInfo> iterator = keys.iterator(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); XMLStreamWriter writer = XmlUtil.getWriter(bos); XmlUtil.startDocument(writer, "UTF-8", "1.0"); String namespace = PropertiesUtil.getString("wadl.namespace"); // XmlUtil.startElement(writer, "application", PropertiesUtil.getString("wadl.namespace")); XmlUtil.startElement(writer, "application"); XmlUtil.addNamespace(writer, "xmlns", "http://wadl.dev.java.net/2009/02"); XmlUtil.addNamespace(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); XmlUtil.addNamespace(writer, "xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); XmlUtil.addNamespace(writer, "xmlns:apigee", "http://api.apigee.com/wadl/2010/07/"); XmlUtil.addNamespace(writer, "xsi:schemaLocation", "http://wadl.dev.java.net/2009/02 http://apigee.com/schemas/wadl-schema.xsd http://api.apigee.com/wadl/2010/07/ http://apigee.com/schemas/apigee-wadl-extensions.xsd");
XmlUtil.startElement(writer, "resources"); XmlUtil.writeAttribute(writer,"base", "http://localhost:8080");
while(iterator.hasNext()) {
RequestMappingInfo key = iterator.next(); HandlerMethod value = mappings.get(key); XmlUtil.startElement(writer, "resource"); PatternsRequestCondition condition = key.getPatternsCondition(); Set<String> patterns = condition.getPatterns(); if (patterns != null) { for (String pattern : patterns) { XmlUtil.writeAttribute(writer, "path", pattern); } }
Map<String, Object> list = new LinkedHashMap<String, Object>();
/** Get parameter type **/ Method method = value.getMethod(); XmlUtil.writeAttribute(writer, "id", method.getName()); /** Get request method **/ Iterator<RequestMethod> itr = key.getMethodsCondition().getMethods().iterator(); while(itr.hasNext()) { RequestMethod mkey = itr.next(); XmlUtil.writeAttribute(writer, "name", mkey.toString()); list.put("method", mkey.toString()); }
Class<?>[] paramTypes = method.getParameterTypes(); int idx = 0; if (paramTypes.length > 0) { XmlUtil.startElement(writer, "request"); for (Class<?> paramType : paramTypes) { XmlUtil.startElement(writer, "param"); String paramClass = paramType.getName(); list.put("input" + idx, paramClass); XmlUtil.writeAttribute(writer, "type", paramClass); XmlUtil.endElement(writer); } XmlUtil.endElement(writer); }
/** Get return type **/ String returnType = method.getReturnType().getName(); if (returnType != null) { XmlUtil.startElement(writer, "response"); list.put("output", returnType); XmlUtil.startElement(writer, "param"); XmlUtil.writeAttribute(writer, "type", returnType); /** Get media type **/ Iterator<MediaTypeExpression> itr1 = key.getProducesCondition().getExpressions().iterator(); String mediaType = null; while(itr1.hasNext()) { MediaTypeExpression exp = itr1.next(); mediaType = exp.getMediaType().toString(); list.put("mediaType", mediaType); XmlUtil.writeAttribute(writer, "mediaType", mediaType); } /** param **/ XmlUtil.endElement(writer); /** response **/ XmlUtil.endElement(writer); }
/** Resource **/ XmlUtil.endElement(writer); } /** resources **/ XmlUtil.endElement(writer); /** application **/ XmlUtil.endElement(writer);
XmlUtil.endDocument(writer); LOGGER.debug("Created xml :: {}", bos.toString());
String xml = XmlUtil.prettyPrint(bos.toString()); OutputStream os = response.getOutputStream(); os.write(xml.getBytes()); LOGGER.info(xml); return resultMap; } |
XML을 생성하기 위해 XMLStreamWriter를 이용하여 XML을 생성한다.
■ XML 생성 모듈
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; public class XmlUtil { public static XMLStreamWriter getWriter(ByteArrayOutputStream bos) throws XMLStreamException {
XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(bos);
return writer; }
public static void startDocument(XMLStreamWriter writer) throws XMLStreamException { writer.writeStartDocument(); }
public static void startDocument(XMLStreamWriter writer, String encoding, String version) throws XMLStreamException { writer.writeStartDocument(); }
public static void endDocument(XMLStreamWriter writer) throws XMLStreamException { writer.writeEndDocument(); }
public static void addNamespace(XMLStreamWriter writer, String namespace, String value) throws XMLStreamException { writer.writeNamespace(namespace, value); }
public static void startElement(XMLStreamWriter writer, String element) throws XMLStreamException { writer.writeStartElement(element); } public static void startElement(XMLStreamWriter writer, String element, String value) throws XMLStreamException { writer.writeStartElement(element, value); }
public static void endElement(XMLStreamWriter writer) throws XMLStreamException { writer.writeEndElement(); }
public static void writeAttribute(XMLStreamWriter writer, String name, String value) throws XMLStreamException { writer.writeAttribute(name, value); }
public static String prettyPrint(String xml) throws ParserConfigurationException, SAXException, TransformerException, IOException {
StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\" ?>"); if (xml == null) { return sb.toString(); }
Document document = toXmlDocument(xml);
TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
DOMSource source = new DOMSource(document); StringWriter strWriter = new StringWriter(); StreamResult result = new StreamResult(strWriter); transformer.transform(source, result); sb.append("\n"); sb.append(strWriter.getBuffer().toString()); return sb.toString(); } public static Document toXmlDocument(String str) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); Document document = docBuilder.parse(new InputSource(new StringReader(str))); return document; } |
■ 출력 결과
<?xml version="1.0" ?> <application xmlns="http://wadl.dev.java.net/2009/02" xmlns:xsi:schemaLocation="http://wadl.dev.java.net/2009/02 http://apigee.com/schemas/wadl-schema.xsd http://api.apigee.com/wadl/2010/07/ http://apigee.com/schemas/apigee-wadl-extensions.xsd" xmlns:xmlns:apigee="http://api.apigee.com/wadl/2010/07/" xmlns:xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <resources base="http://localhost:8080"> <resource id="searchCodeList" path="/deploy/searchCodeList"> <response> <param type="java.util.Map"/> </response> </resource> <resource id="mainPageView" path="/deploy/mainpage.do"> <response> <param type="java.lang.String"/> </response> </resource> <resource id="selectDeployList" path="/deploy/selectDeployList"> <request> <param type="java.util.Map"/> </request> <response> <param type="java.util.Map"/> </response> </resource> <resource id="deploySource" path="/deploy/deployTarget"> <request> <param type="java.util.Map"/> </request> <response> <param type="java.util.Map"/> </response> </resource> <resource id="hudsonJobBuild" path="/deploy/jobBuild"> <request> <param type="java.util.Map"/> </request> <response> <param type="java.util.Map"/> </response> </resource> <resource id="sourceBuild" path="/deploy/uploadFile"> <request> <param type="java.util.Map"/> </request> <response> <param type="java.util.Map"/> </response> </resource> <resource id="showPage" name="GET" path="/system/showPerfPage.do"> <response> <param mediaType="application/json" type="java.lang.String"/> </response> </resource> <resource id="processPerfData" name="POST" path="/system/perf"> <request> <param type="javax.servlet.http.HttpServletRequest"/> <param type="java.util.Map"/> </request> <response> <param mediaType="application/json" type="java.util.Map"/> </response> </resource> <resource id="chartPerf" name="POST" path="/system/chartPerf"> <request> <param type="java.util.Map"/> </request> <response> <param mediaType="application/json" type="java.util.Map"/> </response> </resource> <resource id="searchPerfCpu" name="POST" path="/system/searchPerfCpu"> <request> <param type="java.util.Map"/> </request> <response> <param mediaType="application/json" type="java.util.Map"/> </response> </resource> <resource id="searchPerfMem" name="POST" path="/system/searchPerfMem"> <request> <param type="java.util.Map"/> </request> <response> <param mediaType="application/json" type="java.util.Map"/> </response> </resource> <resource id="generateWsdl" path="/wadl"> <request> <param type="javax.servlet.http.HttpServletRequest"/> <param type="javax.servlet.http.HttpServletResponse"/> </request> <response> <param mediaType="application/json" type="java.util.Map"/> </response> </resource> </resources> </application> |
[Spring Boot] Schedule Task (0) | 2018.08.13 |
---|---|
Spring Framework + Redis를 이용한 Session Clustering (0) | 2018.07.19 |
Spring framewok Redis session 관리 (0) | 2018.06.08 |
Spring framework Future와 AsyncResult 를 이용한 Async 서비스 호출 (0) | 2018.04.23 |
Mybatis ResultHandler를 이용한 ExcelDownload (3) | 2018.04.19 |
Apache에서 rotatelogs를 이용하여 로그 파일을 매일 Rotation할 수 있다.
그러나 로그 파일이 날찌별 동일 디렉토리에 생성되기 때문에 월별로 디렉토리를 생성하여 로그 백업이 필요한 경우 사용할 수 있다.
먼저 shell을 작성한다.
■logback.s
#!/bin/sh CUR_DATE=`date +"%Y%m%d"` LOG_FILE=log_cron_$CUR_DATE.log LOG_HOME=/logs001/apache LOG_BACK_DIR=$LOG_HOME/`date +"%Y%m"` ACCESS_LOG_FILE=$LOG_HOME/access_$CUR_DATE.log ERROR_LOG_FILE=$LOG_HOME/error_$CUR_DATE.log HEALTH_LOG_FILE=$LOG_HOME/health_$CUR_DATE.log #Make log backup directory if [ ! -d $LOG_BACK_DIR ]; then echo "$LOG_BACK_DIR is created" >> $LOG_FILE mkdir $LOG_BACK_DIR fi #Move log file to backup directory for entry in $LOG_HOME/* do if [ ! -d $entry ]; then if [ $entry != $ACCESS_LOG_FILE ] && [ $entry != $ERROR_LOG_FILE ] && [ $entry != $HEALTH_LOG_FILE ]; then echo "$entry is moved to $LOG_BACK_DIR" >> $LOG_FILE #mv $entry $LOG_BACK_DIR fi fi done |
Shell 작성이 완료되었으면 Crontab에 등록하여 주기적으로 shell을 실행하도록 한다.
crontab -e 명령을 이용하여 cron에 Job을 등록한다.
10 0 * * * /engn001/script/logback.sh |
매일 0시 10분에 logback.sh가 실행된다.
Crontab 등록 완료되었으면 crontab -l을 이용하여 등록된 내용을 확인한다.
시스템 개발 시 시각적인 정보를 제공하기 위해 Chart를 이용하는 Line, Bar 등 다양한 Chart를 화면에 보여주게 된다.
Chart.js를 사용하기 위해서는 먼저 JSP에 <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js">를 정의한다.
Chart.js는 Chart 그리는 영역을 정의해 주어야 한다.
■Chart 영역 정의
<div> <canvas id="cpu_line_chart" width="800" height="200"></canvas> </div> |
Chart의 넓이와 높이를 정의한다.
서버에서 받아온 데이터를 이용하여 Chart를 그리는 예제이다.
■Chart 그리기
function fn_searchCallBack(result){ var cpuLabels = []; var cpuUsed = []; var cpuFree = []; var sysCpu = []; var sysUser = [];
$.each(result.cpuInfo, function(i, val){ var dispTime = gfn_nvl(val.updDate); console.log(dispTime); dispTime = dispTime.substring(8,14); console.log(dispTime); cpuLabels.push(dispTime); cpuUsed.push(gfn_nvl(val.cpuUsed)); cpuFree.push(gfn_nvl(val.cpuFree)); sysCpu.push(gfn_nvl(val.sysCpu)); sysUser.push(gfn_nvl(val.sysUser)); });
new Chart(document.getElementById("cpu_line_chart"), { type: 'line', data: { labels: cpuLabels, datasets: [{ data: cpuUsed, label: "Used(%)", borderColor: "#3e95cd", fill: false }, { data: cpuFree, label: "Free(%)", borderColor: "#8e5ea2", fill: false }, { data: sysCpu, label: "Sys(%)", borderColor: "#3cba9f", fill: false }, { data: sysUser, label: "User(%)", borderColor: "#e8c3b9", fill: false } ] }, options: { title: { display: true, text: 'CPU Usage' } } }); } |
먼저 Chart에 Mapping이 필요한 변수를 정의한다.
Line Char는 Array를 사용하기 때문에 var cpuLabels = []; 처럼 Array 변수를 선언한다.
Loop를 돌면서 서버에서 받은 응답 데이터를 Array 변수에 저장한다.
new Chart를 이용하여 Chart를 생성하는데, Chart의 영영이 파라미터로 전달된다.
또한 Array 변수들은 data로 매핑하면 Line Chart가 그려지게 된다.