'전체'에 해당되는 글 386건

  1. EPL 2017-2018 시즌 토트넘 핫스퍼 베스트 스쿼드 11
  2. Spring Boot, Jackson, @JsonView로 멀티 뷰 구성하기
  3. CentOS 6, Zulu(OpenJDK 8) 설치하기
  4. Windows, Zulu(OpenJDK 8) 설치하기
  5. Java 무료 논쟁, Oracle JDK와 OpenJDK의 차이 정리
  6. MySQL/MariaDB, MHA로 레플리케이션 HA 환경 구성하기
  7. PES 2018 (위닝일레븐 2018) 스팀 출시 및 할인 구매하기, 통합 패치 (15)
  8. 주요 API 플랫폼 서비스 정리
  9. Spring Boot, Graylog로 GELF UDP 로그 전송하기
  10. Spring Boot, Redis 저장소에 CRUD 로직 구현하기

EPL 2017-2018 시즌 토트넘 핫스퍼 베스트 스쿼드 11

개요

  • 토트넘 핫스퍼는 명장 포체티노 감독의 지휘 하에 2015-2016 시즌 3위, 2016-2017 시즌 2위로 역대 최고의 전성기를 보내고 있다. 오일 머니의 지원을 받는 빅클럽 사이에서 가성비 스쿼드로 2년 연속 빅4 안에 들며 존재감을 과시하고 있다.
  • 포체티노 감독은 어린 선수들을 발굴하여 경쟁력 있는 선수(델레 알리)로 성장시키는데 능하다. 또한 로테이션에 강점을 발휘하는 멀티 플레이어(에릭 다이어)를 선호한다.
  • 토트넘이 국내에 인기가 많은 것은 손흥민의 소속팀이기 때문이다. 2016-2017 시즌 최고의 활약을 펼친 덕에 유럽 리그에서 활동 중인 아시아 선수 랭킹 1위(2위는 카가와 신지)에 선정됬다.

전술

  • 토트넘은 3-4-2-1 전술에서 가장 높은 승률을 기록하고 있다. 델레 알리크리스티안 에릭센의 공격력을 극대화할 수 있어 2017-2018 시즌에도 가장 많이 사용되고 있다.
  • 문제는 3-4-2-1 전술에서 손흥민의 자리가 없다는 것이다. 공격적인 전개가 필요한 경기에서 좌측 윙백으로 출전하는 정도이다. 손흥민4-2-3-1 전술의 왼쪽 윙으로 출전시 가장 뛰어난 활약을 보여준다.

골키퍼

  • EPL 정상급 골키퍼인 위고 요리스(30세, 프랑스)가 주전 골키퍼이다. 토트넘과 프랑스 대표팀에서 주장을 맡고 있다.

센터백

  • 센터백은 얀 베르통언(30세, 벨기에), 다빈손 산체스(20세, 콜롬비아), 토비 알데르베이럴트(28세, 벨기에)가 주전이다.
  • 교체 멤버는 에릭 다이어(23세, 잉글랜드)가 있다.

측면 미드필더

  • 왼쪽 측면 미드필더는 대니 로즈(26세, 잉글랜드)가 주전이지만 부상으로 시즌 아웃되면서 교체 멤버인 벤 데이비스(24세, 웨일스)가 주전 역할을 하고 있다. 공격적인 전개가 필요할 경우 손흥민(24세, 대한민국)이 투입된다.
  • 오른쪽 측면 미드필더는 EPL 정상급 라이트백인 카일 워커(27세, 잉글랜드)가 시즌 시작 전 맨체스터 시티로 이적하면서 교체 멤버였던 트리피어(27세, 잉글랜드)가 주전 역할을 하고 있다. 교체 멤버로는 카일 워커의 대안으로 영입한 세르주 오리에(24세, 코트디부아르), 에릭 다이어가 있다.

중앙 미드필더

  • 중앙 미드필더는 무사 뎀벨레(30세, 벨기에)가 주전이다. 상대의 압박으로부터 공을 지켜내고 2선에 전달하는 능력이 EPL 정상급이다. 교체 멤버로는 무사 시소코(28세, 프랑스)가 있다.
  • 수비형 미드필더빅터 완야마(26세, 케냐)가 주전이다. 별명이 짐승일 정도로 뛰어난 신체조건으로 제공권을 장악하는 능력이 탁월하다. 교체 멤버로는 에릭 다이어가 있다.

공격형 미드필더

  • 공격형 미드필더는 델레 알리(21세, 잉글랜드), 크리스티안 에릭센(25세, 덴마크)이 굳건한 주전으로 토트넘 공격의 핵심이다. 알리-케인-에릭센 조합은 EPL 정상급 파괴력을 보여준다.
  • 교체 멤버는 손흥민이 있다.

최전방 공격수

  • 최전방 공격수는 EPL 2연속 득점왕인 대체불가 자원 해리 케인(24세, 잉글랜드)이 버티고 있다.
  • 교체 멤버는 손흥민페르난도 요렌테(32세, 스페인)가 있다.

참고 글


저작자 표시 비영리 동일 조건 변경 허락
신고

Spring Boot, Jackson, @JsonView로 멀티 뷰 구성하기

@JsonView 정의

public class View {

    public static class Consumer {
    }

    public static class Repository {
    }
}
  • @JsonView의 장점은 동일한 POJO 오브젝트에 대해서 선택적으로 서로 다른 프라퍼티가 조합된 JSON 문자열을 만들 수 있다는데 있다. 예를 들어 user라는 POJO 오브젝트가 있다고 가정할 경우 password_encrypted 프라퍼티는 API 사용자에게 노출되지 않아야 하지만 저장소에는 반드시 저장되어야할 데이터이다. 이런 서로 다른 상황을 위해 선택적으로 뷰를 적용할 수 있는 것이 바로 @JsonView이다.
  • 위는 API 클라이언트에게 응답할 목적의 Consumer라는 뷰와, 저장소에 저장할 목적의 Repository라는 뷰를 정의하였다.

POJO에 @JsonView 명시

@Data
public class User {

    private Long id;

    @JsonProperty("user_id")
    @JsonView({View.Consumer.class, View.Repository.class})
    private String userId;

    @JsonProperty("passwordEncrypted")
    @JsonView({View.Repository.class})
    private String passwordEncrypted;
}
  • 앞서 설명한대로 @JsonView를 목적에 맞게 명시하였다.

@JsonView 적용: Spring Boot

@RestController
public class UserController {

    @RequestMapping(value = "/users/{userId}", method = RequestMethod.GET)
    @JsonView(View.Consumer.class)
    public ResponseEntity<?> getUser(@PathVariable("userId") String userId) {
        ...
        return new ResponseEntity<>(user, HttpStatus.OK);
    }
}
  • Spring Boot에서는 컨트롤러의 메써드 레벨에 적용할 @JsonView를 명시하면 된다. 위 예제의 경우 응답 JSON 문자열에 user_id만 노출된다.

