웹 소켓(Web Socket) 이란

웹소켓(WebSocket)은 하나의 TCP 접속에 전이중 통신 채널을 제공하는 컴퓨터 통신 프로토콜이다. 웹소켓 프로토콜은 2011년 IETF에 의해 RFC 6455로 표준화되었으며 웹 IDL의 웹소켓 API는 W3C에 의해 표준화되고 있다. (위키피디아)

더 쉽게 말하면 웹 소켓은 웹 서버와 웹 브라우저가 연결된 네트워크(TCP)를 통해 실시간으로 데이터를 주고 받는 기술이며 HTML5에 정의된 사양이다. 일반적으로 웹소켓은 일반 HTML 의 요청/응답과 비교하여 약 50배 정도의 성능 향상이 있는 것으로 보고되고 있다.

[출처] https://github.com/jwvg0425/ProjectArthas/wiki/Network-(TCP-IP-기본)

목적 및 작업 계획

동일한 지도 화면을 공유하여 작업상황을 모니터링 하고, 상황에 따른 지휘 통제를 위해 동일한 작업을 그룹 내에서 공유

일반적인 화상회의 솔루션이 현재 화면의 메모리덤프 형식으로 동작하여 래스터방식의 현재 화면 상태 만을 전송함. 이에 지도 위에서 일어나는 편집 (점, 선, 면) 작업의 저장 및 활용에 제약이 존재하여 GIS 전자지도가 제공하는 기능의 활용에 애로가 있음

  1. 이에 대한 해결 방안으로써 웹 소켓을 이용하여 작업자가 지도 위에서 생성한 객체 정보를 그룹 내 다른 사용자에게 전달
  2. Sender가 보낸 Message를 수신한 사용자 브라우저의 지도 위에 시각화
  3. 앞의 과정을 통해 그룹 내 모든 사용자는 동일한 GIS 실행 화면을 공유


실행 환경 설정

1. Server : Linux Container, 가상서버 위에 설치되는 DBMS
2. WAS(Web Application Server) : Apache Tomcat

 

구성 요약

1. Socket 서버 : 웹 브라우저와 메시지 교환

      • Socket의 기본형태는 위 소스와 같이 Open, Close, Error, Message로 구분된다. 
      • 51행 : @onMessage를 통해 GIS Sender로부터 받은 좌표정보를 Receiver 페이지로 전달
      • 61행 : Recevier 페이지로 전달
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
package egovframework.map.service;
 
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
 
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
 
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
 
import egovframework.map.web.Coordinate;
 
@Controller
@ServerEndpoint("/websocket")
public class WSocketServer {
 
    static List<Session> sessionUsers = Collections.synchronizedList(new ArrayList<Session>());
    
 
    @OnOpen
    public void onOpen(Session userSession) {
        System.out.println("Open Connection!...");
        sessionUsers.add(userSession);
    }
 
    @OnClose
    public void onClose(Session userSession) {
        System.out.println("Close Connection!...");
        sessionUsers.remove(userSession);
    }
 
    @OnError
    public void onError(Throwable e) {
        e.printStackTrace();
    }
    
    @OnMessage
    public void onMessage(String message, Session userSession) throws IOException {
        System.out.println(message);
        
        Iterator<Session> itr = sessionUsers.iterator();
        
        while (itr.hasNext()) {
            try {
                Session session = itr.next();
                
                session.getBasicRemote().sendText(message);
                
            } catch (IOException e) {                        
                e.printStackTrace();
            }
        }
        
    }
 
}
 
cs

 

2. GIS Sender

GIS Sender는 화면공유를 할 수 있는 관리자 페이지로 지도상에서 선택한 위치정보를 포인트 형태로 공유할 수 있게 정보를 전송하는 페이지다. GIS Sender 페이지의 소스는 아래와 같다.

      • GIS Sender의 <scrip></script>부분은 GIS Sender 페이지 로딩 부분과 WebSocket 통신을 통해 정보를 공유하는 부분 이렇게 2개로 구분된다. 
      • 32행 ~ 106행 <scrip></script> : 지도 로딩 및 클릭 이벤트  
      • 124행 ~ 155행 <scrip></script> :  WebSocket 서버 통신 및 Message 등 설정
      • 126행 : WebSocket 서버 접속

