经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Redis » 查看文章
Higress 基于自定义插件访问 Redis
来源:cnblogs  作者:阿里云云栖号  时间:2024/4/29 9:28:35  对本文有异议

简介

基于 wasm 机制,Higress 提供了优秀的可扩展性,用户可以基于 Go/C++/Rust 编写 wasm 插件,自定义请求处理逻辑,满足用户的个性化需求,目前插件已经支持 redis 调用,使得用户能够编写有状态的插件,进一步提高了 Higress 的扩展能力。

文档在插件中调用 Redis[1]中提供了完整的网关通过插件调用 Redis 的例子,包括阿里云 Redis 实例创建与配置、插件代码编写、插件上传与配置、测试样例等流程。接下来本文重点介绍几个基于 Redis 的插件。

多网关全局限流

网关已经提供了 sentinal 限流[2],能够有效保护后端业务应用。通过 redis 插件限流,用户可以实现多网关的全局限额管理。

以下为插件代码示例,在请求头阶段检查当前时间内请求次数,如果超出配额,则直接返回 429 响应。

  1. func onHttpRequestHeaders(ctx wrapper.HttpContext, config RedisCallConfig, log wrapper.Log) types.Action {
  2. now := time.Now()
  3. minuteAligned := now.Truncate(time.Minute)
  4. timeStamp := strconv.FormatInt(minuteAligned.Unix(), 10)
  5. // 如果 redis api 返回的 err != nil,一般是由于网关找不到 redis 后端服务,请检查是否误删除了 redis 后端服务
  6. err := config.client.Incr(timeStamp, func(response resp.Value) {
  7. if response.Error() != nil {
  8. log.Errorf("call redis error: %v", response.Error())
  9. proxywasm.ResumeHttpRequest()
  10. } else {
  11. ctx.SetContext("timeStamp", timeStamp)
  12. ctx.SetContext("callTimeLeft", strconv.Itoa(config.qpm-response.Integer()))
  13. if response.Integer() == 1 {
  14. err := config.client.Expire(timeStamp, 60, func(response resp.Value) {
  15. if response.Error() != nil {
  16. log.Errorf("call redis error: %v", response.Error())
  17. }
  18. proxywasm.ResumeHttpRequest()
  19. })
  20. if err != nil {
  21. log.Errorf("Error occured while calling redis, it seems cannot find the redis cluster.")
  22. proxywasm.ResumeHttpRequest()
  23. }
  24. } else {
  25. if response.Integer() > config.qpm {
  26. proxywasm.SendHttpResponse(429, [][2]string{{"timeStamp", timeStamp}, {"callTimeLeft", "0"}}, []byte("Too many requests\n"), -1)
  27. } else {
  28. proxywasm.ResumeHttpRequest()
  29. }
  30. }
  31. }
  32. })
  33. if err != nil {
  34. // 由于调用redis失败,放行请求,记录日志
  35. log.Errorf("Error occured while calling redis, it seems cannot find the redis cluster.")
  36. return types.ActionContinue
  37. } else {
  38. // 请求hold住,等待redis调用完成
  39. return types.ActionPause
  40. }
  41. }

插件配置如下:

测试结果如下:

结合通义千问实现 token 限流

对于提供 AI 应用服务的开发者来说,用户的 token 配额管理是一个非常关键的功能,以下例子展示了如何通过网关插件实现对通义千问后端服务的 token 限流功能。

首先需要申请通义千问的 API 访问,可参考此链接[3]。之后在 MSE 网关配置相应服务以及路由,如下所示:

