https://github.nhnent.com/game-server-engine/sample-game-server.git
GameAnvil Server API - Java doc
-Dco.paralleluniverse.fibers.detectRunawayFibers=false
-Dco.paralleluniverse.fibers.verifyInstrumentation=false
-Xms6g
-Xmx6g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:+UseStringDeduplication
src/main/resources/
Maven 탭의 install 명령으로 서버를 설치합니다. 이때, 컴파일 타임에 AOT Instrumentation을 진행합니다.
앞서 설정해 두었던 "sample_server" 구성을 이용하여 서버를 실행합니다.
서버가 정상적으로 구동되면 아래와 같이 모든 노드에 대해 onReady 로그가 출력됩니다.
http://127.0.0.1:25150/management/nodeInfoPage
URL을 통해 현재 로컬의 sample_game_server의 상태를 확인할 수 있습니다.
오류 확인
정상적으로 서버가 실행되지 않았다면 설정을 다시 한번 확인해 보거나 log의 에러 부분을 확인해서 문의 부탁드립니다.
<!-- gameanvil-->
<dependency>
<groupId>com.nhn.gameanvil</groupId>
<artifactId>gameanvil</artifactId>
<version>1.1.0-jdk8</version>
</dependency>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<!-- executable jar 에서 main 으로 실행 될 클래스 -->
<mainClass>com.nhn.gameanvil.sample.Main</mainClass>
<!-- jar 파일 안의 META-INF/MANIFEST.MF 에 classpath 정보가 추가됨 -->
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.nhn.gameanvil.sample.Main</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/io.netty.versions.properties</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/java.sql.Driver</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/LICENSE</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/NOTICE</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/reactor.blockhound.integration.BlockHoundIntegration</resource>
</transformer>
</transformers>
<artifactSet>
<excludes>
<exclude>javax.activation:javax.activation-*</exclude>
<exclude>org.javassist:javassist*</exclude>
</excludes>
</artifactSet>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>module-info.class</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/*.MF</exclude>
<exclude>META-INF/*.txt</exclude>
<exclude>about.html</exclude>
</excludes>
</filter>
</filters>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>java</executable>
<arguments>
<argument>-classpath</argument>
<!-- automatically creates the classpath using all project dependencies, also adding the project build directory -->
<classpath/>
<!-- Main class -->
<argument>com.nhn.gameanvil.sample.Main</argument>
</arguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<!-- Ant task for Quasar AOT instrumentation -->
<execution>
<id>Running AOT instrumentation</id>
<phase>compile</phase>
<configuration>
<tasks>
<taskdef name="instrumentationTask" classname="co.paralleluniverse.fibers.instrument.InstrumentationTask" classpathref="maven.dependency.classpath"/>
<instrumentationTask>
<fileset dir="${project.build.directory}/classes/" includes="**/*.class"/>
</instrumentationTask>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<phase>package</phase>
<configuration>
<tasks>
<copy todir="target/config/" overwrite="false">
<fileset dir="target/classes/">
<include name="logback.xml" />
<include name="mybatis-config.xml" />
<include name="GameAnvilConfig.json" />
</fileset>
</copy>
<copy todir="target/query/" overwrite="false">
<fileset dir="target/classes/query/">
<include name="*.xml" />
</fileset>
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
정상적으로 실행되면 ./target/ 폴더에 빌드된 파일들이 있습니다.
서버를 구동하려면 sample_game_server-1.0.1.jar 파일과 config, query 폴더의 파일을 복사해 사용하면 됩니다.
명령 프롬프트에서 실행
명령 프롬프트(cmd)를 실행해서 빌드된 target 폴더로 이동합니다(각자 자신의 환경에 맞는 경로에서 진행).
command 실행
java -Dco.paralleluniverse.fibers.detectRunawayFibers=false -Dco.paralleluniverse.fibers.verifyInstrumentation=false -Dconfig.file=.\config\GameAnvilConfig.json -Dlogback.configurationFile=.\config\logback.xml -DmybatisConfig=.\config\mybatis-config.xml -Xms6g -Xmx6g -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+UseStringDeduplication -jar .\sample_game_server-1.1.0.jar
기본으로 실행 시에 별도의 옵션이 지정되지 않으면 빌드할 때 지정되어 있는 환경 파일 적용
com.nhn.gameanvil.sample.mybatis.GameSqlSessionFactory 참고
onReady가 나오면 정상
매번 maven 빌드로 테스트하기 번거로워 로컬에서 테스트할 때는 Vm Option에-javaagent:.\src\main\resources\META-INF\quasar-core-0.7.10-jdk8.jar=bm
옵션을 추가하고 intelliJ에서 바로 로컬 서버를 실행할 수 있습니다.
<!-- Ant task for Quasar AOT instrumentation -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>Running AOT instrumentation</id>
<phase>compile</phase>
<configuration>
<tasks>
<taskdef name="instrumentationTask" classname="co.paralleluniverse.fibers.instrument.InstrumentationTask" classpathref="maven.dependency.classpath"/>
<instrumentationTask>
<fileset dir="${project.build.directory}/classes/" includes="**/*.class"/>
</instrumentationTask>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
-javaagent:.\src\main\resources\META-INF\quasar-core-0.7.10-jdk8.jar=bm
java -javaagent:.\lib\quasar-core-0.7.10-jdk8.jar=bm -Dco.paralleluniverse.fibers.detectRunawayFibers=false -Dconfig.file=.\config\GameAnvilConfig.json -Dlogback.configurationFile=.\config\logback.xml -DmybatisConfig=.\config\mybatis-config.xml -Xms6g -Xmx6g -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+UseStringDeduplication -jar .\sample_game_server-1.1.0.jar
public static void main(String[] args) {
GameAnvilBootstrap bootstrap = GameAnvilBootstrap.getInstance();
// 클라이언트와 전송할 프로토콜 정의 - 순서는 클라이언트와 동일 해야 한다.
bootstrap.addProtoBufClass(0, Authentication.getDescriptor());
bootstrap.addProtoBufClass(1, GameMulti.getDescriptor());
bootstrap.addProtoBufClass(2, GameSingle.getDescriptor());
bootstrap.addProtoBufClass(3, Result.getDescriptor());
bootstrap.addProtoBufClass(4, User.getDescriptor());
// 게임에서 사용하는 DB 쓰레드풀 지정
bootstrap.createExecutorService(GameConstants.DB_THREAD_POOL, 100);
// 게임에서 사용하는 레디스 쓰레드풀 지정
bootstrap.createExecutorService(GameConstants.REDIS_THREAD_POOL, 100);
// 세션설정
bootstrap.setGateway()
.connection(GameConnection.class)
.session(GameSession.class)
.node(GameGatewayNode.class)
.enableWhiteModules();
// 게임 스페이스 설정
bootstrap.setGame(GameConstants.GAME_NAME)
.node(GameNode.class)
// 싱글 게임
.user(GameConstants.GAME_USER_TYPE, GameUser.class)
.room(GameConstants.GAME_ROOM_TYPE_SINGLE, SingleGameRoom.class)
// 룸 매치 멀티게임 - 방에 들어가서 게임 : 무제한 탭
.room(GameConstants.GAME_ROOM_TYPE_MULTI_ROOM_MATCH, UnlimitedTapRoom.class)
.roomMatchMaker(GameConstants.GAME_ROOM_TYPE_MULTI_ROOM_MATCH, UnlimitedTapRoomMatchMaker.class, UnlimitedTapRoomInfo.class)
// 유저 매치 멀티게임 - 유저들 매칭으로 인해 게임동시 입장 : 스테이크 게임
.room(GameConstants.GAME_ROOM_TYPE_MULTI_USER_MATCH, SnakeRoom.class)
.userMatchMaker(GameConstants.GAME_ROOM_TYPE_MULTI_USER_MATCH, SnakeRoomMatchMaker.class, SnakeRoomInfo.class);
// 서비스 설정
bootstrap.setSupport(GameConstants.SUPPORT_NAME_LAUNCHING)
.node(LaunchingSupport.class);
bootstrap.run();
}
static private PacketDispatcher packetDispatcher = new PacketDispatcher();
static {
packetDispatcher.registerMsg(User.ChangeNicknameReq.getDescriptor(), CmdChangeNicknameReq.class); // 닉네임 변경 프로토콜
packetDispatcher.registerMsg(User.ShuffleDeckReq.getDescriptor(), CmdShuffleDeckReq.class); // 덱 셔플 프로토콜
packetDispatcher.registerMsg(GameSingle.ScoreRankingReq.getDescriptor(), CmdSingleScoreRankingReq.class); // 싱글 점수 랭킹
}
// 처리하는 클래스는 implements IPacketHandler<GameUser> 를 구현해서 만들어야 한다.
private static RoomPacketDispatcher dispatcher = new RoomPacketDispatcher();
static {
dispatcher.registerMsg(GameMulti.SnakeUserMsg.getDescriptor(), CmdSnakeUserMsg.class); // 유저 위치 정보
dispatcher.registerMsg(GameMulti.SnakeFoodMsg.getDescriptor(), CmdSnakeRemoveFoodMsg.class); // food 삭제 정보처리
}
// 처리하는 클래스는 implements IRoomPacketHandler<SnakeRoom, GameUser> 를 구현해서 만들어야 한다.
private static RestPacketDispatcher restMsgHandler = new RestPacketDispatcher();
static {
// launching
restMsgHandler.registerMsg("/launching", RestObject.GET, CmdLaunching.class);
}
// 처리하는 클래스는 implements IRestPacketHandler 를 구현해서 만들어야 한다.
// Gamebse 인증
//----------------------------------- 토큰 유효한지에 대한 검증 Gamebase
String gamebaseUrl = String.format(GameConstants.GAMEBASE_DEFAULT_URL + "/tcgb-gateway/v1.2/apps/X2bqX5du/members/%s/tokens/%s", accountId, authenticationReq.getAccessToken());
HttpRequest httpRequest = new HttpRequest(gamebaseUrl);
httpRequest.getBuilder().addHeader("Content-Type", "application/json");
httpRequest.getBuilder().addHeader("X-Secret-Key", GameConstants.GAMEBASE_SECRET_KEY);
logger.info("httpRequest url [{}]", gamebaseUrl);
HttpResponse response = httpRequest.GET();
logger.info("httpRequest response:[{}]", response.toString());
// Gamebase 응답 json 데이터 객체 파싱
AuthenticationResponse gamebaseResponse = response.getContents(AuthenticationResponse.class);
if (gamebaseResponse.getHeader().isSuccessful())
{
resultCode = ErrorCode.NONE;
} else {
resultCode = ErrorCode.TOKEN_NOT_VALIDATED;
}
//------------------------------------
private RedisClusterClient clusterClient;
private StatefulRedisClusterConnection<String, String> clusterConnection;
private RedisAdvancedClusterAsyncCommands<String, String> clusterAsyncCommands;
/**
* 레디스 연결, 사용하기전에 최초에 한번 호출해서 연결 해야 한다.
*
* @param url 접속 url
* @param port 점속 port
* @throws SuspendExecution
*/
public void connect(String url, int port) throws SuspendExecution { // 레디스 연결 처리
RedisURI clusterURI = RedisURI.Builder.redis(url, port).build();
this.clusterClient = RedisClusterClient.create(Collections.singletonList(clusterURI));
this.clusterConnection = Lettuce.connect(GameConstants.REDIS_THREAD_POOL, clusterClient);
this.clusterAsyncCommands = clusterConnection.async();
}
/**
* 접속 종료 서버가 내려가기전에 호출되어야 한다,
*/
public void shutdown() {
clusterConnection.close();
clusterClient.shutdown();
}
/**
* 유저 데이터 레디스에 저장
*
* @param gameUserInfo 유저 정보
* @return 저장 성고 여부
* @throws SuspendExecution
*/
public boolean setUserData(GameUserInfo gameUserInfo) throws SuspendExecution {
String value = GameAnvilUtil.Gson().toJson(gameUserInfo);
boolean isSuccess = false;
try {
Lettuce.awaitFuture(clusterAsyncCommands.hset(REDIS_USER_DATA_KEY, gameUserInfo.getUuid(), value)); // 해당 리턴값은 최초에 set 할때만 true 이고 있는값갱신시에는 false 응답
isSuccess = true;
} catch (TimeoutException e) {
logger.error("setUserData - timeout", e);
}
return isSuccess;
}
<!-- MySQL 접속 정보를 지정한다. -->
<properties>
<property name="hostname" value="10.77.14.22" />
<property name="portnumber" value="3306" />
<property name="database" value="taptap" />
<property name="username" value="taptap" />
<property name="password" value="nhn!@#123" />
<property name="poolPingQuery" value="select 1"/>
<property name="poolPingEnabled" value="true"/>
<property name="poolPingConnectionsNotUsedFor" value="3600000"/>
</properties>
<mappers>
<!-- 정의된 SQL구문을 맵핑해준다. 기본적으로 리소스 안에 있는 mapper.xml을 사용 할때-->
<mapper resource="query/UserDataMapper.xml"/>
<!-- 외부 지정된 mapper.xml 파일을 지정할때는 전체 경로 지정을 사용한다. -->
<!--<mapper url="file:///C:/_KevinProjects/GameServerEngine/sample-game-server/target/query/UserDataMapper.xml"/>-->
</mappers>
<select id="selectUserByUuid" resultType="com.nhn.gameanvil.sample.mybatis.dto.UserDto">
SELECT uuid,
login_type AS loginType,
app_version AS appVersion,
app_store AS appStore,
device_model AS deviceModel,
device_country AS deviceCountry,
device_language AS deviceLanguage,
nickname,
heart,
coin,
ruby,
level,
exp,
high_score AS highScore,
current_deck AS currentDeck,
create_date AS createDate,
update_date AS updateDate
FROM users
WHERE uuid = #{uuid}
</select>
/**
* 게임에서 사용하는 DB 연결 객체
*/
public class GameSqlSessionFactory {
private static Logger logger = LoggerFactory.getLogger(GameSqlSessionFactory.class);
private static SqlSessionFactory sqlSessionFactory;
/** XML에 명시된 접속 정보를 읽어들인다. */
// 클래스 초기화 블럭 : 클래스 변수의 복잡한 초기화에 사용된다.
// 클래스가 처음 로딩될 때 한번만 수행된다.
static {
// 접속 정보를 명시하고 있는 XML의 경로 읽기
try {
// mybatis_config.xml 파일의 경로 지정
String mybatisConfigPath = System.getProperty("mybatisConfig"); // 파라미터 전달 된경우 서버 (실행시 -DmybatisConfig= 옵선으로지정)
logger.info("mybatisConfigPath : {}", mybatisConfigPath);
if (mybatisConfigPath != null) {
logger.info("load to mybatisConfigPath : {}", mybatisConfigPath);
InputStream inputStream = new FileInputStream(mybatisConfigPath);
if (sqlSessionFactory == null) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
} else { // 파라미터 전달이없는 경우 내부 파일에서 설정 얻는다
Reader reader = Resources.getResourceAsReader("mybatis/mybatis-config.xml");
logger.info("load to resource : mybatis/mybatis-config.xml");
// sqlSessionFactory가 존재하지 않는다면 생성한다.
if (sqlSessionFactory == null) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 데이터베이스 접속 객체를 통해 DATABASE에 접속한 세션를 리턴한다.
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
[2020-12-18 17:36:35,634] [INFO ] [GameAnvil-DB_THREAD_POOL-0] [GameSqlSessionFactory.java:30] mybatisConfigPath : null
[2020-12-18 17:36:35,636] [INFO ] [GameAnvil-DB_THREAD_POOL-0] [GameSqlSessionFactory.java:39] load to resource : mybatis-config.xml
[2020-12-18 17:43:37,871] [INFO ] [GameAnvil-DB_THREAD_POOL-0] [GameSqlSessionFactory.java:30] mybatisConfigPath : .\src\main\resources\mybatis-config.xml
[2020-12-18 17:43:37,871] [INFO ] [GameAnvil-DB_THREAD_POOL-0] [GameSqlSessionFactory.java:32] load to mybatisConfigPath : .\src\main\resources\mybatis-config.xml
/**
* 유저 정보 DB에 저장
*
* @param gameUserInfo 유저 정보 전달
* @return 저장된 레코드 수
* @throws TimeoutException
* @throws SuspendExecution
*/
public int insertUser(GameUserInfo gameUserInfo) throws TimeoutException, SuspendExecution { // Callable 형태로 Async 실행하고 결과 리턴.
Integer resultCount = Async.callBlocking(GameConstants.DB_THREAD_POOL, new Callable<Integer>() {
@Override
public Integer call() throws Exception {
SqlSession sqlSession = GameSqlSessionFactory.getSqlSession();
try {
UserDataMapper userDataMapper = sqlSession.getMapper(UserDataMapper.class);
int resultCount = userDataMapper.insertUser(gameUserInfo.toDtoUser());
if (resultCount == 1) { // 단건 저장이기에 1개면 정상으로 디비 commit
sqlSession.commit();
}
return resultCount;
} finally {
sqlSession.close();
}
}
});
return resultCount;
}
CREATE TABLE `users` (
`uuid` varchar(40) NOT NULL,
`login_type` int(11) NOT NULL,
`app_version` varchar(45) DEFAULT NULL,
`app_store` varchar(45) DEFAULT NULL,
`device_model` varchar(45) DEFAULT NULL,
`device_country` varchar(45) DEFAULT NULL,
`device_language` varchar(45) DEFAULT NULL,
`nickname` varchar(45) DEFAULT NULL,
`heart` int(11) NOT NULL,
`coin` bigint(15) DEFAULT '0',
`ruby` bigint(15) DEFAULT '0',
`level` int(11) DEFAULT '1',
`exp` bigint(15) DEFAULT '0',
`high_score` bigint(15) DEFAULT '0',
`current_deck` varchar(45) NOT NULL,
`create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
{
//-------------------------------------------------------------------------------------
// 공통 정보.
"common": {
"ip": "127.0.0.1", // 노드마다 공통으로 사용하는 IP. (머신의 IP를 지정)
"meetEndPoints": ["127.0.0.1:16000"], // 대상 노드의 common IP와 communicatePort 등록. (해당 서버 endpoint 포함가능 , 리스트로 여러개 가능)
"ipcPort": 16000, // 다른 ipc node 와 통신할때 사용되는 port
"publisherPort" : 13300, // publish socket 을 위한 port
"debugMode": false //디버깅시 각종 timeout 이 발생안하도록 하는 옵션 , 리얼에서는 반드시 false 이어야 한다.
},
//-------------------------------------------------------------------------------------
// LocationNode 설정
"location": {
"clusterSize": 1, // 총 몇개의 머신(VM)으로 구성되는가?
"replicaSize": 3, // 복제 그룹의 크기 (master + slave의 개수)
"shardFactor": 3 // sharding을 위한 인수 (아래의 주석 참고)
// 전체 shard의 개수 = clusterSize x replicaSize x shardFactor
// 하나의 머신(VM)에서 구동할 shard의 개수 = replicaSize x shardFactor
// 고유한 shard의 총 개수 (master 샤드의 개수) = clusterSize x shardFactor
},
// 매치 노드 설정
"match": {
"nodeCnt": 1,
"useLocationDirect": true
},
//-------------------------------------------------------------------------------------
// 클라이언트와의 커넥션을 관리하는 노드.
"gateway": {
"nodeCnt": 4, // 노드 개수. (노드 번호는 0 부터 부여 됨)
"ip": "127.0.0.1", // 클라이언트와 연결되는 IP.
"dns": "", // 클라이언트와 연결되는 도메인 주소.
"maintenance": false,
"tcpNoDelay": false, // Netty Bootstrap 설정시 사용 됨. (디폴트로 필드 미사용 및 기본 값 false)
"connectGroup": { // 커넥션 종류.
"TCP_SOCKET": {
"port": 11200, // 클라이언트와 연결되는 포트.
"idleClientTimeout": 240000 // 데이터 송수신이 없는 상태 이후의 타임아웃. (0 이면 사용하지 않음)
// ,"secure": { // 보안 설정.
// "useSelf": true
//// ,"keyCertChainPath": "gameanvil.crt" // 인증서 경로.
//// ,"privateKeyPath": "privatekey.pem" // 개인 키 경로.
// }
},
"WEB_SOCKET": {
"port": 11400,
"idleClientTimeout": 0
// ,"secure": {
// "useSelf": true
//// ,"keyCertChainPath": "gameanvil.crt"
//// ,"privateKeyPath": "privatekey.pem"
// }
}
}
},
//-------------------------------------------------------------------------------------
// 게임 로비 역할을 하는 노드. (게임 룸, 유저를 포함 하고있음)
"game": [
{
"nodeCnt": 4,
"serviceId": 1,
"serviceName": "TapTap",
"channelIDs": ["","","","",""], // 노드마다 부여할 채널 ID. (유니크하지 않아도 됨. "" 문자열로 채널 구분없이 중복사용도 가능)
"userTimeout": 5000 // disconnect 이후의 유저객체 제거 타임아웃.
}
],
"support": [
{
"nodeCnt": 2,
"serviceId": 2,
"serviceName": "Launching",
"restIp": "127.0.0.1",
"restPort": 10080
}
],
//-------------------------------------------------------------------------------------
// JMX 또는 REST API 사용하여 다른 노드에 대한 관리를 할 수 있는 노드. (서비스 포즈, 전체 유저 카운트 등)
"management": {
"nodeCnt": 2,
"restIp": "127.0.0.1",
"restPort": 25150,
"consoleProxyPort" : 18081, // admin web console port
"logProxyPort" : 18082, // admin log download port
"db": {
"user": "root",
"password": "1234",
"url": "jdbc:h2:mem:gameanvil_admin;DB_CLOSE_DELAY=-1"
}
}
}
<logger name="com.nhn.gameanvil" level="INFO"/>
<logger name="com.nhn.gameanvil.sample" level="DEBUG"/>
<root>
<level value="WARN"/>
<appender-ref ref="ASYNC"/>
<appender-ref ref="STDOUT"/>
</root>