From 2cdd2b8d3e06c22e4e9fa53d4b0e83145c27127f Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Wed, 18 Nov 2020 13:27:02 +0800 Subject: [PATCH 01/15] Update sender.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 采用了新的proxy验证方式,ping一次速度更快 # 修正了原来版本的几个小bug,比如拨号间隔原来实际上是设定值的两倍,如果proxy无效等待时间太长(改成6秒或者其他最低拨号间隔即可) # IP与上次的相同会自动再次拨号 --- adslproxy/sender/sender.py | 57 +++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/adslproxy/sender/sender.py b/adslproxy/sender/sender.py index afa944a..e45f93e 100644 --- a/adslproxy/sender/sender.py +++ b/adslproxy/sender/sender.py @@ -1,4 +1,8 @@ # coding=utf-8 +# 采用了新的proxy验证方式,ping一次速度更快 +# 修正了原来版本的几个小bug,比如拨号间隔原来实际上是设定值的两倍,如果proxy无效等待时间太长(改成6秒或者其他最低拨号间隔即可) +# IP与上次的相同会自动再次拨号 + import re import time import requests @@ -9,6 +13,8 @@ from loguru import logger from retrying import retry, RetryError import redis +import datetime +import os if platform.python_version().startswith('2.'): import commands as subprocess @@ -22,7 +28,7 @@ class Sender(object): """ 拨号并发送到 Redis """ - + ip_pre = '' def extract_ip(self): """ 获取本机IP @@ -42,15 +48,26 @@ def test_proxy(self, proxy): 测试代理,返回测试结果 :param proxy: 代理 :return: 测试结果 + :ping一次测试速度更快,只需要几十毫秒 """ - try: - response = requests.get(TEST_URL, proxies={ - 'http': 'http://' + proxy, - 'https': 'https://' + proxy - }, timeout=TEST_TIMEOUT) - if response.status_code == 200: - return True - except (ConnectionError, ReadTimeout): + # try: + # response = requests.get(TEST_URL, proxies={ + # 'http': 'http://' + proxy, + # 'https': 'https://' + proxy + # }, timeout=TEST_TIMEOUT) + # if response.status_code == 200: + # return True + # except (ConnectionError, ReadTimeout): + # return False + # linux 系统是ping -c, windows系统是ping -n + if platform.system()=='Windows': + con = os.system('ping -n 1 www.baidu.com') + if platform.system()=='Linux': + con = os.system('ping -c 1 www.baidu.com') + print(con) + if con==0: + return True + else: return False @retry(retry_on_result=lambda x: x is not True, stop_max_attempt_number=10) @@ -88,14 +105,25 @@ def loop(self): """ while True: logger.info('Starting dial...') - self.run() - time.sleep(DIAL_CYCLE) + now = datetime.datetime.now() + if now.minute%5==0 and now.second==0: + logger.info('dial time: %s', now.strftime('%Y-%m-%d %H:%M:%S')) + + new_ip = self.run() + if new_ip != self.ip_pre: + + self.ip_pre = new_ip + else: + # IP与上次的相同会自动再次拨号 + logger.info('IP和上次相同,等待重播......') + time.sleep(6) def run(self): """ 拨号主进程 :return: None """ + #time.sleep(10) #给正在运行的作业留出时间结束 logger.info('Dial started, remove proxy') try: self.remove_proxy() @@ -115,7 +143,7 @@ def run(self): ip=ip, port=PROXY_PORT) else: proxy = '{ip}:{port}'.format(ip=ip, port=PROXY_PORT) - time.sleep(10) + # time.sleep(1) if self.test_proxy(proxy): logger.info(f'Valid proxy {proxy}') # 将代理放入数据库 @@ -123,11 +151,14 @@ def run(self): time.sleep(DIAL_CYCLE) else: logger.error(f'Proxy invalid {proxy}') + time.sleep(DIAL_ERROR_CYCLE) else: # 获取 IP 失败,重新拨号 logger.error('Get IP failed, re-dialing') + ip = '' + time.sleep(DIAL_ERROR_CYCLE) self.run() - + return ip def send(loop=True): sender = Sender() From d689020fff6fac2613dbfda0316b46970973c7b4 Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Wed, 18 Nov 2020 13:30:35 +0800 Subject: [PATCH 02/15] Update db.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 放入Redis之前加了一个时间戳,方便判断IP存活时间 --- adslproxy/db.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/adslproxy/db.py b/adslproxy/db.py index 54fe996..b624454 100644 --- a/adslproxy/db.py +++ b/adslproxy/db.py @@ -1,4 +1,5 @@ # coding=utf-8 +# 放入Redis之前加了一个时间戳,方便判断IP存活时间 import redis import random from adslproxy.settings import * @@ -23,7 +24,7 @@ def set(self, name, proxy): :param proxy: 代理 :return: 设置结果 """ - return self.db.hset(self.redis_key, name, proxy) + return self.db.hset(self.redis_key, name, proxy + '_' + str(int(time.time()))) def get(self, name): """ From 4eb495611cc3acd6097a467acb579872dca044b2 Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Wed, 18 Nov 2020 13:40:46 +0800 Subject: [PATCH 03/15] Update db.py --- adslproxy/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adslproxy/db.py b/adslproxy/db.py index b624454..c97aba5 100644 --- a/adslproxy/db.py +++ b/adslproxy/db.py @@ -3,7 +3,7 @@ import redis import random from adslproxy.settings import * - +import time class RedisClient(object): def __init__(self, host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, redis_key=REDIS_KEY): From 9f36d9c9c7f446cee4c742507bf68b90ad364e3f Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 10:15:11 +0800 Subject: [PATCH 04/15] Update sender.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 采用了新的proxy验证方式,ping一次速度更快 # 修正了原来版本的几个小bug,比如拨号间隔原来实际上是设定值的两倍,如果proxy无效等待时间太长(改成6秒或者其他最低拨号间隔即可) # IP与上次的相同会自动会重新拨号 # 连续三次拨号失败自动重启(ADSL VPS这种情况下基本上等于无法继续拨号了) # 增加邮件提醒 # 每次拨出的IP存入redis,方便统计和去重.IP出现2次以上会重新拨号 # 从redis移除移除IP失败立即重启,这个情况下VPS通常已经无法拨号了 --- adslproxy/sender/sender.py | 115 ++++++++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 26 deletions(-) diff --git a/adslproxy/sender/sender.py b/adslproxy/sender/sender.py index e45f93e..4ad4447 100644 --- a/adslproxy/sender/sender.py +++ b/adslproxy/sender/sender.py @@ -1,7 +1,11 @@ # coding=utf-8 # 采用了新的proxy验证方式,ping一次速度更快 # 修正了原来版本的几个小bug,比如拨号间隔原来实际上是设定值的两倍,如果proxy无效等待时间太长(改成6秒或者其他最低拨号间隔即可) -# IP与上次的相同会自动再次拨号 +# IP与上次的相同会自动会重新拨号 +# 连续三次拨号失败自动重启(ADSL VPS这种情况下基本上等于无法继续拨号了) +# 增加邮件提醒 +# 每次拨出的IP存入redis,方便统计和去重.IP出现2次以上会重新拨号 +# 从redis移除移除IP失败立即重启,这个情况下VPS通常已经无法拨号了 import re import time @@ -9,12 +13,14 @@ from requests.exceptions import ConnectionError, ReadTimeout from adslproxy.db import RedisClient from adslproxy.settings import * +from adslproxy.sendemail import EmailClient import platform from loguru import logger from retrying import retry, RetryError import redis import datetime import os +import random if platform.python_version().startswith('2.'): import commands as subprocess @@ -29,6 +35,7 @@ class Sender(object): 拨号并发送到 Redis """ ip_pre = '' + invalid_ip_list = [] def extract_ip(self): """ 获取本机IP @@ -51,19 +58,16 @@ def test_proxy(self, proxy): :ping一次测试速度更快,只需要几十毫秒 """ # try: - # response = requests.get(TEST_URL, proxies={ - # 'http': 'http://' + proxy, - # 'https': 'https://' + proxy - # }, timeout=TEST_TIMEOUT) - # if response.status_code == 200: - # return True - # except (ConnectionError, ReadTimeout): - # return False - # linux 系统是ping -c, windows系统是ping -n - if platform.system()=='Windows': - con = os.system('ping -n 1 www.baidu.com') - if platform.system()=='Linux': - con = os.system('ping -c 1 www.baidu.com') + # response = requests.get(TEST_URL, proxies={ + # 'http': 'http://' + proxy, + # 'https': 'https://' + proxy + # }, timeout=TEST_TIMEOUT) + # if response.status_code == 200: + # logger.info(f'proxy: {proxy}') + # return True + #except (ConnectionError, ReadTimeout): + # return False + con = os.system('ping -c 1 www.baidu.com') print(con) if con==0: return True @@ -75,6 +79,7 @@ def remove_proxy(self): """ 移除代理 :return: None + 通常情况下,连续拨号失败几次就需要重启机器了,这时候VPS已经无法成功拨号连接互联网了 """ logger.info(f'Removing {CLIENT_NAME}...') try: @@ -87,7 +92,8 @@ def remove_proxy(self): return True except redis.ConnectionError: logger.info(f'Remove {CLIENT_NAME} failed') - + logger.error('删除IP失败!从代理池删除IP并重启系统.......') + os.system('/usr/sbin/shutdown -r now') def set_proxy(self, proxy): """ 设置代理 @@ -95,8 +101,26 @@ def set_proxy(self, proxy): :return: None """ self.redis = RedisClient() - if self.redis.set(CLIENT_NAME, proxy): - logger.info(f'Successfully set proxy {proxy}') + self.db = RedisClient().db + # 哈希表来统计拨号VPS的IP + if not self.db.hexists('dialed_IPs', proxy): + self.db.hset('dialed_IPs', proxy, 1) + # 往IP池里插入数据 + if self.redis.set(CLIENT_NAME, proxy): + logger.info(f'Successfully set proxy {proxy}') + return True + else: + num = int(self.db.hget('dialed_IPs', proxy)) + logger.info(f'{proxy} in proxy pools {num} times already') + if num <2: + self.db.hset('dialed_IPs', proxy, num+1) + # 往IP池里插入数据 + if self.redis.set(CLIENT_NAME, proxy): + logger.info(f'Successfully set proxy {proxy}') + return True + else: + + return False def loop(self): """ @@ -114,9 +138,8 @@ def loop(self): self.ip_pre = new_ip else: - # IP与上次的相同会自动再次拨号 logger.info('IP和上次相同,等待重播......') - time.sleep(6) + self.run() def run(self): """ @@ -129,10 +152,21 @@ def run(self): self.remove_proxy() except RetryError: logger.error('Retried for max times, continue') - # 拨号 - (status, output) = subprocess.getstatusoutput(DIAL_BASH) + self.emailclient = EmailClient() + self.emailclient.notification(f'failed too many times {datetime.datetime.now().strftime("%m-%d-%H-%M")}', f'Warning{random.randint(1000,299999)}: 22457 retry error {datetime.datetime.now().strftime("%m-%d-%H-%M")}') + + for i in range(3): + # 拨号 + (status, output) = subprocess.getstatusoutput('adsl-stop;adsl-start') + if not status == 0: + logger.error('Dial failed') + time.sleep(20) + else: + break if not status == 0: - logger.error('Dial failed') + print('连续三次拨号失败,系统重启......') + os.system('sudo reboot') + # 获取拨号 IP ip = self.extract_ip() if ip: @@ -146,11 +180,19 @@ def run(self): # time.sleep(1) if self.test_proxy(proxy): logger.info(f'Valid proxy {proxy}') + self.ip_validity_statistics('valid') # 将代理放入数据库 - self.set_proxy(proxy) - time.sleep(DIAL_CYCLE) + if self.set_proxy(proxy): + time.sleep(DIAL_CYCLE) else: logger.error(f'Proxy invalid {proxy}') + # 连续三次拨号无效 + self.ip_validity_statistics('invalid') + if len(self.invalid_ip_list) > 0: + if self.invalid_ip_list.count('invalid') == 3: + logger.error('连续三次拨号失败!从代理池删除IP并重启系统.......') + self.remove_proxy() + os.system('/usr/sbin/shutdown -r now') time.sleep(DIAL_ERROR_CYCLE) else: # 获取 IP 失败,重新拨号 @@ -159,11 +201,32 @@ def run(self): time.sleep(DIAL_ERROR_CYCLE) self.run() return ip - + def ip_validity_statistics(self, ele): + if len(self.invalid_ip_list) < 3: + self.invalid_ip_list.append(ele) + else: + self.invalid_ip_list.pop(0) + self.invalid_ip_list.append(ele) + def send(loop=True): sender = Sender() sender.loop() if loop else sender.run() if __name__ == '__main__': - send() + try: + emailclient = EmailClient() + emailclient.notification(f'{datetime.datetime.now().strftime("%m-%d-%H:%M")} {random.randint(300, 9999)} proxy restarted', f'{datetime.datetime.now().strftime("%m-%d-%H:%M")} 22457 proxyserver is back {random.randint(300, 9999)}') + print('email test success') + except Exception as e: + print(e) + while True: + con = os.system('ping -c 1 www.baidu.com') + print(con) + if con==0: + time.sleep(6) + send() + break + else: + time.sleep(1) + From 0ea197bbf743e3e32285e41399feb1993545d8a6 Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 10:19:04 +0800 Subject: [PATCH 05/15] Create sendmail.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 邮件提醒,一定要注意163邮箱反垃圾措施,注意邮件内容和标题 --- adslproxy/sendmail.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 adslproxy/sendmail.py diff --git a/adslproxy/sendmail.py b/adslproxy/sendmail.py new file mode 100644 index 0000000..51ab403 --- /dev/null +++ b/adslproxy/sendmail.py @@ -0,0 +1,43 @@ + +# -*- coding: utf-8 -*- +import time +# from playsound import playsound +from datetime import datetime +import smtplib +from email.mime.text import MIMEText +from email.header import Header + +class EmailClient(object): + def __init__(self): + """ + 初始化邮件列表 + + """ + self.to_list = [SENDER_EMAIL, RECEIVER_EMAIL] + def notification(self, body, subj): + sender = SENDER_EMAIL # 邮件发送人 + receiver = RECEIVER_EMAIL # 邮件收件人 + subject = 'adslproxy notification: ' + subj + ' ' + str(datetime.today())[:16] # 主题 + smtpserver = 'smtp.163.com' # 网易的STMP地址 默认端口号为25 + username = EMAIL # 发送邮件的人 + password = PASS # 你所设置的密码.网易在开通SMTP服务后会有个密码设置 + + # 中文需参数‘utf-8',单字节字符不需要 + msg = MIMEText(body, 'plain', 'utf-8') + msg['Subject'] = Header(subject, 'utf-8') # 头部信息:标题 + msg['From'] = 'user' # 头部信息:名称<发件人的地址> + msg['To'] = ",".join(self.to_list) # 头部信息:收件人地址 + m = 0 + while m < 3: + try: + smtp = smtplib.SMTP_SSL('smtp.163.com', 465) + smtp.login(username, password) + smtp.sendmail(sender, receiver, msg.as_string()) + smtp.quit() + print('success') + m += 1 + break + except smtplib.SMTPException as e: + print('Error: ', e) + m += 1 + time.sleep(25) From f7fffce26610387d860e45bea747880b798e6f71 Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 10:21:26 +0800 Subject: [PATCH 06/15] Create setup.cfg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加阿里云setup.py,用户在运行python setup.py时会使用阿里云pip源,加快安装速度 --- setup.cfg | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..e93c707 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ + +[easy_install] + +index_url = http://mirrors.aliyun.com/pypi/simple/ + From 9d4e567faa3e3417698fb523743d2c4f834d1ad8 Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 10:23:41 +0800 Subject: [PATCH 07/15] Create service.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit service.sh文件放在/etc/init.d目录下并且在/etc/rc.local最后添加bash /etc/init.d/service.sh,系统重启后会自动运行拨号脚本. --- service.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 service.sh diff --git a/service.sh b/service.sh new file mode 100644 index 0000000..a258f64 --- /dev/null +++ b/service.sh @@ -0,0 +1,11 @@ + +# coding=utf-8 +#!/bin/bash +# service.sh文件放在/etc/init.d目录下并且在/etc/rc.local最后添加bash /etc/init.d/service.sh,系统重启后会自动运行拨号脚本. +while ! ping -c1 www.baidu.com &>/dev/null + do echo "Ping Fail - `date`" + sleep 6 + adsl-start +done +export PATH="$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin" +nohup /usr/bin/python3 /root/AdslProxy/adslproxy/sender/sender.py >> /root/proxy_reboot.log 2>&1 & From bf6fef9e411c0a029551936b48f5845bb9c70ebe Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 10:24:46 +0800 Subject: [PATCH 08/15] Create hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit github hosts.以便国内用户访问github相关页面 --- hosts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 hosts diff --git a/hosts b/hosts new file mode 100644 index 0000000..54f60d9 --- /dev/null +++ b/hosts @@ -0,0 +1,24 @@ +127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 +::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 + +# GitHub Start +140.82.113.4 github.com +140.82.113.4 gist.github.com +140.82.114.5 api.github.com +185.199.111.153 assets-cdn.github.com +199.232.96.133 raw.githubusercontent.com +199.232.96.133 raw.github.com +199.232.96.133 gist.githubusercontent.com +199.232.96.133 cloud.githubusercontent.com +199.232.96.133 camo.githubusercontent.com +199.232.96.133 avatars0.githubusercontent.com +199.232.96.133 avatars1.githubusercontent.com +199.232.96.133 avatars2.githubusercontent.com +199.232.96.133 avatars3.githubusercontent.com +199.232.96.133 avatars4.githubusercontent.com +199.232.96.133 avatars5.githubusercontent.com +199.232.96.133 avatars6.githubusercontent.com +199.232.96.133 avatars7.githubusercontent.com +199.232.96.133 avatars8.githubusercontent.com +# GitHub End + From 75d0ed6613023282682f0664a1813fdd20202958 Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 10:29:06 +0800 Subject: [PATCH 09/15] =?UTF-8?q?IP=E4=BB=A3=E7=90=86=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E6=95=B0=E6=8D=AEseaborn=E5=8F=AF=E8=A7=86=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IP PROXY监控脚本,seaborn可视化IP代理统计数据 --- proxy_stats.py | 118 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 proxy_stats.py diff --git a/proxy_stats.py b/proxy_stats.py new file mode 100644 index 0000000..799f65e --- /dev/null +++ b/proxy_stats.py @@ -0,0 +1,118 @@ +%matplotlib qt +import os +import re +import pymysql +import seaborn as sns +import matplotlib.pyplot as plt +import pandas as pd +import paramiko +import time +import numpy as np + +# %matplotlib notebook + +# log_file = r'E:\splash\AdslProxy\proxy_reboot.log' +log_file = '/root/proxy_reboot.log' +class PROXY_MON(object): + def __init__(self, hostname, port, username, password, adsl_num): + #服务器信息,主机名(IP地址)、端口号、用户名及密码 + self.adsl=adsl_num + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + client.connect(hostname, port, username, password, compress=True, timeout=10) + self.sftp_client = client.open_sftp() + def log_check(self): + try: + with self.sftp_client.open(log_file, 'r') as file: + contents = file.read().decode() + print(contents[-200:]) + dial_times = re.findall('Dial started', contents) + print(f'total IPs dialed: {len(dial_times)}') + repeat_ips = re.findall('2 times', contents) + print(f'num of repeat IPs: {len(repeat_ips)}') + success_ips = re.findall('Successfully set', contents) + print(f'num of successful IPs set to redis: {len(success_ips)}') + dial_failed = re.findall('Get IP failed', contents) + print(f'num of failed dialing: {len(dial_failed)}') + valid_ip = re.findall('Valid proxy', contents) + print(f'num of Valid proxy IPs: {len(valid_ip)}') + invalid_ip = re.findall('Proxy invalid', contents) + print(f'num of invalid proxy IPs: {len(invalid_ip)}') + consec_ip_repeat = re.findall('IP和上次相同', contents) + print(f'num of consecutive repeat IP dialed: {len(consec_ip_repeat)}') + reboot_ip_del_failure = re.findall('删除IP失败!从代理池删除IP并重启系统', contents) + print(f'num of reboot due to deleltion failure from redis: {len(reboot_ip_del_failure)}') + reboot_ip_3dial_failure = re.findall('连续三次拨号失败!从代理池删除IP并重启系统', contents) + print(f'num of reboot due to 3 consecutive dial failures: {len(reboot_ip_3dial_failure)}') + except Exception as e: + print(e) + finally: + file.close() + + proxy_stats = [len(dial_times), len(repeat_ips), len(success_ips), len(dial_failed), len(valid_ip), len(invalid_ip), len(consec_ip_repeat), len(reboot_ip_del_failure), len(reboot_ip_3dial_failure)] + column_names = ['dial_times', 'repeat_ips', 'success_ips', 'dial_failed', 'valid_ip', 'invalid_ip', 'consec_ip_repeat', 'reboot_ip_del_failure', 'reboot_ip_3dial_failure'] + data_list = [proxy_stats, column_names] + df = pd.DataFrame (data_list).transpose() + df.columns = ['proxy_stats', 'stats_names'] + df + proxy_stats2 = [('server', self.adsl), ('dial_times',len(dial_times)), ('repeat_ips',len(repeat_ips)), + ('success_ips',len(success_ips)), ('dial_failed',len(dial_failed)), ('valid_ip',len(valid_ip)), + ('invalid_ip',len(invalid_ip)), ('consec_ip_repeat',len(consec_ip_repeat)), + ('reboot_ip_del_failure',len(reboot_ip_del_failure)), ('reboot_ip_3dial_failure',len(reboot_ip_3dial_failure)), ('reg_date','2020')] + proxy_stats3 = list(tuple((self.adsl,len(dial_times), len(repeat_ips), len(success_ips), len(dial_failed), len(valid_ip), len(invalid_ip), len(consec_ip_repeat), len(reboot_ip_del_failure), len(reboot_ip_3dial_failure), '2020'))) + + proxy_stats = [self.adsl, len(dial_times), len(repeat_ips), len(success_ips), len(dial_failed), len(valid_ip), len(invalid_ip), len(consec_ip_repeat), len(reboot_ip_del_failure), len(reboot_ip_3dial_failure)] + + # 日志数据总结写入mysql,可以在本地或者远程服务器运行 + db_conn=pymysql.connect(host='IP',port=3306,user='root',passwd='MYSQL_PASSWD',db='proxy',charset='utf8mb4') + cur = db_conn.cursor() + insert_sql="""INSERT IGNORE INTO stats(server, dial_times, repeat_ips, success_ips, dial_failed, valid_ip, invalid_ip,\ + consec_ip_repeat, reboot_ip_del_failure, reboot_ip_3dial_failure) \ + VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) AS new \ + ON DUPLICATE KEY UPDATE \ + dial_times=new.dial_times, repeat_ips=new.repeat_ips, success_ips=new.success_ips,\ + dial_failed=new.dial_failed, valid_ip=new.valid_ip, invalid_ip=new.invalid_ip,\ + consec_ip_repeat=new.consec_ip_repeat, reboot_ip_del_failure=new.reboot_ip_del_failure,\ + reboot_ip_3dial_failure=new.reboot_ip_3dial_failure""" + cur.executemany(insert_sql, [proxy_stats]) + db_conn.commit() +# figure = plt.figure(self.adsl, figsize=(16, 8)) + figure, ax = plt.subplots(1, 1, figsize=(16, 8)) + plt.ion() + sns.barplot(x = 'stats_names', + y = 'proxy_stats', + data = df).set_title(self.adsl + '_proxy_quality_monitor') + + plt.xticks(rotation=30) + plt.tight_layout() + self.show_values_on_bars(ax) + # Show the plot + figure.show() + plt.pause(10) + figure.savefig('E:/splash/AdslProxy/' + self.adsl + '_proxy_quality_monitor' + '.jpg') + figure.clf() + plt.close() + def show_values_on_bars(self, axs): + def _show_on_single_plot(ax): + for p in ax.patches: + _x = p.get_x() + p.get_width() / 2 + _y = p.get_y() + p.get_height() + value = '{:.0f}'.format(p.get_height()) + ax.text(_x, _y, value, ha="center") + + if isinstance(axs, np.ndarray): + for idx, ax in np.ndenumerate(axs): + _show_on_single_plot(ax) + else: + _show_on_single_plot(axs) + + +if __name__ == "__main__": + # 这里是需要监控的拨号服务器ip, port, user, password, adsl_name(给每个服务器取得名字) + servers = [('192.168.1.1', 22222, 'root', '88888', 'adsl1'), + ('192.168.1.2', 22222, 'root', '88888', 'adsl2'), + ] +# while True: + for server in servers: + proxy_monitor = PROXY_MON(*server) + proxy_monitor.log_check() From 421ce64d0493ab3970bfacc96f099260d1c37542 Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 10:54:07 +0800 Subject: [PATCH 10/15] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 增加邮件提醒 2. 修正拨号间隔错误,原脚本的小bug,拨号间隔会是settings中的两倍 3. 增加拨号统计:每次拨出的IP放入redis,每拨一次value +1,如果是2会重新拨号,防止重复IP出现.如果需要可以重置下这个频次的值,参考proxy_reset.py.这个考虑到平台对IP的封禁并非长期,通常24小时后能解封 4. 增加拨号日志可视化监控,在本地运行proxy_stats.py读取远程拨号服务器日志并可视化展示拨号状态,比如这里的adsl1_proxy_quality_monitor.jpg 5. 连续三次拨号无效IP系统会重启,因为这时候服务器已经不能继续拨号了 6. 从redis删除IP失败系统会重启,这个时候一般都是无法拨号了 7. 更新proxy检测方式为ping,拨号一次只需要6-7秒(当然和代理商有关系).这个针对单地区adsl vps特别有效,因为单地区拨号服务器带宽都没问题,拨出的IP都很稳定,只要能ping通都是高速可用的.个人建议 抛弃混拨服务器,带宽低而且拨号慢,不如多个地区的组合.本人测试过三家的拨号服务器,如有需要可提供免费建议. 8. service.sh放到/etc/init.d目录下, /bin/bash /etc/init.d/service.sh放在/etc/rc.local最后,系统重启后会自动运行拨号脚本. 9. 基于以上更新,脚本可以长期运行 --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 8f56c04..2ce5f17 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,18 @@ +## 更新和优化(相对于崔大神原脚本) +1. 增加邮件提醒 +2. 修正拨号间隔错误,原脚本的小bug,拨号间隔会是settings中的两倍 +3. 增加拨号统计:每次拨出的IP放入redis,每拨一次value +1,如果是2会重新拨号,防止重复IP出现.如果需要可以重置下这个频次的值,参考proxy_reset.py.这个考虑到平台对IP的封禁并非长期,通常24小时后能解封 +4. 增加拨号日志可视化监控,在本地运行proxy_stats.py读取远程拨号服务器日志并可视化展示拨号状态,比如这里的adsl1_proxy_quality_monitor.jpg +5. 连续三次拨号无效IP系统会重启,因为这时候服务器已经不能继续拨号了 +6. 从redis删除IP失败系统会重启,这个时候一般都是无法拨号了 +7. 更新proxy检测方式为ping,拨号一次只需要6-7秒(当然和代理商有关系).这个针对单地区adsl vps特别有效,因为单地区拨号服务器带宽都没问题,拨出的IP都很稳定,只要能ping通都是高速可用的.个人建议 +抛弃混拨服务器,带宽低而且拨号慢,不如多个地区的组合.本人测试过三家的拨号服务器,如有需要可提供免费建议. +8. service.sh放到/etc/init.d目录下, /bin/bash /etc/init.d/service.sh放在/etc/rc.local最后,系统重启后会自动运行拨号脚本. +9. 基于以上更新,脚本可以长期运行 + +Field Value +czhen:proxy_password@125.121.137.70:3389 1 + ## 拨号主机设置 首先配置好代理,如使用 Squid,运行在 3128 端口,并设置好用户名和密码。 From 1d0db9b83c65ade5b489a40ac625b9245eea3c57 Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 10:55:12 +0800 Subject: [PATCH 11/15] Add files via upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ip代理服务器拨号统计可视化 --- adsl1_proxy_quality_monitor.jpg | Bin 0 -> 78742 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 adsl1_proxy_quality_monitor.jpg diff --git a/adsl1_proxy_quality_monitor.jpg b/adsl1_proxy_quality_monitor.jpg new file mode 100644 index 0000000000000000000000000000000000000000..247f9ea3d6a7946738e2ec23b58cfd2fd01e077b GIT binary patch literal 78742 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 G#Qz1s;0 Date: Thu, 24 Dec 2020 10:57:26 +0800 Subject: [PATCH 12/15] Update README.md update stats image --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2ce5f17..aeb9a2e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ 2. 修正拨号间隔错误,原脚本的小bug,拨号间隔会是settings中的两倍 3. 增加拨号统计:每次拨出的IP放入redis,每拨一次value +1,如果是2会重新拨号,防止重复IP出现.如果需要可以重置下这个频次的值,参考proxy_reset.py.这个考虑到平台对IP的封禁并非长期,通常24小时后能解封 4. 增加拨号日志可视化监控,在本地运行proxy_stats.py读取远程拨号服务器日志并可视化展示拨号状态,比如这里的adsl1_proxy_quality_monitor.jpg +![image](https://raw.githubusercontent.com/chenxuzhen/AdslProxy/master/adsl1_proxy_quality_monitor.jpg) 5. 连续三次拨号无效IP系统会重启,因为这时候服务器已经不能继续拨号了 6. 从redis删除IP失败系统会重启,这个时候一般都是无法拨号了 7. 更新proxy检测方式为ping,拨号一次只需要6-7秒(当然和代理商有关系).这个针对单地区adsl vps特别有效,因为单地区拨号服务器带宽都没问题,拨出的IP都很稳定,只要能ping通都是高速可用的.个人建议 From d0007994a39afdc548aabc9d293d035fd41d1bc3 Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 11:00:09 +0800 Subject: [PATCH 13/15] Create proxy_reset.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 因为网站封禁IP并非长期,24小时后可以运行该脚本减小拨号出现次数的值,提高IP利用率 --- proxy_reset.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 proxy_reset.py diff --git a/proxy_reset.py b/proxy_reset.py new file mode 100644 index 0000000..39cd62a --- /dev/null +++ b/proxy_reset.py @@ -0,0 +1,15 @@ +# coding=utf-8 +# 因为网站封禁IP并非长期,24小时后可以运行该脚本减小拨号出现次数的值,提高IP利用率 +import redis +import random +import time +import redis +import re + +client = redis.Redis(host=REDIS_HOST, port=7379, db=0, password=REDIS_PASSORD) +client.hvals('dialed_IPs') +client.hkeys('dialed_IPs') +for i in client.hkeys('dialed_IPs'): + num = int(client.hget('dialed_IPs', i)) + if num >=1: + client.hset('dialed_IPs', i, num-1) From f5e8de1d8a0e26b2182294417af1bd856627a065 Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 11:13:01 +0800 Subject: [PATCH 14/15] =?UTF-8?q?=E6=8B=A8=E5=8F=B7=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E4=B8=80=E9=94=AE=E9=83=A8=E7=BD=B2=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 拨号服务器一键部署脚本,只需要输入squid代理的密码,用户名这里是czhen,可以自己修改.注意,这里增加了一段修改sshd端口的脚本,如果服务器没有端口冲突问题这段代码需要hash掉 --- vpsadsl.sh | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 vpsadsl.sh diff --git a/vpsadsl.sh b/vpsadsl.sh new file mode 100644 index 0000000..6e7fcda --- /dev/null +++ b/vpsadsl.sh @@ -0,0 +1,191 @@ + +#/usr/bin/bash +sed -i "s/Port 3389/#Port 3389/" /etc/ssh/sshd_config +service sshd restart + +# 时间同步很重要,不然没法判断代理IP存活时间 +cd /etc/yum.repos.d/ + +mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup +mv /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.backup +# wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo +curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo +curl -O http://mirrors.aliyun.com/repo/epel-7.repo +sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo + +yum clean all +yum makecache +cd ~/ +yum install -y ntpdate +yum -y install wget +cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime +yes | cp -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime +ntpdate cn.pool.ntp.org +crontab -l >/tmp/crontab.bak +echo "*/10 * * * * /usr/sbin/ntpdate cn.pool.ntp.org | logger -t NTP" >> /tmp/crontab.bak +crontab /tmp/crontab.bak + +yum update -y +yum install epel-release -y +yum install --enablerepo="epel" ufw -y +yum install python3 -y + +# 配置pip国内源 +mkdir ~/.pip && cd .pip && touch pip.conf +echo " +[global] +index-url = https://mirrors.aliyun.com/pypi/simple +" > ~/.pip/pip.conf +cd ~/ + +# 安装denyhosts +wget http://soft.vpser.net/lnmp/lnmp1.4beta.tar.gz && tar zxf lnmp1.4beta.tar.gz && cd lnmp1.4/tools/ && bash denyhosts.sh + +# 安装squid +yum install squid httpd -y +echo " +# +# Recommended minimum configuration: +# + +# Example rule allowing access from your local networks. +# Adapt to list your (internal) IP networks from where browsing +# should be allowed +acl localnet src 10.0.0.0/8 # RFC1918 possible internal network +acl localnet src 172.16.0.0/12 # RFC1918 possible internal network +acl localnet src 192.168.0.0/16 # RFC1918 possible internal network +acl localnet src fc00::/7 # RFC 4193 local private network range +acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines + +acl SSL_ports port 443 +acl Safe_ports port 80 # http +acl Safe_ports port 21 # ftp +acl Safe_ports port 443 # https +acl Safe_ports port 70 # gopher +acl Safe_ports port 210 # wais +acl Safe_ports port 1025-65535 # unregistered ports +acl Safe_ports port 280 # http-mgmt +acl Safe_ports port 488 # gss-http +acl Safe_ports port 591 # filemaker +acl Safe_ports port 777 # multiling http +acl CONNECT method CONNECT + +# +# Recommended minimum Access Permission configuration: +# +# Deny requests to certain unsafe ports +http_access allow !Safe_ports + +# Deny CONNECT to other than secure SSL ports +http_access allow CONNECT !SSL_ports + +# Only allow cachemgr access from localhost +http_access allow localhost manager +http_access deny manager + +# We strongly recommend the following be uncommented to protect innocent +# web applications running on the proxy server who think the only +# one who can access services on "localhost" is a local user +#http_access deny to_localhost + +# +# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS +# + +# Example rule allowing access from your local networks. +# Adapt localnet in the ACL section to list your (internal) IP networks +# from where browsing should be allowed +http_access allow localnet +http_access allow localhost + +# And finally deny all other access to this proxy +# http_access allow all +auth_param basic program /usr/lib64/squid/basic_ncsa_auth /etc/squid/passwd +auth_param basic children 5 +auth_param basic realm czhen's squid server +auth_param basic credentialsttl 2 hours +acl czhen proxy_auth REQUIRED +http_access allow czhen +#http_access deny all + +# Squid normally listens to port 3128 +http_port 3389 + +# Uncomment and adjust the following to add a disk cache directory. +#cache_dir ufs /var/spool/squid 100 16 256 + +# Leave coredumps in the first cache dir +coredump_dir /var/spool/squid + +# +# Add any of your own refresh_pattern entries above these. +# +refresh_pattern ^ftp: 1440 20% 10080 +refresh_pattern ^gopher: 1440 0% 1440 +refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 +refresh_pattern . 0 20% 4320 + +#include /etc/squid/peers.conf + +# 配置高匿,不允许设置任何多余头信息,保持原请求header,可在最后加上此两句 +request_header_access Via deny all +request_header_access X-Forwarded-For deny all +" > /etc/squid/squid.conf +sudo systemctl enable squid +service squid start + +# 配置ufw +# yes | ufw enable +# ufw default allow outgoing +# ufw default deny incoming +# ufw allow http +# ufw allow 22 +# ufw allow 5900 +# ufw allow 3389 +# ufw allow from +# ufw status + +# 配置github访问 +# set up hosts for github visit. GFW blocked the parsing of these DNS +echo " +# GitHub Start +140.82.113.4 github.com +140.82.113.4 gist.github.com +140.82.114.5 api.github.com +185.199.111.153 assets-cdn.github.com +199.232.96.133 raw.githubusercontent.com +199.232.96.133 gist.githubusercontent.com +199.232.96.133 cloud.githubusercontent.com +199.232.96.133 camo.githubusercontent.com +199.232.96.133 avatars0.githubusercontent.com +199.232.96.133 avatars1.githubusercontent.com +199.232.96.133 avatars2.githubusercontent.com +199.232.96.133 avatars3.githubusercontent.com +199.232.96.133 avatars4.githubusercontent.com +199.232.96.133 avatars5.githubusercontent.com +199.232.96.133 avatars6.githubusercontent.com +199.232.96.133 avatars7.githubusercontent.com +199.232.96.133 avatars8.githubusercontent.com +# GitHub End +" >> /etc/hosts +cd /root/AdslProxy/ +read -p "Enter adsl client name. eg. adsl1 or adsl2: " adsl +sudo sed -i "s/adsl1/"$adsl"/" /root/AdslProxy/adslproxy/settings.py +sudo sed -i "s/22457/"$adsl"/" /root/AdslProxy/adslproxy/sender/sender.py +yes | python3 /root/AdslProxy/setup.py install +echo 'copy service.sh to /etc/init.d' +cp /root/AdslProxy/service.sh /etc/init.d/ && chmod 777 /etc/init.d/service.sh +echo 'bash /etc/init.d/service.sh' >> /etc/rc.local +sudo service firewalld start +sudo firewall-cmd --permanent --add-port=3128/tcp +firewall-cmd --zone=public --add-port=22/tcp --permanent +firewall-cmd --zone=public --add-port=30050/tcp --permanent + +firewall-cmd --reload +sudo service firewalld restart +sudo service squid restart +sudo systemctl enable firewalld +echo 'you need to RUN htpasswd -c /etc/squid/passwd czhen to set passwd for squid' +echo 'check if squid proxy works and start adslproxy send.' +htpasswd -c /etc/squid/passwd czhen +echo 'double check adslproxy settings.py. make sure adsl client name is setup as expected' From 54d3861d83c7d31c0df166c109edce3507a0ed9d Mon Sep 17 00:00:00 2001 From: chenxuzhen <50033143+chenxuzhen@users.noreply.github.com> Date: Thu, 24 Dec 2020 11:14:53 +0800 Subject: [PATCH 15/15] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aeb9a2e..4bfacc9 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ 7. 更新proxy检测方式为ping,拨号一次只需要6-7秒(当然和代理商有关系).这个针对单地区adsl vps特别有效,因为单地区拨号服务器带宽都没问题,拨出的IP都很稳定,只要能ping通都是高速可用的.个人建议 抛弃混拨服务器,带宽低而且拨号慢,不如多个地区的组合.本人测试过三家的拨号服务器,如有需要可提供免费建议. 8. service.sh放到/etc/init.d目录下, /bin/bash /etc/init.d/service.sh放在/etc/rc.local最后,系统重启后会自动运行拨号脚本. -9. 基于以上更新,脚本可以长期运行 +9. 增加了拨号服务器一键部署,用squid作代理服务器,中间只需要输入一次密码,参考vpsadsl.sh,用户名默认czhen可以自己修改.另外,如果没有需要,hash掉那段关于修改sshd端口的脚本 +10. 基于以上更新,脚本可以长期运行 Field Value czhen:proxy_password@125.121.137.70:3389 1