Go-micro的使用实例

Go-micro创建grpc服务

proto文件

  • protoc --go_out=. model.proto //当前文件下生成.go文件
  • protoc --micro_out=. --go_out=. service.proto //当前文件下生成.micro.go文件
//model.proto
syntax="proto3";
package Services;

//商品模型
message ProdModel{
  //@inject_tag:json:"id"
  int32 ProdID =1;
  //@inject_tag:json:"name"
  string ProdName=2;
}
//service.proto
syntax="proto3";
import "Model.proto";
package Services;


message ProdReq{
  //@inject_tag:form:"size"
  int32 Size =1;
  //@inject_tag:uri:"pid"
  int32 Pid=2;
}
message ProdRsp{
  repeated ProdModel Data =1;
}

message ProdDetailRsp{
  ProdModel Data=1;
}

service ProdService{
  rpc GetProdList(ProdReq)returns (ProdRsp);
  rpc GetProdDetail(ProdReq)returns (ProdDetailRsp);
}

使用protoc-go-inject-tag对protobuf生成的.go文件中的结构体标签进行自定义;protoc-go-inject-tag使用文档:[http://www.github.com/favadi/protoc-go-inject-tag]
(http://www.github.com/favadi/protoc-go-inject-tag)
启动命令:protoc-go-inject-tag -input=./test.pb.go //对文件test.pb.go 的tag进行修改

服务的实现类

可以放在serviceImpl文件夹中统一管理

//实现类
type ProdService struct {
}

func NewProd(id int32,name string)*Services.ProdModel{
	return &Services.ProdModel{
		ProdID: id,
		ProdName: name,
	}
}
//具体业务方法
func (this *ProdService)GetProdList(ctx context.Context,req *Services.ProdReq,rsp *Services.ProdRsp) error{
	<-time.After(time.Second*3)
	var res []*Services.ProdModel
	for i:=1;i<int(req.Size)+1;i++ {
		arg := NewProd(int32(i), "product_"+strconv.Itoa(i))
		res=append(res,arg)
	}
	rsp.Data=res
	return nil
}
//具体业务方法
func (this *ProdService) GetProdDetail(ctx context.Context, in *Services.ProdReq, out *Services.ProdDetailRsp) error {
	res :=new(Services.ProdModel)
	res.ProdName="product_"+strconv.Itoa(int(in.Pid))
	res.ProdID=in.Pid
	out.Data=res
	return nil
}

开启grpc服务

  • 这边使用的注册中心是consul(“github.com/micro/go-plugins/registry/consul”),micro支持mDNS(默认)、etcd、NATs等
  • 使用docker在虚拟机中运行了一个consul服务,端口映射到8500;
    docker run -d --name=“consuldemo2” -p 8500:8500 consul agent -server -bootstrap -ui -client 0.0.0.0开启consul的可视化web界面,查看注册的服务;
    在这里插入图片描述
func main(){
	newRegistry := consul.NewRegistry(
		registry.Addrs("192.168.241.129:8500"),//使用docker在
		)
	newService := micro.NewService(
		//micro.Address(":8002"),//这里可以指定一个端口,也可以cmd指定多个端口提供服务
		micro.Name("prods"),
		micro.Registry(newRegistry),
	)
	newService.Init()
	err := Services.RegisterProdServiceHandler(newService.Server(), new(ServiceImpl.ProdService))
	if err != nil {
		log.Fatal(err)
	}
	newService.Run()
}
//命令行指定服务address:go run main.go --server_address :8001

http-api

  • 使用服务端的proto文件生成.go代码
  • 使用go-micro的web组件开启http服务
  • 使用gin框架提供路由
  • 使用go-micro中的client组件中的Wrapper装饰器处理访问日志
  • 使用github.com/afex/hystrix-go/hystrix进行熔断和降级处理

gin框架生成路由

//middleWare.go //gin中间件管理,传递服务
func InitMiddleWare(prodservice Services.ProdService)gin.HandlerFunc{
	return func(ginctx *gin.Context) {
		ginctx.Keys=make(map[string]interface{})
		ginctx.Keys["prodService"]=prodservice
		ginctx.Next()
	}
}

//handle.go
func ProdListHandle()gin.HandlerFunc{
	return func(ginContext *gin.Context) {
		//从中间件中取值
		prodService:= ginContext.Keys["prodService"].(Services.ProdService)
		var req Services.ProdReq
		err := ginContext.Bind(&req)
		rsp:=new(Services.ProdRsp)
		if err != nil {
			ginContext.JSON(http.StatusInternalServerError,gin.H{"status":err.Error()})
		}else {
			//因为hystrix中的降级方法中已经给定了返回err为nil 所以这里不需要判断
			rsp, _ = prodService.GetProdList(context.Background(), &req)
			ginContext.JSON(http.StatusOK,gin.H{
				"data":rsp.Data,
			})
		}
	}
}

func ProdDetailHandle()gin.HandlerFunc{
	 return func(c *gin.Context) {
		 prodService:= c.Keys["prodService"].(Services.ProdService)
		 req:=new(Services.ProdReq)
		 err := c.BindUri(req)
		 if err != nil {
			 c.JSON(http.StatusInternalServerError,gin.H{"status":err.Error()})
		 }else {
			 rsp, _ := prodService.GetProdDetail(context.Background(), req)
			 c.JSON(http.StatusOK,gin.H{
				 "data":rsp.Data,
			 })
		 }
		recover()

	 }
}
//route.go
func NewGinRoute(prodservice Services.ProdService)*gin.Engine{
	ginRoute := gin.Default()
	ware := InitMiddleWare(prodservice)
	//全局使用该中件间
	ginRoute.Use(ware)
	prodGroup := ginRoute.Group("/prod")
	{
		prodGroup.POST("/list", ProdListHandle())
		prodGroup.GET("/detail/:pid",ProdDetailHandle())
	}
	return ginRoute
}

wrapper处理日志以及hystrix的熔断和降级处理

//prodWrapper.go
//日志
type logWrapper struct {
	client.Client
}
func (l *logWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
	file, err := os.OpenFile("web.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0777)
	if err != nil {
		fmt.Println(err)
	}
	log.SetOutput(file)
	md, _ := metadata.FromContext(ctx)
	log.Printf("[Log Wrapper] ctx: %v service: %s method: %s\n", md, req.Service(), req.Endpoint())
	return l.Client.Call(ctx, req, rsp)
}

func NewLogWrapper(c client.Client) client.Client {
	return &logWrapper{c}
}

//添加访问限制config
type ProdWrapper struct {
	client.Client
}

func defaultFunc(rsp interface{})error{
	var result []*Services.ProdModel
	arg:=Services.ProdModel{
		ProdID: int32(500),
		ProdName: "降级function",
	}
	result=append(result,&arg)
	rsp.(*Services.ProdRsp).Data=result
	return nil
}

func (p *ProdWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error{
	cmdName:=req.Service()+"_"+req.Endpoint()
	configA:= hystrix.CommandConfig{   //设置config
		Timeout: 5000,
	}
	hystrix.ConfigureCommand(cmdName,configA)
	return hystrix.Do(cmdName,func() error{ //hystrix包裹执行第一个函数
		return p.Client.Call(ctx,req,rsp)
	}, func(err error) error {   //第二个函数是降级函数
		return defaultFunc(rsp)
	})
}

func NewProdWrapper(c client.Client) client.Client {
	return &ProdWrapper{c}
}

主函数连接consul调取服务并装载装饰器,开启路由

func main(){
	//调取consul中注册的gprc服务
	myService := micro.NewService(
		micro.Name("prodServiceClient"),
		//log装饰器
		micro.WrapClient(Wrappers.NewLogWrapper),
		//prod访问时间控制熔断
		micro.WrapClient(Wrappers.NewProdWrapper),

	)
	prodService := Services.NewProdService("prods", myService.Client())
	//可以选择是否注册web服务
	newRegistry := consul.NewRegistry(
		registry.Addrs("192.168.241.129:8500"),
	)
	httpService := web.NewService(
		web.Name("httpApiService"),
		web.Registry(newRegistry),
		web.Handler(Weblib.NewGinRoute(prodService)),
		web.Address(":8010"),
	)
	httpService.Init()
	httpService.Run()
}

猜你喜欢

转载自blog.csdn.net/wzb_wzt/article/details/107469571