현재 Spring 을 API서버로 사용하고 있다. Controller의 각 Request Mapping된 로직으로 들어가기 전에, Token을 확인하는 Interceptor를 구현하여 사용하고 있다. Frontend 에서는 javascript가 JQuery의 Ajax로 API 서버에 요청을 보낸다.



// JAVA Interceptor 중 어떤 에러가 발생 되었을때

response.setStatus(401);

return false;




그런데 이럴 경우 해당 response는 text/html 방식으로 프론트에 전달되게 되고, 




// javascript

error: function(xhr){
    console.log(xhr);
},





Xhr은 Status code가 0으로 찍히게 된다.  




이 때 해결 방법은 response의 Header까지 인터셉터 단에서 모두 입력해주어야 하는데 

“Access-Control-Allow-Origin” 에 해당 Request를 요청한 곳의 주소가 들어가야 한다.





// New Java Interceptor

response.setStatus(401);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Connection", "close");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
return false;





이렇게 바꿈으로써 해결되었다. 아마 CORS 관련해서 문제가 생긴듯 싶다. 



Https를 적용하기 위해서는 SSL 인증서가 필요하기 때문에 생성해야한다. 
실제로 서비스를 할 때에는 인증기관에서 인증서를 사면 될 것이다.


SSL 인증서 생성
# keytool -genkey -alias tomcat -keyalg RSA -storepass changeit -keypass changeit -dname 'CN=tomcat’
이렇게 되면 홈 디렉토리에 .keystore 가 생긴다.


톰캣 설정
그러면 이제 이 .kestore을 이용해서 Tomcat 설정을 해보자

Tomcat 홈 디렉토리의 conf/server.xml 로 이동한다.

지금 서비스는 http와 https를 겸해서 쓸 것이기 때문에
    //  기본 http 설정
    <Connector port="8090" protocol="HTTP/1.1"
     connectionTimeout="20000"
     redirectPort="8443" />

   // https 설정
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
     maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
     clientAuth="false" sslProtocol="TLS"
     keystoreFile="/Users/jaeng/.keystore" keystorePass="changeit" />

위와 같은 설정을 한다. keystoreFile, keystorePass와 같은 경우에는 달라질 수 있겠다. 
나머지 설정들에 대해서도 알아보면 좋을 것 같다.

Maven

<dependency>    
        <groupId>org.springframework.security</groupId>    
        <artifactId>spring-security-core</artifactId>        
        <version>4.2.2.RELEASE</version>
</dependency>
<dependency>    
        <groupId>org.springframework.security</groupId>    
        <artifactId>spring-security-web</artifactId>    
        <version>4.2.2.RELEASE</version>
</dependency>
<dependency>    
        <groupId>org.springframework.security</groupId>    
        <artifactId>spring-security-config</artifactId>    
        <version>4.2.2.RELEASE</version>
</dependency>

해당하는 알맞는 버전을 이용하면 되겠다.


스프링 설정
현재 Spring 설정은 모두 java config으로 되어있는 상태이다. Java config으로 어떻게 설정을 하는지 알아보자.

SpringSecurityInitializer라는 클래스를 만들 것이다. 
이 클래스는 AbstractSecurityWebApplicationInitializer 클래스를 상속하는 클래스인데
말 그대로 SecurityWebApplication을 Initialize하는 것이다. 
나의 경우에는 이 클래스를 만들지 않아서 몇 시간을 씨름했었다.

이 것은 xml 설정을 할 때 web.xml에서 springSecurityFilterChain을 설정하던 부분을 대신하는 것이다.

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer{
}

이런식으로 클래스만 만들어도 알아서 작동한다.


그 다음, WebConfig 클래스를 작성한다.


@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
    protected Logger logger = LogManager.getLogger(this.getClass());
  
  @Override    
    protected void configure(HttpSecurity http) throws Exception {
        http.requiresChannel()
                .antMatchers("/api/auth/**").requiresSecure()
                .antMatchers("/api/mypage/**").requiresSecure()
                .antMatchers("/api/**/like").requiresSecure();    
                .antMatchers("/api/jobs").requiresInsecure()  
    }
}


많은 설정을 할 수 있겠지만, 여기서 하는 설정을 설명하자면 어떤 url에는 https를 어떤 url에는 http를 설정하는 것이다.

그래서 .requiresSecure()이 붙는 url은 https로 접근해야만 하는 것이고, 
.requiresInsecure()이 붙은 url은 http로 접근하면 되는 것이다.


이렇게 설정을 하면 http와 https를 함께 사용할 수 있게 된다.







우분투에서 net.core.somaxconn 값을 변경할 때 