@JsonView 적용: ObjectMapper

String user = new ObjectMapper()
        .disable(MapperFeature.DEFAULT_VIEW_INCLUSION)
        .writerWithView(View.Repository.class)
        .writeValueAsString(user);
  • MapperFeature.DEFAULT_VIEW_INCLUSION 옵션을 활성화하면 @JsonView가 명시되지 않은 POJO의 프라퍼티들도 JSON 문자열에 포함된다. 기본값은 활성화되어 있으므로 이를 원치 않을 경우 비활성화 해주어야 한다.

  • writerWithView()로 적용할 @JsonView를 명시하면 Jackson은 해당 @JsonView가 명시된 프라퍼티만 JSON 문자열에 포함한다.

참고 글

저작자 표시 비영리 동일 조건 변경 허락
신고

CentOS 6, Zulu(OpenJDK 8) 설치하기

개요

  • 일반적으로 레드햇 계열의 운영체제에서 OpenJDK를 설치한다고 하면 기본 패키지로 제공되는 java-1.8.0-openjdk-devel를 설치하는데 그친다. 이 패키지는 OpenJDK 레퍼런스 소스 코드를 Red Hat이 자사의 운영체제인 RHEL에 맞게 빌드하여 오라클로부터 TCK 인증 후 배포하는 것이다. [관련 링크]

  • 한편, Zulu는 엄격한 TCK 테스트의 전체 집합을 완전히 통과한 OpenJDK 기반의 재배포 가능한 무료 바이너리이다. [관련 링크] Zulu는 고성능의 상업 JVMZing을 판매하는 Azul Systems에서 배포하여 신뢰할 수 있다.

주의사항

  • OpenJDK의 설치는 root 계정으로 진행해야 한다.
  • Java 애플리케이션의 컴파일 및 실행은 사용자 계정으로 진행해야 한다. [관련 문서]

설치

### Zulu 저장소 등록, 최초 1번만 실행
$ sudo rpm --import http://repos.azulsystems.com/RPM-GPG-KEY-azulsystems
$ sudo curl -o /etc/yum.repos.d/zulu.repo http://repos.azulsystems.com/rhel/zulu.repo

### Zulu RPM 패키지 설치
$ sudo yum -y install zulu-8

### 설치된 Zulu 실행 확인
$ java -version
openjdk version "1.8.0_144"
OpenJDK Runtime Environment (Zulu 8.23.0.3-linux64) (build 1.8.0_144-b01)
OpenJDK 64-Bit Server VM (Zulu 8.23.0.3-linux64) (build 25.144-b01, mixed mode)

### 모든 사용자에게 적용되는 환경변수 적용
$ nano /etc/environment
export JAVA_HOME=/usr/lib/jvm/zulu-8
export JRE_HOME=/usr/lib/jvm/zulu-8/jre

n개 이상의 OpenJDK 설치시 Julu 빌드 적용

$ which java
/usr/bin/java

$ readlink /usr/bin/java
/etc/alternatives/java

$ readlink /etc/alternatives/java
/usr/lib/jvm/zulu-8/bin/java

$ alternatives --config java
There are 2 programs which provide 'java'.

  Selection    Command
-----------------------------------------------
*+ 1           /usr/lib/jvm/zulu-8/bin/java
   2           java-1.8.0-openjdk.x86_64 (/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.144-0.b01.el7_4.x86_64/jre/bin/java)

Enter to keep the current selection[+], or type selection number: 1

참고 글

저작자 표시 비영리 동일 조건 변경 허락
신고

'SW 개발 > CentOS' 카테고리의 다른 글

CentOS 6, Zulu(OpenJDK 8) 설치하기  (0) 2017.09.26

Windows, Zulu(OpenJDK 8) 설치하기

개요

  • Zulu는 미국의 JVM 제작 전문 벤더인 Azul Systems가 무료로 제공하는 OpenJDK 기반의 JDK이다. 대개 운영 환경은 OpenJDK로 운영하고 개발 환경은 Oracle JDK를 설치하는 것이 일반적인데 Zulu를 설치하면 개발 환경과 운영 환경을 통일시킬 수 있다는 장점이 있다.

  • ZuluTCK 인증되었으므로 OpenJDK가 제공하는 것과 기능이 완전히 동일하다.

  • OpenJDKOracle JDK와 관련된 자세한 내용은 이 글을 참고한다.

Zulu 다운로드 및 설치

  • 여기를 클릭하여 Julu를 설치한다. Java Version 8에 해당하는 .MSI 파일을 다운로드하고 설치한다.

환경 변수 추가

  • 시스템 환경 변수 편집 → 환경 변수 → 시스템 변수에 아래 항목을 추가한다. (경로는 설치한 버전에 따라 다르다. 본 글에서는 1.8.0_144 버전을 기준으로 설명한다.)
# 최상단 우선순위로 추가
Path="C:\Program Files\Zulu\zulu-8\bin\"

# 새로 생성
JAVA_HOME="C:\Program Files\Zulu\zulu-8\"

# 새로 생성
JRE_HOME="C:\Program Files\Zulu\zulu-8\jre\"

실행 확인

  • 아래와 같이 정상적으로 실행되는 것을 확인할 수 있다.
$ javac -version
javac 1.8.0_144

$ java -version
openjdk version "1.8.0_144"
OpenJDK Runtime Environment (Zulu 8.23.0.3-win64) (build 1.8.0_144-b01)
OpenJDK 64-Bit Server VM (Zulu 8.23.0.3-win64) (build 25.144-b01, mixed mode)

IDE 설치

  • JDK가 설치되었으니 IDE 설치가 가능하다. IntelliJ IDEA를 권장한다. 이 글을 참고하여 설치한다.
저작자 표시 비영리 동일 조건 변경 허락
신고

Java 무료 논쟁, Oracle JDK와 OpenJDK의 차이 정리

Oracle JDK와 OpenJDK

  • Java 애플리케이션을 실행하기 위해서는 JVM이 필요하고 컴파일하기 위해서는 JDK가 필요하다. 일반적으로 JDK를 설치하면 JVM(Hotspot이라고도 표현, Java 기술의 핵심)도 함께 설치된다.


  • JDK는 2개 버전으로 나뉜다. 하나는 폐쇄적인 상업 코드 기반의 Oracle JDK이고 하나는 오픈 소스 기반의 OpenJDK이다.


  • 둘 간의 큰 차이라면 Oracle JDKOpenJDK에는 없는 재산권이 걸린 플러그인을 제공한다. 해당 플러그인은 Oracle이 재산권을 보유하고 있다. (보다 정확히 설명하면 Oracle이 인수하여 없어진 Sun Microsystems 시절의 유산이다.) [관련 링크]


  • Oracle JDK에 존재하고 OpenJDK에는 없는 대표적 기능으로 글꼴 라이브러리와 Java Web Start가 있다. 사용자의 웹 브라우저에서 자바 애플릿을 실행하려면 필요한 기능이다. 서버 애플리케이션 개발에는 쓰이지 않는 기능들이다.


  • 제공되는 기능적 차이 외에 Java 6 시대에는 분명히 OpenJDKOracle JDK보다 성능이나 안정성이 크게 떨어졌다. 하지만 오늘날에는 Oracle JDK만이 제공하는 일부 라이브러리를 제외하고는 차이가 없다. [관련 링크]

