Vert.X Ignite 로 클러스터 처리를 해보자.(3)-종료 흔한 전산쟁이의 삽질일기

Vert.X Ignite 로 클러스터 처리를 해보자.(2)

우선 현재까지 EventBus를 클러스터 하는건 localhost정도밖에 되지 않았다. 같은 같은 JVM위에서나 도는듯. 더 확인해봐야 한다.



뭐 어쨋든

이제 클러스터로 처리할건데... 이건 어떻게 할거냐. 네트워크상에 여기저기 Vertx를 깔고 이래저래 통신 할 경우, 지들끼리 호환될
메모리 정보가 있으면 공유하기 편할건데 라는걸로 시작했다.

우선 IMDG라는게 있는데 In Memory Data Grid라는거다.


그래서 내부는 보통 key-value식으로 데이터가 구성되고 A서버에 적용해도 B서버에서 같은정보를 끌어올 수있다.

보통 Vertx안에는 hazelcast라는 IMDG가있는데, 나는 그거 안쓰고 apache-Ignite를 갖다썼다.
어쩌다보니 쓰긴 했는데 이게 웃긴게, Vertx정보는 많고 ignite정보도 나름있다. 근데 연동되는걸 보니 거의 없더라 ㅡ.ㅡ..
이놈의 vertx ignite는 정보가 다 파편화되어있어서 여기찾고 저기찾고 vertx만 찾았다 ignite만 찾았다 그래야한다.


어쨋든 시작해야지. https://vertx.io/docs/vertx-ignite/java/ 여기가 관련 페이지고, 이건 메이븐으로 받으면 된다.

    <dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-ignite</artifactId>
  <version>3.5.4</version>
</dependency>

이걸 받고 작업을 해보면, 우선 기존에 있던 시스템을 다음과 같이 고친다.


여기서 핵심은 Vertx.clusteredVertx이다. 지난번에는 그냥 생성한거에 비해 cluster를 설정해야한다.
그리고 옵션을 이것저것 넣어서, 다른 서버 주소라던지, 시작 포트라던지, 이벤트 버스라던지(현재 안됨)
캐시저장이라던지 이런 각 모드를 넣을수 있다.

Vertx는 이게 핵심이고 나머지는 거의 동일하다. 그럼 이제 ClusterManager를 세팅하는 부분을 가 보자.

다른건 적당히 하면 되고, 메모리 그리드 설정하는부분을 보자
clusterDemo의 이름으로 그리드 맵을 설정한다.
이후 캐시모드를 분산할 PARTITIONED인지 복제할 REPLICATED인지 구분하는데, 어차피 통복제인건 알거고,
이렇게 분산모드는 적절히 나눠서 저장한다. 그리고 백업갯수에따라 백업 캐시도 나눠놔서 잘라먹어도 회복되게 한다.

다음 메모리상에 두면 통째로 날려먹으니 하드에도 저장하게 하는게 있는데. 다음과 같다.
데이터 유실이 없게 풀모드로 저장을 할지 OS Buffer등에 사용할지를 결정해야한다. 자세한건 여기 52페이지를 참조하도록.
그리고 중요한게. 원래라면 Ignite를 먼저 시작할 필요가 없는데 이 하드 저장덕에 미리 시작하고 이를 clusterManager에 넣어주는 방식대로 가야 에러가 나지 않는다.


이후 수신하거나 송신할때는 다음과 같이 맵에서 뽑거나 저장하여 호환성을 유지한다.
TCP로 받아 IMDG에 저장하거나 빼는 구문


그러면 동작해 보자.

서버를 켠다. 한 두어개
<120번 서버 동작>

<108번 서버 동작>
밑에 작게 서버가 2개가 된게 보인다.

샘플 데이터를 120번에 넣는다.
<120번에 120_INSERT로 입력>

<Client 수신 결과>

<120번 서버 공유 데이터 내용>

위처럼 asdasdasd로 들어있다가 120_INSERT로 바뀌었다. 그러면 108번을 호출해보자.

<Client 수신 결과>

<120번 서버 수신 결과>

상호 정보가 클러스터링이 된다는 걸 알 수 있다. 실제 여기서 120번을 껐다가 다시 켜도 결과는 마찬가지이다.

이제 두개 다 끄고 다시 켰을때. 저장된 기록이 로딩되는지 확인해야한다. 아까 WalMode를 세팅했으니 껐다켜도 과거 기록이 있을것이다.

<서버를 끄고있으니 발생하는 peer에러>

그리고 다시 켜고 단순하게 호출해보자.

껐다켜도 기록이 사라지지 않는다.