sudo /sbin/sysctl -w net.core.somaxconn=1024

명령어로 바꿀 수 있다. 하지만 이렇게 바꾸면 재부팅을 할 때 다시 원래 default 값으로 돌아오는 경우가 있다. 

그래서 일시적이 아니라 영구적으로 바꾸려면,

/etc/sysctl.conf 

에 입력을 해줘야 한다. 

맨 마지막 줄에 다음과 같이 추가해보자. 

net.core.somaxconn = 1024 

재부팅 뒤에도 변경되어있는 것을 확인할 수 있다. 

문제 상황 

현재 EC2 우분투에 Nginx - Uwsgi - Flask 로 서버가 구성되있는 상태이다.

동시접속자 테스트를 하기 위해서 Jmeter를 이용하여 API 요청을 100개, 200개, 300개를 하는데

100개, 200개일 때는 없던 error가 300개일 때는 20~30% 개씩 생기기 시작했다.

어떤 error인지 확인을 해보았고, 

응답코드가 502 인것을 확인을 하였다. 제일 먼저 아랫단인 uwsgi log를 확인해 봤다.

uwsgi에서는 502로 응답을 보낸 것이 없었다.

즉, uwsgi로 아예 요청이 들어오지 않았다는 것을 알 수 있었다.

그래서 그것보다 윗단인 nginx로그를 확인을 했다. 

2016/06/08 07:45:13 [error] 9913#0: *394 connect() to unix:/home/ubuntu/idletower/server.sock failed (11: Resource temporarily unavailable) while connecting to upstream, client: 175.196.62.240, server: , request: "GET /v1/test HTTP/1.1", upstream: "uwsgi://unix:/home/ubuntu/idletower/server.sock:", host: "52.193.46.113"
"/var/log/nginx/error.log" [readonly] 1646L, 533533C

라는 Error 로그를 발견할 수 있었다


 
해결과정

에러 로그에서 발견한

(11: Resource temporarily unavailable) while connecting to upstream

키워드를 이용해서 구글 검색을 했다. 


를 확인해보면 위의 에러로그는 socket 리소스가 일시적으로 사용할 수 없는 상태라는 것을 의미하고 있엇다.

그것은 현재 socket high load, busy 하다는 것이다.


현재 테스트 하는 서버에서는 uwsgi가 server.sock이라는 유닉스 소켓을 생성해서 그 socket을 이용해 nginx와 통신을 하는데

그 socket이 어떤 제한이 걸렸기 때문에 안되는 거라고 생각을 했다. 


더 검색을 하다

http://simp1e.tistory.com/39 이 곳을 참고하게 되었다.

위 링크를 따라서


sudo sysctl -a | grep somaxconn 


으로 socket max connection 128 밖에 되지 않는것을 확인하고 


sudo /sbin/sysctl -w net.core.somaxconn=1024


추가 시켰다. 다시 테스트를 해봤지만 별 차이가 없었다.

ulimit -a 

라는 명령어를 통해서 open files와, max user processes 수도 늘려봤지만, 별로 달라진게 없었다.

그러다 uwsgi  로그를 확인하던 중 

your server socket listen backlog is limited to 100 connections

라는 메세지를 발견하게 되었고, 구글링을 통해서 backlog를 증가시키는 법을 찾아냈다.


uwsgi 설정에서 

listen = 1024

를 입력해서 다시 서버를 Start 시키니, 1024로 backlog가 늘었다.


그리고 400개의 api를 보내봤는데 error가 발생하지는 않았다. 

여기서 net.core.somaxconn 의 갯수와는 무슨 상관이 있을까 싶어서 숫자를 줄이고 다시 서버를 시작하니


Listen queue size is greater than the system max net.core.somaxconn (1000).


라는 오류가 발생했다.



즉 socket backlog 갯수는 net.core.somaxconn 갯수와 같거나 작아야 하는 것이다. 

다 됐다고 생각하고 더 높은 인스턴스에서 1000개를 보내는 테스트를 하는데 에러가 몇 개정도 나서 확인 해보니

nginx에러 로그에서 Too many open files 이라는 것을 확인하게 되었다.

sudo sh -c "ulimit -n 65535 && exec su $LOGNAME" // open file


이렇게 다 늘리고 나니, 높은 인스턴스에서 정상적으로 1000개, 2000개에도 빠르게 모든 것을 처리하는 것을 확인할 수 있었다.


참고로 open file이나 process 제한을 영구적으로 변경하고 싶다면 

/etc/security/limits.conf 

의 맨 아랫줄에 
# open files
     ubuntu hard nofile 512000
     ubuntu soft no file 512000 

