文章最后附带完整代码
其它相关的.bat
和.sh
是window和linux下快速执行的执行文件而已,方便使用,无特殊意义。
##consul注册发现
consul_addr = "127.0.0.1"
consul_port = 8500
##mysql数据库
mysql_addr = "127.0.0.1"
mysql_port = 3306
mysql_user = "root"
mysql_pwd = "123789"
mysql_db_name = "emicro"
syntax = "proto3";
option go_package = "../pbfile";
package proto;
//声明服务
service UserService{
//声明方法
rpc TestUser(TestReq) returns(TestResp){}
}
message TestReq{
int32 id = 1;
}
message TestResp{
string msg = 1;
}
编写快速生成文件,不用每次都手打protoc编译命令,打开pb.bat
,把编译后文件指定生成到pbfile目录。
protoc --go_out=../pbfile --micro_out=../pbfile "*".proto
编写完毕,执行pb.bat,会在pbfile目录下生成user.pb.go和user.pb.micro.go。
启动服务:
service := common.NewService()
//服务注册
consulRegis := consul.NewRegistry( func(options *registry.Options){
//读取配置的consul服务器地址
addr := common.Config.String("consul_addr")
port, _ := common.Config.Int("consul_port")
addrs := fmt.Sprintf("%v:%d", addr, port)
options.Addrs = []string{
addrs,
}
})
//创建服务,这里不指定服务器ip和端口,通过参数接收,方便扩容
service.Service = micro.NewService(
micro.Name(common.ServerName),
micro.Registry(consulRegis),
)
//注册服务和处理器
pb.RegisterUserServiceHandler(service.Service.Server(), handler.NewUserHandler(service))
//接收命令参数,端口
service.Service.Init()
//解析服务Addr
common.InitAddr(service)
service.Service.Run()
逻辑处理器,user_handler.go:
type UserHandler struct {
service *common.EmicroService
}
//grpc服务接收处理
func (u *UserHandler) TestUser(ctx context.Context, req *pb.TestReq, resp *pb.TestResp) error {
log.Println(req.Id)
resp.Msg = "success"
return nil
}
//创建处理器
func NewUserHandler(service *common.EmicroService) *UserHandler{
handler:= new(UserHandler)
handler.service = service
return handler
}
这里用gorm库来操作
var Db *gorm.DB
func init(){
addr := common.Config.String("mysql_addr")
port, _ := common.Config.Int("mysql_port")
user := common.Config.String("mysql_user")
pwd := common.Config.String("mysql_pwd")
db_name := common.Config.String("mysql_db_name")
dsn := fmt.Sprintf("%v:%v@tcp(%v:%d)/%v?charset=utf8mb4&parseTime=True&loc=Local",
user, pwd, addr, port, db_name)
Db1, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true, //禁用表名复数
},
})
if err!=nil{
log.Println("Init Gorm Err:", err)
}
log.Println("Init Success")
Db = Db1
}
当前服务作为客户端请求其它服务的client_wrapper.go和服务端接收请求处理的server_wrapper.go装饰器
譬如可以对请求做统计,日志输出,监听等处理操作,跟grpc的拦截器类似。之后处理全链路追踪也是基于装饰器。
client_wrapper.go
type CliWrapper struct {
client.Client
}
func (c *CliWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error{
log.Println("client wrapper!!!")
return c.Client.Call(ctx, req, rsp, opts...)
}
func NewCliWrapper(c client.Client) client.Client{
return &CliWrapper{c}
}
server_wrapper.go
//服务端装饰器
func ServerWrapper() server.HandlerWrapper{
return func(h server.HandlerFunc) server.HandlerFunc {
return func(ctx context.Context, req server.Request, rsp interface{}) error {
log.Println("Server wrapper!!!")
return h(ctx, req, rsp)
}
}
}
要在上面第三步服务启动时候声明装饰器
service.Service = micro.NewService(
micro.Name(common.ServerName),
micro.Registry(consulRegis),
micro.WrapHandler(wrapper.ServerWrapper()), //服务端接收处理装饰器
micro.WrapClient(wrapper.NewCliWrapper), //客户端请求装饰器
)
编写user.bat
快速启动服务,需要传递服务的Ip地址和端口参数
go run server.go --server_address 0.0.0.0:5500
出现以下内容则说的启动和注册服务成功
具体目录为user_client
consulRegis := consul.NewRegistry(func(options *registry.Options) {
options.Addrs = []string{
"127.0.0.1:8500",
}
})
service := micro.NewService(
micro.Name("emicro.user.client"),
micro.Registry(consulRegis),
)
userService := pb.NewUserService("emicro.user", service.Client())
res, err := userService.TestUser(context.TODO(), &pb.TestReq{Id:1150})
if err != nil {
fmt.Println(err)
}
fmt.Println(res)
出现如下,代表请求和响应成功
注意:尽量用module依赖管理,并且使用的是go-micro v3,如果用v2会有奇奇怪怪问题和编译不通过等,可参考使用完整代码import的库。
gitee完整代码链接