이렇게 클러스터링이 되는것을 확인했다. IMDG를 사용해서 잘 쓰면 될 듯하다. 그런데 아직 EventBus로 전체 호출하는건, 된다고
하는데 왤케 안되는지 좀 더 연구해 봐야겠다. 우선은 ignite를 빼고 시도해 볼 예정이다.

현재까지는 TCP로 데이터 받고 이를 공유 맵에 넣고 관리 하면 될거 같다. 단지 버스 호출이 되면 좀 더 다양한 용도로
쓸수 있을것 같다.

끗!!




클라이언트 소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.scblood.test.vertx.cluster;
 
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.scblood.test.vertx.DataBean;
 
import io.vertx.core.AbstractVerticle;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetSocket;
 
//http://tutorials.jenkov.com/vert.x/tcp-client.html
public class VertXClientTest extends AbstractVerticle{
    private static final Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();
    private Vertx vertx;
    private NetClient tcpClient;
    public VertXClientTest(){
        vertx = Vertx.vertx();
    }
 
    private String ip="192.168.1.120";
//    private String ip="127.0.0.1";
    public void start() {
        tcpClient = vertx.createNetClient();
        tcpClient.connect(10010, ip,
                new Handler<AsyncResult<NetSocket>>(){
 
                @Override
                public void handle(AsyncResult<NetSocket> result) {
                    NetSocket socket = result.result();
                    DataReq bean = new DataReq();
                    bean.setReqType(1);
                    bean.setUserId("A");
                    bean.setData("120_INSERT");//이 부분이 주석이면  단순 공유맵에서 데이터를 뽑아준다.
                    socket.write(gson.toJson(bean));
                    
                    socket.handler(new Handler<Buffer>(){
                        @Override
                        public void handle(Buffer buffer) {
                            System.out.println("Received data: " + buffer.length());
 
                            System.out.println(buffer.getString(0, buffer.length()));
                            stop();
                        }
                    });
                }
            });
    }
    
    
    public void stop(){
        tcpClient.close();
        vertx.close();
    }
    public static void main(String[] args){
        VertXClientTest a = new VertXClientTest();
        a.start();
        
    }
}
 
cs



빈 두개
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.scblood.test.vertx.cluster;
 
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.google.gson.annotations.SerializedName;
 
import lombok.Data;
 
/**
 * Request 데이터 빈
 * 롬복으로 세팅
 * @author scblood.egloos.com
 *
 */
@Data
public class DataReq {
    
    @JsonProperty("req_type")
    @SerializedName("req_type")        
    private int reqType;
    
    @JsonProperty("user_id")
    @SerializedName("user_id")        
    private String userId;
    
    @JsonInclude(Include.NON_NULL)
    @JsonProperty("data")
    @SerializedName("data")        
    private Object data;
    
    @JsonProperty("ext_data")
    @SerializedName("ext_data")    
    @JsonInclude(Include.NON_NULL)
    private String extData;
    
}
 
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.scblood.test.vertx.cluster;
 
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.google.gson.annotations.SerializedName;
 
import lombok.Data;
 
@Data
public class DataRes {
 
    @JsonProperty("res_code")
    @SerializedName("res_code")    
    private int resCd;
    
    @JsonProperty("res_msg")
    @SerializedName("res_msg")        
    private String resMsg;
    
    @JsonInclude(Include.NON_NULL)
    @JsonProperty("data")
    private Object data;
}
 
cs



서버

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
package com.scblood.test.vertx.cluster;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.InvalidPropertiesFormatException;
import java.util.Map;
import java.util.Properties;
 
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.Ignition;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.CacheWriteSynchronizationMode;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.configuration.WALMode;
import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder;
import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
 
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.scblood.test.vertx.DataBean;
 
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.EventBusOptions;
import io.vertx.core.net.NetServer;
import io.vertx.spi.cluster.ignite.IgniteClusterManager;
 
public class ClusterServerMain extends AbstractVerticle{
    private static final Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();
    NetServer server;
    private String serverName ;
    private int port=0;
    private String discoverRange;
    private int discoverLocalPort;
    private int discoverLocalRange;
    private int tcpCommPort;
    
