因为前段时间的重大会议,本人主服务器的IPv4被屏蔽了。导致某个国内主机长时间无法与后端主服务器(国外)数据库连接。(真是个杯具的故事)
但是,虽然IPv4数据包在回国路由上被丢包,但是IPv6的数据包并没有被阻断。于是对该国内主机上的部分应用进行IPv6改造使其正常连接后端数据库。
一、后端数据库改造
原先后端使用的数据库MySQL版本为5.5,在未指定bind-address的情况下,默认监听的是0.0.0.0
)。在思考后,选择将数据库直接升级到5.7,在5.6.6之后版本中,这个值默认设置成 *
,即同时监听IPv4和IPv6连接。
(参见 https://dev.mysql.com/doc/refman/5.6/en/server-options.html#option_mysqld_bind-address )
| --bind-address=addr | Type | Default |
Permitted Values (<= 5.6.5) | string | 0.0.0.0 |
Permitted Values (>= 5.6.6) | string | * |
具体的升级过程就不在此唠述,因为,本人偷懒使用了lnmp.org自带的升级工具。
二、SSpanel后端改造
SSpanel后端使用cymysql库与主服务器通讯。但是这个库默认不支持以IPv6的形式与数据库进行连接。如果以IPv6的形式连接数据库会报出如下错误
>>> import cymysql
>>> conn = cymysql.connect(host='::1', user='root', passwd='', db='database_name', charset='utf8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/cymysql/__init__.py", line 85, in Connect
return Connection(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/cymysql/connections.py", line 245, in __init__
self._connect()
File "/usr/local/lib/python2.7/dist-packages/cymysql/connections.py", line 410, in _connect
raise OperationalError(2003, "Can't connect to MySQL server on %r (%s)" % (self.host, e.args[0]))
cymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '::1' (-9)")
但是好在 CyMySQL 其实是 PyMySQL 的一个分支,而PyMySQL在这个issue Support IPv6 #118 中已经修复了这个问题。(所以为什么不直接用pymysql嘛,所以为什么cymysql到现在还有没修这个问题嘛。。。。)
补:在提交issues To support IPv6 后,CyMySQL v0.9.2已经可以使用IPv6的形式连接客户端。(真速度.....)
那么就自己动手吧。根据PyMySQL中相关issues和commit,修改上述报错中最后提示的/usr/local/lib/python2.7/dist-packages/cymysql/connections.py
文件。
做如下patch
diff -Naur cymysql/connections.py cymysql-ipv6/connections.py
--- cymysql/connections.py 2017-06-10 03:51:56.000000000 -0400
+++ cymysql-ipv6/connections.py 2017-10-27 05:12:51.125249151 -0400
@@ -398,13 +398,14 @@
self.host_info = "Localhost via UNIX socket"
if DEBUG: print('connected using unix_socket')
else:
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- t = sock.gettimeout()
- sock.settimeout(self.connect_timeout)
- sock.connect((self.host, self.port))
+ sock = socket.create_connection((self.host, self.port), self.connect_timeout)
self.host_info = "socket %s:%d" % (self.host, self.port)
if DEBUG: print('connected using socket')
- sock.settimeout(t)
except socket.error as e:
sock.close()
raise OperationalError(2003, "Can't connect to MySQL server on %r (%s)" % (self.host, e.args[0]))
三、ServerStatus监控脚本改造
原来使用的服务器监控脚本和cymysql的一样情况。虽然脚本的部分地方考虑到了IPv6的情况。如(在这以将ServerStatus中文化的最初repo tenyue/ServerStatus 为例说明):
在监控脚本的L150中,和cymysql一样使用了socket.socket(socket.AF_INET, socket.SOCK_STREAM)
这种只支持IPv4形式与服务器通信。
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
但是很奇怪的是这个监控脚本get_network()
中判断IPv4、IPv6连接的逻辑又是这样的:如果脚本以IPv4的形式与服务器通信,那么这直接默认监控的主机的IPv4是正常的,那么get_network()
接收的参数ip_version
就只有6
,即只检查监控主机的IPv6是否正常。
没办法,自己fork -> Rhilip/ServerStatus 修改吧。而且个人觉得tenyue的部分修改不这么好,比如:
- 移除了IPv6的支持(其实是前端不显示)
- 在添加load 1,5,15的情况下未考虑向前兼容。如果前端接收的字段仍为原先的
load
,但是后端更新后变成load_1
,load_5
,load_15
。丢失了原先代表load 1 的load
字段。 - 原有脚本的是tab缩进的,改成4空格23333
Flask
这个相对简单,只要指定host是 ::
就行。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello world~"
if __name__ == '__main__':
app.run(host="::")