Reset.css
Normalize.css

Web Workers 를 공부하다 SharedWorker 는 각 스크립트들이 MessagePort 를 통해 데이터 전달을 한다는 것을 알게 되었고 관련하여 공부를 하게 됨

MessagePort 는 MessageChannel 간에 데이터를 전달하고 받을 수 있도록 하는 두 개의 ports 중 한 개를 나타냄.

MessageChannel 생성후 channel.port1channel.port2 를 통해 데이터를 전달하고 받을 수 있음

// main.js
const channel = new MessageChannel();

iframe.addEventListener("load", onLoad);
function onLoad() {
  channel.port1.onmessage = function(e) { 
  }

  iframe.contentWindow.postMessage("Hello", "*", [channel.port2]);
}

// iframe.html
<script>
  window.addEventListener('message', onMessage);
  function onMessage(e) {
    e.ports[0].postMessage('Message Back');
  }
</script>

postMessage() 함수의 세번째 인자 transfer 를 사용하면 객체의 소유권을 수신측에 전달함. 송신 측에서는 더 이상 사용 불가능

function onLoad() { 
  channel.port1.onmessage = function(e) {  
  } 
  iframe.contentWindow.postMessage("Hello One", "*", [channel.port2]);
  iframe.contentWindow.postMessage("Hello Two", "*", [channel.port2]); // Uncaught DOMException: Failed to execute 'postMessage' on 'Window': Port at index 0 is already neutered.
}

'IT > Web' 카테고리의 다른 글

[Web API] Web Workers API  (0) 2022.09.14

스크립트 연산을 주 실행 스레드와 분리된 별도의 백그라운드 스레드에서 실행할 수 있는 기술

종류

  • 전용 워커 - 단일 스크립트에서만 사용하는 워커 : DedicatedWorkerGlobalScope
  • 공유 워커 - 여러 스크립트에서 공유하는 워커 : SharedWorkerGlobalScope
    • SharedWorker
  • 서비스 워커 - 웹 응용 프로그램, 브라우저, 네트워크 사이의 프록시 서버 역할
    • 네트워크 요청 인터셉트
    • 푸시 알람
    • 백그라운드 동기화
  • 오디오 워커 - 직접적인 오디오 처리 능력 제공

제한사항

  1. 워커에서 DOM 직접 조작 불가능
  2. window 의 일부 메소드 사용 불가능
  3. WebSocket 과 IndexedDB 는 사용 가능

데이터 교환

워커와 메인 스레드 간의 데이터 교환은 메세지 시스템 사용 (postMessage, onmessage) 데이터는 복사하고 공유는 하지 않음

Example

!! 워커 생성 시 Worker() 생성자 사용함. 워커 스레드 생성시에는 현재 window 와는 다른 전역 Context 에서 생성해야 동작함
(테스트 위해서 $ npx lite-server 이용했음)


전용워커)

// main.js
if (window.Worker) {
  const myWorker = new Worker("worker.js");
  const payload = {flag: 1}
  myWorker.postMessage(payload);

  myWorker.onmessage = function(e) {
    if (payload.data == e.data) {
      console.log('Never called');
    } else {
      console.log('Always called');
    }
  }
}

// worker.js
onmessage = function(e) {
  console.log(e.data) // {flag: 1}

  e.data.flag = 2;  // 데이터를 공유하지 않기에 main.js 의 payload 객체에 영향을 주지 않음

  postMessage(e.data.flag);
}

공유워커)

// main.js
if (window.SharedWorker) {
  const myWorker = new SharedWorker("worker.js");
  const payload = {flag: 1}
  myWorker.port.postMessage(payload);
  myWorker.port.onmessage = function(e) {
    console.log(e.data);
  }
}

// worker.js
onconnect = function (event) {
  const port = event.ports[0];

  port.onmessage = function(e) {
    port.postMessage('');
  }
}

'IT > Web' 카테고리의 다른 글

[WebAPI] MessagePort  (0) 2022.09.14

테스트 환경

  • Zookeeper 3대를 앙상블로 구현
  • Kafka 의 server.properties 에 zookeeper 설정

설정 과정

  1. 한 서버에서 Zookeeper 프로세스 3대를 띄어서 앙상블로 구현
  2. Kafka 의 server.properties 설정 정보]
zookeeper.connect=localhost:2181,localhost:2182,localhost:2183

테스트


토픽 생성 Command

$ /path/kafka/kafka-topics.sh --create --topic test-topic --bootstrap-server localhost:9092 --replication-factor 1 --partitions 4

Zookeeper 서비스를 종료 (1개부터 2개까지 종료시키기)

