Python] Postgresql결과를 CSV 파일로 저장 하기

SQL의 결과를 CSV 파일로 저장하는 간단한 프로그램이다.

제약 조건은 다음과 같다.

  1. 1000만건 이상의 데이터가 있으므로 페이징 처리가 되어야 한다.
  2. 테이블명은 년_월_일_시간 형식이다.
  3. 시간은 2시간씩 텀을 가지고 있다.
  4. 결과 파일은 테이블 명과 동일 해야 한다.

프로그램은 다음과 같다.

# -*- coding: utf-8 -*
import psycopg2
import csv
import time

pagination_size = 100000
table_name = "wk_log_2018_08_{}.tb_log_http_2018_08_{}_{} "
sql = "select host, uri, srvAdd, cliAdd, srvprt, rcvTime from " + table_name
sql_cnt = "select count(*) from " + table_name
limit = " offset {} limit " + str(pagination_size)


def save_db_data(rows, save_file):
    with open(save_file + ".log", 'wb') as csv_file:
        csv_w = csv.writer(csv_file, delimiter="|")
        for row in rows:
            row_list = list(row)
            csv_w.writerow(row_list)


def use_pagination():
    try:
        with psycopg2.connect("dbname='admin' user='admin' host='localhost' password='admin'") as conn:
            with conn.cursor() as cur:
                for day in xrange(1, 19):
                    for hour in xrange(0, 24, 2):
                        cur.execute(sql_cnt.format("{0:02d}".format(day), "{0:02d}".format(day),
                                                   "{0:02d}".format(hour)))
                        time.sleep(1)
                        for x in xrange(0, cur.fetchone()[0], pagination_size):
                            cur.execute(sql.format("{0:02d}".format(day), "{0:02d}".format(day),
                                                   "{0:02d}".format(hour)) + limit.format(x))
                            rows = cur.fetchall()
                            if len(rows) > 0:
                                save_db_data(rows, table_name.format("{0:02d}".format(day), "{0:02d}".format(day),
                                                                     "{0:02d}".format(hour)) + "_" + str(x))
                            else:
                                break;
    except Exception as e:
        print("I am unable to connect to the database: " + str(e))


if __name__ == "__main__":
    use_pagination()

파이썬을 이용하면 참 쉽줘~

Posted by lahuman

SpringBoot에서 Websocket 사용하기

Websocket 이란?

서버와 클라이언트 사이에 양방향 통신 채널을 구축할 수 있는 통신 프로토콜이다. 동작 방식은 먼저 HTTP 통신을 연결하고 이후 Upgrade 헤더를 보내 양방향 연결로 업그레이드한다. Websocket은 최신 브라우저에서는 대부분 지원한다.

전체 소스는 참고 내역에 있는 소스를 확인하면 된다.

주요 설정은 다음과 같다.

1. WebSocket Configuration

package com.example.websocketdemo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.*;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.setApplicationDestinationPrefixes("/app");
        registry.enableSimpleBroker("/topic");
    }
}

@EnableWebSocketMessageBroker 은 websocket 서버를 사용한다는 설정이다. 또한 WebSocketMessageBrokerConfigure를 상속 받아 몇몇 메소드를 구현하여 websocket 연결 속성을 설정한다. registerStompEndpoints를 이용하여 클라이언트에서 websocket에 접속하는 endpoint를 등록한다. withSockJS()를 이용시에는 브라우져에서 websocket을 지원하지 않을 경우 fallback 옵션을 활성화 하는데 사용됩니다.

메소드 이름에 STOMP(Simple Text Oriented Messaging Protocol)라는 단어가 있다. 이는 스프링프레임워크의 STOMP 구현체를 사용한다는 의미다. STOMP가 필요 한 이유는 websocket은 통신 프로토콜이지 특정 주제에 가입한 사용자에게 메시지를 전송하는 기능을 제공하지 않는다. 이를 쉽게 사용하기 위해 STOMP를 사용한다.

두변째 메소드configureMessageBroker는 한 클라이언트에서 다른 클라이언트로 메시지를 라우팅 할 때 사용하는 브로커를 구성한다. 첫번째 라인에서 정의된 /app로 시작하는 메시지만 메시지 헨들러로 라우팅한다고 정의한다. 두번째 라인에서 정의된 /topic로 시작하는 주제를 가진 메시지를 핸들러로 라우팅하여 해당 주제에 가입한 모든 클라이언트에게 메시지를 방송한다.

