Golang基于泛化調(diào)用與Nacos實(shí)現(xiàn)Dubbo代理
前言
由于工作中使用的 rpc 框架是 dubbo,經(jīng)常需要調(diào)試不同環(huán)境的 dubbo 接口,例如本地環(huán)境、開發(fā)環(huán)境和測試環(huán)境。而為了統(tǒng)一管理 http 接口和 dubbo 接口,希望使用統(tǒng)一的調(diào)試工具,例如 PostMan 或 ApiPost 等,因此萌生了開發(fā)一個 dubbo 的 http 代理工具的念頭。
準(zhǔn)備
由于是通用的 dubbo 代理,因此肯定需要使用泛化調(diào)用。而我們使用的注冊中心是 nacos,因此也需要使用 nacos-sdk 來獲取 provider 的實(shí)例信息。
實(shí)現(xiàn)
項(xiàng)目結(jié)構(gòu)
│
├── dubbo/
│ ├─ generic.go # 泛化調(diào)用 dubbo 接口
│ ├─ models.go # 數(shù)據(jù)模型
│ └─ nacos.go # 獲取 nacos 元信息
├── web/
│ └─ server.go # 對外 http 接口
│
├── main.go # main 入口函數(shù)
└── go.mod # 模塊描述文件
go.mod
module dubbo-proxy go 1.20 require ( dubbo.apache.org/dubbo-go/v3 v3.0.5 github.com/apache/dubbo-go-hessian2 v1.12.0 github.com/gin-gonic/gin v1.9.0 github.com/nacos-group/nacos-sdk-go/v2 v2.1.2 )
返回?cái)?shù)據(jù)格式
dubbo/models.go:
type DataResult struct {
Env string `json:"env,omitempty"` // 當(dāng)前調(diào)用環(huán)境
Code string `json:"code,omitempty"` // 返回結(jié)果碼
Data any `json:"data,omitempty"` // 返回結(jié)果
Message string `json:"message,omitempty"` // 返回消息
}
獲取 nacos 元信息
根據(jù)環(huán)境創(chuàng)建 nacos client
func buildClient(env string, serverCfgs []constant.ServerConfig) naming_client.INamingClient {
client, _ := clients.NewNamingClient(
vo.NacosClientParam{
ClientConfig: constant.NewClientConfig(
constant.WithNamespaceId(env),
constant.WithNotLoadCacheAtStart(true),
),
ServerConfigs: serverCfgs,
},
)
return client
}
獲取服務(wù)實(shí)例
func SelectInstance(env, servName string) (string, bool) {
cli, ok := cliMap[env]
if !ok {
return "client not found from " + env, false
}
instances, e := cli.SelectInstances(vo.SelectInstancesParam{
ServiceName: fmt.Sprintf("providers:%s:1.0.0:", servName),
HealthyOnly: true,
})
if e != nil {
return "instance not found, " + e.Error(), false
}
if len(instances) <= 0 {
return "instance not found", false
}
return fmt.Sprintf("dubbo://%s:%d", instances[0].Ip, instances[0].Port), true
}
完整代碼
dubbo/nacos.go:
package dubbo
import (
"fmt"
"github.com/nacos-group/nacos-sdk-go/v2/clients"
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/nacos-group/nacos-sdk-go/v2/vo"
)
var cliMap = make(map[string]naming_client.INamingClient)
func init() {
serverCfgs := []constant.ServerConfig{
*constant.NewServerConfig("127.0.0.1", 6801, constant.WithContextPath("/nacos")),
}
cliMap["local"] = buildClient("local", serverCfgs)
cliMap["dev"] = buildClient("develop", serverCfgs)
cliMap["test"] = buildClient("test", serverCfgs)
}
func buildClient(env string, serverCfgs []constant.ServerConfig) naming_client.INamingClient {
client, _ := clients.NewNamingClient(
vo.NacosClientParam{
ClientConfig: constant.NewClientConfig(
constant.WithNamespaceId(env),
constant.WithNotLoadCacheAtStart(true),
),
ServerConfigs: serverCfgs,
},
)
return client
}
func SelectInstance(env, servName string) (string, bool) {
cli, ok := cliMap[env]
if !ok {
return "client not found from " + env, false
}
instances, e := cli.SelectInstances(vo.SelectInstancesParam{
ServiceName: fmt.Sprintf("providers:%s:1.0.0:", servName),
HealthyOnly: true,
})
if e != nil {
return "instance not found, " + e.Error(), false
}
if len(instances) <= 0 {
return "instance not found", false
}
return fmt.Sprintf("dubbo://%s:%d", instances[0].Ip, instances[0].Port), true
}泛化調(diào)用
dubbo root 配置
var dubboRoot = cfg.NewRootConfigBuilder().SetProtocols(map[string]*cfg.ProtocolConfig{
dubbo.DUBBO: {
Params: map[string]interface{}{
"getty-session-param": map[string]interface{}{
"max-msg-len": 1024000,
},
},
},
}).Build()
泛化調(diào)用
func GenericInvoke(iName, method, env string, req []byte) DataResult {
instance, ok := SelectInstance(env, iName)
if !ok {
return DataResult{
Code: "ERROR",
Message: instance,
}
}
cfg.Load(cfg.WithRootConfig(dubboRoot))
refConf := cfg.ReferenceConfig{
InterfaceName: iName,
Cluster: "failover",
Protocol: dubbo.DUBBO,
Generic: "true",
Version: "1.0.0",
URL: instance,
}
refConf.Init(dubboRoot)
refConf.GenericLoad("dubbo-proxy")
var args = utils.Unmarshal(req, &map[string]hessian.Object{})
raw, err := refConf.GetRPCService().(*generic.GenericService).Invoke(context.Background(), method, nil, []hessian.Object{args})
if err != nil {
panic(err)
}
rawResult := raw.(map[interface{}]interface{})
result := DataResult{
Code: rawResult["code"].(string),
Message: rawResult["message"].(string),
Data: utils.ConvertAs(rawResult["data"], map[string]interface{}{}),
}
return result
}
注意25-30行要根據(jù)業(yè)務(wù)自身的返回?cái)?shù)據(jù)格式包裝結(jié)果:
/*
這個例子的 dubbo 調(diào)用都會返回通過的結(jié)構(gòu):
{
"code": "",
"message": "",
"data": // 真正的調(diào)用結(jié)果
}
*/
rawResult := raw.(map[interface{}]interface{})
result := DataResult{
Code: rawResult["code"].(string),
Message: rawResult["message"].(string),
Data: rawResult["data"],
}完整代碼
dubbo/generic.go:
package dubbo
import (
"context"
"dubbo-proxy/utils"
cfg "dubbo.apache.org/dubbo-go/v3/config"
"dubbo.apache.org/dubbo-go/v3/config/generic"
_ "dubbo.apache.org/dubbo-go/v3/imports"
"dubbo.apache.org/dubbo-go/v3/protocol/dubbo"
hessian "github.com/apache/dubbo-go-hessian2"
)
var dubboRoot = cfg.NewRootConfigBuilder().SetProtocols(map[string]*cfg.ProtocolConfig{
dubbo.DUBBO: {
Params: map[string]interface{}{
"getty-session-param": map[string]interface{}{
"max-msg-len": 1024000,
},
},
},
}).Build()
func GenericInvoke(iName, method, env string, req []byte) DataResult {
instance, ok := SelectInstance(env, iName)
if !ok {
return DataResult{
Code: "ERROR",
Message: instance,
}
}
cfg.Load(cfg.WithRootConfig(dubboRoot))
refConf := cfg.ReferenceConfig{
InterfaceName: iName,
Cluster: "failover",
Protocol: dubbo.DUBBO,
Generic: "true",
Version: "1.0.0",
URL: instance,
}
refConf.Init(dubboRoot)
refConf.GenericLoad("dubbo-proxy")
var args = utils.Unmarshal(req, &map[string]hessian.Object{})
raw, err := refConf.GetRPCService().(*generic.GenericService).Invoke(context.Background(), method, nil, []hessian.Object{args})
if err != nil {
panic(err)
}
rawResult := raw.(map[interface{}]interface{})
result := DataResult{
Code: rawResult["code"].(string),
Message: rawResult["message"].(string),
Data: utils.ConvertAs(rawResult["data"], map[string]interface{}{}),
}
return result
}提供 http 服務(wù)
dubbo/generic.go:
package web
import (
"dubbo-proxy/dubbo"
"github.com/gin-gonic/gin"
"net/http"
)
func Run() {
router := gin.Default()
router.POST("/:intf/:method", func(c *gin.Context) {
intf := c.Param("intf")
method := c.Param("method")
env := c.Query("env")
data, err := c.GetRawData()
if err != nil {
panic(err)
}
res := dubbo.GenericInvoke(intf, method, env, data)
res.Env = env
c.JSON(http.StatusOK, res)
})
panic(router.Run(":7788"))
}啟動
main.go:
package main
import "dubbo-proxy/web"
func main() {
web.Run()
}效果

以上就是Golang基于泛化調(diào)用與Nacos實(shí)現(xiàn)Dubbo代理的詳細(xì)內(nèi)容,更多關(guān)于Golang Dubbo代理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go實(shí)現(xiàn)用戶每日限額的方法(例一天只能領(lǐng)三次福利)
這篇文章主要介紹了Go實(shí)現(xiàn)用戶每日限額的方法(例一天只能領(lǐng)三次福利)2022-01-01
golang協(xié)程關(guān)閉踩坑實(shí)戰(zhàn)記錄
協(xié)程(coroutine)是Go語言中的輕量級線程實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于golang協(xié)程關(guān)閉踩坑的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03
Go語言net包RPC遠(yuǎn)程調(diào)用三種方式http與json-rpc及tcp
這篇文章主要為大家介紹了Go語言net包RPC遠(yuǎn)程調(diào)用三種方式分別使用http與json-rpc及tcp的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-11-11
基于Go和PHP語言實(shí)現(xiàn)爬樓梯算法的思路詳解
這篇文章主要介紹了Go和PHP 實(shí)現(xiàn)爬樓梯算法,本文通過動態(tài)規(guī)劃和斐波那契數(shù)列兩種解決思路給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05