编写插件代码,插件中,在响应 body 阶段去写入该请求使用的 token 额度,在处理请求头阶段去读 redis 检查当前剩余 token 额度,如果已经没有 token 额度,则直接返回响应,中止请求。

  1. func onHttpRequestBody(ctx wrapper.HttpContext, config TokenLimiterConfig, body []byte, log wrapper.Log) types.Action {
  2. now := time.Now()
  3. minuteAligned := now.Truncate(time.Minute)
  4. timeStamp := strconv.FormatInt(minuteAligned.Unix(), 10)
  5. config.client.Get(timeStamp, func(response resp.Value) {
  6. if response.Error() != nil {
  7. defer proxywasm.ResumeHttpRequest()
  8. log.Errorf("Error occured while calling redis")
  9. } else {
  10. tokenUsed := response.Integer()
  11. if config.tpm < tokenUsed {
  12. proxywasm.SendHttpResponse(429, [][2]string{{"timeStamp", timeStamp}, {"TokenLeft", fmt.Sprint(config.tpm - tokenUsed)}}, []byte("No token left\n"), -1)
  13. } else {
  14. proxywasm.ResumeHttpRequest()
  15. }
  16. }
  17. })
  18. return types.ActionPause
  19. }
  20. func onHttpResponseBody(ctx wrapper.HttpContext, config TokenLimiterConfig, body []byte, log wrapper.Log) types.Action {
  21. now := time.Now()
  22. minuteAligned := now.Truncate(time.Minute)
  23. timeStamp := strconv.FormatInt(minuteAligned.Unix(), 10)
  24. tokens := int(gjson.ParseBytes(body).Get("usage").Get("total_tokens").Int())
  25. config.client.IncrBy(timeStamp, tokens, func(response resp.Value) {
  26. if response.Error() != nil {
  27. defer proxywasm.ResumeHttpResponse()
  28. log.Errorf("Error occured while calling redis")
  29. } else {
  30. if response.Integer() == tokens {
  31. config.client.Expire(timeStamp, 60, func(response resp.Value) {
  32. defer proxywasm.ResumeHttpResponse()
  33. if response.Error() != nil {
  34. log.Errorf("Error occured while calling redis")
  35. }
  36. })
  37. }
  38. }
  39. })
  40. return types.ActionPause
  41. }

测试结果如下:

基于 cookie 的缓存、容灾以及会话管理

除了以上两个限流的例子,基于 Redis 可以实现更多的插件对网关进行扩展。例如基于 cookie 来做缓存、容灾以及会话管理等功能。

  • 缓存&容灾:基于用户 cookie 信息缓存请求应答,一方面能够减轻后端服务压力,另一方面,当后端服务不可用时,能够实现容灾效果。
  • 会话管理:使用 Redis 存储用户的认证鉴权信息,当请求到来时,先访问 redis 查看当前用户是否被授权访问,如果未被授权再去访问认证鉴权服务,可以减轻认证鉴权服务的压力。
  1. func onHttpRequestHeaders(ctx wrapper.HttpContext, config HelloWorldConfig, log wrapper.Log) types.Action {
  2. cookieHeader, err := proxywasm.GetHttpRequestHeader("cookie")
  3. if err != nil {
  4. proxywasm.LogErrorf("error getting cookie header: %v", err)
  5. // 实现自己的业务逻辑
  6. }
  7. // 根据自己需要对cookie进行处理
  8. cookie := CookieHandler(cookieHeader)
  9. config.client.Get(cookie, func(response resp.Value) {
  10. if response.Error() != nil {
  11. log.Errorf("Error occured while calling redis")
  12. proxywasm.ResumeHttpRequest()
  13. } else {
  14. // 实现自己的业务逻辑
  15. proxywasm.ResumeHttpRequest()
  16. }
  17. })
  18. return types.ActionPause
  19. }

总结

Higress 通过支持 redis 调用,大大增强了插件的能力,使插件功能具有更广阔的想象空间,更加能够适应开发者多样的个性化需求,如果大家有更多关于 Higress 的想法与建议,欢迎与我们联系!

相关链接:

[1] 在插件中调用 Redis

https://help.aliyun.com/zh/mse/user-guide/develop-gateway-plug-ins-by-using-the-go-language?spm=a2c4g.11186623.0.0.45a53597EVVAC0#5e5a601af18al

[2] sentinal 限流

https://help.aliyun.com/zh/mse/user-guide/configure-a-throttling-policy?spm=a2c4g.11186623.0.i4

[3] 链接

https://help.aliyun.com/zh/dashscope/developer-reference/api-details?spm=a2c4g.11186623.0.i4#602895ef3dtl1

作者: 钰诚

原文链接

本文为阿里云原创内容,未经允许不得转载。

原文链接:https://www.cnblogs.com/yunqishequ/p/18163905

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号