adslproxy 代理池原理:
本文采用 redis 做代理IP的存储。存储代理IP前,先使用 adslproxy 做重新拨号和代理IP可用性校验,可用后再保存到 redis 里。在进行重新拨号前,先删除这个原来保存的代理 IP,然后再重新进行拨号操作。
然后使用的 adslproxy 搭建一个提取代理 IP 的工具,通过一个 web 接口给客户端使用的时候。客户端用的时候,直接读取这个 web 接口就可以使用代理 IP。
拨号服务器操作步骤如下:
本文采用阿斯云的拨号vps服务器做搭建平台。
- 停止拨号的命令:
pppoe-stop
- 开始拨号的命令:
pppoe-start
所以连起来重新拨号更换IP就是pppoe-stop;pppoe-start
1. 安装squid 和python3
yum -y install squid python3
2. 记录hosts
echo "127.0.0.1 $HOSTNAME" >> /etc/hosts
记录 hosts 的作用是,进行重新拨号更换IP的时候,可以更快的让squid 完成绑定网口。不然每次更换IP,squid都需要对主机名进行 DNS 查询,会比较慢。不然会在/var/log/squid/cache.log
会出现如下信息:
2022/04/16 12:19:41 kid1| WARRNING: '4151624asy259' rDNS test failed: (0) No error.
2022/04/16 12:19:41 kid1| WARRNING: Could not determine this machines public hostname. Please configure one or set 'visible_hostname'.
2022/04/16 12:19:41 kid1| WARRNING: '4151624asy259' rDNS test failed: (0) No error.
2022/04/16 12:19:41 kid1| WARRNING: Could not determine this machines public hostname. Please configure one or set 'visible_hostname'.
2022/04/16 12:19:41 kid1| WARRNING: '4151624asy259' rDNS test failed: (0) No error.
2022/04/16 12:19:41 kid1| WARRNING: Could not determine this machines public hostname. Please configure one or set 'visible_hostname'.
2022/04/16 12:19:41 kid1| WARRNING: '4151624asy259' rDNS test failed: (0) No error.
2022/04/16 12:19:41 kid1| WARRNING: Could not determine this machines public hostname. Please configure one or set 'visible_hostname'.
2022/04/16 12:19:41 kid1| WARRNING: '4151624asy259' rDNS test failed: (0) No error.
2022/04/16 12:19:41 kid1| WARRNING: Could not determine this machines public hostname. Please configure one or set 'visible_hostname'.
又或者按照提示,在 squid 的配置文件里面设置 visible_hostname
。
3. 备份 squid.conf 文件
cp /etc/squid/squid.conf /etc/squid/squid.conf.bak
4. 设置代理的用户名和密码
squid 服务中设置代理的用户名密码可以用 htpasswd
命令来生成。该命令可以通过执行 apt-get/yum install http-tools
来获取。
创建用户‘proxy_username’ 的命令如下:
htpasswd -c /etc/squid/passwd proxy_username
输入相应的密码后,生成 文件 /etc/squid/passwd
squid 配置代理的用户名密码会使用basic_ncsa_auth
这个 lib 文件,一般会自带,因此需要正确的路径。 文件地址是 : /usr/lib64/squid/basic_ncsa_auth
5. 配置 squid 的文件
cat > /etc/squid/squid.conf << EOF
acl ::/0 src all
acl Safe_ports port 80
acl Safe_ports port 443
acl CONNECT method CONNECT
http_access allow all Safe_ports
http_access deny CONNECT !Safe_ports
auth_param basic program /usr/lib64/squid/basic_ncsa_auth /etc/squid/passwd
acl auth_user proxy_auth REQUIRED
http_access allow auth_user
http_port 3008
request_header_access Via deny all
request_header_access X-Forwarded-For deny all
EOF
6. 启动 squid 的开机默认启动
systemctl start squid
systemctl enable squid
7. 安装 adslproxy
pip3 install adslproxy
8. 修改源代码
在目前的v3.0.3版本的adslproxy代码中,发现两次地方有点问题,因此手动修改源代码:
(1) 修改 sender.py 的 extract_ip 方法
由于在 sender.py 中的 run() 里面如果subprocess.getstatusoutput(DIAL_BASH)
执行重新拨号的命令提前结束,就会陷入无限拨号的问题。比如subprocess.getstatusoutput(DIAL_BASH)
执行完了,拨号还没成功,还没获取到IP地址,需要等个10秒才获取到IP地址。此时提前进入self.extract_ip()
获取IP,就会获取不到,ip 这个变量为空,进入 else 语句,马上重新执行 self.run() 重新拨号。。
所以需要修改 extract_ip 方法。修改如下文件:
/usr/local/lib/python3.6/site-packages/adslproxy/sender/sender.py
def extract_ip(self):
"""
获取本机IP
:param ifname: 网卡名称
:return:
"""
WAIT_TIME = 120
while WAIT_TIME:
(status, output) = subprocess.getstatusoutput('ifconfig')
if not status == 0: return
pattern = re.compile(DIAL_IFNAME + '.*?inet.*?(\d+\.\d+\.\d+\.\d+).*?netmask', re.S)
result = re.search(pattern, output)
if result:
# 返回拨号后的 IP 地址
return result.group(1)
WAIT_TIME -= 1
time.sleep(1)
(2) 修改 sender.py 的 run 方法
在DIAL_CYCLE
设置的时间是600秒进行一次重新拨号,实际是1200秒才进行重新拨号。如下代码所示:
def loop(self):
"""
循环拨号
:return:
"""
while True:
logger.info('Starting dial...')
self.run()
time.sleep(DIAL_CYCLE)
def run(self):
"""
拨号主进程
:return: None
"""
logger.info('Dial started, remove proxy')
try:
self.remove_proxy()
except RetryError:
logger.error('Retried for max times, continue')
# 拨号
(status, output) = subprocess.getstatusoutput(DIAL_BASH)
if not status == 0:
logger.error('Dial failed')
# 获取拨号 IP
ip = self.extract_ip()
if ip:
logger.info(f'Get new IP {ip}')
if PROXY_USERNAME and PROXY_PASSWORD:
proxy = '{username}:{password}@{ip}:{port}'.format(username=PROXY_USERNAME,
password=PROXY_PASSWORD,
ip=ip, port=PROXY_PORT)
else:
proxy = '{ip}:{port}'.format(ip=ip, port=PROXY_PORT)
time.sleep(10)
if self.test_proxy(proxy):
logger.info(f'Valid proxy {proxy}')
# 将代理放入数据库
self.set_proxy(proxy)
time.sleep(DIAL_CYCLE)
else:
logger.error(f'Proxy invalid {proxy}')
else:
# 获取 IP 失败,重新拨号
logger.error('Get IP failed, re-dialing')
self.run()
在代码中看到的逻辑是 self.run() 里的self.set_proxy(proxy)
写入完代理后,进行一次time.sleep(DIAL_CYCLE)
,然后 self.run()
执行完后又time.sleep(DIAL_CYCLE)
了一次。所以需要去掉if self.test_proxy(proxy):
里的time.sleep(DIAL_CYCLE)
。
9. 创建运行脚本
新建 /data/adslproxy.sh 执行文件
#!/bin/bash
export REDIS_HOST='***'
export REDIS_PORT=6379
export REDIS_PASSWORD='***'
export DIAL_BASH='pppoe-stop;pppoe-start'
export DIAL_IFNAME='ppp0'
export DIAL_CYCLE=600
export CLIENT_NAME='adsl1'
export PROXY_PORT=3008
export PROXY_USERNAME='***'
export PROXY_PASSWORD='***'
nohup adslproxy send > /data/adslproxy.log 2>&1 &
设置的变量作用如下:
- REDIS_HOST:保存到
redis
服务的 host 域名或者IP地址。 - REDIS_PORT:保存到
redis
服务端口号。 - REDIS_PASSWORD:保存到
redis
的服务密码 - DIAL_BASH:是拨号云服务器的重新拨号的命令,这里是
pppoe-stop;pppoe-start
。 - DIAL_IFNAME:是拨号云服务器上网卡名称,这里是
ppp0
网卡。 - DIAL_CYCLE:是重新拨号的等待时间,单位为秒。
- CLIENT_NAME:是设置到
redis
代理池里面的名字。 - PROXY_PORT:用来测试代理可用性的代理服务端口号。
- PROXY_USERNAME:设置代理的用户名。
- PROXY_PASSWORD:设置代理的密码。
10. 启动 adslproxy,并设置开机启动
chmod u+x /data/adslproxy.sh
echo '/data/adslproxy.sh' >> /etc/rc.local
nohup adslproxy send > /data/adslproxy.log 2>&1 &
拨号服务器端搭建完成。
服务端操作步骤如下:
服务端通过 Docker 的形式去搭建这个 web 服务。
- Dockerfile 内容如下:
FROM python:3.8.8-alpine3.13
RUN pip3 install --no-cache-dir adslproxy
CMD ["adslproxy" "serve"]
- 构建镜像:
docker build -t adslproxy:0.1 .
- 通过 docker-compose 文件启动这个镜像:
docker-compose.yaml
文件内容如下:
version: '3'
services:
adslproxy:
image: 'adslproxy:0.1'
container_name: adslproxy
command: adslproxy serve
ports:
- "8425:8425"
restart: always
environment:
REDIS_HOST: ${REDIS_HOST}
REDIS_PASSWORD: ${REDIS_PASSWORD}
- 启动后,可以通过
http://IP:8425
这个接口去使用这个代理
比如我的IP是192.168.1.240
,就可以访问http://192.168.1.240:8425/
去使用这个接口工具
通过http://IP:8425/random
就可以随机获取一个代理,在代理里直接使用即可。