# process
     ubuntu hard nproc 512000
     ubuntu soft nproc 512000

을 추가해주자. 


Element Tree 를 이용한다. python3.5 기준으로 내장되어있다. 


1. Xml이 문자열일 때


다음과 같은 Xml string이 있다.






getroot 함수는 root 태그인 GXG_RES 라는 태그를 받아온다.

그리고 그 하위 태그는 바로 result이기 때문에 result를 find하고,

다시 또 거기서 하위를 찾는 등으로 찾을 수 있다.

findall을 이용해서 모든 태그를 다 찾을 수도 있다. 


2.xml이 파일일 때




나머지는 위와 동일하게 할 수 있다. 

Nginx를 통해 파일 업로드를 할 때


nginx error – 413 Request Entity Too Large

라는 에러가 뜰 때가 있다.

업로드 용량이 처음에는 default로 1mb가 잡혀있기 때문에 그것을 늘려야 더 큰 용량을 업로드 할 수 있다. 


/etc/nginx/nginx.conf 에서

http {
     server {
          client_max_body_size 20M;
     }
}

이런식으로 client_max_body_size를 더 늘려주면 된다. 


python flask + uwsgi + postgresql(sqlalchemy, psycopg2) 를 쓰고 있는데
uwsgi setting 에서 workers를 늘리면 다음과 같은 에러가 발생한다.

sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) SSL error: decryption failed or bad record mac


문제 해결법은 다음과 같다.


uwsgi 설정을 기록하는 .ini 파일에 다음의 옵션을 추가한다.

     lazy-apps = true

  • uWSGI tries to (ab)use the Copy On Write semantics of the fork() call whenever possible. By default it will fork after having loaded your applications to share as much of their memory as possible. If this behavior is undesirable for some reason, use the lazy-apps option. This will instruct uWSGI to load the applications after each worker’s fork(). Beware as there is an older options named lazy that is way more invasive and highly discouraged (it is still here only for backward compatibility)

즉 lazy-apps를 true로 바꾼다는 것은 worker가 applicaition을 fork()가 끝난 뒤에 load한다는 것이다. 



$ sudo apt-get install libpq-dev python-dev


를 설치를 해주면 된다. 

Postgresql에서 테이블 구성 시 Enum type을 정의하고 내용을 정의한다.

필자의 경우 python flask + postgresql + sqlalchemy를 함께 사용하는데 



    value_type = db.Column(

        Enum('int', 'float', 'bool', 'string', name='constants_value_type'),

        nullable=False

    )


다음과 같이 enum type을 정의할 때가 있다. 여기서 constants_value_type이 Enum type name이 되는데


문제는 내가 여기 enum에 double 이라는 type을 추가하고 싶을 때 일어난다.

나는 분명히 데이터를 추가하고 재시작 했음에도 불구하고 실제 데이터 베이스에는 적용이 되지 않는 현상이 일어나는 것이다. 


그 이유는 postgresql 내부에 pg_enum이라는 테이블이 있는데 그 곳에 처음 실행 시 Enum type이 저장이 되기 때문이다.

그래서 코드상으로는 타입이 추가가 되어도 테이블에 추가가 되지 않기 때문에 그런 현상이 일어나는 것이다.


방법은 두 가지가 있다.


첫번째는 postgresql UI 프로그램을 통해서 직접 그 값을 추가시키거나 바꿔주는 것이다. 그런데 이것은 가끔 권한문제를 일으킬 수도 있고

여러모로 어려움이 많다. 


그래서 그 때 쓸 수 있는 것이 바로 


drop type constants_value_type cascade;


라는 명령어를 터미널에서 psql 로 들어가서 쿼리문을 실행 해주는 것이다.


그 후에 새로 시작을 하면 내가 작성한 Enum이 적용된다는 것을 알 수 있다. 

아이폰에서 패킷 분석을 할 필요가 있을 때, mac과 wireshark로 해당 작업을 수행할 수 있다.


먼저 wireshark를 다운을 받는다. 


그리고 ITunes로 들어가서 좌측 상단에 폰 모양을 누르면 내 아이폰 정보로 들어가게 된다.


용량, 전화번호 밑에 부분을 클릭을 하면 항복이 바뀐다. UDID를 확인하고 복사를 한다.


terminal로 들어간다.



# 시작

$ rvictl -s $UDID 복사한UDID


# 종료

$ rvictl -s $UDID 복사한UDID



위의 명령어로 시작을 하게 되면 wireshark 에서 rvi0 를 통해서 추적 가능하다.



그리고 각종 필터를 이용해서 원하는 패킷을 찾아 분석을 하면 되겠다. 

+ Recent posts