Go-Micro客戶端請求報500錯誤的解決方法

phpwyl發表於2024-10-04

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.客戶端程式碼

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.發起請求報錯

客戶端請求報錯如下:

程式碼語言: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:

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

在指定了服務的請求協議後,成功解決該問題~😄

相關文章