Oracle JDK는 무료인가?

  • Java의 무료 논쟁은 2011년 오라클이 소유권을 가지면서 시작되었다. 일단 명확히 해야할 점은 Java라는 프로그래밍 언어 자체는 GPL 라이센스로 무료이다. [관련 링크] 그렇지 않다면 오늘날 Java가 시장지배적인 언어가 되지 못했을 것이다. 중요한 부분은 Java로 작성한 프로그램을 컴파일(JDK)하고 실행(JVM)시키는 데 있다.


  • JDKJVM을 제공하는 OpenJDKGPL v2 with the Classpath Exception 라이센스로 무료이다.


  • 문제는 Oracle JDK이다. 결론부터 이야기하면 Oracle JDK는 사용 목적에 따라 무료일수도 유료일수도 있다. 먼저 Oracle JDK(Java SE)의 구성 요소를 살펴보면 아래와 같다.

1. JDK
2. JRE(JVM)
3. JavaFX SDK
4. JavaFX Runtime
5. JRockit JDK
  • Oracle JDKBCL(Oracle Binary Code License) 라이센스로 위 기능을 일반적인 목적의 컴퓨팅(General Purpose Computing)에만 사용시 무료라고 밝히고 있다. [관련 링크] 일반적인 목적의 컴퓨팅이란 무엇일까? 오라클은 데스크탑, 노트북, 스마트폰, 태플릿 등에서의 사용이 이에 해당한다고 설명하고 있다. 즉, 개발환경에서는 사용에 있어 아무 문제가 없다.


  • 일반적이지 않은 목적의 컴퓨팅(Non-General Purpose Computing)은 별도의 라이센스비를 지불해야 하는 유료 과금 대상이 된다. 오라클은 특정 기능 만을 위해 작동하는 시스템 및 솔루션에서의 사용이 이에 해당한다고 설명하고 있다. 이 부분의 애매한 표현 때문에 대부분의 상업 프로젝트를 진행하는 회사들은 OpenJDK를 사용한다.

OpenJDK는 운영 환경에 부적합한가?

  • 결론부터 이야기하면 TCK 인증을 받은 OpenJDK 기반의 빌드 버전을 사용하면 운영 환경에 아무런 문제가 없다.

  • Java SE 7부터 모든 JDKOpenJDK 레퍼런스 소스 코드를 기반으로 제작된다. (Jave SE 6 버전의 OpenJDK는 백포트 방식으로 구현되어 성능 저하 이슈가 존재한다.) OpenJDK는 자바 스펙을 명시한 JSR 336, JSR 337를 빠짐없이 완전히 구현한 구현체이다.

  • OpenJDK의 운영 주체는 오라클이다. 오라클 또한 OpenJDK를 기반으로 자사의 부가적인 기능을 추가한 Oracle JDK를 제작하여 배포한다.

  • 오라클이 아닌 서드파티 업체가 OpenJDK를 기반으로 공인된 JDK를 제작하여 배포하려면 오라클의 유료 라이센스인 OCTLA에 가입해야 한다. 현재 전세계에 19개 업체가 가입되어 있다. [관련 링크] 이 업체들이 OpenJDK 기반의 자체 빌드를 배포하려면 오라클의 엄격한 TCK 인증을 통과해야 한다. 수만가지의 테스트를 단 하나라도 통과하지 못하면 인증되지 않는다.

  • OpenJDK를 멀티 플랫폼 바이너리로 빌드하여 배포하는 대표적인 업체로는 Azul Systems가 있다. 개발 환경과 운영 환경 모두 Oracle JDK의 대안으로 좋은 평가를 받고 있다. [관련 링크]

OpenJDK 구현체

  • OpenJDK의 명세는 JCP(오라클, IBM, SAP 등으로 구성)에 의해서 결정된다. OpenJDK는 결정된 스펙(Java SE 7: JSR 336, Java SE 8: JSR 337)을 구현한 레퍼런스 소스 코드이다. 많은 상업 JVM 벤더들이 이 OpenJDK 레퍼런스 소스 코드를 기반으로 각자의 JVM을 제작한다. 엄격한 테스트의 TCK 인증을 통과하면 OpenJDK를 완벽히 준수하는 구현체로 인정된다. Oracle JDK 또한 Java SE 7부터 이 OpenJDK를 기반으로 앞서 설명한 추가 요소를 담아 배포한다. [관련 링크]


  • 트위터는 OpenJDK를 자사에 맞게 튜닝한 Twitter-JDK 위에서 자사의 서비스를 운영한다. [관련 링크]

  • Azul Systems(미국 소재의 Java Runtime 제작 전문 회사)는 Zulu라는 OpenJDK 기반 빌드에 부가 기능을 추가한 Windows, Linux, Mac OS X 바이너리를 무료로 제공한다. 이 회사는 서버 부하에 최적화된 Zing이라는 JVM을 판매한다. 엔터프라이즈 시장에서 Oracle JDK의 대안으로는 현재 독보적 위치에 있다. 테스트에서 Oracle JDK보다 성능이 좋았다는 사례도 있다. [관련 링크] 실제로 고성능을 요구하는 Cassandra 노드들은 Zing 기반으로 운영된다. [관련 링크]

참고 글

저작자 표시 비영리 동일 조건 변경 허락
신고

MySQL/MariaDB, MHA로 레플리케이션 HA 환경 구성하기

레플리케이션

  • 하나의 MySQL/MariaDB 서버가 존재한다고 생각해보자. 만약 이 서버의 동기화된 복제본이 n개 추가되고 애플리케이션으로 부터의 모든 읽기(Read) 요청을 이 복제본이 대신 받는다고 생각해보자. 읽기에 대한 부담이 여러 대의 복제본으로 분산되면서 원본 서버의 쓰기 성능까지 덩달아 안정적으로 작동하게 될 것이다. 또한, 예기치 못한 장애로 원본 서버가 유실되어도 복제본에 의해 데이터는 보존된다. 이러한 데이터베이스 구성 방법을 마스터-슬레이브 레플리케이션(Master-Slave Replication)이라고 부른다.

