Higress 基于自定义插件访问 Redis

2024-04-02 11:12

本文主要是介绍Higress 基于自定义插件访问 Redis,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者:钰诚

简介

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

图片

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

多网关全局限流

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

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

func onHttpRequestHeaders(ctx wrapper.HttpContext, config RedisCallConfig, log wrapper.Log) types.Action {now := time.Now()minuteAligned := now.Truncate(time.Minute)timeStamp := strconv.FormatInt(minuteAligned.Unix(), 10)// 如果 redis api 返回的 err != nil,一般是由于网关找不到 redis 后端服务,请检查是否误删除了 redis 后端服务err := config.client.Incr(timeStamp, func(response resp.Value) {if response.Error() != nil {log.Errorf("call redis error: %v", response.Error())proxywasm.ResumeHttpRequest()} else {ctx.SetContext("timeStamp", timeStamp)ctx.SetContext("callTimeLeft", strconv.Itoa(config.qpm-response.Integer()))if response.Integer() == 1 {err := config.client.Expire(timeStamp, 60, func(response resp.Value) {if response.Error() != nil {log.Errorf("call redis error: %v", response.Error())}proxywasm.ResumeHttpRequest()})if err != nil {log.Errorf("Error occured while calling redis, it seems cannot find the redis cluster.")proxywasm.ResumeHttpRequest()}} else {if response.Integer() > config.qpm {proxywasm.SendHttpResponse(429, [][2]string{{"timeStamp", timeStamp}, {"callTimeLeft", "0"}}, []byte("Too many requests\n"), -1)} else {proxywasm.ResumeHttpRequest()}}}})if err != nil {// 由于调用redis失败,放行请求,记录日志log.Errorf("Error occured while calling redis, it seems cannot find the redis cluster.")return types.ActionContinue} else {// 请求hold住,等待redis调用完成return types.ActionPause}
}

插件配置如下:

图片

测试结果如下:

图片

结合通义千问实现 token 限流

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

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

图片

图片

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

func onHttpRequestBody(ctx wrapper.HttpContext, config TokenLimiterConfig, body []byte, log wrapper.Log) types.Action {now := time.Now()minuteAligned := now.Truncate(time.Minute)timeStamp := strconv.FormatInt(minuteAligned.Unix(), 10)config.client.Get(timeStamp, func(response resp.Value) {if response.Error() != nil {defer proxywasm.ResumeHttpRequest()log.Errorf("Error occured while calling redis")} else {tokenUsed := response.Integer()if config.tpm < tokenUsed {proxywasm.SendHttpResponse(429, [][2]string{{"timeStamp", timeStamp}, {"TokenLeft", fmt.Sprint(config.tpm - tokenUsed)}}, []byte("No token left\n"), -1)} else {proxywasm.ResumeHttpRequest()}}})return types.ActionPause
}func onHttpResponseBody(ctx wrapper.HttpContext, config TokenLimiterConfig, body []byte, log wrapper.Log) types.Action {now := time.Now()minuteAligned := now.Truncate(time.Minute)timeStamp := strconv.FormatInt(minuteAligned.Unix(), 10)tokens := int(gjson.ParseBytes(body).Get("usage").Get("total_tokens").Int())config.client.IncrBy(timeStamp, tokens, func(response resp.Value) {if response.Error() != nil {defer proxywasm.ResumeHttpResponse()log.Errorf("Error occured while calling redis")} else {if response.Integer() == tokens {config.client.Expire(timeStamp, 60, func(response resp.Value) {defer proxywasm.ResumeHttpResponse()if response.Error() != nil {log.Errorf("Error occured while calling redis")}})}}})return types.ActionPause
}

测试结果如下:

图片

图片

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

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

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

总结

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

这篇关于Higress 基于自定义插件访问 Redis的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/869824

相关文章

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Redis 中的热点键和数据倾斜示例详解

《Redis中的热点键和数据倾斜示例详解》热点键是指在Redis中被频繁访问的特定键,这些键由于其高访问频率,可能导致Redis服务器的性能问题,尤其是在高并发场景下,本文给大家介绍Redis中的热... 目录Redis 中的热点键和数据倾斜热点键(Hot Key)定义特点应对策略示例数据倾斜(Data S

如何自定义Nginx JSON日志格式配置

《如何自定义NginxJSON日志格式配置》Nginx作为最流行的Web服务器之一,其灵活的日志配置能力允许我们根据需求定制日志格式,本文将详细介绍如何配置Nginx以JSON格式记录访问日志,这种... 目录前言为什么选择jsON格式日志?配置步骤详解1. 安装Nginx服务2. 自定义JSON日志格式各

Android自定义Scrollbar的两种实现方式

《Android自定义Scrollbar的两种实现方式》本文介绍两种实现自定义滚动条的方法,分别通过ItemDecoration方案和独立View方案实现滚动条定制化,文章通过代码示例讲解的非常详细,... 目录方案一:ItemDecoration实现(推荐用于RecyclerView)实现原理完整代码实现

redis+lua实现分布式限流的示例

《redis+lua实现分布式限流的示例》本文主要介绍了redis+lua实现分布式限流的示例,可以实现复杂的限流逻辑,如滑动窗口限流,并且避免了多步操作导致的并发问题,具有一定的参考价值,感兴趣的可... 目录为什么使用Redis+Lua实现分布式限流使用ZSET也可以实现限流,为什么选择lua的方式实现

Redis中管道操作pipeline的实现

《Redis中管道操作pipeline的实现》RedisPipeline是一种优化客户端与服务器通信的技术,通过批量发送和接收命令减少网络往返次数,提高命令执行效率,本文就来介绍一下Redis中管道操... 目录什么是pipeline场景一:我要向Redis新增大批量的数据分批处理事务( MULTI/EXE

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

Redis中的常用的五种数据类型详解

《Redis中的常用的五种数据类型详解》:本文主要介绍Redis中的常用的五种数据类型详解,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Redis常用的五种数据类型一、字符串(String)简介常用命令应用场景二、哈希(Hash)简介常用命令应用场景三、列表(L

Redis解决缓存击穿问题的两种方法

《Redis解决缓存击穿问题的两种方法》缓存击穿问题也叫热点Key问题,就是⼀个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击,本文给大家介绍了Re... 目录引言解决办法互斥锁(强一致,性能差)逻辑过期(高可用,性能优)设计逻辑过期时间引言缓存击穿:给

基于Spring实现自定义错误信息返回详解

《基于Spring实现自定义错误信息返回详解》这篇文章主要为大家详细介绍了如何基于Spring实现自定义错误信息返回效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录背景目标实现产出背景Spring 提供了 @RestConChina编程trollerAdvice 用来实现 HTT