141행 : WebSocket으로 GeoJson형태의 위치정보 전송 및 Textarea에 좌표정보 표출

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
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ page session="false" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Tomcat WebCommander</title>
<link type="text/css" rel="stylesheet" href="<c:url value='/css/openlayers/ol.css'/>" />
<link type="text/css" rel="stylesheet" href="<c:url value='/css/bootstrap/bootstrap.min.css'/>" />
 
<script src="<c:url value='/js/openlayers/ol.js'/>"></script>
<script src="<c:url value='/js/jquery/jquery-1.12.4.min.js'/>"></script>
<script src="<c:url value='/js/bootstrap/bootstrap.min.js'/>"></script>
 
<style>
#map {
    height: 550px;
    width: 100%;
}
 
#echoText{
    height: 200px;
    width: 100%;
}
#message{
    width: 300px;
    margin: 10px;
}
</style>
 
</head>
<script type="text/javascript">
var map;
var hdms;
var geojsonStr;
var iconStyle = new ol.style.Style({
    image: new ol.style.Icon(({
        anchor: [0.580],
        anchorXUnits: 'fraction',
        anchorYUnits: 'pixels',
        src: '/images/common/logo.png',
    size: [150150],
    scale: 0.5
    }))
});
    
var writer = new ol.format.GeoJSON();
 
var vectorSource = new ol.source.Vector();
 
var vectorLayer = new ol.layer.Vector({
    source: vectorSource
});
 
$(document).ready(function(){
    
    var extent = [0.82683994988722991.296765637000096168.1705899498872449.63660938700009];
    
    map = new ol.Map({
        layers: [
              new ol.layer.Tile({
                source: new ol.source.OSM({
                    
                })
              }),
              vectorLayer
        ],
        target: 'map',
        interactions: ol.interaction.defaults({
            dragPan: false
        }),
        view: new ol.View({
            projection : 'EPSG:4326',     
            center: ol.extent.getCenter(extent),
              zoom: 3,
              maxZoom: 3,
              minZoom:3
        })
    });
    
    map.on('click'function(evt) {
 
        vectorSource.clear();
        
        var coordinate = evt.coordinate;
        hdms = ol.coordinate.toStringHDMS(coordinate);
        
        var coordLon = coordinate[0];
        var coordLat = coordinate[1];
        
        var iconFeature = new ol.Feature({
            geometry: new ol.geom.Point([coordLon,coordLat])
          });
        
        iconFeature.setStyle(iconStyle);
        
        vectorSource.addFeature(iconFeature);
    
        $('#message').attr('value',coordinate);        
        
        geojsonStr = writer.writeFeatures(vectorSource.getFeatures());
        
    });
 
});
</script>
<body>
    <nav class="navbar navbar-dark bg-primary">
        <div class="container-fluid">
            <div class="navbar-header">
                <h1>GIS Sender </h1>
            </div>
        </div>
    </nav>
    <h1>Web Socket을 활용한 GIS 화면 공유 </h1>
    <form>
        <input id="message" type="text">
        <input onclick="wsSendMessage();" value="위치 전송" type="button">
        <input onclick="wsCloseConnection();" value="Disconnect" type="button">
    </form>
    <div id="map"></div>
    <br>
    <textarea id="echoText" rows="500" cols="700"></textarea>
    <script type="text/javascript">
        //웹소켓 서버 접속
        var webSocket = new WebSocket("ws://mapview.paas.lx.or.kr/websocket");
        
        var echoText = document.getElementById("echoText");
        echoText.value = "";
        
        var message = document.getElementById("message");
        
        webSocket.onopen = function(message){ wsOpen(message);};
        webSocket.onclose = function(message){ wsClose(message);};
        webSocket.onerror = function(message){ wsError(message);};
        
        
        function wsOpen(message){
            echoText.value += "Connected ... \n";
        }
        function wsSendMessage(){
            webSocket.send(geojsonStr);
              echoText.value += "GIS sender dent the location information to the Recevier : " + hdms + "\n";
        }
        function wsCloseConnection(){
            webSocket.close();
        }
        function wsClose(message){
            echoText.value += "Disconnect ... \n";
        }
 
        function wserror(message){
            echoText.value += "Error ... \n";
        }
    </script>
    <footer class="page-footer font-small blue">
 
          <div class="footer-copyright text-center py-3">
            
            <a href="https://progworks.tistory.com">
                <img alt="logo" width="70px" height="70px" src="/images/common/logo.png">
            </a>
            PROGWORKS - https://progworks.tistory.com/
            
          </div>
    </footer>
