mysql] max_allowed_packet 관련 오류

max_allowed_packet에 대한 설명은 다음과 같다.

통신 패킷은 MySQL 서버로 전송되는 단일 SQL 문, 클라이언트로 전송되는 단일 행 또는 마스터 복제본 서버에서 슬레이브로 전송되는 바이너리 로그 이벤트입니다. MySQL 8.0 서버 또는 클라이언트에서 전송할 수 있는 가장 큰 패킷은 1GB입니다.

패킷이라고 나오지만 실제 통신에서 전달되는 크기이다.

이 값은 기본이 16M으로 되어 있으나, 대용량 처리로 인하여 값을 변경해야 할 수 있다. 변경은 my.cnf의 설정에서 다음을 변경하면 된다.(128M로 변경 샘플)

[mysqld]
max_allowed_packet=128M

변경 내역을 반영하려면, 서버를 재기동하거나 다음의 명령어로 재기동 없이 반영 가능하다.

# mysql 설치 위치

> ./bin/mysqladmin -h 127.0.0.1 -u root -p reload

참고 자료


Posted by lahuman

MongoDB 3.2.20 Authentication Setting

MongoDB 3.2.20 버젼으로 진행된다. 설치와 cluster가 완료 되었다는 가정하에 진행이 된다. 또한 ElasticSearch와 MongoDB는 mongo-connect를 이용하여 연결 되어 있다.

고객사의 요청으로 MongoDB 접근에 대한 권한을 설정해야 하는 일이 생겼다.

간단하게 설정으로 해결되는 문제로 생각했는데 삽질이 컸다.

현재 MongoDB의 구조는 다음과 같다.

MongoDB Architecute

총 5대의 서버로 구성되어 있으며, 2대는 데이터를 적재하는 MongoDB, 1대는 Primary/Slave의 투표를 하는 ARBITER, 클러스터의 메타 데이터 및 구성 설정을 저장하는 config 서버, 마지막으로 클라이언트 응용 프로그램과 공유 된 클러스터 사이의 인터페이스를 제공하는 쿼리 라우터 역할 하는 mongos로 구성된다.

인증을 위한 KeyFile 생성

MongoDB는 다음 2가지 인증 매커니즘을 지원 합니다.

본 글에서는 간단하게 사용이 가능한 SCRMA을 이용합니다

키 생성 명령어

openssl rand -base64 756 > <path-to-keyfile>
chmod 400 <path-to-keyfile>

생성된 키를 각 설정 파일에 설정 해야 한다.

security:
  keyFile: <path-to-keyfile>
replication:
  replSetName: <replicaSetName>
net:
   bindIp: localhost,<ip address>

Server-A(20001), Server-B(20002), Server-C(20003), Server-D(20004)의 설정파일에 security: keyFile: 이 모두 설정되어야 한다. Server-F(10001)는 실행시 option으로 처리 한다.

재기동 옵션 A~D, F

/usr/bin/mongod --config /etc/mongod_repl1.conf
/usr/bin/mongod --config /etc/mongod_repl2.conf
/usr/bin/mongod --config /etc/mongod_repl3_arbiter.conf
/usr/bin/mongod --config /etc/mongod_repl4_config.conf --configsvr
/usr/bin/mongos --configdb localhost:20004 --port 10001 --fork --logpath /var/lib/mongodb/mongod_mongos.log --chunkSize 1 --keyFile /etc/m_key

MongoDB 재기동 & 계정 생성

설정이 완료된 이후에 MongoDB를 재기동 한다.

재기동이 완료 되면 계정을 생성 한다.

여기서는 단순하게 계정을 생성 하도록 한다. 계정 생성은 mongos와 repl1에 모두 생성 해야 한다.

  1. mongos 설정 ~~~ $> mongo –port 10001

use admin; db.createUser( { user: “admin”, pwd: “1234”, roles: [ { role: “root”, db: “admin” } ] } );

2. repl1 설정

$> mongo –port 20001

use admin; db.createUser( { user: “admin”, pwd: “1234”, roles: [ { role: “root”, db: “admin” } ] } );

