1.创建项目
编写app.api
syntax = "v1"
type (
UserLoginReq struct{}
UserInfoReq {
Id int64 `path:"id"`
}
UserLoginResp struct{}
UserInfoResp struct{}
UserInfoUpdateReq struct{}
UserInfoUpdateResp struct{}
)
type (
UserRoleReq struct{}
UserRoleResp struct{}
UserRoleUpdateReq struct{}
UserRoleUpdateResp struct{}
UserRoleAddReq struct{}
UserRoleAddResp struct{}
UserRoleDeleteReq struct{}
UserRoleDeleteResp struct{}
)
type (
UserClassReq struct{}
UserClassResp struct{}
UserClassUpdateReq struct{}
UserClassUpdateResp struct{}
UserClassAddReq struct{}
UserClassAddResp struct{}
UserClassDeleteReq struct{}
UserClassDeleteResp struct{}
)
@server (
prefix: /v1
group: user
//signature: true
middleware: UserAgentMiddleware
)
service user-api {
@handler UserLogin
post /user/login (UserLoginReq) returns (UserLoginResp)
@handler getUserInfo
get /user/info/:id (UserInfoReq) returns (UserInfoResp)
@handler UserInfoUpdate
post /user/info/update (UserInfoUpdateReq) returns (UserInfoUpdateResp)
@handler UserList
get /user/list returns ([]UserInfoResp)
}
@server (
prefix: /v1
group: role
)
service user-api {
@handler UserRoleList
get /user/role/list returns ([]UserRoleResp)
@handler UserRoleUpdate
get /user/role/update (UserRoleUpdateReq) returns (UserRoleUpdateResp)
@handler UserRoleInfo
get /user/role/info (UserRoleReq) returns (UserRoleResp)
@handler UserRoleAdd
get /user/role/add (UserRoleAddReq) returns (UserRoleAddResp)
@handler UserRoleDelete
get /user/role/delete (UserRoleDeleteReq) returns (UserRoleDeleteResp)
}
@server (
prefix: /v1
group: class
)
service user-api {
@handler UserClassList
get /user/class/list returns ([]UserClassResp)
@handler UserClassUpdate
get /user/class/update (UserClassUpdateReq) returns (UserClassUpdateResp)
@handler UserClassInfo
get /user/class/info (UserClassReq) returns (UserClassResp)
@handler UserClassAdd
get /user/class/add (UserClassAddReq) returns (UserClassAddResp)
@handler UserClassDelete
get /user/class/delete (UserClassDeleteReq) returns (UserClassDeleteResp)
}
编写deposit.proto文件
syntax = "proto3";
package mock;
option go_package = "./mock";
message DepositRequest {
float amount = 1;
}
message DepositResponse {
bool ok = 1;
}
service DepositService {
rpc Deposit(DepositRequest) returns (DepositResponse);
}
分别使用如下命令生成代码
goctl api go --api app.api --dir ../ --style go_zero
goctl rpc protoc deposit.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=../ --style go_zero -m
2.安装依赖
go get -u github.com/zeromicro/zero-contrib/zrpc/registry/nacos
3.修改grpc服务端代码
package main
import (
"flag"
"fmt"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/zeromicro/zero-contrib/zrpc/registry/nacos"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/core/service"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"service/internal/config"
depositserviceServer "service/internal/server/depositservice"
"service/internal/svc"
"service/proto/pb/mock"
)
var configFile = flag.String("f", "etc/deposit.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
mock.RegisterDepositServiceServer(grpcServer, depositserviceServer.NewDepositServiceServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode {
reflection.Register(grpcServer)
}
})
defer s.Stop()
// 注册服务
sc := []constant.ServerConfig{
*constant.NewServerConfig(c.Nacos.Ip, c.Nacos.Port),
}
cc := &constant.ClientConfig{
NamespaceId: c.Nacos.Namespace,
TimeoutMs: 50000,
NotLoadCacheAtStart: c.Nacos.NotLoadCacheAtStart,
LogDir: "/tmp/nacos/log",
CacheDir: "/tmp/nacos/cache",
LogLevel: c.Nacos.LogLevel,
}
opts := nacos.NewNacosConfig(c.RpcServerConf.Name, c.ListenOn, sc, cc)
nacos.RegisterService(opts)
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
s.Start()
}
配置文件
Name: deposit
ListenOn: 0.0.0.0:8080
#Etcd:
#Hosts:
#- 172.18.0.30:2379
#Key: deposit
Nacos:
Ip: 172.18.0.145
Port: 8848
Namespace: local_test
NotLoadCacheAtStart: true
LogLevel: debug
Log:
Encoding: plain
4.修改客户端代码
import 加上 _ "github.com/zeromicro/zero-contrib/zrpc/registry/nacos"
主要目的是让执行init方法,注册nacos的解析builder
func init() {
resolver.Register(&builder{})
}
完整代码
package main
import (
"flag"
"fmt"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
"github.com/zeromicro/zero-contrib/zrpc/registry/nacos"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/core/service"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"service/internal/config"
depositserviceServer "service/internal/server/depositservice"
"service/internal/svc"
"service/proto/pb/mock"
)
var configFile = flag.String("f", "etc/deposit.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
mock.RegisterDepositServiceServer(grpcServer, depositserviceServer.NewDepositServiceServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode {
reflection.Register(grpcServer)
}
})
defer s.Stop()
// 注册服务
sc := []constant.ServerConfig{
*constant.NewServerConfig(c.Nacos.Ip, c.Nacos.Port),
}
cc := &constant.ClientConfig{
NamespaceId: c.Nacos.Namespace,
TimeoutMs: 50000,
NotLoadCacheAtStart: c.Nacos.NotLoadCacheAtStart,
LogDir: "/tmp/nacos/log",
CacheDir: "/tmp/nacos/cache",
LogLevel: c.Nacos.LogLevel,
}
opts := nacos.NewNacosConfig(c.RpcServerConf.Name, c.ListenOn, sc, cc)
nacos.RegisterService(opts)
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
s.Start()
}
配置文件
Name: user-api
Host: 0.0.0.0
Port: 8888
Timeout: -1
DepositServiceConf:
# Etcd:
#Hosts:
#- 172.18.0.30:2379
#Key: deposit
Timeout: -1
Target: nacos://172.18.0.145:8848/deposit?namespaceid=local_test
Log:
Encoding: plain
其他业务代码与ETCD方式一样,自己可以调试一下
个人集成nacos使用的zero-contrib版本v1.1.0,目前最新的,发现一直调不通,一直报错
GOROOT=D:\Program Files\Go\go1.20.7 #gosetup
GOPATH=D:\Program Files\Go #gosetup
"D:\Program Files\Go\go1.20.7\bin\go.exe" build -o C:\Users\27383\AppData\Local\JetBrains\GoLand2023.1\tmp\GoLand\___go_build_client.exe -gcflags "all=-N -l" client #gosetup
"D:\Program Files\JetBrains\GoLand 2023.1.2\plugins\go-plugin\lib\dlv\windows\dlv.exe" --listen=127.0.0.1:61692 --headless=true --api-version=2 --check-go-version=false --only-same-user=false exec C:\Users\27383\AppData\Local\JetBrains\GoLand2023.1\tmp\GoLand\___go_build_client.exe --
API server listening at: 127.0.0.1:61692
2023-09-23T16:47:40.823+0800 INFO nacos_client/nacos_client.go:65 logDir:<C:\Users\27383\AppData\Local\JetBrains\GoLand2023.1\tmp\GoLand\log> cacheDir:<C:\Users\27383\AppData\Local\JetBrains\GoLand2023.1\tmp\GoLand\cache>
2023-09-23T16:47:48.662+08:00 info [Nacos resolver] Watch has been finished caller=nacos@v0.0.0-20230807142214-5952d42ba776/resolver.go:70
2023/09/23 16:47:48 rpc dial: nacos://172.18.0.145:8848/deposit?namespaceid=local_test, error: context deadline exceeded, make sure rpc service "deposit?namespaceid=local_test" is already started
goroutine 1 [running]:
runtime/debug.Stack()
D:/Program Files/Go/go1.20.7/src/runtime/debug/stack.go:24 +0x7a
github.com/zeromicro/go-zero/core/logx.Must({0x3286fc0, 0xc0002918a0})
D:/Program Files/Go/pkg/mod/github.com/zeromicro/go-zero@v1.5.4/core/logx/logs.go:225 +0x65
github.com/zeromicro/go-zero/zrpc.MustNewClient({{{0x0, 0x0, 0x0}, {0x0, 0x0}, 0x0, {0x0, 0x0}, {0x0, 0x0}, ...}, ...}, ...)
D:/Program Files/Go/pkg/mod/github.com/zeromicro/go-zero@v1.5.4/zrpc/client.go:45 +0x13d
client/internal/svc.NewServiceContext({{{{0xc000114af0, 0x8}, {{...}, {...}, {...}, {...}, {...}, {...}, 0x0, 0x0, ...}, ...}, ...}, ...})
D:/Program Files/Go/IdeaProject/gozero/client/internal/svc/service_context.go:26 +0x105
main.main()
D:/Program Files/Go/IdeaProject/gozero/client/user.go:26 +0x198
2023-09-23T16:47:48.662+08:00 fatal rpc dial: nacos://172.18.0.145:8848/deposit?namespaceid=local_test, error: context deadline exceeded, make sure rpc service "deposit?namespaceid=local_test" is already started
goroutine 1 [running]:
runtime/debug.Stack()
D:/Program Files/Go/go1.20.7/src/runtime/debug/stack.go:24 +0x7a
github.com/zeromicro/go-zero/core/logx.Must({0x3286fc0, 0xc0002918a0})
D:/Program Files/Go/pkg/mod/github.com/zeromicro/go-zero@v1.5.4/core/logx/logs.go:225 +0x65
github.com/zeromicro/go-zero/zrpc.MustNewClient({{{0x0, 0x0, 0x0}, {0x0, 0x0}, 0x0, {0x0, 0x0}, {0x0, 0x0}, ...}, ...}, ...)
D:/Program Files/Go/pkg/mod/github.com/zeromicro/go-zero@v1.5.4/zrpc/client.go:45 +0x13d
client/internal/svc.NewServiceContext({{{{0xc000114af0, 0x8}, {{...}, {...}, {...}, {...}, {...}, {...}, 0x0, 0x0, ...}, ...}, ...}, ...})
D:/Program Files/Go/IdeaProject/gozero/client/internal/svc/service_context.go:26 +0x105
main.main()
D:/Program Files/Go/IdeaProject/gozero/client/user.go:26 +0x198
Debugger finished with the exit code 0
然后漫长的调试跟源码发现问题,这里实际上是zero-contrib v1.1.0的bug引起的,看如下相关截图
简单理解就是由于 客户端启动的时候会加载本地缓存导致的,客户端加载到服务后会已文件的方式缓存到本地磁盘,下次启动的时候再次从磁盘加载,然后与远端naocs服务请求做对比,如果发现服务没有变化导致相关关键代码没有执行
实际上客户端使用本地缓存也是为了保证服务的稳定性,比如客户端访问不到nacos的情况下,可以加载本地缓存使用服务,不会导致整个服务不可用
不过这里最简单的修改方式是直接不使用缓存,官方没有给相关的配置option给用户手动设置,只能手动修改源码解决,但是这种方式只是临时解决方案,实际上还是需要想办法触发关键代码来解除阻塞,后续有时间再pr相关代码进行修复