- Zookeeper 프로세스 3개 중에서 1개를 종료

$ systemctl stop zookeeper1
$ /path/kafka/kafka-topics.sh --create --topic test-topic --bootstrap-server localhost:9092 --replication-factor 1 --partitions 4

결과) Created topic test-topic

- Zookeeper 프로세스를 3개 중에서 2개까지 종료

$ systemctl stop zookeeper2 
$ /path/kafka/kafka-topics.sh --create --topic test-topic --bootstrap-server localhost:9092 --replication-factor 1 --partitions 4  

결과) 토픽 생성하지 못하고 계속 대기...

- Zookeeper 프로세스를 다시 2개 이상으로 살리기

$ systemctl start zookeeper2

결과) 이후에 토픽 생성 명령어를 실행하면, 정상적으로 수행이 된다.

Imagemagick 모듈

imagemagick 를 사용하게 된 이유 : 이미지 프로세싱 하는 타 모듈은 memoryMaxSize 가 설정되어 있어서 큰 사이즈의 이미지는 처리가 안됨. (limit 을 늘리지 않은 이상)

limit 없이 사용할 수 있는 모듈을 찾다가 imagemagick 모듈을 사용하게 됨

아래는 Example 파일 경로를 읽고 결과물을 파일로 저장하는 코드

URL 로 데이터를 읽고, CDN 에 업로드를 해야 하는 과정이 추가된다면? 파일을 굳이 저장할 필요 없이 Buffer 를 활용하면 됨.

const im = require('imagemagick')

const opt = {};
opt.srcPath = './src/to/path';
opt.dstPath = './dst/to/path';
opt.width = 250;

im.resize(opt, (err, stdout, stderr) => {
  //stdout
});

Node.js 의 Buffer

Buffer : 데이터를 한 곳에 일시적으로 보관할 수 있는 메모리의 영역을 말함

Node.js 에서는 Buffer 를 바이너리 데이터를 저장할 수 있는 특수한 유형의 객체로서 제공한다.

  • 바이너리데이터는 0과 1로 이루어져있음
  • Buffer 객체는 16진수 바이트로 표현 (ex. <Buffer 44 12 3e ff ... )

Node.js 에서 Buffer 를 어떻게 활용하는지 알기 위해서 fs 모듈을 이용한 테스트를 진행해봄

const fs = require('fs');

var srcData = fs.readFileSync('sample.jpg');
console.log(srcData);  // <Buffer aa bb ...>

fs.writeFileSync('copySample.jpg');
var dstData = fs.readFileSync('copySample.jpg');
console.log(dstData);  // <Buffer aa bb ...>

결과 : fs 로 jpg 파일을 읽으면 바이너리 데이터를 Buffer 형식으로 제공


imagemagick 와 Node.js 의 Buffer 를 활용해보기

imagemagick 의 srcData 옵션으로 이미지의 데이터를 Buffer 형태로 제공

im.resize 결과로 stdout 데이터는 바이너리로 제공됨 (binary 데이터를 처리하기 위해서는 buffer 객체에 담아야 함)

테스트로 알게 된 것은 Buffer.from(data, 'encoding') 을 활용하여 버퍼에 담을 데이터 형식을 지정할 수 있다라는 것이다.

const im = require('imagemagick');
const fs = require('fs');

var srcData = fs.readFileSync('sample.jpg');

const opt = {}; 
opt.srcData = srcData; 
opt.width = 250; 
im.resize(opt, (err, stdout, stderr) => { 
  // stdout 데이터는 binary 형식으로 전달됨 -> 파일을 저장하기 위해 Buffer 형태로 제공해야 함
  fs.writeFileSync('resizedSample.jpg', Buffer.from(stdout, 'binary'));
});

그럼 base64 인코딩 된 데이터도 버퍼에 담아서 처리도 할 수 있다.

var imageBase64EncodedData = 'R01GOD1AAE.....BADs=';

const opt = {}; 
opt.srcData = Buffer.from(imageBase64EncodedData, 'base64');
opt.width = 250;

im.resize(opt, (err, stdout, stderr) => {  
  fs.writeFileSync('resizedSample.jpg', Buffer.from(stdout, 'binary')); 
});

Mysql UDF 작성

1) 공유 라이브러리 파일 작성

윈도우는 lib, linux/unix 계열은 so 파일을 만들어야 한다.

CMake 를 이용하는게 더 간편하기에 자주 사용하는 명령어에 대해서만 간략하게 정리한다.

