gnocchi-api 访问基本在10s +, why ?
gnocchi-api 使用了 wsgiref , wsgiref 使用了 :
/usr/bin/gnocchi-api:
1 |
server = wss.make_server(args.host, args.port, build_wsgi_app()) |
/usr/lib64/python2.7/wsgiref/simple_server.py :
(这里提到了个REMOTE_HOST的环境变量,含义就是“REMOTE_ADDR 对应的域名”, 而 address_string() 的命名也是ip地址对应的域名的意思,因为绝大部分的ip地址是反解不到域名的,所以,这个逻辑基本可以注释掉,不过,直接修改人家的代码不大好)
然而上面的 WSGIRequestHandler 基本上会执行到get_environ() , 进而执行到 BaseHTTPServer.py 中的 self.address_string() ,如下:
address_string() 函数又调用了 /usr/lib64/python2.7/socket.py 中的 getfqdn(), 如下:
然后就肯定会执行到gethostbtaddr() 了,该函数的具体实现又是什么逻辑呢? socket.py import了 _socket 模块中的所有函数,而gethostbyaddr()正是_socket 模块实现的,_socket 模块的实现见: /usr/lib64/python2.7/lib-dynload/_socketmodule.so , 可见,这是一个c实现的so文件,稍后再看:
测试发现,该函数当遇到IP地址时,肯定会做一次反向地址解析,反向地址解析不是所有dns都能支持的很好的,有些能快速返回,有些却不能(具体原因,稍后再查),比如: 公网地址的反向地址解析可以很快返回,私网地址的反向地址解析就很慢
解决办法:
办法一: 在 dns 上给自己的IP地址添加反向地址解析,这样反向地址解析就可以很快; 给每个IP地址都添加反向地址解析的话,比较麻烦,最好能有一个更好的办法,让某一类IP地址能直接返回错误,或返回一个自定义的域名; 这个办法的优点是: 不需要修改程序 ; 如果搞不定dns,那就修改程序吧
办法二: 修改/usr/lib64/python2.7/BaseHTTPServer.py ,在 address_string() 中直接返回host,而不进行socket.getfqdn(host) 的调用
办法三: 修改 /usr/lib64/python2.7/socket.py 中的 getfqdn() 函数,对于ip地址的情况,不再调用 gethostbyaddr()
办法四: 其实,不是特别有信心的话,不要修改的太底层,没准儿影响到别的程序的; 更好的办法是:
在 /usr/bin/gnocchi-api 中wss.make_server(…) 时,提供了三个参数,还有两个参数是可以定制的,我们可以自己在 /usr/bin/gnocchi-api 中实现一个 MyWSGIRequestHandler ,继承自./simple_server.py 中的WSGIRequestHandler , 然后覆盖其中的address_string() 方法即可
按照办法四 修改后的 /usr/bin/gnocchi-api 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
#!/usr/bin/python2 #PBR Generated from u'wsgi_scripts' import threading from gnocchi.rest.app import build_wsgi_app from wsgiref.simple_server import WSGIRequestHandler class GnocchiWSGIRequestHandler(WSGIRequestHandler): def address_string(self): return self.client_address[0] if __name__ == "__main__": import argparse import socket import sys import wsgiref.simple_server as wss my_ip = socket.gethostbyname(socket.gethostname()) parser = argparse.ArgumentParser( description=build_wsgi_app.__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter, usage='%(prog)s [-h] [--port PORT] [--host IP] -- [passed options]') parser.add_argument('--port', '-p', type=int, default=8041, help='TCP port to listen on') parser.add_argument('--host', '-b', default='', help='IP to bind the server to') parser.add_argument('args', nargs=argparse.REMAINDER, metavar='-- [passed options]', help="'--' is the separator of the arguments used " "to start the WSGI server and the arguments passed " "to the WSGI application.") args = parser.parse_args() if args.args: if args.args[0] == '--': args.args.pop(0) else: parser.error("unrecognized arguments: %s" % ' '.join(args.args)) sys.argv[1:] = args.args server = wss.make_server(args.host, args.port, build_wsgi_app(), handler_class = GnocchiWSGIRequestHandler) print("*" * 80) print("STARTING test server gnocchi.rest.app.build_wsgi_app") url = "http://%s:%d/" % (server.server_name, server.server_port) print("Available at %s" % url) print("DANGER! For testing only, do not use in production") print("*" * 80) sys.stdout.flush() server.serve_forever() else: application = None app_lock = threading.Lock() with app_lock: if application is None: application = build_wsgi_app() |
测试发现,访问确实快多了,不再感觉到延迟了