今天学哨兵结合 Hertz 的例子。
sentinel-golang 结合 hertz 使用的示例
代码分服务端和客户端,服务端的例子可以单独跑,客户端的例子必须配合服务端才能跑。服务端的代码如下:
/*
* Copyright 2022 CloudWeGo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"context"
"log"
sentinel "github.com/alibaba/sentinel-golang/api"
"github.com/alibaba/sentinel-golang/core/flow"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/utils"
hertzSentinel "github.com/hertz-contrib/opensergo/sentinel/adapter"
)
func initSentinel() {
err := sentinel.InitDefault()
if err != nil {
log.Fatalf("Unexpected error: %+v", err)
}
_, err = flow.LoadRules([]*flow.Rule{
{
Resource: "server_test",
Threshold: 0.0,
TokenCalculateStrategy: flow.Direct,
ControlBehavior: flow.Reject,
StatIntervalInMs: 1000,
},
})
if err != nil {
log.Fatalf("Unexpected error: %+v", err)
return
}
}
func main() {
initSentinel()
h := server.Default(server.WithHostPorts(":8081"))
h.Use(hertzSentinel.SentinelServerMiddleware(
// customize resource extractor if required
// method_path by default
hertzSentinel.WithServerResourceExtractor(func(c context.Context, ctx *app.RequestContext) string {
return "server_test"
}),
// customize block fallback if required
// abort with status 429 by default
hertzSentinel.WithServerBlockFallback(func(c context.Context, ctx *app.RequestContext) {
ctx.AbortWithStatusJSON(400, utils.H{
"err": "too many request; the quota used up",
"code": 10222,
})
}),
))
h.GET("/server_test", func(c context.Context, ctx *app.RequestContext) {})
h.Spin()
}
客户端的代码如下:
/*
* Copyright 2022 CloudWeGo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"context"
"fmt"
"log"
"net/http"
sentinel "github.com/alibaba/sentinel-golang/api"
"github.com/alibaba/sentinel-golang/core/flow"
"github.com/cloudwego/hertz/pkg/app/client"
"github.com/cloudwego/hertz/pkg/protocol"
"github.com/cloudwego/hertz/pkg/protocol/consts"
hertzSentinel "github.com/hertz-contrib/opensergo/sentinel/adapter"
)
func initSentinel() {
err := sentinel.InitDefault()
if err != nil {
log.Fatalf("Unexpected error: %+v", err)
}
_, err = flow.LoadRules([]*flow.Rule{
{
Resource: "client_test",
Threshold: 0.0,
TokenCalculateStrategy: flow.Direct,
ControlBehavior: flow.Reject,
StatIntervalInMs: 1000,
},
})
if err != nil {
log.Fatalf("Unexpected error: %+v", err)
return
}
}
func main() {
initSentinel()
c, err := client.NewClient()
if err != nil {
log.Fatalf("Unexpected error: %+v", err)
return
}
c.Use(hertzSentinel.SentinelClientMiddleware(
// customize resource extractor if required
// method_path by default
hertzSentinel.WithClientResourceExtractor(func(ctx context.Context,
request *protocol.Request, response *protocol.Response,
) string {
return "client_test"
}),
// customize block fallback if required
// abort with status 429 by default
hertzSentinel.WithClientBlockFallback(func(ctx context.Context, req *protocol.Request,
resp *protocol.Response, blockError error,
) error {
resp.SetStatusCode(http.StatusBadRequest)
resp.SetBody([]byte("request failed"))
return blockError
}),
))
req := &protocol.Request{}
res := &protocol.Response{}
req.SetMethod(consts.MethodGet)
req.SetRequestURI("http://127.0.0.1:8081/client_test")
err = c.Do(context.Background(), req, res)
fmt.Printf("response body: %v, code: %v\n", string(res.Body()), res.StatusCode())
fmt.Printf("error: %v", err)
}
这里面用到了社区里面贡献的哨兵适配器,看样子是把哨兵当成中间件来用了。在其中可以自己写资源提取,也可以用现成的方法,限流的方法也可以自定义或者用现成的。
因为这里只需要看服务端,所以老样子单文件运行,然后新开终端窗口执行 curl --location --request GET 'http://127.0.0.1:8081/server_test' 看结果。