# cmake 최소 요구 버전 - 이하일시 에러 발생 CMAKE_MINIMUM_REQUIRED (VERSION 3.5)  # CMAKE_PROJECT_NAME 설정 PROJECT(name)  # project 이름을 콘솔에 출력 MESSAGE(${CMAKE_PROJECT_NAME})  # 전처리기 매크로 추가 ADD_DEFINITIONS(-fPIC)  # 바이너리 생성 ADD_EXECUTABLE(app foo.c bar.c)  # 라이브러리 생성 ADD_LIBRARY(app SHARED foo.c bar.c )  # 각 소스 파일에서 #include 구문으로 포함시킨 헤더 파일을 찾을 디렉토리 추가 INCLUDE_DIRECTORIES(include /usr/local/include)  # 링크 과정에서 필요한 라이브러리 디렉토리 목록 추가 LINK_DIRECTORIES(lib /usr/local/lib)  # Target에 포함된 소스 파일에서 #include 구문에 포함된 헤더 파일 찾을 디렉토리 추가 TARGET_INCLUDE_DIRECTORIES(include /usr/local/include) 

더 다양한 명렁에 대한 정리는 다음 블로그를 참조하면 된다.

CMake 명령어 정리
CMake Tutorial

2) UDF 작성

위에서 CMAKE를 작성하는 법에 대해 알아보았다. 이제 공유 라이브러리를 만들기 위해 필요한 소스 파일을 작성해야 한다.

Mysql UDF 는 2가지 종류로 작성할 수 있다.

  • xxx_init() -> xxx() -> xxx_deinit()
  • xxx_init() -> xxx_clear() -> xxx_add() -> xxx() -> xxx_deinit()

xxx()는 본문 함수이고 UDF를 추가하기 위해서는 위의 형식을 지키며 함수를 추가해줘야 한다. 첫번째 형식이 가장 일반적인 경우이다.

두번째 형식은 Aggregate UDF라고 Group으로 xxx() 함수를 적용시킬 수 있는 경우이다.

ex) select sum(cost) from table group by dept;

Header 추가

먼저 각 인자들의 Type 을 위해 mysql 헤더를 추가한다.

#include <mysql.h> 

init, deinit

기본적으로 init, deinit 과정이 있으며 각각 다음과 같은 역할을 한다.

init

  • set default properties
  • validate arguments
  • allocate resources
  • signal an error

deinit

  • de-allocate any resources
my_bool xxx_init(UDF_INIT * initid, UDF_ARGS * args, char * message) 
void xxx_deinit(UDF_INIT * initid); 

본문 함수 작성

xxx() 는 본문함수이며 return 값에 따라 2가지 타입으로 선언할 수 있다.

// Integer Type long long xxx(UDF_INIT *initid, UDF_ARGS *args, my_bool *is_null, my_bool *error);  // Real type double xxx(UDF_INIT *initid, UDF_ARGS *args, my_bool *is_null, my_bool *error);  // String type, DECIMCAL type도 동일하게 작성 char * xxx(UDF_INIT *initid, UDF_ARGS * args, char * result, unsigned long length, my_bool *is_null, my_bool *error); 

string return value에 대해서만 더 자세하게 적도록 한다.

char * xxx(UDF_INIT *initid, UDF_ARGS * args, char * result, unsigned long length, my_bool *is_null, my_bool *error) {     // xxx(arg1, arg2) 입력 파라미터 값을 받을 수 있음.     char arg1 = args->args[0];      char arg2 = args->args[1];      // 로직 작성     char *value = fn();      // 지역변수를 return하게 되면 안된다. result에 담아서 보내야 한다.     strcpy(result, value);      return result; } 

다음 링크를 참고하면 Mysql에서는 xxx() 함수에 대해 result라는 버퍼를 제공하는데(최적화 이유) 255 크기로 제한이 되어 있다. -- 운영체제와 mysql 메모리 관리 방식은 따로 공부를 해봐야겠다.

mysql udf return values

만약 255 이상의 데이터를 보내고 싶다면 어떻게 해야 할까? UDF_INIT * initid 인자를 이용해야 한다.

UDF_INIT 구조체는 result에 대한 정보를 정의할 수가 있다.

mysql UDF_INIT Ref

만약 xxx_init()에서 mysql에서 제공하는 buffer(result) 를 이용하지 않고 새로 만들었다면 아래와 같이 사용가능하다.

xxx_init(..) {     char * arr = malloc(1024);     initid->ptr = arr;     initid->max_length = 1024; }  char * xxx(UDF_INIT *initid, UDF_ARGS * args, char * result, unsigned long length, my_bool *is_null, my_bool *error) {     char * buffer = initid->ptr;      return buffer; }  xxx_deinit(..) {     char * arr = initid->ptr;      if(arr) {         free(arr);     } } 

3) Mysql 에 UDF 추가

