前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于Openresty+Lua实现微服务Api 网关

基于Openresty+Lua实现微服务Api 网关

作者头像
用户1107783
发布2023-09-11 11:02:57
9570
发布2023-09-11 11:02:57
举报
什么是微服务Api Gateway

微服务网关(Microservices Gateway)是微服务架构中的一种关键组件,它作为一个入口点,接收客户端的请求并将其路由到相应的微服务上。它起到了前端与后端微服务之间的“门户”的作用,协调整个微服务系统的请求流量和服务访问。具备的功能如下:

  1. 路由:微服务网关根据请求的路径和其他条件将请求路由到对应的微服务实例上。它能够根据不同的URL模式和规则将请求转发到相应的微服务,从而隐藏了后端微服务的实际地址和结构。
  2. 负载均衡:微服务网关可以将请求均匀地分发到多个后端微服务实例上,以保证各个实例的负载平衡,提高系统的可用性和性能。
  3. 鉴权与认证:微服务网关可以处理用户认证和鉴权的任务,确保只有经过授权的用户可以访问特定的微服务。这有助于保护后端服务免受未经授权的访问。
  4. 安全性:微服务网关可以实现一些安全措施,如防火墙、攻击防范等,保护后端微服务免受恶意攻击。
  5. 缓存:网关可以缓存一些频繁请求的数据,从而减轻后端微服务的压力,提高系统响应速度。
  6. 日志与监控:微服务网关可以记录请求和响应的日志,并提供监控指标,帮助开发团队诊断和解决问题。

微服务网关简化了客户端与后端微服务之间的交互过程,减少了客户端需要处理的逻辑,并且提供了一层中间件,可以更好地管理和维护整个微服务系统。常见的微服务网关有Nginx、Spring Cloud Gateway、Kong、INgress 、Istio等。

为什么要新造轮子?

正因为微服务API网关地位位如此重要,所以它一直处于兵家必争之地,传统的 IT 巨头在这个领域很早就都有布局。根据 2018 年 Gartner 发布的 API 全生命周期报告,谷歌、CA、IBM、红帽、Salesforce 都是处于领导地位的厂商,开发者更熟悉的 Kong 则处于远见者的区间内。

那么,问题就来了,为什么我们还要新造一个轮子呢?简单来说,这是因为当前的微服务 API 网关都不足以满足我们的需求。我们首先来看闭源的商业产品,它们的功能都很完善,覆盖了 API 的设计、多语言 SDK、文档、测试和发布等全生命周期管理,并且提供 SaaS 服务,有些还与公有云做了集成,使用起来非常方便。但同时,它们也带来了两个痛点。

  • 平台锁定问题。API 网关是业务流量的入口,它不像图片、视频等 CDN 加速的这种非业务流量可以随意迁移,API 网关上会绑定不少业务相关的逻辑。你一旦使用了闭源的方案,就很难平滑和低成本地迁移到其他平台。
  • 无法二次开发的问题。一般的大中型企业都会有自己独特的需求,需要定制开发,但这时候你只能依靠厂商,而不能自己动手去做二次开发。

这也是为什么开源的 API 网关方案开始流行的一个原因。不过,现有的开源产品也不是万能的,自身也有很多不足。

  • 赖 PostgreSQL、MySQL 等关系型数据库。这样,在配置发生变化的时候,网关节点只能轮询数据库。这不仅造成配置生效慢,也给代码增加了复杂度,让人难以理解;同时,数据库也会成为系统的单点和性能瓶颈,无法保证整体的高可用。如果你把 API 网关用于 Kubernetes 环境下,关系型数据库会显得更加笨重,不利于快速伸缩。
  • 插件不能热加载。当你新增一个插件或者修改现有插件的代码后,必须要重载服务才能生效,这和修改 Nginx 配置后需要重载是一样的,显然会影响用户的请求。
  • 代码结构复杂, 难以掌握。有些开源项目做了多层面向对象的封装,一些简单的逻辑也变得雾里看花。但其实,对于 API 网关这种场景,直来直去的表达会更加清晰和高效,也更有利于二次开发。
  • 所以,我们需要一个更轻巧、对云原生和开发友好的 API 网关

什么是Openresty

OpenResty(开放式网络架构)是一个基于Nginx和LuaJIT的开源Web应用服务器,它将Nginx与Lua脚本语言的强大功能相结合,为开发者提供了一种高性能、可扩展、灵活的方式来构建Web应用和微服务。OpenResty利用Nginx作为服务器和代理,通过嵌入Lua脚本引擎(LuaJIT)来扩展Nginx的功能。Lua是一种轻量级的脚本语言,具有简洁的语法和强大的扩展性,使得OpenResty能够实现更复杂的逻辑和定制化的功能,而不需要修改Nginx的源代码。主要特点和优势:

  1. 高性能:OpenResty利用Nginx的异步、非阻塞事件驱动架构,加上LuaJIT的高性能,能够处理大量并发请求,适用于高流量和性能要求较高的场景。
  2. 可扩展性:通过Lua脚本,开发者可以方便地扩展和定制OpenResty的功能,满足特定需求,而无需修改Nginx核心代码。
  3. 轻量级:OpenResty本身占用资源较少,安装和部署相对简单。
  4. 灵活性:OpenResty可以充当Web服务器、反向代理、负载均衡器、API网关等多种角色,适用于不同类型的应用场景。
  5. 社区支持:OpenResty拥有活跃的社区,提供丰富的插件和模块,方便开发者使用和扩展。

OpenResty常用于构建高性能的Web应用、API服务和微服务网关。它可以代替传统的Web服务器,同时提供更多的灵活性和自定义功能。在大规模应用和高并发环境下,OpenResty展现出其强大的优势,被广泛应用于互联网公司、在线游戏、视频流媒体等领域。

