Go-Micro客户端请求报500错误的解决方法

Go-Micro客户端请求报500错误的解决方法

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.客户端代码

javascript 复制代码
复制代码
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)
}
javascript 复制代码
javascript 复制代码
 

3.发起请求报错

客户端请求报错如下:
代码语言:javascript
复制

javascript 复制代码
{"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:

javascript 复制代码
复制代码
    if node.Metadata[key] == val {
   
                    nodes = append(nodes, node)
                }
javascript 复制代码
javascript 复制代码

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"}),
    )

在指定了服务的请求协议后,成功解决该问题~😄

相关推荐
东阳马生架构14 分钟前
生成订单链路中的技术问题说明文档
后端
程序员码歌3 小时前
【零代码AI编程实战】AI灯塔导航-总结篇
android·前端·后端
七七&5563 小时前
2024年08月13日 Go生态洞察:Go 1.23 发布与全面深度解读
开发语言·网络·golang
java坤坤3 小时前
GoLand 项目从 0 到 1:第八天 ——GORM 命名策略陷阱与 Go 项目启动慢问题攻坚
开发语言·后端·golang
元清加油3 小时前
【Golang】:函数和包
服务器·开发语言·网络·后端·网络协议·golang
健康平安的活着4 小时前
java之 junit4单元测试Mockito的使用
java·开发语言·单元测试
bobz9654 小时前
GPT-4.1 对比 GPT-4o
后端
Java小白程序员4 小时前
Spring Framework :IoC 容器的原理与实践
java·后端·spring
小小愿望5 小时前
前端无法获取响应头(如 Content-Disposition)的原因与解决方案
前端·后端
DjangoJason5 小时前
C++ 仿RabbitMQ实现消息队列项目
开发语言·c++·rabbitmq