linux 를 기준으로 설명하면 /usr/local/mysql/lib/plugin 디렉토리가 존재한다.

생성한 so 파일을 해당 디렉토리에 넣어준다.

다음 명령을 통해 udf 를 추가할 수 있다.

create function func1 returns string soname 'lib_mysqludf.so'; create function func2 returns integer soname 'lib_mysqludf.so'; 

정리

CMake부터 시작하여 UDF를 작성하여 mysql에 추가해보는 시간을 가졌다. 실습을 해보면서 링크 및 컴파일에 대한 개념이 부족함을 알게 되었고, Mysql과 운영체제에서의 메모리 관리 방식에 대해서도 공부를 해봐야 겠다는 생각이 들었다.

Aggregate UDF 에 대해서는 다른 블로그를 참고하면서 직접 구현해보면 좋을 것 같다. clear를 기준으로 group이 나뉜다고 생각하면 쉽다.

Selenium_install

이 포스팅은 AWS EC2 ubuntu에 Python과 Selenium을 설치하여 연동하는데 도움을 줄 수 있는 글입니다.

Python Install

apt로 python을 설치한다.

sudo apt install python

만약 아래와 같은 에러가 뜬다면

sudo apt-get install python-pip
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package python-pip

python-pip 가 universe repositories에 있기 때문에 local repo에다가 추가해줘야 한다.

sudo apt-get install software-properties-common
sudo apt-add-repository universe
sudo apt-get update
sudo apt-get install python-pip

Selenium Instal On Ubuntu

  1. Chrome Driver 설치

  2. ./install.sh 실행

만약 아래와 같은 에러가 뜬다면

※ selenium.common.exceptions.WebDriverException:
Message: unknown error: cannot find Chrome binary
// Add Key
$ wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -

// Set repo
$ echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | sudo tee /etc/apt/sources.list.d/google-chrome.list

// Install package
$ sudo apt-get update
$ sudo apt-get install google-chrome-stable

Chrome Headless 사용

GUI 가 지원이 안되는 ubuntu에서 chrome driver를 이용하면 에러가 나는 경우가 있다.

이 경우에는 아래와 같이 headless option을 설정해줘야 한다.

options = webdriver.ChromeOptions()
    options.add_argument('--disable-extensions')
    options.add_argument('--headless')
    options.add_argument('--disable-gpu')
    options.add_argument('--no-sandbox')
    return webdriver.Chrome(chrome_options=options)

'IT > BackEnd' 카테고리의 다른 글

Mysql UDF 작성  (0) 2019.03.20
SSL 인증서 발급 및 Nginx 적용. 그리고 체인 이슈 해결하기  (1) 2018.02.28
Atom Remote-FTP package 소개  (0) 2017.11.02
JWT  (0) 2017.09.27

XMLHttpRequest란?

XMLHttpRequest는 HTTP를 통해서 쉽게 데이터를 받을 수 있게 해주는 오브젝트를 제공한다.

Ajax로 실행되는 HTTP 통신도 XMLHttpRequest 규격을 이용하고 있다.

How to use?

var xhr = new XMLHttpRequest();

xhr.open('GET', 'http://localhost:3000/', true);

xhr.send(null);

위 코드를 사용하게 되면 http://localhost:3000 로 비동기로 HTTP 요청을 하게 된다.

정리하면 아래와 같다.

  1. XMLHttpRequest()를 통해 오브젝트 생성을 한다.

  2. 그 후 open() 함수를 통해 메소드 방식, url, 그리고 비동기 여부를 설정한다.

  3. send(data) 로 설정한 방식대로 request를 보낸다.(data는 null이어도 된다.)

How to response?

xhr.onreadystatechange = function()
{
    if(xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
        console.log(xhr.responseText);
    }
}

위 코드를 사용하게 XMLHttpRequest 오브젝트에 onreadystatechange를 등록하면 request 후 해당 server로부터 status code가 200이면 response 받은 text를 콘솔에 출력한다.

How to send data?

xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

var data = 'id=' + id + '&' + 'pw' + pw;
xhr.send(data);

x-www-form-urlencoded로 Content-type을 지정하면 data는 form 형식을 유지해야 한다.

xhr.setRequestHeader('Content-Type', 'application/json');

var data = {
  id: id,
  pw: pw
}

xhr.send(data);

application/json 타입이면 data는 json 타입이어야 한다.

'IT > FrontEnd' 카테고리의 다른 글

ajax를 사용하기 그리고 사용하며..  (0) 2018.01.13

+ Recent posts