最佳实践介绍

言归正传,现在我们采用基于Openresty+Lua自己实现一个具备token解析认证功能的微服务Api Gateway(在Openresty中实现token解析认证,如果token无效直接拒绝)。带大家熟悉一下Openresty+Lua开发过程,流程图如下:

流程说明:

  • 用户输入账号密码提交请求,openresty接收到请求后,判断是login接口,则不需要校验认证,直接把请求转发授权服务
  • 授权服务拿到用户账号密码并进行验证,认证成功后则返回token给用户
  • 用户拿到token后,携带token访问其他请求,openresty接收到请求后,判断该接口如果需要校验认证
  • openresty进行对token进行校验解析,如果校验失败,则返回401给用户,如果校验成功,则把响应的请求转发给微服务

部署示例服务

代码语言:javascript
复制
package main

import (
 "fmt"
 jwt "github.com/dgrijalva/jwt-go"
 "github.com/gin-gonic/gin"
 "net/http"
 "time"
)

const secretKey = "Dav7kfq3iA8S!JUj8&CUkdnQe72E@Cw6" // Replace this with a strong secret key

// User struct represents a user.
type User struct {
 ID       int    `json:"id"`
 Username string `json:"username"`
 Password string `json:"password"`
}

var users = []User{
 {1, "kubesre", "123456"},
}

func GenerateToken(user User) (string, error) {
 token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
  "id":       user.ID,
  "username": user.Username,
  "exp":      time.Now().Add(time.Hour * 2).Unix(), // Token expires in 2 hours
 })

 return token.SignedString([]byte(secretKey))
}

func LoginHandler(c *gin.Context) {
 var input struct {
  Username string `json:"username"`
  Password string `json:"password"`
 }
 if err := c.ShouldBindJSON(&input); err != nil {
  c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
  return
 }

 for _, user := range users {
  if user.Username == input.Username && user.Password == input.Password {
   token, err := GenerateToken(user)
   if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
    return
   }

   c.JSON(http.StatusOK, gin.H{"token": token})
   return
  }
 }

 c.JSON(http.StatusUnauthorized, gin.H{"error": "登陆失败,请确认账号密码"})
}

func UserInfo(c *gin.Context) {
 c.JSON(http.StatusOK, gin.H{"message": "云原生运维圈!"})
}

func main() {
 r := gin.Default()

 // Route to handle user login
 r.POST("/login", LoginHandler)

 // Routes protected by JWT authentication middleware
 // You need to include the JWT token in the "Authorization" header for these routes
 r.GET("/user/info", UserInfo)

 fmt.Println("Server started at http://localhost:8080")
 r.Run(":8080")
}

运行服务

代码语言:javascript
复制
$ go mod tidy
$ go run main.go

部署Openresty

代码语言:javascript
复制
# 安装仓库管理工具包:
$ yum install yum-utils

# 添加仓库地址:
$ yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo

# 安装resty:
$ yum install openresty-resty

# 安装opm:
$ yum install openresty-opm

# 安装Jwt组件:
$opm get SkyLothar/lua-resty-jwt

此时lua-resty-jwt安装好了,可以直接使用了。

配置Openresty

Auth-JWT插件:

代码语言:javascript
复制
$ cat /usr/local/openresty/lualib/resty/jwt.lua;
local auth_token = ngx.var.http_token
--引入json库
local secret= "Dav7kfq3iA8S!JUj8&CUkdnQe72E@Cw6"
local cjson = require "cjson"
local jwt = require("resty.jwt")

--ngx.say(auth_token)
if auth_token == nil then
   local response = {}
   response["code"]=401
   response["message"]="口令不存在"
   ngx.say(cjson.encode(response))
   ngx.exit(response.code)
else
  local jwt_obj = jwt:verify(secret, auth_token)
  if jwt_obj.verified == false then
        local response = {}
        response["code"]=401
        response["message"]="令牌无效"
        ngx.say(cjson.encode(response))
        ngx.exit(response.code)
   else
     ngx.exec('@user')
   end
end

配置Openresty:

代码语言:javascript
复制
$ cat /usr/local/openresty/nginx/conf/nginx.conf
...
# 需要认证
location /user/info {
              # 引入jwt.lua扩展
              content_by_lua_file /usr/local/openresty/lualib/resty/jwt.lua;
        }

# 不需要认证
location /login {
   proxy_pass http://192.168.40.125:8080;
}

location @user {
  proxy_pass http://192.168.40.125:8080;
}
...

重新加载Openresty生效:

代码语言:javascript
复制
$ /usr/local/openresty/bin/openresty -t
nginx: the configuration file /usr/local/openresty/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/openresty/nginx/conf/nginx.conf test is successful
$  /usr/local/openresty/bin/openresty -s reload

验证

用户登陆成功,返回token(输出账户密码登陆并返回Token):

如果不传入token,请求192.168.1.102/user/info(会返回口令不存在):

如果传入错误token,请求192.168.1.102/user/info(会返回口令无效):

传入正确的token,请求192.168.1.102/user/info(正常返回内容)

总结

本文介绍了微服务Gateway Api以及我们为什么要造轮子等问题,并通过openresty+lua实现了JWT解析认证功能的小案例,并对openresty扩展功能有了一定了解,接下来文章内容中会分享更多企业级实战案例,请敬请期待!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-08-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云原生运维圈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么要新造轮子?
  • 什么是Openresty
  • 最佳实践介绍
  • 部署示例服务
  • 部署Openresty
  • 配置Openresty
  • 验证
  • 总结
相关产品与服务
API 网关
腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档