49 | 微服务API网关搭建三步曲(三)
下载APP
关闭
渠道合作
推荐作者
49 | 微服务API网关搭建三步曲(三)
2019-09-16 温铭 来自北京
《OpenResty从入门到实战》
课程介绍
讲述:温铭
时长07:03大小6.47M
你好,我是温铭。
今天这节课,微服务 API 网关搭建就到了最后的环节了。让我们用一个最小的示例来把之前选型的组件,按照设计的蓝图,拼装运行起来吧!
Nginx 配置和初始化
我们知道,API 网关是用来处理流量入口的,所以我们首先需要在 Nginx.conf 中做简单的配置,让所有的流量都通过网关的 Lua 代码来处理。
这里我们使用开源 API 网关 APISIX 为例,所以上面的代码示例中带有 apisix 的关键字。在这个示例中,我们监听了 9080 端口,并通过 location / 的方式,把这个端口的所有请求都拦截下来,并依次通过 access、rewrite、header filter、body filter 和 log 这几个阶段进行处理,在每个阶段中都会去调用对应的插件函数。其中, rewrite 阶段便是在 apisix.http_access_phase 函数中合并处理的。
而对于系统初始化的工作,我们放在了 init_worker 阶段来处理,这其中包含了读取各项配置参数、预制 etcd 中的目录、从 etcd 中获取插件列表、对于插件按照优先级进行排序等。我这里列出了关键部分的代码并进行讲解,当然,你可以在 GitHub 上看到更完整的初始化函数。
通过阅读这段代码,你可以发现,router 和 plugin 这两部分的初始化相对复杂一些,主要涉及到读取配置参数,并根据参数的不同做一些选择。因为这里会涉及到从 etcd 中读取数据,所以我们使用的是 ngx.timer 的方式,来绕过“不能在 init_worker 阶段使用 cosocket”的这个限制。如果你对这部分很感兴趣并且学有余力,建议一定要去读读源码,加深理解。
匹配路由
在最开始的 access 阶段里面,我们首先需要做的就是匹配路由,根据请求中携带 uri、host、args、cookie 等,来和已经设置好的路由规则进行匹配:
对外暴露的,其实只有上面一行代码,这里的api_ctx 中存放的就是 uri、host、args、cookie 这些请求的信息。而具体的 match 函数的实现,就用到了我们前面提到过的 lua-resty-radixtree。如果没有命中,就说明这个请求并没有设置与之对应的上游,就会直接返回 404。
加载插件
当然,如果路由可以命中,就会走到过滤插件和加载插件的步骤,这也是 API 网关的核心所在。我们先来看下面这段代码:
在这段代码中,我们首先通过 table pool 的方式,申请了一个长度为 32 的 table,这是我们之前介绍过的性能优化技巧。然后便是插件的过滤函数。你可能疑惑,为什么需要这一步呢?在插件的 init worker 阶段,我们不是已经从 etcd 中获取插件列表并完成排序了吗?
事实上,这里的过滤是和本地配置文件来做对比的,主要有下面两个原因。
第一,新开发的插件需要灰度来发布,这时候新插件在 etcd 的列表中存在,但只在部分网关节点中处于开启状态。所以,我们需要额外做一次交集的运算。
第二,为了支持 debug 模式。终端的请求经过了哪些插件的处理?这些插件的加载顺序是什么?这些信息在调试的时候会很有用,所以在过滤函数中也会判断其是否处于 debug 模式,并在响应头中记录下这些信息。
因此,在 access 阶段的最后,我们会把这些过滤好的插件,按照优先级逐个运行,如下面这段代码所示:
你可以看到,在遍历插件的时候,我们是以 2 为间隔进行的,这是因为每个插件都会有两个部分组成:插件对象和插件的配置参数。现在,我们来看上面示例代码中最核心的那一行代码:
单独看这行代码会有些抽象,我们用一个具体的 limit_count 插件来替换一下,就会清楚很多:
编写插件
现在,距离一个完整的 demo 还差一件事情,那就是编写一个插件,让它可以跑起来。我们以 limit-count 这个限制请求数的插件为例,它的完整实现只有 60 多行代码,你可以点击链接查看。下面,我来详细讲解下其中的关键代码。
首先,我们要引入 lua-resty-limit-traffic ,作为限制请求数的基础库:
然后,使用 rapidjson 中的 json schema ,来定义这个插件的参数有哪些:
插件的这些参数,和大部分 resty.limit.count 的参数是对应的,其中包含了限制的 key、时间窗口的大小、限制的请求数。另外,插件中增加了一个参数: rejected_code,在请求被限速的时候返回指定的状态码。
最后一步,我们把插件的处理函数挂载到 rewrite 阶段:
上面的代码中,进行限制判断的逻辑只有一行,其他的都是来做准备工作和设置响应头的。如果没有超过阈值,就会继续按照优先级运行下一个插件。
写在最后
今天这节课,通过整体框架和插件的编写,我们就完成了一个 API 网关的 Demo。更进一步,利用本专栏学到的 OpenResty 知识,你可以在上面继续添砖加瓦,搭建更丰富的功能。
最后,给你留一个思考题。我们知道,API 网关不仅可以处理七层的流量,也可以处理四层的流量,基于此,你能想到它的一些使用场景吗?欢迎留言说说你的看法,也欢迎你把这篇文章分享出去,和更多的人一起学习、交流。
分享给需要的人,Ta购买本课程,你将得18元
生成海报并分享
赞 1
提建议
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
上一篇
48 | 微服务API网关搭建三步曲(二)
下一篇
50 | 答疑(五):如何在工作中引入 OpenResty?
精选留言(11)
- 铁匠2020-02-23我自己实现了一个简单的网关服务,注册中心使用etcd,因为是初学,还有很多改进的地方,设计思路和优化点欢迎老师和大家指点。github: https://github.com/fengjx/resty-gateway7
- 一声扣钉2019-09-16老师,如果需要修改respones body的内容,就只能在body_filter里做修改,这样引起body长度与content-length长度不一致,应该如何处理?
作者回复: 这是个好问题。如果你修改了响应体,那么也同时需要在header_filter阶段把 content_length 置为 nil: ngx.header.content_length = nil
共 2 条评论7 - 蓝色海洋2019-10-15老师您好,我这边想实现一个kong的灰度发布的插件,感觉没有思路,请老师指点一下谢谢共 2 条评论2
- 言身寸飞2019-10-07插件那块不是很明白可以详细讲讲么1
- 段先森2019-09-17老师 把openresty作为IM系统的链接层可行吗 就是维护ws链接 应用层ack这些共 1 条评论1
- CheverJohn2022-02-10给老师更新一下,目前limit-count 插件选用了 jsonschema 了,而不是 腾讯的 rapidjson 啦。建议注明1
- 王慧东2020-08-12老师,麻烦问一下怎么在init_worker_by_lua 加载数据库信息??
- 半夜一声笑2020-06-02老师,如果这样调用ngx.timer.at(0,func) 可以获取到func返回的结果吗 找了一圈没找到合适的方法
- 陈海源2019-09-18温老师,这个demo的源代码的地址哪里可以查看共 1 条评论
- helloworld2019-09-16通过老师的专栏,我又系统掌握了一门技术,现在已经可以使用or开发一些实用的api项目了。感谢老师的分享。共 2 条评论
- SMTCode2019-09-16快结课了,基本上跟下来了,但自己实践偏少(工作中目前未用)。不过确实是强大的课。感谢温老师的持续分享。后期工作中择机引入~
作者回复: 可以先从 OpenResty 替换 Nginx 开始入手,这是成本最低的:)