MHA

  • 앞서 언급한 Single Master, Multi Slave 구성을 통해 부하 분산 효과와 데이터의 유실 방지는 가능해졌지만 애플리케이션 입장에서는 마스터 서버에 장애가 발생할 경우 수동으로 작업을 복구하기 까지 서비스 중단이 불가피하다. 이런 상황에 대비하여 최소의 다운타임(10~30초)으로 자동 Failover 기능을 제공하는 것이 MHA(MySQL Master High Availability)이다.
  • MHA는 데이터베이스 엔지니어인 Yoshinori Matsunobu(현재 페이스북 근무)가 2011년 개발하여 공개한 오픈 소스 툴의 집합이다.
  • MHA의 장점은 기존 구조의 변형(운영 환경 상의 설정 변경 등) 없이 각 노드에 MHA 제공 툴만 설치하면 최소한의 비용으로 자동 Failover가 가능해진다는 것에 있다. 각 Master, Slave 노드에는 MHA Node라는 툴을 설치하면 된다. 추가적으로 별도의 서버에 이들 노드를 모니터링하는 MHA Manger를 설치해야 하는데 Slave 노드 중 하나에 설치해도 무방하다.
  • 마스터 노드가 다운되면 가장 최신의 복제 내용을 가진 슬레이브 노드가 자동으로 선택되어 마스터 노드로 승격되고 나머지 슬레이브 노드는 새로 승격된 마스터 노드를 바라보게 된다. 여기까지 걸리는 시간은 10~30초이며 실제로는 훨씬 빠르게 적용된다.

참고 글

저작자 표시 비영리 동일 조건 변경 허락
신고

PES 2018 (위닝일레븐 2018) 스팀 출시 및 할인 구매하기, 통합 패치

PES 2018 출시!

  • 2017-09-14 기다리고 기다리던 PES 2018(위닝일레븐 2018) PC 버전이 드디어 스팀에 출시되었다. 초기 판매가는 62,000원으로 선뜻 구매하기가 망설여진다. 하지만 우리에게는 스팀 할인코드를 판매하는 cdkeys.com이 있다. 해외 결제가 가능한 비자 또는 마스터카드를 지원하는 신용카드만 있으면 누구나 할인된 가격에 구매가 가능하다. 나는 할인코드를 통해 정가의 절반 가격인 $27.73에 구매하였다.



PES 2018 스팀 할인코드로 구매하기

  • https://www.cdkeys.com에 가입한다. 이름(익명도 가능)과 이메일 주소를 입력하면 가입이 가능하다. 로그인된 상태에서 PES 2018에 구매(Buy) 버튼을 클릭하고 할인 코드(Discount Code)을 입력하는 페이지에서 대기한다.

  • 페이스북에서 PES 2018 전용 5% 할인 코드를 받는다. cdkeys.com 계정 등록 후 최초 구매자만 할인 코드를 받을 수 있다. 이메일 주소로 도착한 할인 코드를 재빨리 앞서 대기 중인 페이지에 입력하고 구매를 진행한다. 내 경우 최초 구매가 $29.19에서 $27.73로 할인되었다. Proceed to Checkout 버튼을 클릭하여 신용카드 결제 페이지로 이동한다.

  • 신용카드 결제시 영문 집 주소와 우편번호를 요구한다. 영문 주소는 http://www.juso.go.kr에서 쉽게 조회할 수 있다.

  • 결제가 완료되면 이메일로 다운로드 링크가 도착한다. 링크로 이동하여 전화번호를 인증하여 문자로 도착한 인증번호 4자리를 입력하면 드디어 스팀 할인코드를 받을 수 있다.

  • 이제 할인코드를 이용하여 스팀에서 게임을 영구 구매할 차례이다. 스팀 클라이언트 실행 후 게임Steam에 제품 등록 메뉴를 실행하여 할인코드를 입력한다. 영구적으로 내 계정에 등록되면서 게임 설치가 시작된다.




그래픽 설정

  • 스팀에서 게임이 설치된 직후 첫 게임시 나타나는 그래픽에 실망할 수도 있다. 기본 그래픽 설정이 낮게 되어 있기 때문인데 메인 메뉴의 시스템 설정에서 변경할 수 있다.


  • 화면 밝기 설정이다. 기본값은 살짝 어둡게 되어 있는데 각자의 모니터에 적합하게 설정한다. 값이 너무 낮아도 너무 높아도 문제가 된다.


  • 그래픽 품질 설정이다. 화질 전환을 가장 좋은 High으로 변경한다. 내 경우 과한 모션 블러가 부담스러워 모션 블러 옵션만 꺼두었다.

컨트롤러 설정

  • 컨트롤러 설정은 게임 플레이에 상당한 영향을 끼친다. 플레이어의 성향에 따라 편차가 크므로 자신에게 적합한 설정을 해야 한다. 내 경우 기본 설정에서 커서 전환: 세미어시스트 → 어시스트, 패스 지원 레벨: 레벨 1 → 레벨 3으로만 변경하여 플레이하는데 만족스럽다. PES 2018은 싱글 플레이시 역대 최악의 한박자 느린 커서 변환이 관찰되기 때문에 직접 조작하는 것을 포기했다.

게임 스크린샷

  • 아래는 스팀 구매 직후 캡쳐한 인게임 스크린샷이다.


  • 왼쪽부터 시계 방향으로 메시, 네이마르, 디에고 코스타, 해리 케인이다. 실사가 적용된 선수들의 얼굴은 실사와 구분이 안될 정도로 사실적이다.



  • 손흥민 역시 실명으로 등록되어 있다. 다만 토트넘 핫스퍼의 팀 라이센스가 없어 추리닝 차림이다. 군대스리가 진출인가...



  • 팀 라이센스가 있는 파리 생제르망의 카바니의 모습이다.



  • PS4와 동일한 폭스 엔진이 사용된 PES 2018PC 버전은 전작과 다르게 유독 모션 블러가 두드러진다.



  • 인게임 모션은 더욱 사실적으로 발전하였다. 패스의 곡선 또한 전작보다 훨씬 사실적으로 변하였다.


옵션 파일 패치

  • PES 2018는 소수의 팀과 선수를 제외하고는 라이센스가 미적용된 반쪽 짜리 게임이다. 시리즈의 전통(?)답게 패치는 유저의 몫이다.
  • 우리에게는 완성도 높은 통합 패치를 제공하는 PTE Patch가 있지만 정품이 출시된지 얼마되지 않아 한참을 기다려야 한다. 그동안 즐길 수 있는 패치로는 옵션 파일 패치가 있다. 안그래도 전세계 다양한 커뮤니티에서 옵션 파일을 배포하고 있다.
  • 현재 배포 중인 옵션 파일 패치 중에서는 레딧WEPES에서 제공하는 Reddit Community Mega Pack 2018을 추천한다.
  • 옵션 파일 패치 다운로드 및 압축 해제 후 save 폴더를 %USERPROFILE%\Documents\KONAMI\PRO EVOLUTION SOCCER 2018에 복사하면 패치가 적용되어 라이센스된 팀과 선수로 플레이를 즐길 수 있다.



















