基于 OpenResty 的接口网关设计( 三 )


然而,目前的代理架构受到了当前整体架构的约束,实际上两层的 HAProxy 代理并不是必需的 。

  • 对于外层 HAProxy internet,由于我们使用了与 HAProxy 紧密结合的 Openshift 架构,所以多了一层 HAProxy 的转发;一般情况下,基于 OpenResty 的 Nginx 网关层可以直接在外网上提供服务 。
  • 对于内层的 HAProxy internal,由于我们当前还没有实现服务治理,所以需要内层的 HAProxy internal 进行一层转发;当实现了服务治理,可以消除内层 HAProxy 代理,减少转发消耗 。
在我们当前的系统量级下,这两层 HAProxy 转发消耗非常小可以被接受,所以调整架构的优先级还不高,以后再慢慢演进 。
3.2.2 接口网关
接下来这一节是最为重点的接口网关的设计 。接口网关主要利用前文所述的 OpenResty 执行阶段对请求与响应进行流程处理,包括接口地址的重写,IP 与资源白名单的控制,请求的解密与验签,请求的路由以及响应的签名与加密等 。
这里分成主流程,配置服务,安全服务三部分进行讲述 。
3.2.2.1 主流程设计
主流程是网关的核心,是请求处理的控制中心;它是通过 OpenResty 的 Lua 脚本处理流程来实现对请求的处理 。
基于 OpenResty 的接口网关设计

文章插图
 
 
A. 主流程
  1. 在 OpenResty 服务启动之后,首先通过 init_by_lua_block 阶段初始化常量(包括调用配置服务以及安全服务所需的主机地址、端口、URL 地址等)、引入依赖(包括常用的 http 以及 cjson 依赖等)等作为全局使用;
  2. 接着通过 init_worker_by_lua_file 阶段设置定时任务调用内网配置服务来缓存配置,为处理第三方的请求做准备,其中加载的配置可供 URL 重写(即接口映射)、IP 以及资源(URI)白名单限制、请求的解密验签以及响应的签名加密使用,详情查看配置服务一节 。
  3. 当第三方请求通过 HAProxy Internet 进入到网关后,根据配置通过 rewrite_by_lua_file 阶段做 URL 重写(即接口映射) 。
  • 服务接口 URL 发生变更,为了兼容旧的第三方调用,需要重写第三方请求 URL 到新服务接口上
  • Restful 接口的 Path Variable 在 Nginx 环境与在 Tomcat 环境上正则匹配的差异
  • 需要重写的原因可能有:
  • URL 重写后,通过 rewrite_by_lua_file 进入访问控制阶段,此时根据授权的第三方 IP 白名单列表,授权予第三方的开放接口列表,校验请求的 IP 以及 URL 。
  • IP 与 URI 校验通过后,同样在 rewrite_by_lua_file 阶段根据配置调用内网的安全服务进行请求的解密与验签,获取明文 。
  • 在 content_by_lua_file 阶段通过 ngx.location.capture 将原请求头部信息以及参数等信息封装到子请求中,借助自请求转发原请求到开发接口服务中 。
  • 注意:根据官方文档说明,ngx.location.capture 发送子请求会缓存响应在内存中,直到整个请求处理结束 。那么,当有响应报文特别长或者请求并发非常高时,需要使用 cosocket 来替代 ngx.location.capture,避免因内存不足造成网关服务失效 。
  • 同样在 content_by_lua_file 阶段根据配置调用安全服务进行响应的签名与加密,获取签名与密文返回给第三方 。
  • B. 文件结构
  • 项目的大致结构如下,主要分为 Lua 代码目录和环境配置目录 。
--openapi --lua --access_by_lua.lua --cache_management.lua --content_by_lua.lua --init_work_by_lua.lua --rewrite_by_lua.lua --security.lua --prod --Dockerfile --nginx.conf --sit --Dockerfile --nginx.conf --README.md
  •  
  • C. 主流程在 conf 中的配置
# Nginx worker 进程个数,直接影响性能 。# 如果确认不会出现阻塞式调用,那么有多少 CPU 内核设置多少个进程# 如果有可能出现阻塞式调用,需要配置多一些进程worker_processes 1; events { worker_connections 1024;}http { # 内网地址 resolver xxx.x.x.xxx yyy.y.y.yyy; # 日志格式配置 log_format graylog2_format '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" ' '<msec=$msec|connection=$connection|connection_requests=$connection_requests|millis=$request_time>'; # 日志路径配置 access_log syslog:server=<host>:<port> graylog2_format; error_log syslog:server=<host>:<port> warn; # 配置 Lua 包地址 lua_package_path '$prefix/lua/?.lua;;'; init_by_lua_block { # 引入依赖(可能会污染全局环境,待研究) http = require "resty.http" cjson = require "cjson" cache_management = require "cache_management" ... } # 设置定时任务缓存配置,及上面的 cache_management 模块 init_worker_by_lua_file lua/init_work_by_lua.lua; # Nginx Web 服务配置 server { listen 80; # ngx.location.capture 子请求代理,转发原请求到接口服务 location = /ngx_proxy/ { internal; proxy_set_header Accept-Encoding ''; proxy_pass http://$context$http_host_suffix$proxy_uri; } # 匹配所有请求,进行 URL 重写、访问控制、转发请求以及响应处理(各阶段的处理在此配置) 。location / { set $context ''; ... rewrite_by_lua_file lua/rewrite_by_lua.lua; access_by_lua_file lua/access_by_lua.lua; content_by_lua_file lua/content_by_lua.lua; } }}


推荐阅读