</body>
</html>
 
cs

3. Receiver

Receiver는 GIS Sender를 통해 보내진 위치정보를 사용자가 공유 받는 페이지이다.

      • Receiver 페이지는 GIS Sender 페이지와 유사하지만 별도의 기능이 없이 WebSocket을 통해 Sender페이지에서 받은 위처정보만을 지도상에 보여준다.
      • Receiver 페이지 또한 지도 로딩과 WebSocket 통신부분으로 2개의 <scrip></script>부분이 존재한다.
      • 28행 ~ 76행 <script></script> : 지도 로딩
      • 90행 ~ 137행 <script></script> : WebSocket 서버 통신, message 및 icon 생성
      • 92행 : WebSocket 접속
      • 110행 : WebSocket을 통해 위치정보를 전달받아 function addFeature() 실행 및 Textarea에 좌표정보 표출
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
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ page session="false" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Tomcat WebSocket</title>
<link type="text/css" rel="stylesheet" href="<c:url value='/css/openlayers/ol.css'/>" />
<link type="text/css" rel="stylesheet" href="<c:url value='/css/bootstrap/bootstrap.min.css'/>" />
 
<script src="<c:url value='/js/openlayers/ol.js'/>"></script>
<script src="<c:url value='/js/jquery/jquery-1.12.4.min.js'/>"></script>
<script src="<c:url value='/js/bootstrap/bootstrap.min.js'/>"></script>
 
<style>
#map {
    height: 550px;
    width: 100%;
}
 
#echoText{
    height: 200px;
    width: 100%;
}
</style>
 
</head>
<script type="text/javascript">
var map;
var hdms;
 
var iconStyle = new ol.style.Style({
    image: new ol.style.Icon(({
        anchor: [0.580],
        anchorXUnits: 'fraction',
        anchorYUnits: 'pixels',
        src: '/images/common/logo.png',
    size: [150150],
    scale: 0.5
    }))
});
var vectorSource = new ol.source.Vector();
 
var vectorLayer = new ol.layer.Vector({
    source: vectorSource
});
 
$(document).ready(function(){
    var extent = [0.82683994988722991.296765637000096168.1705899498872449.63660938700009];
    
    var proj = new ol.proj.Projection({
        code: 'EPSG:4326',
        units: 'm',
        extent: extent
    });
    
    map = new ol.Map({
        layers: [
              new ol.layer.Tile({
                source: new ol.source.OSM()
              }),
              vectorLayer
        ],
        target: 'map',
        interactions: ol.interaction.defaults({
            dragPan: false
        }),
        view: new ol.View({
            projection : 'EPSG:4326',     
            center: ol.extent.getCenter(extent),
            zoom: 3,
              maxZoom: 3,
              minZoom:3
        })
    });
});
</script>
<body>
    <nav class="navbar navbar-dark bg-primary">
        <div class="container-fluid">
            <div class="navbar-header">
                <h1>Receiver </h1>
            </div>
        </div>
    </nav>
    <h1>Web Socket을 활용한 GIS 화면 공유 </h1>    
    <div id="map"></div>
    <br>
    <textarea id="echoText" rows="500" cols="700"></textarea>
    <script type="text/javascript">
          //웹소켓 서버 접속        
        var webSocket = new WebSocket("ws://mapview.paas.lx.or.kr/websocket");
          
        var echoText = document.getElementById("echoText");
        echoText.value = "";
        
        var message = document.getElementById("message");
        
        webSocket.onopen = function(message){ wsOpen(message);};
        webSocket.onmessage = function(message){ wsGetMessage(message);};
        webSocket.onclose = function(message){ wsClose(message);};
        webSocket.onerror = function(message){ wsError(message);};
        
        function wsOpen(message){
            echoText.value += "Connected ... \n";
        }
        function wsCloseConnection(){
            webSocket.close();
        }
        function wsGetMessage(message){
            addFeature(message.data);
              echoText.value += "Location information received from to the GIS Sender : " + hdms + "\n";
        }
        function wsClose(message){
            echoText.value += "Disconnect ... \n";
        }
 
        function wserror(message){
            echoText.value += "Error ... \n";
        }
        
        function addFeature(ft){
            
            vectorSource.clear();
            
            var selectFt = ft;
            var writer = new ol.format.GeoJSON();
            var features = writer.readFeatures(selectFt);
            
              for (var i = 0; i < features.length; i++) {
                  features[i].setStyle(iconStyle)
                  coordInfo = features[i].getGeometry().getCoordinates()
                  hdms = ol.coordinate.toStringHDMS(coordInfo);
                  vectorSource.addFeature(features[i]);
            }
        }
    </script>
    <footer class="page-footer font-small blue">
 
          <div class="footer-copyright text-center py-3">
            
            <a href="https://progworks.tistory.com">
                <img alt="logo" width="70px" height="70px" src="/images/common/logo.png">
            </a>
            PROGWORKS - https://progworks.tistory.com/
            
          </div>
    </footer>