게임성 패치

  • 옵션 패치나 통합 패치와 별개로 PES 2018의 공의 속도와 물리성 등 게임성을 조절하는 게임성 패치(Gameplay Patch)가 존재한다. 오리지널과 상당히 다른 느낌을 주므로 기본적으로 추천하지 않는다. 최고 난이도로도 너무 쉬워 게임이 질린다거나 또는 자신의 스타일과 너무 맞지 않이 재미를 못 느끼는 사용자에게 마지막 코스로 게임성 패치를 추천한다.
  • 해외 유저인 kilay가 제작한 게임성 패치를 추천한다. 경기의 템포가 오리지널보다 느려지고 묵직한 플레이가 가능해진다.
  • 설치 방법은 압축 해제 후 dt18_x64.cpk 파일을 C:\Program Files (x86)\Steam\steamapps\common\PRO EVOLUTION SOCCER 2018\Data 폴더에 복사한다. 원복을 대비하여 복사 전 반드시 원본 파일을 백업해둔다.

통합패치 PTE Patch 2018 설치

  • PES 2018이 출시된지 3주째인 2017-10-08 드디어 PTE Patch 2018 통합패치가 릴리즈되었다. PTE Patch 2018은 과도한 변경을 지양하고 라이센스 문제 만을 패치하여 원본의 게임 밸런스를 최대한 유지하는 일명 바닐라 패치로 유명하다. 6.0, 6.1 순서로 패치를 다운로드 후 차례로 설치하면 된다. 6.0은 여기를, 6.1은 여기를 클릭하여 안내 글을 숙지하고 설치 파일을 다운로드한다.
  • 다운로드한 PTE Patch 2018 압축 해제 후 Setup.exe를 실행한다.
    PTE Patch 2018의 설치 경로로 C:\Program Files (x86)\Steam\steamapps\common\PRO EVOLUTION SOCCER 2018를 선택하고 설치를 진행한다. 유저 패치 설치가 완료되면 평소와 같이 게임을 실행하여 즐기면 된다.

게임 소감

  • PES 2018은 역대 최고의 실축스러운 위닝 일레븐으로 기록될 것이다! 역대 최고작인 PES 6 이후 10년 가까이 목각인형이 스케이팅하는 듯한 느낌에 많은 올드팬들이 피파로 떠났었다. PES 2017에서 살짝 가능성이 보이더니 이번 작은 코나미가 제대로 벼르고 출시한 느낌이다.
  • 컴퓨터와 대전할 때 수비에서 한박자 늦은 커서 전환으로 컴퓨터에게 억울한 골을 허용하는 사례가 많아 코나미가 많은 비판을 받고 있다. 연습 만이 살 길이다. 자유 트레이닝에서 최고 난이도로 설정하고 집중적으로 수비 연습을 해서 감을 잡을 필요가 있다. 가장 중요한 점은 화면 하단 레이더에 집중해야 한다. 커서 전환시 무작정 컴퓨터에게 접근하면 공간을 내주어 바로 스루패스를 허용하게 된다. 방향키로 레이더에 보이는 포위망을 자연스럽게 유지하면서 압박해야 한다. 공중볼에서도 방향키를 이용하여 최대한 컴퓨터와 몸싸움을 붙어야 한다. 이번작 수비의 핵심은 레이더와 오프 더 볼 상태에서의 방향키 조작이라고 생각된다.
  • 공격 또한 외곽 크로스 남발을 유발하던 단조로운 패턴의 전작과 달리 2:1 패스를 중심으로 확실한 상황에서 전방 로빙 패스를 적절히 사용해야 한다. 게임으로 표현하기 힘들던 이스코, 아자르 같은 테크니션들의 활약이 가능해졌다!
  • PES 6부터 올드팬들이 인정하는 경기 당 플레이 시간은 15분이 적절하다. 짧지도 길지도 않은 적절한 시간 동안 긴장감 있는 플레이를 즐길 수 있다.
  • 유저 인터페이스는 전작부터 느끼지만 정말 답답하고 최악이다. 게임을 종료하기 위해 EXTRA 메뉴로 이동하여 EXIT 버튼을 눌러야 하는 것은 코미디에 가깝다. 항상 ALT + F4로 게임을 종료하게 된다.


저작자 표시 비영리 동일 조건 변경 허락
신고

주요 API 플랫폼 서비스 정리

개요

  • 한국에는 드물지만 미국을 비롯한 서구권에는 API를 전문 비즈니스 영역으로 하는 IT 회사들이 많다. 이 회사들의 주요 서비스는 API Proxy(API Gateway)이다. 굳이 돈 주고 써야 하나 싶지만 장점이 한 두개가 아니다. 외부 고객에게 제공되어야 할 사내 API가 각각 분산되어 서로 다른 프로토콜과 형식으로 존재한다고 가정해보자. 진시황이 중국을 통일하자 마자 가장 먼저 도량형을 통일했듯이 클라이언트에게 일관되고 예측 가능한 API를 제공하는 것은 굉장히 중요하다. API Proxy 서비스를 이용하면 이러한 분산되고 불규칙한 서비스를 한데 모아 일관된 규칙의 REST API를 제공할 수 있다.(물론 메타 데이터 맵핑 작업이 선행되어야 한다. 이를 트랜스포메이션이라고 부른다.) 추가적으로 보안(OAuth 2.0), 확장(스케일링), 분석(모니터링) 기능까지 제공하니 의외로 장점이 많다. 이번 글에서는 북미를 중심으로 한 주요 API 플랫폼 서비스를 소개하고자 한다.

Apigee Edge

  • Apigee EdgeApigee(미국 소재, 2016년 Google이 인수)가 제공하는 API Proxy 서비스이다.
  • 고유한 Organization 식별자로 가입하게 되면 클라이언트에게 https://{organization}.apigee.net/ 베이스 주소로 REST API 서비스를 제공할 수 있다.
  • 클라이언트에게 제공되는 엔드포인트와 다르게 API Proxy 관리 기능은 https://api.enterprise.apigee.com/v1/organizations/{organization}을 베이스 주소로 제공된다.
  • 보안 측면에서 OAuth 2.0 Server 기능을 제공한다. 클라이언트에게 노출될 엔드포인트와 관리 목적의 엔드포인트로 이원화하여 제공한다.
  • 한편, ApigeeREST API 개발자를 위한 유용한 가이드를 무료 제공하고 있다. Web API Design 정독을 추천한다.

Axway API Gateway

  • Axway API GatewayAxway(프랑스 소재)가 제공하는 유료 API Gateway(API Gateway는 의미적으로 API Proxy를 포괄하는 더 큰 개념) 솔루션이다.
  • 보안 기능으로 OAuth 2.0 서버 및 클라이언트 기능을 제공한다.