    IgniteClusterManager clusterManager ;
    
    
    public ClusterServerMain() throws MalformedURLException, IgniteCheckedException{
        init();
        //설정
        IgniteConfiguration cfg =   new IgniteConfiguration();
        //TCP 조회하는 범위 설정
        TcpDiscoverySpi discoverySpi = new TcpDiscoverySpi();
        //로컬 포트와, 로컬포트 할당 범위를 설정한다. 45800 20 하면 45800~45820까지 쓰겠단 소리다.
        discoverySpi.setLocalPort(discoverLocalPort);
        discoverySpi.setLocalPortRange(discoverLocalRange);
        
        //외부를 뒤질 IP와 포트설정인데, 나는 고정으로 썼다. 그리고 자기 IP도 설정을 해줘야 한다고 한다.192.168.1.108,192.168.1.120 식.    여기서도 위처럼 48500..48520식도 가능    
        TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder();
        ipFinder.setAddresses(Arrays.asList(discoverRange.split(",")));
        discoverySpi.setIpFinder(ipFinder);
        
        //Verx가 사용하는 TCP포트를 설정한다.
        TcpCommunicationSpi commSpi = new TcpCommunicationSpi();
        commSpi.setLocalPort(tcpCommPort);
        cfg.setDiscoverySpi(discoverySpi);
        cfg.setCommunicationSpi(commSpi);
 
        //메모리 그리드의 설정을 한다. 우선 사용할 MAP의 이름을 정한다. 
        CacheConfiguration cacheCfg = new CacheConfiguration("clusterDemo");
        //파티션 모드로 쓸 것인지. 완전복제(CacheMode.REPLICATED) 모드로 쓸것인지 결정한다.
        cacheCfg.setCacheMode(CacheMode.PARTITIONED);
        cacheCfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_ASYNC); //백업할때의 이야기 싱크설정
        //백업갯수를 몇개 만들것인지.
        cacheCfg.setBackups(1);
        cfg.setCacheConfiguration(cacheCfg); 
        
        // 메모리 초기 설정모드로 용량을 제한거는거다. 
        DataStorageConfiguration psCfg = new DataStorageConfiguration();
        psCfg.getDefaultDataRegionConfiguration().setPersistenceEnabled(true);
        
        psCfg.getDefaultDataRegionConfiguration().setMaxSize(4L * 1024 * 1024 * 1024); //4G메모리 사용
        psCfg.setConcurrencyLevel(5); //cpu 몇개쓸것인가.
        psCfg.setPageSize(1024*4); //페이징 크기
                
//        //이건 메모리상에만 넣으면 맛이갈 수 있으니 디스크에도 쓴다는 소리이다. 경로는 /tmp/ignite/work에 저장
        psCfg.setWalMode(WALMode.DEFAULT); //FULLSYNC, LOG_ONLY등으로 설정 가능
        cfg.setDataStorageConfiguration(psCfg);
        // 이거 할라면 active로 해야함
        Ignite ignite = Ignition.start(cfg);  //캐시를 위해서 이렇게 작업을 해야함.
        
