GLB下部署的Sanic应用获取用户ip

前情提要

最近要把给部署的机器都升级,从Debian9升级到Debian10. Debian9的默认python版本是python3.5,而Debian10的默认版本是python3.7,为了部署方便和日后升级,我把要部署的Sanic应用也从python3.5升级到python3.7了。

升级完之后,在测试的时候却发现,request.remote_addr不能像往常一样获取客户端IP了,经过一番折腾,终于搞定。

环境

我们的应用架构较为简单,GLB --> Nginx --> Sanic.

  • GLB:Google Load Balancing. 谷歌的全球范围的负载均衡器,我们通过GLB,来进行路由转发、流量分发到相应的backend。每个backend是一个实例组,实例组下面有若干个VM实例(VM,既虚拟机)。

  • Nginx:不多说。每个实例运行Nginx进行机器内部的负载均衡,把流量分发到机器内部的若干个Sanic进程

  • Sanic:一个python高性能web异步框架,框架特性开箱即用,无需像Flask一样需要部署Gunicone 或者uWISG之类的Web Server。此时升级是从 Sanic 19.3.1升级到Sanic==21.6.0

    Nginx 配置如下

        location / {
            proxy_pass_header Server;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Scheme $scheme;
    ​
            real_ip_header    X-Forwarded-For;
            real_ip_recursive on;
            proxy_pass http://backend_hosts;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }

    正文

    源码解析

    查看Sanic源码,发现从 Sanic 19.3.1升级到Sanic==21.6.0后,request.remote_addr代码有所更改。

    remote_addr

    # Sanic 19.3.1
    @property
    def remote_addr(self):
        """Attempt to return the original client ip based on X-Forwarded-For.
        :return: original client ip.
        """
        if not hasattr(self, "_remote_addr"):
            forwarded_for = self.headers.get("X-Forwarded-For", "").split(",")
            remote_addrs = [
                addr
                for addr in [addr.strip() for addr in forwarded_for]
                if addr
            ]
            if len(remote_addrs) > 0:
                self._remote_addr = remote_addrs[0]
            else:
                self._remote_addr = ""
        return self._remote_addr
    # Sanic 21.6.0
        @property
        def remote_addr(self) -> str:
            """
            Client IP address, if available.
            1. proxied remote address `self.forwarded['for']`
            2. local remote address `self.ip`
    ​
            :return: IPv4, bracketed IPv6, UNIX socket name or arbitrary string
            :rtype: str
            """
            if not hasattr(self, "_remote_addr"):
                self._remote_addr = str(
                    self.forwarded.get("for", "")
                )  # or self.ip
            return self._remote_addr

    从源码看来,在19.3.1版本获取ip时,是直接从http header获取X-Forwarded-For字段,并且如果X-Forwarded-For里面存在多个ip,取第一个。而升级到了21.6.0的时候,则多了一些处理。

    forwarded

        @property
        def forwarded(self) -> Options:
            """
            Active proxy information obtained from request headers, as specified in
            Sanic configuration.
    ​
            Field names by, for, proto, host, port and path are normalized.
            - for and by IPv6 addresses are bracketed
            - port (int) is only set by port headers, not from host.
            - path is url-unencoded
    ​
            Additional values may be available from new style Forwarded headers.
    ​
            :return: forwarded address info
            :rtype: Dict[str, str]
            """
            if self.parsed_forwarded is None:
                self.parsed_forwarded = (
                    parse_forwarded(self.headers, self.app.config)      # 注释1
                    or parse_xforwarde
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值