2. ChatController

package com.example.websocketdemo.controller;

import com.example.websocketdemo.model.ChatMessage;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;

@Controller
public class ChatController {

    @MessageMapping("/chat.sendMessage")
    @SendTo("/topic/public")
    public ChatMessage sendMessage(@Payload ChatMessage chatMessage) {
        return chatMessage;
    }

    @MessageMapping("/chat.addUser")
    @SendTo("/topic/public")
    public ChatMessage addUser(@Payload ChatMessage chatMessage, 
                               SimpMessageHeaderAccessor headerAccessor) {
        // Add username in web socket session
        headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
        return chatMessage;
    }

}

@MessageMapping는 클라이언트에서 보내는 메시지를 매핑한다. 호출 되는 주소는 /app/chart.addUer/app/chat.sendMessage가 된다.

3. main.js

javascript 에서 실제 사용은 다음 같이 사용한다.

function connect(event) {
    username = document.querySelector('#name').value.trim();

    if(username) {
        usernamePage.classList.add('hidden');
        chatPage.classList.remove('hidden');

        var socket = new SockJS('/ws');
        stompClient = Stomp.over(socket);

        stompClient.connect({}, onConnected, onError);
    }
    event.preventDefault();
}


function onConnected() {
    // Subscribe to the Public Topic
    stompClient.subscribe('/topic/public', onMessageReceived);

    // Tell your username to the server
    stompClient.send("/app/chat.addUser",
        {},
        JSON.stringify({sender: username, type: 'JOIN'})
    )
    connectingElement.classList.add('hidden');
}

.... 중략

connect를 통해 클라이언트는 websocket을 연결 합니다. 연결에 성공하면 /topic/public 주제에 가입하여 메시지를 주고 받습니다.

참고 내역


Posted by lahuman

Python3.7 설치와 새로 알게된 몇가지 사실

Python3.7을 설치 하고 가상환경을 설정하는 것을 테스트 해보았다. Python2.X 에서는 virtualenv 라는 모듈을 설치하여야 가상환경을 설정 할 수 있었지만, 3.7에서는 기본으로 제공되었다.

Python3.7 소스 설치 하기

How to Install Python 3.7.0 on CentOS/RHEL 7/6 & Fedora 28-23 를 확인하고 해당 내역을 따라서 진행 하면 다음과 같다.

# 필수 모듈 설치
$> yum install gcc openssl-devel bzip2-devel

# Python 3.7 download & 설치
$> cd /usr/src
$> wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgz
$> tar xzf Python-3.7.0.tgz
$> cd Python-3.7.0
$> ./configure --enable-optimizations
$> make altinstall

위와 같이 설치를 진행하면 다음 모듈이 설치 되지 않았다는 오류 메시지를 make altinstall 를 실행시 만난다. 아래와 같이 두개의 모듈을 추가로 설치 하자.

# 누락된 모듈 설치
$> yum install zlib-devel
$> yum install libffi-devel

설치가 완료 메시지를 보면 pip와 몇가지 모듈이 함께 설치 완료 되었다는 것을 확인할 수 있다.

Python3.7 사용시 처리 사항

  1. virtualenv 는 python3.7에 기본적으로 설치된다. 다음과 같은 명령어를 이용하면 가상 환경을 생성 할 수 있다.
# python 3.7에서 가상환경 만들기
# python3.7 -m venv <directory>
$> python3.7 -m venv venv

# 생성된 가상환경 적용하기
$> source venv/bin/activate
(venvv) $>

# 가상환경 나오기
(venvv) $> deactivate
$>
  1. openssl 을 설치 하지 않은 상태에서 python을 설치할 경우 pip를 이용시 SSL 오류 발생한다.
# 처리 방법은 openssl, openssl-devel 을 설치후 python3.7 재설치
$> yum install openssl opennssl-devel
$> python install cmmand

참조 자료

  1. How to Install Python 3.7.0 on CentOS/RHEL 7/6 & Fedora 28-23


Posted by lahuman