3. mongo-connector 실행(생성한 계정의 id/pw를 option에 추가 한다.)

/usr/bin/python /usr/bin/mongo-connector -m localhost:10001 -t localhost:9200 -d elastic2_doc_manager -a admin -p 1234 ~~~

테스트

계정 처리가 완료 되면, 데이터를 조회/삽입 해본다.

$> mongo --port 10001

mongos> db.test.find({})
Error: error: {
	"ok" : 0,
	"errmsg" : "not authorized on test to execute command { find: \"test\", filter: {} }",
	"code" : 13
}
mongos>
mongos> use admin
switched to db admin
mongos> db.auth("admin", "1234")
1
mongos> db.test.find({})
mongos> db.test.insert({"x":1, "y":10})
WriteResult({ "nInserted" : 1 })
mongos> db.test.find({})
{ "_id" : ObjectId("5b6b76a593ec1b18e1f5f441"), "x" : 1, "y" : 10 }

참고자료


Posted by lahuman

INT4 형식으로 저장된 IP 값을 Function을 이용하여 처리 하기

IP를 postgresql에 저장을 할때 문자열로 저장을 하기도 하지만, Network Address Types으로 제공도 된다.

NameStorage SizeDescription
cidr7 or 19 bytesIPv4 and IPv6 networks
inet7 or 19 bytesIPv4 and IPv6 hosts and networks
macaddr6 bytesMAC addresses

또한 다른 방식으로는 int8로 저장이 되기도 한다.

문제는 int4 형식으로 저장되었을 경우이다.

이 경우에는 음수값에 대하여 IP 형식으로 표출하기 위해서는 int4보다 큰 양수 형식으로 변환하여 표시 해야 한다.

간단하게 function을 이용하여 만들어 보았다.

create or replace function int2ip(intVal integer)
returns TABLE(
    ipVal text
)
as $$
begin
    if intVal < 0 then
      return QUERY SELECT ('0.0.0.0'::inet + ((2147483649+intVal)::bigint+2147483647::bigint))::text;
      -- return QUERY SELECT '0.0.0.0'::inet + ( 'x' || to_hex(intVal))::bit(32)::bigint
    else
      return QUERY SELECT ('0.0.0.0'::inet + intVal)::text;
    end if;
    
end; $$
language 'plpgsql';


-- 211.115.106.210
select int2ip(-747410734);

결과

int2ip
211.115.106.210/32

postgresql을 설치 하지 않고도 온라인에서 간단하게 테스트 하는 사이트를 이용하였다..

참고 사이트


Posted by lahuman

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

면접 질문 

1. 객체지향 개발 5대 원리: SOLID

- 기본중의 기본! 기본은 늘 중요 하니까!!!

2. SpringFramework 의 중요 컨셉 3가지

1) IOC : 제어의 역전 / 의존성 주입
2) AOP : 관심의 분리
3) PSA : 일관성 있는 추상화

3. CSS 선택자 종류와 속도

1) 전체 선택자
2) 태그 선택자
3) 클래스 선택자
4) 아이디 선택자
5) 복합 선택자
6) 속성 선택자
7) 가상 클래스 선택자
8) 기타(언어, 부정, 목적, UI요소)

4. JPA를 사용하면서 어려운점

1) 영속성 이해
2) 다중 JOIN QUERY
3) 관계 DB와 객체 지향적 DB 설계

5. MSA 관련

1) Eureka
2) Zuul
3) Histrix 
4) 서킷 브레이커 등

6. CQRS란 무엇인가?

7. 데이터베이스 Isolation Level

8. 업무 능력이 떨어지는 동료와 일할 경우 어떻게 대처할 것인가?

참고 자료

  1. 객체지향 개발 5대 원리: SOLID
  2. Spring 의 시작, 프레임워크의 구성요소와 동작원리
  3. CSS: 선택자(Selector) 이해
  4. CQRS란 무엇인가?
  5. 데이터베이스 Isolation Level


Posted by lahuman