        ignite.active(true);
        clusterManager = new IgniteClusterManager(ignite);
    } 
    
 
    public IgniteClusterManager getManager(){
        return clusterManager;
    }
        
    private  Properties properties = new Properties();
    private void loadConfiguration(String path) throws InvalidPropertiesFormatException, IOException {
        File file = new File(path);
        InputStream input = new FileInputStream(file);
        properties.loadFromXML(input);
}
 
    private String getString(String key) {
        String propertiesValue = properties.getProperty(key);
        if (propertiesValue != null)
            return propertiesValue;
        else
            return "";
    }
    
    public void init(){
        try {
            loadConfiguration(new File("conf/config.xml").getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        serverName = getString("HOSTNAME");
        port = Integer.parseInt(getString("PORT"));
        discoverRange = getString("DISCOVER_RANGE");
        discoverLocalPort = Integer.parseInt(getString("DISCOVERY_LOCALPORT"));
        discoverLocalRange = Integer.parseInt(getString("DISCOVERY_LOCALPORTRANGE"));
        tcpCommPort = Integer.parseInt(getString("TCP_COMM_PORT"));
    }
    
    
    @Override
    public void start() throws Exception {
        System.out.println("START "+serverName+" port "+port);
        server = vertx.createNetServer();
        
        setNetServer();
        server.listen(port);
        setEvent();
    }
 
    //안먹히느중
    public void setEvent(){
          vertx.eventBus().consumer("Event_Address", message -> {
                System.out.println("["+serverName+"] Recieve Event" +message.body());
            });
          
          vertx.eventBus().consumer(serverName, message -> {
                System.out.println("["+serverName+"] Recieve Event" +message.body());
            });
          
    }
    //안먹히느중
    public void sendEventMsg(String msg) {
        vertx.eventBus().publish("Event_Address", msg);
 
    }
        
      
    public void stop() {
        setServerClose();
    }
 
    public void setNetServer() {
        server.connectHandler(socket -> {
            socket.handler(buff -> {
                String key=null;
                DataReq bean = gson.fromJson(buff.getString(0, buff.length()), DataReq.class);
                System.out.println("[" + serverName + "]" + gson.toJson(bean));
                
                /*
                 *  이부분은 클러스터가 연결되어있을 경우. 모든 작업이 끝난다음 공유Map에 변경을 하게 되어있는것 같음
                 *  실제 단독동작시에는 맵에 넣고 바로 빼도 변경되어있지만,
                 *  클러스터시에는 입력하고 바로 빼도 이전데이터가 존재하며 다음호출때는 변경되어있는 것을 알 수 있다.
                 */
                if(bean.getData()!= null){
                    Map map = clusterManager.getSyncMap("clusterDemo"); // shared distributed map
                    map.put("data", bean.getData());
                    key = bean.getData().toString(); //이벤트고 뭐고 기존에 받은걸로 처리하고 공유 맵에는 끝난다음 들어가기떄문에 여기서 도로 뽑는 헛짓거리를 하지 말것
                }
                
 
                if(key==null){
                    Map map = clusterManager.getSyncMap("clusterDemo"); // shared distributed map
                    System.out.println("[" + serverName + "]SHARE " + map.get("data"));
                    if (map.get("data"!= null) {
                        key = map.get("data").toString();
                    }
                }
                
                DataRes res = new DataRes();
                res.setResCd(200);
                res.setResMsg(serverName);
                res.setData(key);
                Buffer outBuffer = Buffer.buffer();
                outBuffer.appendString(gson.toJson(res));
                socket.write(outBuffer);
                sendEventMsg("[" + serverName + "] Event "+key);
            });
        });
    }
 
    public void setServerClose() {
        server.close(asyncResult -> {
            if (asyncResult.succeeded()) {
                System.out.println("CLOSE");
            }
        });
        //굳이 여기까지 종료할 필요는 없다.
        vertx.close();
    }
    
    public void runServer(){
        //옵션설정, 이건 이벤트버스를 해볼라고 하는데 지금 잘 안된다. 현재는 무의미함
        VertxOptions options = new VertxOptions();
        options.setClusterManager(getManager());
        options.setClustered(true);
        EventBusOptions eventBus = new EventBusOptions();
        eventBus.setClustered(true);
        options.setEventBusOptions(eventBus);         
        
        //클러스터 동작
        Vertx.clusteredVertx(options, res -> {
          if (res.succeeded()) {                          
            Vertx vertx = res.result();        
            vertx.deployVerticle(this);
            
          } else {
              System.out.println("ClusterFail");
          }
        });
        
    }
     
    public static void main(String[] args) throws MalformedURLException, IgniteCheckedException {
        ClusterServerMain a = new ClusterServerMain();
        a.runServer();
      }  
    
    
}
 
cs


메이븐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.scblood.test</groupId>
  <artifactId>vertx</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
 
  <name>vertx</name>
  <url>http://maven.apache.org</url>
 
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
 
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <!-- 
     <dependency>
          <groupId>io.vertx</groupId>
          <artifactId>vertx-core</artifactId>
          <version>3.5.4</version>
    </dependency>
     -->    
    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-ignite</artifactId>
      <version>3.5.4</version>
    </dependency>
    <dependency>
           <groupId>com.google.code.gson</groupId>
           <artifactId>gson</artifactId>
           <version>2.7</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.2</version>
        <optional>true</optional>
    </dependency>
  </dependencies>
  
  <build>
        <plugins>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
 
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
 
                </configuration>
            </plugin>
            <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <executions>
         <execution>
            <id>copy-dependencies</id>
            <phase>prepare-package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
               <outputDirectory>${project.build.directory}/lib</outputDirectory>
               <overWriteReleases>false</overWriteReleases>
               <overWriteSnapshots>false</overWriteSnapshots>
               <overWriteIfNewer>true</overWriteIfNewer>
            </configuration>
        </execution>
      </executions>
   </plugin>
   <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <configuration>
        <archive>
           <manifest>
            <addClasspath>true</addClasspath>
            <classpathPrefix>lib/</classpathPrefix>
            <mainClass>theMainClass</mainClass>
           </manifest>
         </archive>
       </configuration>
   </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
 
cs



xml 프로퍼티

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="HOSTNAME"><![CDATA[HOST_192.168.1.108]]></entry>
<entry key="PORT">10010</entry>
<entry key="DISCOVER_RANGE">192.168.1.108,192.168.1.120</entry>
<entry key="DISCOVERY_LOCALPORT">48500</entry>
<entry key="DISCOVERY_LOCALPORTRANGE">20</entry>
<entry key="TCP_COMM_PORT">48100</entry>
</properties>



이상.

참고 사이트







번외도 있음 ㅋ

덧글

댓글 입력 영역