분류 전체보기
- 스타일 초기화 코드 2022.12.28
- [WebAPI] MessagePort 2022.09.14
- [Web API] Web Workers API 2022.09.14
- [Kafka + Zookeeper] 장애 테스트 2022.09.07
- imagemagick + Buffer 에 대하여 2022.09.06
- Mysql UDF 작성 2019.03.20
- Crawling을 위한 Selenium, Chrome Driver 설치 2018.07.15
- XMLHttpRequest 간단 소개 2018.03.23
스타일 초기화 코드
[WebAPI] MessagePort
Web Workers 를 공부하다 SharedWorker 는 각 스크립트들이 MessagePort
를 통해 데이터 전달을 한다는 것을 알게 되었고 관련하여 공부를 하게 됨
MessagePort 는 MessageChannel
간에 데이터를 전달하고 받을 수 있도록 하는 두 개의 ports 중 한 개를 나타냄.
MessageChannel 생성후 channel.port1
과 channel.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 |
---|
[Web API] Web Workers API
스크립트 연산을 주 실행 스레드와 분리된 별도의 백그라운드 스레드에서 실행할 수 있는 기술
종류
- 전용 워커 - 단일 스크립트에서만 사용하는 워커 : DedicatedWorkerGlobalScope
- 공유 워커 - 여러 스크립트에서 공유하는 워커 : SharedWorkerGlobalScope
- SharedWorker
- 서비스 워커 - 웹 응용 프로그램, 브라우저, 네트워크 사이의 프록시 서버 역할
- 네트워크 요청 인터셉트
- 푸시 알람
- 백그라운드 동기화
- 오디오 워커 - 직접적인 오디오 처리 능력 제공
제한사항
- 워커에서 DOM 직접 조작 불가능
- window 의 일부 메소드 사용 불가능
- 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 |
---|
[Kafka + Zookeeper] 장애 테스트
테스트 환경
- Zookeeper 3대를 앙상블로 구현
- Kafka 의 server.properties 에 zookeeper 설정
설정 과정
- 한 서버에서 Zookeeper 프로세스 3대를 띄어서 앙상블로 구현
- 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 + Buffer 에 대하여
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 작성
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)
더 다양한 명렁에 대한 정리는 다음 블로그를 참조하면 된다.
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 메모리 관리 방식은 따로 공부를 해봐야겠다.
만약 255 이상의 데이터를 보내고 싶다면 어떻게 해야 할까? UDF_INIT * initid 인자를 이용해야 한다.
UDF_INIT 구조체는 result에 대한 정보를 정의할 수가 있다.
만약 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이 나뉜다고 생각하면 쉽다.
'IT > BackEnd' 카테고리의 다른 글
Crawling을 위한 Selenium, Chrome Driver 설치 (0) | 2018.07.15 |
---|---|
SSL 인증서 발급 및 Nginx 적용. 그리고 체인 이슈 해결하기 (1) | 2018.02.28 |
Atom Remote-FTP package 소개 (0) | 2017.11.02 |
JWT (0) | 2017.09.27 |
Crawling을 위한 Selenium, Chrome Driver 설치
이 포스팅은 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
-
./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란?
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 요청을 하게 된다.
정리하면 아래와 같다.
XMLHttpRequest()를 통해 오브젝트 생성을 한다.
그 후 open() 함수를 통해 메소드 방식, url, 그리고 비동기 여부를 설정한다.
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 |
---|