최고 효율의 Proxy 서버를 찾아서(Node, Apache, Socks)
인덱스
1. 프록시란
2. 프록시 서버가 필요하게 된 이유
3. NodeJS를 활용한 Proxy Server
4. Apache를 활용한 Proxy Server
5. Socks5를 활용한 Proxy Server
6. 스트레스 테스트 및 결과
프록시란
프록시(Proxy)는 "대리"의 의미로, 인터넷과 관련해서 쓰이는 경우, 특히 내부 네트워크에서 인터넷 접속을 할 때에, 빠른 액세스나 안전한 통신등을 확보하기 위한 중계서버를 "프록시 서버"라고 일컫는다. 클라이언트와 Web서버의 중간에 위치하고 있어, 대신 통신을 받아 주는 것이 프록시 서버이다.
출처: https://engineer-mole.tistory.com/288
프록시 서버가 필요 하게 된 이유
모사의 API를 이용하다 보니 문제가 하나 있었다.
Response는 굉장히 빠른데, API Rule에 따라 데이터를 받기위해서는 요청해야 할 Request 또한 엄청 많았다.
사실 Request가 아무리 많아도 Response만 빠르게 빠르게 잘 return 된다면 concurrency를 잘 활용해서 처리할 수 있었다.
그러나 동시에 특정 숫자의 요청이 넘어가게 되니 Throttle 이 생기는 듯 했다.
IP 단위로 쓰로틀이 생기는것인지 확인해보기 위해 빠르게 Lightsail + Node를 활용해 Proxy 서버를 만들어 테스트를 해 보았고, 예상대로 IP 단위대로 쓰로틀이 형성되는 것 이었다.
이로서 Proxy 서버를 구축해야하는 임무가 생겼다.
이를 적용시키기 위해 세가지의 Proxy 서버 종류를 확인했고, 최적의 Proxy를 찾기위해 제작 및 스트레스 테스트를 진행 해 볼 것이다.
NodeJS를 활용한 Proxy Server
1. 기본 세팅(NodeJS 설치 및 pm2 설치는 생략한다.)
# Create Directory
$ mkdir proxy
# Enter to Directory
$ cd proxy
# Setting up NPM
$ npm init
# Install required packages
$ npm install express
$ npm install http-proxy
2. index.js 파일 생성 및 코드 입력
const express = require('express');
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer({});
const app = express();
app.all('*', function(req, res) {
proxy.web(req, res, { target: `${req._parsedUrl.protocol}//${req.hostname}` });
});
app.listen(3000);
3. pm2 를 사용하여 proxy 서버 실행
$ pm2 start --name proxy index.js
4. Proxy 사용 방법
const response = await axios.get('https://www.naver.com', {
proxy: {
host: proxy_server_ip,
port: proxy_server_port
}
});
Apache를 활용한 Proxy Server
1. Apache 설정 변경 (필자는 Lightsail - Node.js 서버를 생성하여 설치되어있는 Apache를 활용하였다.)
1 - 1. proxy.conf 파일 생성 및 코드 입력
# Enter to Apache config directory
$ cd /opt/bitnami/apache/conf
# Get to vhosts directory
$ cd vhosts
# Create and Edit proxy.conf file
$ vi proxy.conf
<VirtualHost *:3001>
ProxyRequests On
SSLProxyEngine On
SSLProxyCheckPeerCN on
SSLProxyCheckPeerExpire on
<Proxy *>
</Proxy>
</VirtualHost>
1-2. httpd.conf 파일 수정 - Listen port 추가
# Get to Apache configure directory
$ ../
# Open the httpd.conf file
$ vi httpd.conf
# Using "/Listen" command, find the Listen 80
# Add Listen 3001
...
Listen 80
Listen 3001
...
2. Apache 재시작
$ sudo /opt/bitnami/ctlscript.sh restart apache
3. Proxy 사용 방법(NodeJS와 동일)
const response = await axios.get('https://www.naver.com', {
proxy: {
host: proxy_server_ip,
port: proxy_server_port
}
});
Socks5를 활용한 Proxy Server
내 팀장은 Socks를 활용한 Proxy 통신이 가장 효율적이라고 생각했다.
실제로 우리 서비스에서 이미 사용을 하고있으며, Request 관련해서 Throttle과 같은 문제없이 사용하고 있었기 때문이었다.
이 친구를 활용하는 방법은 두가지가 있는 것 같다.
1. Socks5 서버를 실행시켜주는것
2. SSH 커넥션을 통해 Socks5를 사용하는것
현재 우리회사의 경우에는 Docker Swarm을 활용하여 Docker Instanse가 십여개이다.
이 경우에 모든 Docker Instance에 SSH Connection을 뚫는것은 아닌 것 같다고 생각했다.
이에 따라 프록시 서버로 활용할 서버에서 Socks5 서버를 실행시켜주는 방식으로 진행하기로 했다.
서버는 Dante라는 서버로 하였고, 세팅은 아래의 Docs를 참고하였다.
1. 기본 세팅
https://www.digitalocean.com/community/tutorials/how-to-set-up-dante-proxy-on-ubuntu-20-04
2. Proxy 사용 방법
2-1 socks-proxy-agent module 설치 - 이친구는 다른 Proxy와 다르게 agent 모듈이 필요하다.
$ npm install socks-proxy-agent
2-2 axios에서 사용 방법
const { SocksProxyAgent } = require('socks-proxy-agent');
const response = await axios.get('https://www.naver.com', {
httpsAgent: new SocksProxyAgent(`socks5://proxy_test:test1234@${proxy_host}:${proxy_port}/`),
});
스트레스 테스트 및 결과
Proxy 서버를 활용할 때 문제가 발생한다면 동시 요청량이 많을 때 발생할 것 이라고 생각했다.
또한 각각의 요청이 1초 ~ 3초의 시간이 걸린다고 할 때를 테스트하였다.
이에 따라 원하는 시간 후에 Response를 보내줄 수 있는 페이지를 하나 만들었다.
기존에 사용하고있는 내 호스팅을 사용하였고 CodeIgniter(PHP) 를 활용하여 간단하게 만들었다.
/**
* 타임아웃테스트
*/
public function timeout()
{
$second = $this->input->get('s');
sleep($second);
}
https://주소/timeout?s=5
위의 주소로 요청하게되면 5초 후에 200 response를 return 해주었다.
테스트 조건
- Lightsail 1vCore 1GB $5짜리 서버
테스트 방법
1. n * 100 개의 Request를 동시에 보내고, 이런 Request를 5회 진행한다.
2. 걸리는 시간은 1초로 테스트한다.
테스트 코드
const axios = require('axios');
const { SocksProxyAgent } = require('socks-proxy-agent');
async function test() {
console.time('checking');
try {
const host = '3.36.103.81';
const port = 1080;
const howMany = 700;
for (let r = 0; r < 5; r += 1) {
console.log(`${r} start`, new Date());
const requests = [];
for (let i = 0; i < howMany; i += 1) {
const request = axios.default.get('https://주소/timeout', {
params: {
s: 1,
},
proxy: {
host: 'host',
port: 3001,
},
// httpsAgent: new SocksProxyAgent(`socks5://proxy_test:test1234@${host}:${port}/`),
});
requests.push(request);
}
await Promise.all(requests);
console.log(`${r} end`, new Date());
}
console.timeEnd('checking');
return true;
} catch (error) {
console.timeEnd('checking');
console.log(error.response);
throw error;
}
}
test();
테스트 결과
- NodeJS
동시 400개의 Request도 버티지 못하고 Error 내뿜음
- Apache
동시 700개의 Request를 진행할 때 약 38초가 걸림
- Socks5
동시 700개의 Request를 진행할 때 약 2분이 걸림
Apache 프로세스가 가장 빨랐다.. 나는 암호화 통신 없이 진행된다고 하는 Socks5가 가장 빠를줄 알았다.
하지만 결과는 Apache가 더 빨랐다.
이 결과에는 Apache worker 수정이나 Socks5 session max 수정이 들어가지 않았다.
이후에 Socks5 Dante 서버 세팅에 Session.max 문제인가 싶어 5000으로 입력 해 보았지만 속도는 동일했다.
아마 CPU사용량이 아파치에 비해서 비효율적이지 않을까 싶었다.
이 내용을 내일모레 팀장한테 공유해줘야지..