前言
在微服务架构中,网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过网关这一层。实现了统一集中管理api,避免客户端直接与各个业务服务访问,同时对请求进行负载均衡、权限认证、访问控制和流量限制等。这次我将主要给大家分享我们用到的网关的选型依据和使用经验。主要是从网关的作用,网关框架选型、网关服务开发,以及我们遇到的问题和一些总结。
网关作为连接服务的消费方和服务提供方的中间件系统,将各自的业务系统的演进和发展做了天然的隔离,使业务系统更加专注于业务服务本身,同时网关还可以为服务提供和沉淀更多附加功能,主要作用如下:
网关是微服务架构的出现,不同的微服务一般有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成完成一个业务需求,如果让客户端直接与各个业务服务访问,会出现以下的问题:
- 客户端会多次请求不同的微服务,增加了客户端的复杂性。
- 存在跨域请求,在一定场景下处理相对复杂。
- 认证复杂,每个服务都需要独立的认证。
- 难以重构,随着项目的迭代。可能需要重新划分微服务。如果客户端与业务服务直接访问,那么重构将会很复杂。
二、网关框架选型
网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过API网关这一层。也就是说,API网关可以完成安全、性能、监控等功能,而服务提供者可以专门的完成具体的业务逻辑。易于监控,可以在网关收集监控数据并将其推送到外部系统进行分析;易于认证,可以在网关进行认证,然后再将请求转发到后端的微服务,而无需在每个微服务中进行认证;减少客户端和各个微服务之间的交互次数。
常用网关
SpringCloud的生态圈里面有非常多优秀的工具,
包括目前常用的微服务网关有zuul、Spring Cloud
gateway,还有另一种选择OpenResty。
OpenResty 为什么能实现网关呢? 有一个非常重要的因素是,对于每一个请求,OpenResty 会把请求分为不同阶段,从而可以让第三方模块通过挂载行为来实现不同阶段的自定义行为。这种机制可以非常方便的设计api网关。
Nginx 本身在处理一个用户请求时,会按照不同的阶段进行处理,总共会分为 11个阶段。而 OpenResty 的执行指令,就是在这 11 个步骤中挂载 lua 执行脚本实现扩展,我们分别看看每个指令的作用。
initbylua : 当 Nginx master 进程加载 nginx 配置文件时会运行这段 lua 脚本,一般用来注册全局变量或者预加载 lua 模块
initwokerby_lua: 每个 Nginx worker 进程启动时会执行的 lua 脚本,可以用来做健康检查
setbylua:设置一个变量
rewritebylua:在 rewrite 阶段执行,为每个请求执行指定的 lua 脚本
accessbylua:为每个请求在访问阶段调用 lua 脚本
contentbylua:前面演示过,通过 lua 脚本生成 content 输出给 http 响应
balancerbylua:实现动态负载均衡,如果不是走 contentbylua,则走 proxy_pass,再通过 upstream 进行转发
headerfilterby_lua: 通过 lua 来设置 headers 或者 cookie
bodyfilterby_lua:对响应数据进行过滤
logbylua : 在 log 阶段执行的脚本,一般用来做数据统计,将请求数据传输到后端进行分析
OpenResty优势
首先我们选择使用OpenResty,其是由Nginx核心加很多第三方模块组成,其最大的亮点是默认集成了Lua开发环境,使得Nginx可以作为一个Web Server使用。利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应,使在Nginx上开发Web应用更方便更简单。目前在京东如实时价格、秒杀、动态服务、单品页、列表页等都在使用Nginx+Lua架构,其他公司如淘宝、去哪儿网等。
网关用了OpenResty,是一个基于nginx的可伸缩的Web应用服务器,提供了很多高质量的第三方模块,开发人员可以使用lua脚本调动nginx支持的各种C以及lua模块。在性能方面,OpenResty可以快速构造出支持10k以上并发连接响应的高性能Web应用系统。OpenResty在本项目中,用于JWT权限校验、Token超时时间更新、限流保护、无需登录接口的加密串验证。
三、网关服务开发
服务启动时,将自己的节点信息注册到etcd,包括:服务名称、ip、端口。网关服务从etcd监听服务节点信息变更,并保存到缓存中,从客户端请求的url中提取服务名称,通过服务名称查找节点信息,将请求转发到后端服务。
- 对外api前缀:/open/api/service_name/**,后端服务路由:/open/api/**
- 对内api前缀:/inner/api/service_name/**,后端服务路由:/**
例如/open/api/user/fav会转发到user服务的/open/api/fav接口;/inner/api/user/info会转发到user服务的/info接口。通过url规范我们就能很轻易的做api鉴权,而不需要将对外api和对内api部署成2个服务(当然也要考虑实际情况,是否符合实际项目要求)。