• bottle框架剖析


    bottle框架剖析

    1. 使用

    2. 源码分析

    一、使用

    大致有以下几部分

    1. quick start

    2. request routing

    3. generate contents

    4. request Data

    5. templates

    quick start

    下载: pip install bottle

    from bottle import route, run
    
    @route('/hello')
    def hello():
        return "Hello World!"
    
    run(host='localhost', port=8080, debug=True)

    visit http://localhost:8080/hello       将会看到hello world

    默认app

    当第一次运行route() 会自动产生一个Bottle类的一个实例,bottle

    request routing

    route 装饰器将url path 与回调函数联系起来。并给默认的app 添加一个url.一个app可以添加好多个url.

    like the following

    @route('/')
    @route('/hello/<name>')
    def greet(name='Stranger'):
        return template('Hello {{name}}, how are you?', name=name)

    多个url

    from bottle import route, run, template
    
    @route('/')
    @route('/hello/<name>')
    def index(name='stranger'):
        return template('<b>hello</b>{{name}}',name=name)
    
    run(host='127.0.0.1', port=8080)

     1可以将多个路由绑定到一个回调函数上。

     2您可以添加通配符到URL并通过关键字参数访问它们。

    动态路由

    url里面有通配符的叫做动态路由,匹配一个或者多个url 在同样时间里。

     A simple wildcard consists of a name enclosed in angle brackets (e.g. <name>) and accepts one or more characters up to the next slash (/).

    For example, the route /hello/<name> accepts requests for /hello/alice as well as /hello/bob, but not for /hello, /hello/ or /hello/mr/smith.

     每一个通配符都会将URL包含到那部分作为关键字参数传给请求的回调函数。

    HTTP 请求方法

    使用route()装饰器的method关键字参数或者是用5种装饰器 get() post() put() delete() or patch()

    
    
    from bottle import route, run, template,get,post,request
    @get('/login')
    def login():
    return '''
    <form action="/login" method="post">
    Username: <input name="username" type="text" />
    Password: <input name="password" type="password" />
    <input value="Login" type="submit" />
    </form>
    '''
    @post('/login')
    def do_login():
    username = request.forms.get('username')
    password = request.forms.get('password')
    if username == 'yuyang' and password == '123':
    return "<p>Your login information was correct.</p>"
    else:
    return "<p>Login failed.</p>"
    run(host='127.0.0.1', port=8080)
     

    reponse object

    Response 的元数据比如状态吗,响应头,cookie 都存储在response 对象里。

    Status Code

    默认200 ok ,通常不需要手动设置

    Response Header

    Response headers such as Cache-Control or Location are defined via Response.set_header(). This method takes two parameters, a header name and a value. The name part is case-insensitive:

    响应头。比如Cache-Control or Location 通过Response.set_header() 定义。这个方法需要两个参数,一个是头名字,一个是值,名称部分不区分大小写。

    大部分的头是独一无二的。意味着只有一个标头会被发给客户端,有些特别的头允许在相应出现不止一次,为了添加额外的标头,使用Response.add_header() 去代替Response.set_header()

    response.set_header('Set-Cookie', 'name=value')
    response.add_header('Set-Cookie', 'name2=value2')

    这只是个例子,如果想使用cookie. 往下看。

    Cookies

    访问之前定义的cookie 可以通过 Request.get_cookie(),设置新的cookies 用Response.set_cookie()

    @route('/hello')
    def hello_again():
        if request.get_cookie("visited"):
            return "Welcome back! Nice to see you again"
        else:
            response.set_cookie("visited", "yes")
            return "Hello there! Nice to meet you"

    Response.set_cookie()还接受一系列的关键字参数

    • max_age: Maximum age in seconds. (default: None)    存在时间
    • expires: A datetime object or UNIX timestamp. (default: None)   到期时间,datetime 对象或者unix timestamp
    • domain: The domain that is allowed to read the cookie. (default: current domain)    
    • path: Limit the cookie to a given path (default: /)   限制cookie 取得路径
    • secure: Limit the cookie to HTTPS connections (default: off).
    • httponly: Prevent client-side javascript to read this cookie (default: off, requires Python 2.7 or newer).
    • same_site: Disables third-party use for a cookie. Allowed attributes: lax and strict.
    • In strict mode the cookie will never be sent. In lax mode the cookie is only sent with a top-level GET request.

      使用cookie 注意的几点

    • cookie 被限制在4KB以内。
    • 有时候用户会选择将cookie 服务关掉,确保你的应用在没哟cookie情况下仍然能够使用
    • cookie 被存储在客户端,并且没有被加密,用户能够读取它。更糟糕的是,攻击者可能偷掉用户的cookies,通过xss 攻击,
    • 一些病i毒也知道读取浏览器的cookie,,因此永远不要吧机密信息放在cookie中。
    • cookie很容易被篡改。不要信任cookie

    signed cookie

    cookies 可以轻易的被恶意的客户端篡改。Bottle能够通过加密技术签名你的cookie,来防止被篡改。

    你所需要做的是提供一个key 用过secret 关键字参数传进去。无论什么时候去读或者设置cookie 保持这个key 是秘密的。如果cookie 没有签名或者不匹配,Resuqst.get_cookie将返回None

     回到顶部

    REQUEST DATA

    request object 是 BaseRequest 的子类。有丰富的借口去访问数据。

    FormDic

    使用一种特别的字典类型去存储data 和cookies ,FormDict 的行为像是一个正常的字典,但是额外的特色让你生活更简单,轻松。

    属性访问

    多值一个key    getall()

    FormsDict is a subclass of MultiDict and can store more than one value per key.

    The standard dictionary access methods will only return a single value, but the getall() method returns a (possibly empty) list of all values for a specific key:

    cookies

    所有被客户端发来的cookies 通过BaseRequest.cookies(a FormDcit)都是可用的

    from bottle import route, request, response
    @route('/counter')
    def counter():
        count = int( request.cookies.get('counter', '0') )
        count += 1
        response.set_cookie('counter', str(count))
        return 'You visited this page %d times' % count

    .get_cookie() 可以解密签名的cookies

    HTTP HEADERS

    所有从客户端发的HTTP headers 都被存放在 WSGIHeaderDcit 里,并能通过BaseRequest.headers 属性访问到,WSGIHeaderDcit 是基于key 大小写不敏感的字典。

    from bottle import route, request
    @route('/is_ajax')
    def is_ajax():
        if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
            return 'This is an AJAX request'
        else:
            return 'This is a normal request'

    Query Variables

    通过BaseRequest.query属性(FormDict)

    BaseRequest.query_string 是获得整个字符串。

    POST FORM 表单

    post data 存放在 BaseRequest.forms

     JSON Content

    BaseRequest.json 包含解析后的数据结构。

    回到顶部


    源码分析:

    从request 入手

    bootle 框架,只有一个bottle.py 文件,

    若不指明,以下所有代码都是bottl.py 文件里的

    bottle.py
    request = LocalRequest()
    class LocalRequest(BaseRequest):
        ''' A thread-local subclass of :class:`BaseRequest` with a different
            set of attributes for each thread. There is usually only one global
            instance of this class (:data:`request`). If accessed during a
            request/response cycle, this instance always refers to the *current*
            request (even on a multithreaded server). '''
        bind = BaseRequest.__init__
        environ = local_property()
    def local_property(name=None):
        if name: depr('local_property() is deprecated and will be removed.') #0.12
        ls = threading.local()
        def fget(self):
            try: return ls.var
            except AttributeError:
                raise RuntimeError("Request context not initialized.")
        def fset(self, value): ls.var = value
        def fdel(self): del ls.var
        return property(fget, fset, fdel, 'Thread-local property')

    在local_prperty() 函数中,定义了三个子函数,并用到了property修饰,使得ls 可以获取值,设置值。删除值,

    比如request.environ  #获取

    request.environ= 'asdasds'  #设置

    del request.environ  #删除

    自己测试

    dic={} 没用

    '
    >>> class Foo():
        dic={}
        def __init__(self,na):
            self._name = na
        def fget(self):
            return self._name
        def fset(self,value):
            self._name = value
            return value
        def fdel(self):
            del self._name
        name = property(fget,fset,fdel)
    
        
    >>> f1=Foo()
    Traceback (most recent call last):
      File "<pyshell#15>", line 1, in <module>
        f1=Foo()
    TypeError: __init__() missing 1 required positional argument: 'na'
    >>> f1=Foo('yuyang')
    >>> f1.name
    'yuyang'
    >>> f1.name = 'alex'
    >>> f1.name
    'alex'
    >>> del f1.name
    >>> f1.name
    Traceback (most recent call last):
      File "<pyshell#21>", line 1, in <module>
        f1.name
      File "<pyshell#14>", line 6, in fget
        return self._name
    AttributeError: 'Foo' object has no attribute '_name'
    >>> class Foo():
        dic={}
        def __init__(self,na):
            self._name = na
        def fget(self):
            return self._name
        def fset(self,value):
            self._name = value
            return value
        
        name = property(fget,fset)
    
        
    >>> f1=Foo('yuyang')
    >>> f1.name
    'yuyang'
    >>> f1.name ='da
    SyntaxError: EOL while scanning string literal
    >>> f1.name ='da'
    >>> del f1.name
    Traceback (most recent call last):
      File "<pyshell#28>", line 1, in <module>
        del f1.name
    AttributeError: can't delete attribute
    >>> 
    View Code

    property([fget[, fset[, fdel[, doc]]]])

    Return a property attribute for new-style classes (classes that derive from object).

    fget is a function for getting an attribute value, likewise fset is a function for setting, and fdel a function for del’ing, an attribute. Typical use is to define a managed attribute x:

    https://docs.python.org/release/2.6/library/functions.html#property

    environ = property(fget, fset, fdel, 'Thread-local property')   #用到了闭包函数。

    ls = threading.local()

    Thread-local data is data whose values are thread specific. To manage thread-local data, just create an instance of local (or a subclass) and store attributes on it:

    mydata = threading.local()
    mydata.x = 1

    The instance’s values will be different for separate threads.

    class threading.local

    A class that represents thread-local data.

    For more details and extensive examples, see the documentation string of the _threading_local module.

    import time
    import threading
    from threading import Thread
    
    def func(i):
        mydata.x=i
        print('xiancheng %s mydata.x is %s' %(i,mydata.x))
    
    
    
    mydata = threading.local()
    mydata.x = 'zhuxiancheng'
    ls = []
    for i in range(2):
        t = Thread(target=func,args=(i,))
        ls.append(t)
        t.start()
    
    for i in ls:
        i.join()
    
    print(mydata.x)
    print('end')
    View Code

    从上面的代码可以看出threading.local() 在不同的线程的值是不一样的。

     总的来说 bottle框架只有一个py文件 当import bottle 时

    做了以下几件事

    request = LocalRequest()
    
    #: A thread-safe instance of :class:`LocalResponse`. It is used to change the
    #: HTTP response for the *current* request.
    response = LocalResponse()
    
    #: A thread-safe namespace. Not used by Bottle.
    local = threading.local()
    
    # Initialize app stack (create first empty Bottle app)
    # BC: 0.6.4 and needed for run()
    app = default_app = AppStack()
    
    app.push()

    实例化一个request对象,response对象,执行,app = default_app = AppStack()  返回app=AppStack 实例对象  app.push()  会在列表里添加一个bottle 实例对象。

    class AppStack(list):
        """ A stack-like list. Calling it returns the head of the stack. """
    
        def __call__(self):
            """ Return the current default application. """
            return self[-1]
    
        def push(self, value=None):
            """ Add a new :class:`Bottle` instance to the stack """
            if not isinstance(value, Bottle):
                value = Bottle()
            self.append(value)
            return value
     AppStack 类继承Python列表

    local = threading.local()

    这是一个线程安全的名称空间

    第二部分   route

    first_bottle.py
    @route('/hello') def hello(): if request.get_cookie('account'): return 'welcome back! nice to see you again!' else: return 'you are not logged in ,access denied!'

    源码:

    route     = make_default_app_wrapper('route')
    
    
    
    def make_default_app_wrapper(name):
        ''' Return a callable that relays calls to the current default app. '''
        @functools.wraps(getattr(Bottle, name))
        def wrapper(*a, **ka):
            return getattr(app(), name)(*a, **ka)
        return wrapper
    @route('/hello') 是一个有参装饰器。


    关于有参装饰器

    @route('/hello')
    def login():
    pass
    实际会执行以下的代码:
    tmp = route('/hello') #第一步
    
    def login():         #第二步
        pass
    login = tmp(login)    #第三步

    当然在import bottle.py 时,就执行了这句代码:
    route     = make_default_app_wrapper('route')
    所以这时候route 实际是 wrapper 函数。
    执行步骤应该是:
    tmp = wrapper('/hello')
    tmp = getattr(app(), name)(*a, **ka)
    app 之前分析是由AppStack类实例出的对象,app() 会调用__call__() 方法,返回self[-1] 实际就是bottle 实例对象。

    我们知道name= 'route'
    从bottle对象去反射,找route方法,并调用

    让我们看下Bottle 类下的route 方法做了什么
    def route(self, path=None, method='GET', callback=None, name=None,
                  apply=None, skip=None, **config):
            """ A decorator to bind a function to a request URL. Example::
    
                    @app.route('/hello/:name')
                    def hello(name):
                        return 'Hello %s' % name
    
                The ``:name`` part is a wildcard. See :class:`Router` for syntax
                details.
    
                :param path: Request path or a list of paths to listen to. If no
                  path is specified, it is automatically generated from the
                  signature of the function.
                :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of
                  methods to listen to. (default: `GET`)
                :param callback: An optional shortcut to avoid the decorator
                  syntax. ``route(..., callback=func)`` equals ``route(...)(func)``
                :param name: The name for this route. (default: None)
                :param apply: A decorator or plugin or a list of plugins. These are
                  applied to the route callback in addition to installed plugins.
                :param skip: A list of plugins, plugin classes or names. Matching
                  plugins are not installed to this route. ``True`` skips all.
    
                Any additional keyword arguments are stored as route-specific
                configuration and passed to plugins (see :meth:`Plugin.apply`).
            """
            if callable(path): path, callback = None, path
            plugins = makelist(apply)
            skiplist = makelist(skip)
            def decorator(callback):
                # TODO: Documentation and tests   
                if isinstance(callback, basestring): callback = load(callback)
                for rule in makelist(path) or yieldroutes(callback):
                    for verb in makelist(method):
                        verb = verb.upper()
                        route = Route(self, rule, verb, callback, name=name,
                                      plugins=plugins, skiplist=skiplist, **config)
                        self.add_route(route)
                return callback
            return decorator(callback) if callback else decorator
    
    
    
    分析这段代码,不难得知,我们将'/hello'传进去,path = '/hello'
    先判断path 是不是可调用对象,比如函数,实例等,如果是的话,callback 和path 的值互换。
    makelist() 是吧一个对象放在列表中
    def makelist(data): # This is just to handy
        if isinstance(data, (tuple, list, set, dict)): return list(data)
        elif data: return [data]
        else: return []
    
    
    
    这个方法其实也是一个装饰器。

    所以回到最初
    getattr(app(), name)(*a, **ka) 这段代码执行结果会得到decorator(callback) if callback else decorator

    这次我们并没有传递callback 进去,所以会得到decorator

    所以tmp = decorator

    至此有参装饰器执行完第一步,

    第二步是定义login 函数
    第三步
    login = decorator(login)

            def decorator(callback):
                # TODO: Documentation and tests   
                if isinstance(callback, basestring): callback = load(callback)
                for rule in makelist(path) or yieldroutes(callback):
                    for verb in makelist(method):
                        verb = verb.upper()
                        route = Route(self, rule, verb, callback, name=name,
                                      plugins=plugins, skiplist=skiplist, **config)
                        self.add_route(route)
                return callback
    
    
    
    callback参数对应是login 函数
    basestring = str #bottle.py 提前定义的

    所以这段代码执行完,会实例化一个route 对象,并吧route 对象添加到bottle 对象里。返回callback 函数,所以得出被route装饰后的login 实际还是原来的login 函数。

        def add_route(self, route):
            ''' Add a route object, but do not change the :data:`Route.app`
                attribute.'''
            self.routes.append(route)
            self.router.add(route.rule, route.method, route, name=route.name)
    
    
    
    
    
    class Bottle
    self.routes = [] # List of installed :class:`Route` instances. self.router = Router() # Maps requests to :class:`Route` instances.
    
    
    
    有点复杂,先略过

    执行路由大致分析完了

    现在让我们分析最后让服务器启动的代码
    first_bottle.py

    run(host='127.0.0.1', port=8080,reloader=True)
    
    
    
    
    
    def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,
            interval=1, reloader=False, quiet=False, plugins=None,
            debug=None, **kargs):
        """ Start a server instance. This method blocks until the server terminates.
    
            :param app: WSGI application or target string supported by
                   :func:`load_app`. (default: :func:`default_app`)
            :param server: Server adapter to use. See :data:`server_names` keys
                   for valid names or pass a :class:`ServerAdapter` subclass.
                   (default: `wsgiref`)
            :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on
                   all interfaces including the external one. (default: 127.0.0.1)
            :param port: Server port to bind to. Values below 1024 require root
                   privileges. (default: 8080)
            :param reloader: Start auto-reloading server? (default: False)
            :param interval: Auto-reloader interval in seconds (default: 1)
            :param quiet: Suppress output to stdout and stderr? (default: False)
            :param options: Options passed to the server adapter.
    
    
    
    reloader 参数表示是不是重新加载,默认是false   ,interval 表示隔多长时间加载一次,默认是一秒。
    quiet 表示是不是废止输出错误信息等。默认是输出错误信息等。如果是True.不会再终端打印某些错误信息等。
        if reloader and not os.environ.get('BOTTLE_CHILD'):
            try:
                lockfile = None
                fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock')
                os.close(fd) # We only need this file to exist. We never write to it
                while os.path.exists(lockfile):
                    args = [sys.executable] + sys.argv
                    environ = os.environ.copy()
                    environ['BOTTLE_CHILD'] = 'true'
                    environ['BOTTLE_LOCKFILE'] = lockfile
                    p = subprocess.Popen(args, env=environ)
                    while p.poll() is None: # Busy wait...
                        os.utime(lockfile, None) # I am alive!
                        time.sleep(interval)
                    if p.poll() != 3:
                        if os.path.exists(lockfile): os.unlink(lockfile)
                        sys.exit(p.poll())
            except KeyboardInterrupt:
                pass
            finally:
                if os.path.exists(lockfile):
                    os.unlink(lockfile)
            return
    上面这段是当你设置参数重新加载为True 执行的代码,暂不分析这段代码,
    以下应该是run 函数的核心代码。

        try:
            if debug is not None: _debug(debug)
            app = app or default_app()
            if isinstance(app, basestring):
                app = load_app(app)
            if not callable(app):
                raise ValueError("Application is not callable: %r" % app)
    
            for plugin in plugins or []:
                app.install(plugin)
    
            if server in server_names:
                server = server_names.get(server)
            if isinstance(server, basestring):
                server = load(server)
            if isinstance(server, type):
                server = server(host=host, port=port, **kargs)
            if not isinstance(server, ServerAdapter):
                raise ValueError("Unknown or unsupported server: %r" % server)
    
            server.quiet = server.quiet or quiet
            if not server.quiet:
                _stderr("Bottle v%s server starting up (using %s)...
    " % (__version__, repr(server)))
                _stderr("Listening on http://%s:%d/
    " % (server.host, server.port))
                _stderr("Hit Ctrl-C to quit.
    
    ")
    
            if reloader:
                lockfile = os.environ.get('BOTTLE_LOCKFILE')
                bgcheck = FileCheckerThread(lockfile, interval)
                with bgcheck:
                    server.run(app)
                if bgcheck.status == 'reload':
                    sys.exit(3)
            else:
                server.run(app)
        except KeyboardInterrupt:
            pass
        except (SystemExit, MemoryError):
            raise
        except:
            if not reloader: raise
            if not getattr(server, 'quiet', quiet):
                print_exc()
            time.sleep(interval)
            sys.exit(3)
    debug 默认是None, 下面这句并不执行。当debug is None
    if debug is not None: _debug(debug) 


    app = app or default_app()
    if isinstance(app, basestring):
                app = load_app(app)
            if not callable(app):
                raise ValueError("Application is not callable: %r" % app)

    app 由之前分析,是个AppStack 实例对象,app 有__call__方法,所以是可调用对象

    测试:

    >>> class Foo: pass >>> isinstance(Foo,type) True >>> callable(Foo) True >>> f1 = Foo() >>> callable(f1) False >>> class Foo: def __call__(self): pass >>> f1 = Foo() >>> callable(f1) True
    只要一个对象有__call__ 属性就是可调用对象

            if server in server_names:
                server = server_names.get(server)
            if isinstance(server, basestring):
                server = load(server)
            if isinstance(server, type):
                server = server(host=host, port=port, **kargs)
            if not isinstance(server, ServerAdapter):
                raise ValueError("Unknown or unsupported server: %r" % server)
    server  = 'wsgiref' 
    server_names = {
        'cgi': CGIServer,
        'flup': FlupFCGIServer,
        'wsgiref': WSGIRefServer,
        'waitress': WaitressServer,
        'cherrypy': CherryPyServer,
        'paste': PasteServer,
        'fapws3': FapwsServer,
        'tornado': TornadoServer,
        'gae': AppEngineServer,
        'twisted': TwistedServer,
        'diesel': DieselServer,
        'meinheld': MeinheldServer,
        'gunicorn': GunicornServer,
        'eventlet': EventletServer,
        'gevent': GeventServer,
        'geventSocketIO':GeventSocketIOServer,
        'rocket': RocketServer,
        'bjoern' : BjoernServer,
        'auto': AutoServer,
    }
    server_names 是个字典,key 是server 字符串,value 是对应的类。

    至此,server = WSGIRefServer
    执行下面代码:


    if isinstance(server, type):
                server = server(host=host, port=port, **kargs)
    server 是一个WSGIRefServer 实例化的对象。
    ServerAdapter 类是一个抽象类,所有服务器的类都继承这个类。

    到了关键的服务器启动的一步
    server.run(app)
    
    
    server.run(app)
    server.run(app)
    来看WSGIRefServe 类
    class WSGIRefServer(ServerAdapter):
        def run(self, app): # pragma: no cover
            from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
            from wsgiref.simple_server import make_server
            import socket
    
            class FixedHandler(WSGIRequestHandler):
                def address_string(self): # Prevent reverse DNS lookups please.
                    return self.client_address[0]
                def log_request(*args, **kw):
                    if not self.quiet:
                        return WSGIRequestHandler.log_request(*args, **kw)
    
            handler_cls = self.options.get('handler_class', FixedHandler)
            server_cls  = self.options.get('server_class', WSGIServer)
    
            if ':' in self.host: # Fix wsgiref for IPv6 addresses.
                if getattr(server_cls, 'address_family') == socket.AF_INET:
                    class server_cls(server_cls):
                        address_family = socket.AF_INET6
    
            srv = make_server(self.host, self.port, app, server_cls, handler_cls)
            srv.serve_forever()
    ServerAdapter类,初始化,run方法又子类重写。
    class ServerAdapter(object):
        quiet = False
        def __init__(self, host='127.0.0.1', port=8080, **options):
            self.options = options
            self.host = host
            self.port = int(port)
    
        def run(self, handler): # pragma: no cover
            pass
    
    
    
    看到这里,我想我要去研究WSGIREF 模块的源码去了。。。。。。。
    server.run(app)主要看红色部分的代码,调用了wsgiref 模块
    调用wsgiref的make_server 实例了一个srv对象,srv调用serve_forever() 方法

    simple_server.py
    def
    make_server( host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler ): """Create a new WSGI server listening on `host` and `port` for `app`""" server = server_class((host, port), handler_class) server.set_app(app) return server
    
    
    
    实例了WSGIServer对象
    WSGIServer 继承自
    HTTPServer ,
    
    
    class WSGIServer(HTTPServer):
    
        """BaseHTTPServer that implements the Python WSGI protocol"""
    
        application = None
    
        def server_bind(self):
    
    
    
    
    
    HTTPServer ,继承自socketserver.TCPServer
    class HTTPServer(socketserver.TCPServer):
    
        allow_reuse_address = 1    # Seems to make sense in testing environment
    
        def server_bind(self):
            """Override server_bind to store the server name."""
            socketserver.TCPServer.server_bind(self)
            host, port = self.server_address[:2]
            self.server_name = socket.getfqdn(host)
            self.server_port = port
    TCPServer继承自BaseServer
    创建一个socket对象
    然后执行server_bind()方法和server_active()方法
    server_bind()绑定ip端口,设置基本环境变量

    
    
    
    
    
    class TCPServer(BaseServer):
    
        address_family = socket.AF_INET
    
        socket_type = socket.SOCK_STREAM
    
        request_queue_size = 5
    
        allow_reuse_address = False
    
        def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
            """Constructor.  May be extended, do not override."""
            BaseServer.__init__(self, server_address, RequestHandlerClass)
            self.socket = socket.socket(self.address_family,
                                        self.socket_type)
            if bind_and_activate:
                try:
                    self.server_bind()
                    self.server_activate()
                except:
                    self.server_close()
                    raise
      
    cket.getsockname()
    
    
    
     
    TCPServer
     绑定
     TCPServer   类下的

    def server_bind(self): """Called by constructor to bind the socket. May be overridden. """ if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) self.server_address = self.socket.getsockname()
    
    
    
     监听
        def server_activate(self):
            """Called by constructor to activate the server.
    
            May be overridden.
    
            """
            self.socket.listen(self.request_queue_size)
    
    
    
    至此实例化结束,执行srv.serve_forever()  这个函数是处理请求,直到关机。使用轮询,用到了selector 模块,
    def serve_forever(self, poll_interval=0.5):
            """Handle one request at a time until shutdown.
    
            Polls for shutdown every poll_interval seconds. Ignores
            self.timeout. If you need to do periodic tasks, do them in
            another thread.
            """
            self.__is_shut_down.clear()
            try:
                # XXX: Consider using another file descriptor or connecting to the
                # socket to wake this up instead of polling. Polling reduces our
                # responsiveness to a shutdown request and wastes cpu at all other
                # times.
                with _ServerSelector() as selector:
                    selector.register(self, selectors.EVENT_READ)
    
                    while not self.__shutdown_request:
                        ready = selector.select(poll_interval)
                        if ready:
                            self._handle_request_noblock()
    
                        self.service_actions()
            finally:
                self.__shutdown_request = False
                self.__is_shut_down.set()
    
    
    
    note :这里的self.__is_shut_down 是一个event 对象,
    self.__is_shut_down = threading.Event()
    
    
    
    
    
    self.__shutdown_request = False
    
    
    
     






















    srv.serve_foreverT放到 适当放松的()
     

























































































































































































































     


















    于洋 回到顶部

  • 相关阅读:
    学习Kubernetes,这些负载均衡知识点得知道!
    Nginx请求处理流程
    字节跳动面试题+答案,答对了30+
    Cache 和 Buffer 的区别在哪里
    优化你的HTTPS(下),你需要这么做
    优化你的HTTPS(上),你需要这么做
    swift之Mac中NSSplitView的简单实用
    oc之mac中- NSBox
    Mac之NSImageView的简单实用
    swift之预防 Timer 的循环引用
  • 原文地址:https://www.cnblogs.com/yuyang26/p/7860322.html
Copyright © 2020-2023  润新知