Spring boot jetty websocket 서버 개발, 웹소켓 서버 구현
웹 소켓은 HTML5 표준 웹어플리케이션 통신기술 입니다.
브라우저와 서버가 양방향 통신을 할 수 있고, 통신을 하는데 별다른 제약이 없습니다.
크롬, 사파리, 파이어폭스, 오페라, IE 최신버전에서 지원을 하고 있습니다.
TCP 기반이라 기존 ajax와 같은 동적 통신방식 보다 가볍고
실시간 통신 성능이 우수합니다.
그럼 Spring boot 와 jetty 를 활용하여 websocket 서버를 구현하는 방법을 알아보도록 하겠습니다.
gradle에 jetty websocket 의존성 주입을 합니다.
dependencies {
compile group: 'org.eclipse.jetty.websocket', name: 'websocket-server', version: '9.4.12.v20180830'
.
.
.
}
이제 서버를 실행할 클래스를 구현합니다.
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
|
@Service
public class WebSocketServer{
private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
public void start(){
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(13701);
server.addConnector(connector);
// Setup the basic application "context" for this application at "/"
// This is also known as the handler tree (in jetty speak)
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
// Add a websocket to a specific path spec
ServletHolder holderEvents = new ServletHolder("ws-events", EventServlet.class);
context.addServlet(holderEvents, "/events/*");
try{
server.start();
server.dump(System.err);
server.join();
}catch (Throwable t){
logger.error("WebSocket Server Start Error", t);
t.printStackTrace(System.err);
}
}
}
|
cs |
포트는 13701, 컨텍스트 패스는 기본(/) 으로 하고 ServletHoler 인스턴스를 생성해 이벤트를 핸들할 경로를 설정합니다.
이렇게 하면 ws://localhost:13701/events/* 로 요청되는 웹소켓통신에 대한 경로가 설정됩니다.
이제 WebSocketAdapter 를 상속받아 실제 이벤트를 처리할 클래스를 구현하도록 합니다.
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
|
public class EventServlet extends WebSocketAdapter{
private final Logger logger = LoggerFactory.getLogger(EventSocket.class);
/*웹소켓 연결*/
@Override
public void onWebSocketConnect(Session sess){
super.onWebSocketConnect(sess);
logger.info("Socket Connected : "+sess);
}
/*웹소켓 요청에 대한 처리*/
@Override
public void onWebSocketText(String message){
super.onWebSocketText(message);
RemoteEndpoint remote = super.getSession().getRemote();
if(message.equals("monitoringData")){
/* 브라우저로 부터 monitoringData 이벤트 요청이 왔을 때 처리 */
/*String data를 리턴할 경우 예*/
try {
remote.sendString(브라우저로 전송할 데이터);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
.
.
.
}
/*웹소켓이 닫혔을 경우*/
@Override
public void onWebSocketClose(int statusCode, String reason){
super.onWebSocketClose(statusCode,reason);
logger.info("Socket Closed: [" + statusCode + "] " + reason);
}
/*에러가 발생했을 경우*/
@Override
public void onWebSocketError(Throwable cause){
super.onWebSocketError(cause);
cause.printStackTrace(System.err);
}
|
cs |
이제 브라우저에서 ws://localhost:13701/events/ 경로로 웹소켓을 연결하고
monitoringData 라는 메세지 요청이 있을 경우 처리가 가능하게 됩니다.
서버를 실행하기 위한 코드를 작성 하겠습니다.
1
2
3
4
5
6
7
8
9
10
11
|
@SpringBootApplication
public class MonitoringProcessApplication {
private static final Logger logger = LoggerFactory.getLogger(MonitoringProcessApplication.class);
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(MonitoringProcessApplication.class, args);
WebSocketServer ws = ctx.getBean(WebSocketServer.class);
logger.info("■□■□■Monitoring Websocket Server Start■□■□■");
ws.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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
<script>
'use strict';
var wsUri = "ws://211.189.132.186:13701/events/";
var websocket;
/*웹소켓 접속 함수*/
function webSocketInit(){
try{
websocket = new WebSocket(wsUri);
/*웹소켓 연결시 이벤트*/
websocket.onopen = function(evt) {
onOpen(evt);
};
/*웹소켓 서버로 부터 데이터를 받았을 때 이벤트*/
websocket.onmessage = function(evt) {
onMessage(evt);
};
/*에러가 발생했을 경우 이벤트*/
websocket.onerror = function(evt) {
onError(evt)
};
}catch(exception){
console.log(exception);
console.log('소켓서버 접속에러');
}
}
function webSocketInit(){
try{
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) {
onOpen(evt);
};
websocket.onmessage = function(evt) {
onMessage(evt);
};
websocket.onerror = function(evt) {
onError(evt)
};
}catch(exception){
console.log(exception);
console.log('소켓서버 접속에러');
}
}
function onOpen(evt) {
console.log('webSocket connected');
timer = AF.startTimer(doSend, 1500);
// doSend();
}
function onMessage(evt) {
var data = evt.data;
data = JSON.parse(data);
}
function onError(evt) {
alert('모니터링 서버와의 연결에 실패 하였습니다.\n모니터링 서버 구동을 확인하십시오.');
}
window.addEventListener("load", webSocketInit, false);
</script>
|
cs |
웹소켓은 브라우저에서 제공을 하기 때문에 따로 의존성 주입을 하실 필요는 없습니다.
요청은 간단합니다. 주석을 보시면 이해가 되실꺼라고 생각됩니다.
저의 경우에는 서버에서 DB에서 조회된 데이터를 Gson의 toJson 메소드를 사용해 json String 으로 전송을 하고
브라우저에서는 해당 데이터를 Json으로 parsing 해 요청에 대한 처리를 하였습니다.
확실히 이전에 실시간 페이지에서 사용하던 ajax을 활용한 DB 폴링 방식보다는 가벼운 느낌을 받았습니다.
1000건 이상의 데이터를 1.5초마다 요청해
지도상에 표출하는데도 성능상에 문제가 없어 보였습니다.
하지만 웹소켓도 단점이 존재합니다.
저의경우 운영단말 페이지 내에서만 요청이기 때문에 클라이언트가 많지 않지만,
클라이언트가 많아지면 서버의 부담이 급증하게 됩니다.
그리고 보안과 여러가지 문제로 인해 최신 브라우저가 아니면 웹소켓을 지원하지 않습니다. 지원하는 브라우저를 잘 확인하시어 구현을 하셔야 합니다.
'Programing > Springboot' 카테고리의 다른 글
Redis pub/sub 사용 중 발생 에러 Spring Data repository 관련 (0) | 2019.06.24 |
---|---|
Java springboot nkzawa socket.io-client 소켓 클라이언트 구현 Java soket.io client (0) | 2018.11.06 |
Springboot logback 설정하기, 콘솔 및 파일저장 (1) | 2018.08.17 |
SpringBoot Redis(Jedis) 활용 Publish/Subscribe pub/sub 구현 방법 (0) | 2018.08.13 |
Eclipse SpringBoot 프로젝트 생성, 이클립스 스트링부트 프로젝트 생성, jar 배포하기 (0) | 2018.08.10 |
댓글