Kong

  • 앞서 소개한 솔루션은 모두 유료로만 판매되는 상품이다. 반면에 Kong은 오픈 소스 API Gateway이다. 다양한 유무료 플러그인을 적절히 조합하여 활용하면 입맛에 맞는 완성도 높은 API Gateway를 제작할 수 있다.
  • Reverse Proxy 역할을 하는 PushpinKong의 앞에 두면 WebSocket과 같이 긴 연결을 가진 프로토콜을 중재하여 Kong 뒤에 위치한 HTTP만 지원하는 백엔드 API가 양방향으로 통신할 수 있다.
  • 제공되는 플러그인을 이용하여 OAuth 2.0 서버 기능을 제공할 수 있다.
  • 제공되는 플러그인을 이용하여 Loggly, Datadog와 같은 고품질의 유료 로깅 솔루션에 API 로그를 적재하고 리얼타임 대시보드를 제작할 수 있다.

Okta

  • Okta는 보안에 특화된 API 인증 플랫폼이다. 고객의 API에 앞서 OAuth 2.0 Authorization ServerAuthentication Server 기능을 제공한다.

참고 글

저작자 표시 비영리 동일 조건 변경 허락
신고

Spring Boot, Graylog로 GELF UDP 로그 전송하기

개요

  • Graylog는 분산 애플리케이션에 대한 중앙 로그 수집 및 조회 기능을 제공하는 통합 로깅 플랫폼이다. Key-Value 기반의 독자적인 GELF 로그 포맷이 지원하며 최근 릴리즈된 v2.3.0에서는 최신 버전의 ElasticSearch 5.x를 지원한다. [관련 링크] 이번 글에서는 Spring Boot 기반의 애플리케이션에서 GELF UDP 로그를 전송하는 방법을 설명하고자 한다.

앞서 읽어볼만한 글

목표

  • Spring Boot 기반으로 구조화된 GELF UDP 로그를 전송할 수 있다.

구조화된 로그가 필요한 이유

  • AI의 등장과 함께 이제 애플리케이션 로그는 사람이 보는 정보가 아니라 기계가 분석하는 정보가 되었다. 기계가 로그를 분석하려면 기존의 전통적인 라인 단위의 텍스트 로그로는 부족하다. 잘 설계된 구조화된 로그를 전송하면 기계에 의해 분석되어 유의미한 데이터로 가공될 수 있다. 구조화된 GELF 로그 포맷은 그러한 목적으로 탄생하였다. [관련 링크]

라이브러리 종속성 추가

  • 프로젝트 루트의 build.gradle에 아래 내용을 추가한다.
dependencies {
    compile group: 'biz.paluch.logging', name: 'logstash-gelf', version: '1.11.1'
}
  • logstash-gelf 아티팩트는 다양한 로깅 라이브러리에서 GELF(TCP/UDP) 로깅을 가능하게 해준다. 본 예제는 Spring Boot 기반이므로 기본 로깅 라이브러리인 Logback을 기준으로 설명한다.

로깅 작성 예

@Slf4j(topic = "GELF")
public class SomeClass {

    public someMethod() {

        log.info("SOME_METHOD_FINISHED");
    }
}
  • GELF 로깅을 작성할 클래스에서는 @Slf4j(topic = {LoggerName})과 같이 클래스 레벨에 로거의 이름을 명시해주어야 한다. 위 예제에서는 일반 로거와의 쉬운 구분을 위해 로거의 이름으로 GELF를 명시해주었는데 명명은 자유롭게 해도 무방하다. (일반적으로 로거의 이름은 클래스를 포함한 패키지명으로 명명하는 것이 관행이다.)

  • log.info({message})가 실행되는 즉시 GELF 형식의 로그가 Graylog 서버로 전송된다. 아규먼트로 전달된 message 값은 GELF 포맷의 short_message, full_message 필드에 담겨 전송된다. 나머지 필수 필드는 logstash-gelf 라이브러리에 의해 자동 생성되어 전송된다. 실제 전송되는 필드 정보는 아래와 같다.

{
    "LoggerName": "GELF",
    "Severity": "INFO",
    "SourceClassName": "com.jsonobject.example.SomeClass",
    "SourceLineNumber": 6,
    "SourceMethodName": "someMethod",
    "SourceSimpleClassName": "SomeClass",
    "Thread": "main",
    "Time": "2017-09-06 17:44:33,0919"
    "facility": "logstash-gelf",
    "message": "SOME_METHOD_FINISHED",
    "full_message": "SOME_METHOD_FINISHED",
    "level": 6,
    "source": "jsonobejct",
    "timestamp": "2017-09-06T08:44:33.919Z"
}
  • GELF 메시지의 원본은 위와 같은 JSON 문자열이다. 이 문자열은 어펜더에 의해 전송 전 GZIP으로 압축되는데 UDP의 경우 압축된 총 크기가 8,192바이트를 초과할 경우 분할 전송된다. 이 경우 문제는 메시지 인풋이 실행 중인 Graylog 서버가 로드 밸런싱되고 있을 경우 분할 전송된 패킷 조차 로드 밸런싱되어 메시지가 유실된다. 따라서 압축된 로그 메시지의 크기가 8,192바이트를 초과하지 않도록 설계하는 것이 바람직하다. 애플리케이션에서 발생하는 대부분의 로그는 이 크기 안에서 소화가 가능하다.

커스텀 필드 전송

  • GELF는 필드명의 앞에 _가 붙는 커스텀 필드(additional field)를 지원한다. 아래와 같이 커스텀 필드를 추가할 수 있다.
MDC.put("some_field", "some_value");
MDC.put("another_field", "another_value");
log.info("SOME_METHOD_FINISHED");
MDC.clear();
  • 위 방법은 Slf4j가 제공하는 MDC를 이용한 것이다. MDC는 쓰레드 단위의 생명주기를 가지며 원하는 추가 필드를 담을 수 있다. 번거롭지만 쓰레드 풀 환경에서 엉뚱한 커스텀 필드가 남겨지는 것을 예방하기 위해 반드시 MDC.clear()로 로깅을 마무리해야 한다.
  • 이 부분을 조금이라도 효율적으로 보완하자면 아래와 같은 유틸리티 클래스를 만드는 방법도 있다.
package com.jsonobject.example.util;

import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;

import java.util.ArrayList;
import java.util.List;

@NoArgsConstructor
public class GelfCustomFields {

    private List<String> fieldNames;

    public GelfCustomFields add(String fieldName, String fieldValue) {

        if (this.fieldNames == null) {
            this.fieldNames = new ArrayList<>();
        }
        if (!StringUtils.isEmpty(MDC.get(fieldName))) {
            return this;
        }
        if (StringUtils.isEmpty(fieldValue)) {
            return this;
        }
        this.fieldNames.add(fieldName);
        MDC.put(fieldName, fieldValue);

        return this;
    }

