|
|
51CTO旗下网站
|
|
移动端

微服务入门:Openresty实现API网关

如果大家清楚“网关”这个概念,那就很容易理解“API网关“,即所有API的入口。 从面向对象设计的角度看,它与外观模式类似,封装了系统内部架构。

作者:热爱技术君来源:今日头条|2019-09-24 08:44

【大咖·来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》

微服务入门:Openresty实现API网关

概念介绍

如果大家清楚“网关”这个概念,那就很容易理解“API网关“,即所有API的入口。 从面向对象设计的角度看,它与外观模式类似,封装了系统内部架构。在单体应用架构中,没有「 API网关 」的概念,每个项目都会用到filter/过滤器之类的东西,filter的作用就是把项目中的一些非业务逻辑的功能抽离出来独立处理,避免与业务逻辑混在一起增加代码复杂度。比如 鉴权认证功能、Session处理、安全检查、日志处理等等。

如果采用微服务架构,那一个项目中微服务节点很多,如果让每一个节点都去处理上面这些 “鉴权认证功能、Session处理、安全检查、日志处理等” 会多出很多冗余的代码,也会给增加业务代码的复杂度,因此就需要有一个API网关把这些公共的功能独立出来成为一个服务来统一的处理这些事情。

微服务入门:Openresty实现API网关

主要功能

API网关就像是微服务的一扇门,是连通外部客户端与内部微服务之间的一个桥梁。

其主要功能有:

  • 路由转发 API网关是内部微服务的对外唯一入口,所以外面全部的请求都会先发到API网上,然后由API网关来根据不同的请求去路由到不同的微服务节点上。
  • 负载均衡 API网关收到外部请求之后,可以根据内部微服务每个实例的负荷情况进行动态的负载均衡调节。一旦内部的某个微服务实例负载很高,甚至是不能及时响应,则API网关就通过负载均衡策略减少或停止向这个实例转发请求。当所有的内部微服务实例都处理不过来的时候,API网关还可以采用限流或熔断的形式阻止外部请求,以保障整个系统的可用性。
  • 安全认证 API网关就像是微服务的大门守卫,每一个请求进来之后,都必须先在API网关上进行安全验证或身份验证,验证通过后才转发给后面的服务。
  • 日志记录 所有的请求都需要走API网关,那么就可以在API网关上统一集中的记录下这些行为日志。
  • 数据转换 因为API网关对外是面向多种不同的客户端,不同的客户端所传输的数据类型可能是不一样的。因此API网关还需要具备数据转换的功能,将不同客户端传输进来的数据转换成同一种类型再转发给内部微服务上,这样,兼容了这些请求的多样性,保证了微服务的灵活性。

OpenResty

API网关最主要的功能实现就是请求拦截,在网络请求的整个生命阶段加入各种filter/过滤器, OpenResty提供了这样的功能。

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

OpenResty 处理一个请求,它的处理流程请参考下图(从 Request start 开始):

微服务入门:Openresty实现API网关