</body>
</html>
 
cs

 

실행 및 사용법

실행을 시키기 위해 GIS Sender 페이지와 Receiver 페이지에 접속한다. 테스트를 진행하기 위해 Receiver 페이지는 크롬, Microsoft Edge, IE 3곳에서 실행한다.
(Sender 한 명이 지도 상에 위치를 선택하면 선택한 위치가 사이트에 접속되어 있는 모든 사용자에게 동일하게 나타남)

    1. GIS Sender 및 Receiver 페이지(크롬, Microsoft Edge, IE)에 접속을 한다. 
    2. GIS Sender 페이지에서 지도상에 임의의 지점을 클릭한다.
    3. 좌측 상단 <input id="message" type="text">부분에 좌표정보가 출력되는지 확인한다. 
    4. 좌표 전송 버튼을 클릭한다. <textarea id="echoText" rows="500" cols="700"></textarea>부분에 아래와 같은 문구와 함께 좌표정보가 나타나면 정상작동된것이다.
      ex) "GIS sender sent the location information to the Recevier : 48° 03′ 21″ N 69° 06′ 29″ E"
    5. Receiver 페이지에서 좌표정보를 받아 지도상에 마커 및 <textarea id="echoText" rows="500" cols="700"></textarea>부분에 좌표정보가 출력되는지 확인한다.
      ex) "Location information received from to the GIS Sender : 48° 03′ 21″ N 69° 06′ 29″ E"

 

 

적용 및 응용 분야

  1. 실시간 상황 공유 기반 지휘 관제
  2. 폴리곤, 선 등 동일한 객체를 다중 편집자가 동시 작업
  3. 화면 덤프 방식으로 공유하는 영상회의 솔루션과 함께 사용되어 GIS 벡터 형식 자료 공유

 

결론

1. HTML5 웹 소켓을 통해 GIS 기반 위치 Push, 동일 객체 다중 편집 등이 가능함을 확인
2. 보통 채팅 등의 용도로 이용되는 웹 소켓이 GIS 부문 응용 앱의 기능을 더욱 풍부하게 할 수 있음을 확인

개요

구현된 Earth 사이트 링크

Node.js 와 D3.js 기반으로 구현된 Earth 모듈(nullschool) 에 GFS(Global Forecast System)으로 부터 6시간 간격으로 업데이트되는 전세계 기상예보 데이터를 이용하여 지도(구) 기반으로 표출하는 시스템입니다.

현재는 지상데이터(Surface)를 이용하여 지도 상에 위치에 따른 풍향, 풍속을 나타내며, 흐름을 표출합니다. 미국 기상청으로부터 주기적으로 기상정보를 수집한 후 서비스를 위한 변환을 자동화하는 작업을 진행 중입니다. 향후 게시되는 링크를 통해 다양한 기상정보의 조회 서비스를 제공할 예정입니다.

NullSchool 활용을 위한 설정 등은 하단에 기술된 단락을 참조하십시오. 

 

 

NullSchool 설치 및 셋팅