    public void clear() {

        if (this.fieldNames != null) {
            for (String fieldName : this.fieldNames) {
                MDC.remove(fieldName);
            }
        }
    }
}
  • 사용 예는 아래와 같다.
{
    GelfCustomFields fields = new GelfCustomFields()
             .add("some_filed", "some_value")
             .add("another_field", "another_value");

    log.info("SOME_METHOD_FINISHED");
    fields.clear();
}

Logback 환경설정

  • 프로젝트 루트의 /src/main/resources/logback-spring.xml 파일을 아래와 같이 작성한다.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>

<configuration>

    <appender name="GELF_UDP" class="biz.paluch.logging.gelf.logback.GelfLogbackAppender">
        <host>udp:{hostname}</host>
        <port>{port}</port>
        <version>1.1</version>
        <extractStackTrace>true</extractStackTrace>
        <filterStackTrace>true</filterStackTrace>
        <mdcProfiling>true</mdcProfiling>
        <timestampPattern>yyyy-MM-dd HH:mm:ss,SSSS</timestampPattern>
        <includeFullMdc>true</includeFullMdc>
        <maximumMessageSize>8192</maximumMessageSize>
    </appender>

    <logger name="GELF" level="INFO" additivity="false">
        <appender-ref ref="GELF_UDP"/>
    </logger>

</configuration>
  • host에는 Graylog 서버의 호스트명(도메인 또는 IP 주소)을 입력한다.
  • port에는 Graylog 서버에서 실행 중인 GELF UDP Input의 포트명을 입력한다.
  • 로거 이름이 GELF인 로그들은 모두 GELF_UDP 어펜더를 통해 전송되도록 작성했다.

참고 글


저작자 표시 비영리 동일 조건 변경 허락
신고

'SW 개발 > Spring > Logback' 카테고리의 다른 글

Spring Boot, Graylog로 GELF UDP 로그 전송하기  (0) 2017.09.06

Spring Boot, Redis 저장소에 CRUD 로직 구현하기

개요

  • Redis는 바이너리 데이터 저장에 최적화된 인메모리 Key-Value 스토어로 RDBMS를 제외한 데이터 저장소 중 가장 유명하고 널리 쓰이고 있다. 캐시 저장소로 Redis를 적절히 활용하면 API 응답 시간을 10ms 수준으로 낮출 수 있다.

목표

  • Spring Boot 기반으로 Redis 저장소에 대한 CRUD를 수행할 수 있다.
  • Java 오브젝트를 바이너리 변환된 JSONMessagePack 형식으로 Redis 저장소에 저장할 수 있다.

라이브러리 종속성 추가

프로젝트 루트의 build.gradle에 아래 내용을 추가한다.

dependencies {
    compile 'org.springframework.data:spring-data-redis'
    compile group: 'redis.clients', name: 'jedis', version: '2.9.0'
    compile group: 'org.msgpack', name: 'jackson-dataformat-msgpack', version: '0.8.13'
}
  • jackson-dataformat-msgpack 아티팩트는 MessagePack 형식을 지원하기 위한 Jackson의 확장 라이브러리이다. MessagePackJSON 문자열의 내용은 그대로 유지하면서 바이너리 형식에 최적화한 것으로 Redis와 같은 바이너리 저장소에 특화된 형식이다. 저장소의 메모리 공간을 절약할 수 있다.

@Configuration 클래스 작성

package com.jsonobject.example.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;

@Configuration
public class RedisConfig {

    @Bean("jedisConnectionFactory")
    public JedisConnectionFactory jedisConnectionFactory() {

        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setHostName({HOST_NAME});
        factory.setPort({PORT});
        factory.setPassword({PASSWORD});
        factory.setUsePool(true);

        return factory;
    }

    @Bean("stringRedisTemplate")
    public StringRedisTemplate stringRedisTemplate(@Qualifier("jedisConnectionFactory") JedisConnectionFactory jedisConnectionFactory) {

        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(jedisConnectionFactory);

        return template;
    }