依据OpenResty的请求处理流程,和各种第三方模块,就可以在流程节点中加入我们的API网关逻辑代码。例如,对API请求数量进行统计:

  1. lua_package_path "module/lua-resty-hmac/lib/?.lua;module/lua-resty-redis/lib/?.lua;module/lua-resty-mysql/lib/?.lua;module/lua-resty-jwt/lib/?.lua;;"
  2. server { 
  3.  listen 80; 
  4.  server_name gw.gitlib.cn; 
  5.  access_log /var/log/nginx/gw.gitlib.cn.access.log access; 
  6.  # lua_code_cache off
  7.  set $redis_host "192.168.1.106"
  8.  set $redis_port "6379"
  9.  set $redis_incrkey "api:access:num"
  10.  access_by_lua_file gateway/intercept.lua; # 对所有请求进行拦截处理 
  11.  location = /num { 
  12.   
  13.  content_by_lua_block { 
  14.  local _redis = require "resty.redis" 
  15.  local redis = _redis:new() 
  16.  redis:set_timeout(1000) 
  17.   
  18.  local ok, err = redis:connect(ngx.var.redis_host, ngx.var.redis_port) 
  19.  if not ok then 
  20.  ngx.say("failed to connect: ", err) 
  21.  return 
  22.  end 
  23.  local res, err = redis:get(ngx.var.redis_incrkey) 
  24.  if not res then 
  25.  ngx.say("failed to get key: ", err) 
  26.  return 
  27.  end 
  28.  if res == ngx.null then 
  29.  ngx.say("key not found."
  30.  return 
  31.  end 
  32.  ngx.say("api:access:num:", res) 
  33.  } 
  34.  } 
  35.   
  36.  location ~ ^/api/([\w]+) { 
  37.  default_type text/html;  
  38.  content_by_lua_file /web/gw/api/$1.lua; 
  39.  } 

上面是我们的nginx配置,引入了redis模块,用于存储API请求数量,接下来,我们在gateway/intercept.lua中实现API请求数量统计的处理逻辑:

  1. local function increseNum(key
  2.     -- get key from rediskey 
  3.     local _redis = require "resty.redis" 
  4.     local redis = _redis:new() 
  5.     redis:set_timeout(100) 
  6.     local ok, err = redis:connect(ngx.var.redis_host, ngx.var.redis_port) 
  7.     if not ok then 
  8.         ngx.log(ngx.ERR, "failed to connect to redis: ", err) 
  9.         return nil 
  10.     end  
  11.     if ngx.var.redis_auth then 
  12.         local ok, err = redis:auth(ngx.var.redis_auth) 
  13.         if not ok then 
  14.             ngx.log(ngx.ERR, "failed to authenticate: ", err) 
  15.             return nil 
  16.         end 
  17.     end 
  18.     if ngx.var.redis_db then 
  19.  local ok, err = redis:select(ngx.var.redis_db) 
  20.  if not ok then 
  21.  ngx.log(ngx.ERR, "failed to select db: ", ngx.var.reddb, " ", err) 
  22.  return nil 
  23.  end 
  24.  end 
  25.  local res, err = redis:incr(key
  26.  if not res then 
  27.  ngx.log(ngx.ERR, "failed to incr key: "key ,", ", err) 
  28.  return nil 
  29.  end 
  30.  if res == ngx.null then 
  31.  ngx.log(ngx.ERR, "key "key" not found"
  32.  return ngx.null 
  33.  end 
  34.  local ok, err = redis:close() 
  35.  if not ok then 
  36.  ngx.log(ngx.ERR, "failed to close: ", err) 
  37.  end 
  38.  return res 
  39. end 
  40. increseNum(ngx.var.redis_incrkey) 

就这样,我们实现了API网关的一个小功能,其他功能实现,就靠大家去摸索了。目前市面上成熟的API网关实现方案有很多,采用openresty 开发出的api网关,比如比较流行的kong、orange等, 大家可以自行了解。

【编辑推荐】

  1. 网络排查只会Ping?3个实用命令帮你详细定位问题点
  2. 这样解释IP地址、子网掩码、网关之间的联系,不会技术也能听懂
  3. API网关从入门到放弃
  4. 为什么边界网关协议(BGP)可见性比以往更加重要
  5. 部署Nginx Plus作为API网关:Nginx
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

用Python玩转excel

用Python玩转excel

让重复操作傻瓜化
共3章 | DE8UG

187人订阅学习

AI入门级算法

AI入门级算法

算法常识
共22章 | 周萝卜123

164人订阅学习

这就是5G

这就是5G

5G那些事儿
共15章 | armmay

132人订阅学习

读 书 +更多

SUN Solaris 9/10系统管理员认证指南

本书专门根据SUN官方的SCSA for Solaris 9&10考试大纲撰写而成,全面覆盖了SCSA for Solaris 9/10的认证考点,除此之外本书还有大量的非考...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微