리눅스 서버에 node.js 와 npm 을 설치한 후에, github 로 부터 "earth" 를 복사하고 관련된 라이브러리 등을 설치합니다. 설치 및 환경 셋팅을 위한 명령어는 다음과 같습니다. 

git clone https://github.com/cambecc/earth cd earth npm install

※ 참조 사이트: https://github.com/cambecc/earth 

earth 모듈 설치가 성공적으로 완료되면, <earth 설치 디렉토리> 로 이동하여 다음과 같은 명령어를 실행하여 웹서버를 실행합니다.

node dev-server.js 8080 

웹서버가 실행되면, 다음과 같은 주소로 접속하여 서비스를 확인할 수 있습니다.

http://localhost:8080

서버는 정적 S3 버킷 호스팅을위한 스탠드 인 역할을 하기 때문에 거의 서버 측 로직을 포함하지 않으며,  "earth/public" 디렉토리에 있는 모든 파일들이 주로 사용됩니다. 주요 소스코드는 "public/index.html" 및 "public/libs/earth/*.js" 를 참조하십시오. 데이터 파일은 "public/data" 디렉토리에 있으며, "data/weather/current" 로 실시간으로 변환되어 전송된 날씨 레이어가 표출됩니니다. 

 

기상 데이터 생성 방법

