解决如何对kratos服务进行颜色标签过滤
应用场景
- 生产环境下新功能测试
- 带标签状态的需求
- 对来源用户进行分类服务划分
给服务打上color标签
在main.go中,metadata加入color标签即可,可以参考一下方式。从启动命令中读取
...
func init() {
flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml")
flag.StringVar(&color, "color", "", "color tag, eg: -color blue")
}
func newApp(logger log.Logger, gs *grpc.Server, hs *http.Server) *kratos.App {
metadata := map[string]string{}
if color != "" {
metadata["color"] = color
}
opts := []kratos.Option{
kratos.ID(Name + "_" + Version + "_" + id + "_" + strconv.Itoa(int(time.Now().UnixMicro()))),
kratos.Name(Name),
kratos.Version(Version),
kratos.Metadata(metadata),
kratos.Logger(logger),
kratos.Server(
gs,
hs,
),
}
if reg != nil {
opts = append(opts, kratos.Registrar(reg))
}
return kratos.New(opts...)
}
...
服务内部调用接入
过滤器(color.go)
目录位置参考github.com/go-kratos/kratos/selector/filter
package filter
import (
"context"
"github.com/go-kratos/kratos/v2/selector"
)
// Color is color filter.
func Color(color string) selector.NodeFilter {
return func(_ context.Context, nodes []selector.Node) []selector.Node {
newNodes := make([]selector.Node, 0, len(nodes))
for _, n := range nodes {
if n.Metadata() == nil || n.Metadata()["color"] == "" {
continue
}
if n.Metadata()["color"] == color {
newNodes = append(newNodes, n)
}
}
return newNodes
}
}
使用方法
在newClient时,引入grpc.WithNodeFilter(filter.Color("blue"))
或http.WithNodeFilter(filter.Color("blue"))
可以请求指定服务是"color:blue"的节点
func NewGrpcClient(dsn string) (GreeterClient, error) {
if dsn == "" {
if addr := os.Getenv(api.HTTPAddrEnvName); addr != "" {
dsn = fmt.Sprintf("consul://%s", addr)
}
}
dis, err := discovery.Create(dsn)
if err != nil {
return nil, err
}
conn, cErr := grpc.DialInsecure(
context.Background(),
grpc.WithEndpoint("discovery:///helloworld"),
grpc.WithDiscovery(dis),
grpc.WithTimeout(15*time.Second),
grpc.WithMiddleware(
tracing.Client(),
),
grpc.WithNodeFilter(filter.Color("blue")),
)
if cErr != nil {
return nil, cErr
}
return NewGreeterClient(conn), nil
}
func NewHttpClient(dsn string) (GreeterHTTPClient, error) {
dis, err := discovery.Create(dsn)
if err != nil {
return nil, err
}
conn, cErr := http.NewClient(
context.Background(),
http.WithMiddleware(
tracing.Client(),
recovery.Recovery(),
),
http.WithEndpoint("discovery:///helloworld"),
http.WithDiscovery(dis),
http.WithNodeFilter(filter.Color("blue"))
)
if cErr != nil {
return nil, cErr
}
return NewGreeterHTTPClient(conn), nil
}
在go-kratos/gateway使用
官方提供了https://github.com/go-kratos/gateway
api/middleware/selectorFilter/v1/filter.proto
syntax = "proto3";
package gateway.middleware.selectorFilter.v1;
option go_package = "github.com/go-kratos/gateway/api/gateway/middleware/selectorFilter/v1";
message Filters {
repeated Filter filters = 1;
}
message Filter {
string name = 1; // 已支持 color、version;之前请求来来源的header携带X-相应标签时可触发,如:X-Color、X-Version
}
middleware/selectorFilter/selector.go
package selectorFitler
import (
"net/http"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
config "github.com/go-kratos/gateway/api/gateway/config/v1"
v1 "github.com/go-kratos/gateway/api/gateway/middleware/selectorFilter/v1"
"github.com/go-kratos/gateway/app/gateway/middleware"
"github.com/go-kratos/kratos/selector/filter" // 这里自行改
)
func init() {
middleware.Register("selector_filter", Middleware)
}
func Middleware(c *config.Middleware) (middleware.Middleware, error) {
return func(next http.RoundTripper) http.RoundTripper {
return middleware.RoundTripperFunc(func(req *http.Request) (reply *http.Response, err error) {
options := &v1.Filters{}
if c.Options != nil {
if err := anypb.UnmarshalTo(c.Options, options, proto.UnmarshalOptions{Merge: true}); err != nil {
return nil, err
}
}
if len(options.Filters) > 0 {
for _, f := range options.Filters {
switch f.Name {
case "color":
color := req.Header.Get("X-Color")
if color != "" {
middleware.WithSelectorFitler(req.Context(), filter.Color(color))
}
case "version":
version := req.Header.Get("X-Version")
if version != "" {
middleware.WithSelectorFitler(req.Context(), filter.Version(version))
}
}
}
}
return next.RoundTrip(req)
})
}, nil
}
使用方法
这里给完整的helloword.yaml文件。配置selector_filter插件,开启color过滤即可
endpoints:
- path: /helloworld/*
timeout: 15s
protocol: HTTP
backends:
- target: 'discovery:///helloworld'
middlewares:
- name: circuitbreaker
options:
'@type': type.googleapis.com/gateway.middleware.circuitbreaker.v1.CircuitBreaker
successRatio: { "success": 0.6, "request": "100", "bucket": "10", "window": "3s" }
responseData:
status_code: 503
header:
- key: 'Content-Type'
value: [ 'application/json' ]
assertCondtions:
- { "by_status_code": "200" }
- name: rewrite
options:
'@type': type.googleapis.com/gateway.middleware.rewrite.v1.Rewrite
- name: selector_filter
options:
'@type': type.googleapis.com/gateway.middleware.selectorFilter.v1.Filters
filters:
- name: color
- name: version