    @Bean("messagePackRedisTemplate")
    public RedisTemplate<String, byte[]> messagePackRedisTemplate(@Qualifier("jedisConnectionFactory") JedisConnectionFactory jedisConnectionFactory) {

        RedisTemplate<String, byte[]> template = new RedisTemplate<>();
        template.setConnectionFactory(jedisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setEnableDefaultSerializer(false);

        return template;
    }

    @Bean("messagePackObjectMapper")
    public ObjectMapper messagePackObjectMapper() {

        return new ObjectMapper(new MessagePackFactory()).registerModule(new JavaTimeModule()).disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}
  • JedisJava 플랫폼에서 가장 신뢰할 수 있는 경량의 Redis 클라이언트 라이브러리이지만 설계 구조상 Thread Safety가 보장되지 않는다. 따라서 웹 애플리케이션 같은 멀티 쓰레드 기반의 애플리케이션에서는 커넥션 풀을 사용해야 Thread Safety를 보장 받을 수 있다.
  • Redis는 기본적으로 Key, Value를 바이트의 배열로 저장한다. StringRedisTemplate를 사용하면 Key, Value 모두 문자열로 저장할 수 있다.
  • RedisTemplateRedis 저장소에 오브젝트를 저장할 때 기본값으로 정의된 JdkSerializationRedisSerializer을 이용한다. 따라서 해당 오브젝트는 반드시 java.io.Serializable 인터페이스를 구현해야 한다. 이 방식의 문제점은 다른 언어 환경에서 Redis 저장소에 접근할 경우 값을 인식하지 못한다는 것이다. 또한 오브젝트의 클래스 메타 정보를 저장하다보니 크기 또한 커진다. 특정 언어에 종속시키지 않으려면 저장되는 값으로 JSON 문자열 또는 MessagePack 형식을 고려해야 한다. 이를 위해 messagePackObjectMappermessagePackRedisTemplate 빈을 작성했다.

@Repository 클래스 작성

  • 아래는 토큰 오브젝트를 MessagePack 형식으로 Redis 저장소에 저장하고 불러오는 예제이다.
@Repository
public class TokenDAO {

    @Autowired
    @Qualifier("messagePackRedisTemplate")
    private RedisTemplate<String, byte[]> messagePackRedisTemplate;

    @Autowired
    @Qualifier("messagePackObjectMapper")
    private ObjectMapper messagePackObjectMapper;

    public void createToken(Token token) {

        messagePackRedisTemplate.opsForValue().set("token:" + token.getToken(), messagePackObjectMapper.writeValueAsBytes(accessToken));
    }

    public Token getToken(String token) {

        return messagePackObjectMapper.readValue(messagePackRedisTemplate.opsForValue().get("token:" + token, Token.class);
    }
}
  • createToken()을 통해 앞서 정의된 messagePackObjectMapper 빈을 이용하여 Java POJO 오브젝트를 플랫폼 중립적인 순수한 MesssagePack 형식으로 저장한다. JSON 문자열 대비 2/3 절약된 크기로 오브젝트를 저장할 수 있으며 어떤 언어에서도 해당 값에 접근하고 해석할 수 있다.

  • 마찬가지로 getToken()을 통해 저장된 MessagePack 값을 오브젝트로 변환하여 불러온다.

일치하는 패턴의 Key 이름 조회

  • Redis는 초고속의 Key-Value 저장소로서 조회 및 검색 관점에서 RDBMS와 비교하면 제공하는 기능이 매우 단순하고 제한적이다.
  • 특히 특정 패턴과 일치하는 Key 이름을 조회하는 것은 성능 문제로 운영 환경에서 가장 지양해야 하지만 불가피하게 필요할 때가 있다.
  • 가장 먼저 아래는 StringRedisTemplate을 이용하여 KEYS 명령을 수행하는 예제이다. USER_ID:로 시작하는 Key 이름을 한 번에 조회한다. KEYS 명령이 실행되면 응답이 완료되기 까지 다른 요청은 모두 블록되기 때문에 운영 환경에서 절대 사용을 자제해야할 안티 패턴으로 취급된다.
// KEYS "USER_ID:*"
Set<String> keys = stringRedisTemplate.keys("USER_ID:*");
Iterator<String> it = redisKeys.iterator();
while (it.hasNext()) {
    System.out.println(it.next()); // 조회된 Key의 이름을 출력
}
  • 아래는 SCAN 명령을 수행하여 위와 동일한 결과를 출력하는 예제이다. SCAN 명령은 커서를 기반으로 부분적으로 데이터를 조회하기 때문에 KEYS보다는 운영 환경에 미치는 부담이 상대적으로 적다.
// SCAN 0 MATCH "USER_ID:*" COUNT 10
RedisConnection redisConnection = null;
try {
    redisConnection = stringRedisTemplate.getConnectionFactory().getConnection();
    ScanOptions options = ScanOptions.scanOptions().match("USER_ID:*").count(10).build();
    Cursor<byte[]> cursor = redisConnection.scan(options);
    while (cursor.hasNext()) {
        System.out.println(new String(cursor.next())); // 조회된 Key의 이름을 출력
    }
} finally {
    redisConnection.close();
}

예외 처리: Could not get a resource from the pool

[Message] Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
[Exception] org.springframework.data.redis.RedisConnectionFailureException
[Root Exception] java.net.ConnectException
  • 2가지 경우에 발생한다. 첫째, Redis 인스턴스가 어떠한 문제로 중단되어 연결이 불가능한 경우이다. 둘째, 설정된 최대 동시 접속 허용 클라이언트 수(maxclients)를 초과할 경우 발생한다. 해결책은 후자의 경우 서버와 클라이언트 모두 적절한 클라이언트 수 제한이 필요하다.
  • 서버 측인 Redis 인스턴스에서는 아래와 같이 설정할 수 있다.
$ nano /etc/redis.conf
maxclients 10000 // (기본값 10000)
  • 클라이언트에서는 아래와 같이 설정할 수 있다.
JedisPoolConfig poolConfig = new JedisPoolConfig();

// 커넥션 풀의 최대 생성 연결 값을 설정한다. (기본값은 8)
poolConfig.setMaxTotal(1000);

// 커넥션 풀이 가득 찼을 경우 준비된 연결이 도착하기를 기다린다.
poolConfig.setBlockWhenExhausted(true);

// 커넥션 풀이 가득 찼을 경우 새로운 연결을 기다리지 않고 NoSuchElementException 예외를 발생시킨다.
poolConfig.setBlockWhenExhausted(false);

JedisConnectionFactory factory = new JedisConnectionFactory();
...
factory.setUsePool(true);
factory.setPoolConfig(poolConfig);

예외 처리: OOM command not allowed when used memory > ‘maxmemory’.

[Message] JedisDataException: OOM command not allowed when used memory > 'maxmemory'.
[Exception] org.springframework.dao.InvalidDataAccessApiUsageException
[Root Exception] redis.clients.jedis.exceptions.JedisDataException
  • Redis 인스턴스에 대한 모든 쓰기 동작 실행시 발생하는 오류이다. Redis 인스턴스에 저장된 데이터 양이 설정된 메모리 한계치(maxmemory)에 도달하여 더이상 쓰기 행위가 불가능하기 때문에 발생한다.
  • 서버 측인 Redis 인스턴스에서 아래와 같이 변경할 수 있다.
$ nano /etc/redis.conf
maxmemory 1gb // 메모리 한계치, 기본값은 0으로 무제한
maxmemory-policy volatile-lru // 한계치 도달시 데이터 삭제 정책 설정, 기본값은 volatile-lru로 만기 시점을 가지는 키에 한하여 읽기 동작이 가장 오래 전에 발생한 순서로 제거
  • 해당 오류를 예방하는 가장 효과적인 조치는 maxmemory 값을 충분히 늘리는 것이다. 2차 조치는 maxmemory-policy를 수정하는 것이다. noeviction은 한계치 도달시 어떠한 행위도 하지 않기 때문에 위 오류가 발생한다. allkeys-lru는 읽기 동작이 가장 오래 전에 발생한 키를 삭제하여 저장 공간을 확보하므로 위 오류가 발생할 확률이 줄어든다. [관련 링크]

예외 처리: LOADING Redis is loading the dataset in memory

[Message] JedisDataException: LOADING Redis is loading the dataset in memory
[Exception] org.springframework.dao.InvalidDataAccessApiUsageException
[Root Exception] redis.clients.jedis.exceptions.JedisDataException
  • Redis 인스턴스 재시작 시점에 모든 읽기 동작 실행시 발생하는 오류이다. Redis 인스턴스가 기동되면서 RDB/AOF 파일로부터 데이터를 로딩하는 중이라 요청 수행이 불가능할 때 발생한다. 시간이 지나면 응답이 가능하므로 엄밀히 말하면 오류는 아니다.
  • Redis 저장소는 RDB/AOF 파일을 이용하여 메모리 상의 데이터를 디스크에 보관하여 인스턴스를 재시작하더라도 영구적으로 저장된 데이터를 유지할 수 있다. 만약 완전한 비휘발성 저장소로 사용하고자 한다면 아래와 같이 설정할 수 있다.
$ nano /etc/redis.conf
appendonly no
#save 900 1
#save 300 10
#save 60 10000
save ""

참고 글

저작자 표시 비영리 동일 조건 변경 허락
신고

'SW 개발 > Spring > Jedis' 카테고리의 다른 글

Spring Boot, Redis 저장소에 CRUD 로직 구현하기  (0) 2017.09.04