기상 데이터는 미국 기상청 (National Weather Service)에서 운영하는 GFS (Global Forecast System)에 의해 생성된 데이터를 이용합니다. 예보는 매일 4 번 생성되며 NOMADS (http://nomads.ncep.noaa.gov/)에서 다운로드 할 수 있습니다. 파일은 GRIB2 형식이며 300 개가 넘는 레코드가 있으며, 특정 등온선에서 풍력 데이터를 시각화하기 위해 이러한 기록 중 일부만 필요합니다. 다음과 같은 명령어는 1000 hPa 바람 벡터를 다운로드하고 grib2json 유틸리티를 사용하여 JSON 형식으로 변환합니다. 
YYYYMMDD=<a date, for example: 20190115>
curl "http://nomads.ncep.noaa.gov/cgi-bin/filter_gfs_1p00.pl?file=gfs.t00z.pgrb2.1p00.f000&lev_10_m_above_ground=on&var_UGRD=on&var_VGRD=on&leftlon=0&rightlon=360&toplat=90&bottomlat=-90&dir=%2Fgfs.${YYYYMMDD}00" -o gfs.t00z.pgrb2.1p00.f000

 

grib2json -d -n -o current-wind-surface-level-gfs-1.0.json gfs.t00z.pgrb2.1p00.f000

 

cp current-wind-surface-level-gfs-1.0.json <earth-git-repository>/public/data/weather/current

 ※ grib2json 모듈 참조 사이트: https://github.com/cambecc/grib2json

 

구현된 Earth 사이트 링크

 

국가지점번호 지도서비스

> 서비스 바로가기

국내의 공공 부문 지도서비스, 대형포털의 지도서비스, 주소정보시스템, 공공개방포털 어디에서도 국가지점번호 전자지도를 찾을 수 없었습니다. 직접 제작했습니다. 그리고 최초로(현재 서비스하는 곳이 없는 것 같습니다) 지도 서비스로 개방합니다.


Progworks가 개방하는 국가지점번호 지도 서비스는 어떤 비즈니스 목적을 가진 사용자를 대상으로 하는 서비스는 아닙니다. 국가지점번호가 홍보 되고 있는 만큼 활용되지 않는 부족한 부분을 채우기 위한 단순한 지점 번호 형상 조회 서비스 입니다.
국가지점번호 데이터를 만드는 일은 그리 어렵지 않았지만 실제 서비스로 이행하는 부분은 수고를 필요로 합니다.

국가지점번호는 우리나라를 포함하는 가로 700Km, 세로 800Km 영역 내에 정의되는 10m 격자이지만, 지도서비스 및 활용 편의를 위하여 아래와 같이 격자크기를 구분하여 제작하였습니다.

데이터를 제작하고 PostGIS에 적재하고 보니 DB 크기가 무려 900 Gigabytes 입니다. 결국 목표로 하는 응답시간을 준수하도록 구성하는 것이 가장 어려운 작업이었습니다.

지점번호의 해당 지역 식별을 위하여 배경지도는 OSM(OpenStreetMap)을 설정했습니다. Vworld 배경 지도보다 지원하는 Scale 범위가 넓어 10m 그리드의 식별이 용이합니다. 대신 배경지도를 불러오는 속도가 느린 점은 양해 부탁드립니다.


국가지점번호 공간정보를 제작하고 서비스로 발행하는 일련의 과정에서 얻은 몇 가지는 다음과 같습니다.

첫번째, 공개SW인 PostGIS가 보인 성능에 놀랐습니다. 특히 국가지점번호 10m 격자의 경우 41억건의 데이터에 용량만 900Gb에 육박하는 빅테이블임에도 충분한 조회 성능을 보였습니다.


두번째, 클라우드 인프라가 가지는 유연성과 확장성의 편안함을 만끽했습니다. 최초 DBMS서버의 용량은 150Gb 였지만, 작업을 진행하며, 볼륨을 생성하고 연결(Attach)하는 간단한 작업만으로 부족한 디스크 용량을 확보할 수 있었습니다.

세번째, 클라우드를 통해 잘 설정된 개발환경 만으로 서비스 배포가 쉽다는 것입니다. 저희는 이미 제작된 소스에 간단한 레이어 추가 작업 후 클라우드 파운드리가 제공하는 배포도구를 통해 간단하게 지도서버를 배포할 수 있었습니다.


블로그 내 국가지점번호 관련 자료(하위 제목 클릭 시 바로가기)


> 서비스 바로가기

개요

며칠 전 리얼타임테크의 카이로스를 데이터 프로바이더로 설정하여 지도서비스를 발행하고, Disk 기반 DBMS 대비 응답 속도가 확실히 빠른 것을 확인할 수 있었다. 이에 동일한 환경에서 각각 어느정도 성능을 보이는지 Apache JMeter를 이용하여 시험을 진행

> 이전 포스트 확인 : 메인 메모리 기반 고성능 웹 맵 서비스 (WoW! Kairos)


목적

메인메모리 DBMS와 일반 DBMS가 보이는 성능 차이를 정량적으로 확인함


환경구성

1. Server : Linux Container, 가상서버 위에 설치되는 DBMS
2. DBMS : Kairos (제조사:리얼타임테크),  PostGIS
3. WAS(Web Application Server) : Linux Container(메모리 512M 할당) 에 설치된 Apache Tomcat

4. Test 대상 App : 자체 제작한 간단한 WMS
5. 테스트 도구 : Apache JMeter
6. 테스트 방법 : 동사시용자를 1명에서 10명까지 늘려가며 각 10번씩 호출하고 나온 계측 값을 평가


시험결과

1. 메인메모리 DBMS가 빠른 처리 성능으로 단일 테이블에 접근하는 WMS의 경우 동시에 수용할 수 있는 사용자가 더 많음을 확인 
2. 제한적인 경량 컨테이너 환경에서 카이로스와 공개SW인 PostGIS 모두 만족할 만한 성능을 보임
3. 경량컨테이너로 구성되는 개별 서비스를 Proxy로 웹서버 계층에서 통합한다면 저비용으로 고성능 서비스가 가능함
4. PostGIS의 경우 동시사용자 7명을 기준으로 초당 쓰루풋이 감소하여, 성능향상을 위한 확장이 요구되었고, Kairos의 경우 9명을 기준으로 성능향상을 위한 확장이 요구됨





서비스 바로가기 (Kairos 맵 서비스)

서비스 바로가기 (PostGIS 맵 서비스)

서비스바로가기

개요

WMS(Web Map Service) 제공을 위한 데이터 프로바이더 계층에 메인메모리 DBMS를 사용함. 메인메모리 DBMS는 국내 업체인 리얼타임테크의 "Kairos"를 사용하였음. 데이터 프로바이더로써 클라우드서비스가 제공하는 가상서버에 DBMS를 설치하고 PaaS가 제공하는 컨테이너에 WMS 어플리케이션을 적재함.

제조사 홈페이지 바로가기: http://www.realtimetech.co.kr



목적

GIS 응용 서비스 제공을 위하여 메인메모리 DBMS가 Disk DBMS와 비교할 때 어느 정도의 성능향상을 보이는지 확인함


환경구성

1. Server : Linux Container, 가상서버 위에 설치되는 DBMS
2. DBMS : Kairos (제조사:리얼타임테크)
3. WAS(Web Application Server) : Apache Tomcat


개발환경 및 배포

1. 이클립스에서 일반 웹앱을 제작하는 방법을 준수하고, 
   배포서버를 이클립스에 등록된 서버 중 CloudFoundry를 지정하고 연결계정을 등록하여 해당 서버로 배포함
2. 최초 제작된 웹 앱(WMS)을 복사하여 어플리케이션 명칭을 변경하여, 필요한 갯수만큼 클라우드(CloudFoundry)에 배포
   ( 웹 어플리케이션의 수평적 확장 적용)

구현

1. 다양한 데이터프로바이더 수용을 위해 성능제약이 존재하는 지오서버 등의 Pakage SW 사용을 배제하고
2. DBMS가 반환하는  GIS 자료구조( WKB, WKT)를 자바 도형(java.awt.Shape) 으로 변환하여 이미지 렌더링
  (흔히 말하는 GIS 엔진의 사용을 배제 : 이미 공간 DBMS가 훌륭한 GIS 서비스 엔진임)

구현결과

1. 연속지적도(전국 SHP 파일 용량 기준 약 30GB)를 기준으로 8개의 웹 앱으로 클러스터링 했을 경우,
   동일한 환경의 Disk DBMS (PostGIS)  대비 약 10배 이상의 성능 향상을 확인
2. 수평확장되는 웹 앱(WMS)의 갯수만큼 성능이 향상확인 (저비용 서버 확장)

성능향상을 위한 개선 사항

1. 인프라 레벨에서 물리 또는 가상으로 이용될 수 있는 가속장치가 있을 경우,
   렌더링시켜야하는 도면데이터 조회 후 이미지 렌더링 속도 약 20% 향상 가능
2. 데이터 프로바이더(DBMS) 계층의 수평적 확장으로 처리되는 WMS 서버의 갯수를 최적화 시킴으로써 동시에 수용할 수 있는 사용자 증가


적용 및 응용 분야

1. 실시간 변경되는 공간정보의 실시간 서비스 발행
2. 사용자에게 빨리 전달되야 하는 비싼(?) 데이터의 저장 및 서비스 발행에 유용


결론

1. 동일한 환경 조건일 경우, 메인메모리 DBMS가 일반적인 DBMS가 월등한 성능을 보이는 것을 확인
2. 제한적인 메모리의 용량을 고려할 때 메인메모리에 적재되어야 하는 데이터의 식별이 중요함


서비스 바로가기

> 서비스 비교 (Disk 기반 웹 맵서비스)



서비스바로가기

개요

PostGIS의 좌표 변환 SQL을 이용하여 좌표변환(Coordinates Transformation)을 진행하고 웹에 게시함 

geometry ST_Transform(geometry g1, integer srid);
geometry ST_Transform(geometry geom, text to_proj);
geometry ST_Transform(geometry geom, text from_proj, text to_proj);
geometry ST_Transform(geometry geom, text from_proj, integer to_srid);




목적

PostGIS 가 지원하는 좌표변환(ST_Transform) 함수가 국내에서 이용되는 좌표계에서 정확한 전환을 제공하는지 확인함


환경구성

1. Server : Linux Container, 가상서버 위에 설치되는 DBMS
2. DBMS : Postgre + PostGIS
3. WAS(Web Application Server) : Apache Tomcat


개발환경 및 배포

1. 이클립스에서 일반 웹앱을 제작하는 방법을 준수하고, 
   배포서버를 이클립스에 등록된 서버 중 CloudFoundry를 지정하고 연결계정을 등록하여 해당 서버로 배포함

구현

1. Datum 변환을 위한 축적계수가 등록되지 않은 5174좌표계에 대한 정확한 정의를 Postgre에 신규 등록 (User Custom SRS)
2. ST_Transform 함수를 이용하여 주요 업무기능 제작

구현결과

1. EPSG Database 에 등록된 좌표계 및 국내 좌표계에서의 전환을 정확하게 수행함을 확인함
2. 저사양 서버환경에서도 단 건 좌표변환의 경우 충분한 응답속도를 보이는 것을 확인


이용 제약 사항

1. 웹기반 환경에서 대용량 좌표 셋의 좌표 변환은 서비스 제공 여부는 지양하는 것이 좋음 


2. 위 그림에서와 같이 타원체가 변하는 투영좌표계(Projection Coordinate System) 에서 투영좌표계로 변환하는 것은 7번의 전환과정을 요구함
3. 이 때 순간적으로 많은 연산이 요구됨으로써 비교적 과도한 컴퓨팅 파워를 요구함
4. 따라서, 대용량 데이터의 좌표변환을 실시간으로 처리하는 로직의 적용 여부는 신중을 기해야 함


결론

1. PostGIS의 기본 설정만으로도 훌륭한 좌표변환서비스가 가능함을 확인


서비스 바로가기



개요

PaaS를 구현하는 클라우드 서비스로써 파스타 (CloudFoundry 계열) 플랫폼에서 제공하는 경량 컨테이너에서 동작하는 OGC WMS(Web Map Service)의 GetMap 인터페이스를 Sping Web 과 Java Graphics2D를 이용하여 구현한 후,
이를 클러스터링하여 OpenLayers에 통합하여 서비스 UI를 구현함

 

 

목적

클라우드서비스(PaaS)가 제공하는 컨테이너 환경에서 GIS응용어플리케이션이 만족할만한 성능을 제공하는지 확인함

 

환경구성

1. Server : Linux Container, 가상서버 위에 설치되는 DBMS
2. DBMS : Postgre + PostGIS
3. WAS(Web Application Server) : Apache Tomcat

 

개발환경 및 배포

1. 이클립스에서 일반 웹앱을 제작하는 방법을 준수하고,
   배포서버를 이클립스에 등록된 서버 중 CloudFoundry를 지정하고 연결계정을 등록하여 해당 서버로 배포함
2. 최초 제작된 웹 앱(WMS)을 복사하여 어플리케이션 명칭을 변경하여, 필요한 갯수만큼 클라우드(CloudFoundry)에 배포
   ( 웹 어플리케이션의 수평적 확장 적용)

구현

1. 다양한 데이터프로바이더 수용을 위해 성능제약이 존재하는 지오서버 등의 Pakage SW 사용을 배제하고
2. DBMS가 반환하는 결과목록을 일반적으로 적용되는 GIS 자료구조( WKB, WKT)로 변환하지않고, 직접 자바 도형(java.awt.Shape) 으로 변환
  (지오서버가 제공하는 WMS 대비 2배 이상의 성능 향상 확인)

구현결과

1. 연속지적도(전국 SHP 파일 용량 기준 약 30GB)를 기준으로 8개의 웹 앱으로 클러스터링 했을 경우,
   맵 타일 제작 과정 없이도 동적 지도서비스가 가능함을 확인
2. 수평확장되는 웹 앱(WMS)의 갯수만큼 성능 향상 확인 (저비용 서버 확장)
3. DBMS(1) + 컨테이너(8)의 구성만으로도 동시 접속자 100명 이상 수용가능함을 확인

 

성능향상을 위한 개선 사항

1. 인프라 레벨에서 물리 또는 가상으로 이용될 수 있는 가속장치가 있을 경우,
   렌더링시켜야하는 도면데이터 조회 후 이미지 렌더링 속도 약 20% 향상 가능
2. 데이터 프로바이더(DBMS) 계층의 수직적 확장 (가상 코어 증설) 및
   수평확장 (가상 서버 증설) 을 통해 동시에 수용할 수 있는 사용자 증가

 

적용 및 응용 분야

1. 실시간 변경되는 공간정보의 실시간 서비스 발행
2. 저비용으로 고성능 GIS 응용 서비스 제공

 

결론

1. 가상서버, 경량 컨테이너를 제공하는 물리서버의 성능 향상으로 클라우드컴퓨팅 환경에서 GIS 서비스가 충분한 성능을 제공함을 확인
2. 경우에 따라 흔히 GIS엔진이라고 하는 패키지SW 사용을 배제하고, 공간DBMS를 이용하는 코드의 최적화만으로 충분한 성능을 제공함을 확임

+ Recent posts