Compare commits

...

8 Commits

6 changed files with 27 additions and 37 deletions

View File

@ -7,7 +7,9 @@
// Date : 2025-05-07 21:13
package abstract
import "git.zhangdeman.cn/zhangdeman/network/httpclient/define"
// RateLimiter v2 流控口约束, v3移除了, 人工补齐
type RateLimiter interface {
Allow() bool
Allow(reqCfg *define.Request) bool
}

View File

@ -10,7 +10,6 @@ package httpclient
import (
"fmt"
"git.zhangdeman.cn/zhangdeman/network/httpclient/implement"
"git.zhangdeman.cn/zhangdeman/wrapper"
"net"
"strings"
"time"
@ -241,8 +240,8 @@ func (hc *HttpClient) Request() *define.Response {
"remaining_ttl": ttl,
}, hc.reqCfg)
// 配置了最小剩余时间,并且key剩余有效期小于最小剩余时间
// 预热加锁, 并发请求触发预热, 仅触发一个即可, 使用接口key + query参数做key, 按照一般约定, 写请求不会做缓存, 只有读请求会
lockKey := wrapper.String(hc.reqCfg.FullUrl + serialize.JSON.MarshalForStringIgnoreError(hc.reqCfg.Query)).Md5().Value
// 预热加锁, 并发请求触发预热, 仅触发一个即可, 使用接口缓存key + LOCK做锁的key {{CACHE_KEY}}_LOCK, 按照一般约定, 写请求不会做缓存, 只有读请求会
lockKey := hc.reqOption.CacheInstance.GetKey(hc.reqCfg) + "_LOCK"
if err := hc.reqOption.CacheInstance.Lock(lockKey); err != nil {
log.RecordWarn("接口请求命中缓存, 缓存结果有效期大于剩余时长小于配置阈值, 触发预热, 加锁失败, 未执行预热", map[string]any{
"min_ttl": cachePreHeatConfig.MinTTL,
@ -456,7 +455,7 @@ func (hc *HttpClient) newResponse() *define.Response {
//
// Date : 16:04 2024/6/3
func (hc *HttpClient) getCacheResult() *define.Response {
if nil == hc.reqOption.CacheInstance {
if nil == hc.reqOption || nil == hc.reqOption.CacheInstance {
log.RecordDebug("接口请求前缓存检测, 未设置缓存实例", map[string]any{}, hc.reqCfg)
return nil
}

View File

@ -7,34 +7,6 @@
// Date : 2024-05-31 14:51
package define
// Http4xxHandler 4xx handler
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:54 2024/5/31
type Http4xxHandler func(req *Request, rep *Response)
// Http5xxHandler 5xx handler
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:55 2024/5/31
type Http5xxHandler func(req *Request, rep *Response)
// HttpBusinessErrorHandler 接口请求业务错误
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:04 2024/6/1
type HttpBusinessErrorHandler func(req *Request, rep *Response)
// RequestSendErrorHandler 请求发送失败的处理逻辑
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:23 2024/6/1
type RequestSendErrorHandler func(req *Request)
// RequestFinishHandler 请求最终完成事件, 不区分成功 OR 失败
//
// Author : go_developer@163.com<白茶清欢>

View File

@ -30,6 +30,7 @@ type Request struct {
Method string `json:"method"` // 请求方法
DataField string `json:"data_field"` // 数据字段
CodeField string `json:"code_field"` // 业务状态码字段
CodeLocation string `json:"code_location"` // 业务状态码位置
MessageField string `json:"message_field"` // code描述字段
DataReceiver any `json:"-"` // 响应data部分数据解析
SuccessHttpCodeList []int `json:"success_http_code_list"` // 哪些http状态码视为成功, 不配置, 默认2xx

View File

@ -14,6 +14,7 @@ import (
"git.zhangdeman.cn/zhangdeman/network/httpclient/define"
"git.zhangdeman.cn/zhangdeman/serialize"
"github.com/tidwall/gjson"
"net/http"
"strings"
)
@ -67,6 +68,9 @@ func (r *Response) fillResponseBody(reqCfg *define.Request, response *define.Res
responseContentType := response.RestyResponse.Header().Get(consts.HeaderKeyContentType.String())
if responseContentType == "" {
// 返回数据未说明 Content-Type
if response.RestyResponse.StatusCode() != http.StatusOK {
return nil
}
return errors.New("response content type is empty")
}
typeArr := strings.Split(strings.Split(responseContentType, ";")[0], "/")
@ -94,8 +98,20 @@ func (r *Response) fillResponseBody(reqCfg *define.Request, response *define.Res
return errors.New("response body Marshal error :" + err.Error())
}
response.Data = string(jsonByte)
if strings.ToLower(reqCfg.CodeLocation) == "header" {
if reqCfg.CodeField == "code" {
response.Code = fmt.Sprintf("%v", response.HttpCode)
response.Message = response.RestyResponse.Status()
} else {
response.Code = response.RestyResponse.Header().Get(reqCfg.CodeField)
response.Message = response.RestyResponse.Header().Get(reqCfg.MessageField)
}
} else {
// 统一认为Body
response.Code = gjson.Get(response.Data, reqCfg.CodeField).String()
response.Message = gjson.Get(response.Data, reqCfg.MessageField).String()
}
businessData := gjson.Get(response.Data, reqCfg.DataField)
if businessData.Value() == nil {
// data为空指针, 归一化成空对象

View File

@ -83,11 +83,11 @@ func NewRestyClient(reqConfig *define.Request, reqOption *RequestOption) (*resty
}
// 限流处理, 增加限流中间件
client.AddRequestMiddleware(func(client *resty.Client, request *resty.Request) error {
if nil != reqOption && nil != reqOption.RateLimiter {
if nil == reqOption || nil == reqOption.RateLimiter {
// 未配置流控
return nil
}
if !reqOption.RateLimiter.Allow() {
if !reqOption.RateLimiter.Allow(reqConfig) {
// 命中流控
return define.ErrRateLimitExceeded
}