1.服务端代码
package main
import (
"github.com/gin-gonic/gin"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/web"
"github.com/micro/go-plugins/registry/consul"
"net/http"
)
func main() {
consulReg := consul.NewRegistry(registry.Addrs(":8500"))
engine := gin.Default()
engine.POST("/hello", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "hello,world",
})
})
service := web.NewService(
web.Name("cas"),
web.Address(":8001"),
web.Registry(consulReg),
web.Handler(engine),
)
service.Init()
service.Run()
}
2.客户端代码
package main
import (
"context"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/registry"
"github.com/micro/go-plugins/client/http"
"github.com/micro/go-plugins/registry/consul"
"log"
)
func main() {
consulReg := consul.NewRegistry(registry.Addrs(":8500"))
selector := selector.NewSelector(
selector.Registry(consulReg),
selector.SetStrategy(selector.RoundRobin),
)
httpClient := http.NewClient(
// 选择器
client.Selector(selector),
// 响应格式默认格式protobuf,设置为json
client.ContentType("application/json"),
)
req := map[string]string{}
request := httpClient.NewRequest("cas", "/hello", req)
rsp := map[string]interface{}{}
err := httpClient.Call(context.Background(), request, &rsp)
if err != nil {
log.Fatalf("request err: %+v", err)
}
log.Printf("%+v",rsp)
}
3.发起请求报错
客户端请求报错如下:
{"id":"go.micro.client","code":500,"detail":"none available","status":"Internal Server Error"}
4.问题分析
1.顺着客户端调用的Call()
方法,进入源码github.com\micro\go-plugins\client\http\http.go
,找到获取服务节点的方法:
// get next nodes from the selector
next, err := h.next(req, callOpts)
2.再继续查看next()
方法,找到第63行,这里为Selector节点选择器添加了过滤器,传递了两个参数"protocol", "http"
,可以发现是个键值对:
// only get the things that are of mucp protocol
selectOptions := append(opts.SelectOptions, selector.WithFilter(
selector.FilterLabel("protocol", "http"),
))
3.进一步进入FilterLabel()
方法,在第41行可以发现,上一步传的两个参数在这里做了校验,分别作为的Metadata(元数据)
的map的键和值,相当于验证协议需要为http:
if node.Metadata[key] == val {
nodes = append(nodes, node)
}
4.回到http.go
的69行,如果不满足http协议,则获取服务节点失败,返回我们所遇到的这个err:
// get next nodes from the selector
next, err := h.opts.Selector.Select(service, selectOptions...)
if err != nil && err == selector.ErrNotFound {
return nil, errors.NotFound("go.micro.client", err.Error())
} else if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
}
到这里其实已经可以基本确定我们遇到的问题了:在使用go-plugins
插件进行服务调用时,在服务发现时为选择器添加了过滤,限定了请求协议,要求Metadata
的键值必须为"protocol":"http"
,否则返回的服务节点切片长度将为0。
5.解决方法
因此解决方法则是在服务端进行服务注册的时候,为注册的服务添加上Metadata
配置,指定请求协议为http:
service := web.NewService(
web.Name("cas"),
web.Address(":8001"),
web.Registry(consulReg),
web.Handler(engine),
// 为注册的服务添加Metadata,指定请求协议为http
web.Metadata(map[string]string{"protocol" : "http"}),
)
在指定了服务的请求协议后,成功解决该问题~😄