• 18.增加商品详细API(rpc)


    首先定义proto文件

    syntax = "proto3";
    package Services;
    import "Models.proto";
    message ProdsRequest {
        // @inject_tag: json:"size" form:"size"
        int32 size = 1;
        // @inject_tag: uri:"pid"
        int32 prod_id = 2; //因为id前面有_所以在生成的时候这个字段会变成ProdId变成大写,uri的tag是get请求的参数,我们绑定路由时用的代码
        /*
       func NewGinRouter(prodService Services.ProdService) *gin.Engine {
        ginRouter := gin.Default()
        ginRouter.Use(InitMiddleware(prodService))
        ginRouter.Use(ErrorMiddleware())
        v1Group := ginRouter.Group("/v1")
        {
            v1Group.Handle("POST", "/prods", GetProdsList)
            v1Group.Handle("GET", "/prods/:pid", GetProdDetail) //这里的pid就对应上面反射的tag
        }
        return ginRouter
    }
        */
    }
    
    message ProdListResponse {
        repeated ProdModel data = 1;
    }
    
    message ProdDetailResponse {
        ProdModel data = 1;
    }
    
    service ProdService {
        rpc GetProdsList (ProdsRequest) returns (ProdListResponse);
        rpc GetProdDetail (ProdsRequest) returns (ProdDetailResponse);
    }

    根据生成的pb文件编写rpc服务端

    package ServiceImpl
    
    import (
        "context"
        "go-micro-grpc/Services"
        "strconv"
        "time"
    )
    
    type ProdService struct {
    }
    
    func (*ProdService) GetProdsList(ctx context.Context, in *Services.ProdsRequest, res *Services.ProdListResponse) error {
        time.Sleep(time.Second * 3) //设置3秒延迟
        ret := make([]*Services.ProdModel, 0)
        var i int32
        for i = 0; i < in.Size; i++ {
            ret = append(ret, newProd(100+i, "prodname"+strconv.Itoa(100+int(i))))
        }
        res.Data = ret
        return nil
    }
    
    func (*ProdService) GetProdDetail(ctx context.Context, req *Services.ProdsRequest, res *Services.ProdDetailResponse) error {
        res.Data = newProd(req.ProdId, "无印良品")
        return nil
    }
    
    func newProd(id int32, pname string) *Services.ProdModel {
        return &Services.ProdModel{ProdID: id, ProdName: pname}
    }
    

    先把proto文件拷贝到gin(rpc客户端)这里来生成pb文件

    首先所有的中间件都是在handler之前执行的,编写错误处理中间件

    package Weblib
    
    import (
        "fmt"
        "github.com/gin-gonic/gin"
        "go-micro/Services"
    )
    
    func InitMiddleware(prodService Services.ProdService) gin.HandlerFunc { //返回一个中间件
        return func(context *gin.Context) { //使用中间件封装prodService到context中
            context.Keys = make(map[string]interface{})
            context.Keys["prodservice"] = prodService
            context.Next()
        }
    }
    
    func ErrorMiddleware() gin.HandlerFunc { //所有的中间件都是在请求绑定的handler之前执行完成的
        return func(context *gin.Context) {
            defer func() {
                if r := recover(); r != nil {
                    context.JSON(500, gin.H{"status": fmt.Sprintf("%s", r)})
                    context.Abort()
                }
            }()
            context.Next()
        }
    }
    

    gin绑定中间件并绑定handler

    package Weblib
    
    import (
    "github.com/gin-gonic/gin"
    "go-micro/Services"
    )
    
    func NewGinRouter(prodService Services.ProdService) *gin.Engine {
        ginRouter := gin.Default()
        ginRouter.Use(InitMiddleware(prodService))
        ginRouter.Use(ErrorMiddleware())
        v1Group := ginRouter.Group("/v1")
        {
            v1Group.Handle("POST", "/prods", GetProdsList)
            v1Group.Handle("GET", "/prods/:pid", GetProdDetail)
        }
        return ginRouter
    }
    

    编写gin的路由处理函数handler并在handler中调用rpc服务

    package Weblib
    
    import (
        "context"
        "fmt"
        "github.com/gin-gonic/gin"
        "go-micro/Services"
        "strconv"
    )
    
    func newProd(id int32, pname string) *Services.ProdModel {
        return &Services.ProdModel{ProdID: id, ProdName: pname}
    }
    
    func PanicIfError(err error) {
        if err != nil {
            panic(err)
        }
    }
    
    func GetProdDetail(ginCtx *gin.Context) {
        var prodReq Services.ProdsRequest
        PanicIfError(ginCtx.BindUri(&prodReq)) //这里要绑定的是uri,因为是get请求参数从uri中拿到的不是form表单
        prodService := ginCtx.Keys["prodservice"].(Services.ProdService) //类型断言为对应的请求类型
        resp, _ := prodService.GetProdDetail(context.Background(), &prodReq)
        ginCtx.JSON(200, gin.H{"data": resp.Data})
    }
    
    func defaultProds() (*Services.ProdListResponse, error) {
        models := make([]*Services.ProdModel, 0)
        var i int32
        for i = 0; i < 5; i++ {
            models = append(models, newProd(100+i, "prodname"+strconv.Itoa(100+int(i))))
        }
        res := &Services.ProdListResponse{}
        res.Data = models
        return res, nil
    }
    
    func GetProdsList(ginCtx *gin.Context) {
        prodService := ginCtx.Keys["prodservice"].(Services.ProdService) //类型断言为对应的请求类型
        var prodReq Services.ProdsRequest
        err := ginCtx.Bind(&prodReq)
        if err != nil {
            ginCtx.JSON(500, gin.H{"status": err.Error()})
        } else {
            prodRes, _ := prodService.GetProdsList(context.Background(), &prodReq)
            ginCtx.JSON(200, gin.H{"data": prodRes.Data})
        }
    }
    

    启动gin server并且使用postman访问gin server从而拿到rpc server的数据

    package main
    
    import (
        "context"
        "fmt"
        "github.com/micro/go-micro"
        "github.com/micro/go-micro/client"
        "github.com/micro/go-micro/registry"
        "github.com/micro/go-micro/web"
        "github.com/micro/go-plugins/registry/consul"
        "go-micro/Services"
        "go-micro/Weblib"
        "go-micro/Wrappers"
    )
    
    type logWrapper struct {
        client.Client
    }
    
    func (this *logWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
        fmt.Println("调用接口") //这样每一次调用调用接口时都会
        return this.Client.Call(ctx, req, rsp)
    }
    func NewLogWrapper(c client.Client) client.Client {
        return &logWrapper{c}
    }
    func main() {
        consulReg := consul.NewRegistry( //新建一个consul注册的地址,也就是我们consul服务启动的机器ip+端口
            registry.Addrs("localhost:8500"),
        )
        //下面两局代码是注册rpcserver调用客户端
        myService := micro.NewService(
            micro.Name("prodservice.client"),
            micro.WrapClient(NewLogWrapper),            //在注册时只需要传入方法名即可,底层会自动给这个方法传入client
            micro.WrapClient(Wrappers.NewProdsWrapper), //在注册时只需要传入方法名即可,底层会自动给这个方法传入client
        )
        prodService := Services.NewProdService("prodservice", myService.Client()) //生成的这个客户端绑定consul中存储的prodservice服务,只要调用了prodservice接口就会调用我们上面注册的中间件
    
    
    
        //其实下面这段代码的作用就是启动webserver的同事的时候把服务注册进去
        httpserver := web.NewService( //go-micro很灵性的实现了注册和反注册,我们启动后直接ctrl+c退出这个server,它会自动帮我们实现反注册
            web.Name("httpprodservice"),                   //注册进consul服务中的service名字
            web.Address(":8001"),                          //注册进consul服务中的端口,也是这里我们gin的server地址
            web.Handler(Weblib.NewGinRouter(prodService)), //web.Handler()返回一个Option,我们直接把ginRouter穿进去,就可以和gin完美的结合
            web.Registry(consulReg),                       //注册到哪个服务器上的consul中
        )
        httpserver.Init() //加了这句就可以使用命令行的形式去设置我们一些启动的配置
        httpserver.Run()
    }
    

    这里的pid就是之前生成pb文件时绑定的tag uri:pid





  • 相关阅读:
    JZ2440开发板开发环境搭建
    20180730-宿主机开发环境搭建
    20180319-双网卡电脑同时上内外网
    嵌入式ARM板子起步
    20180127-服务器开发环境搭建
    Pool多进程示例
    Python基础-day01
    解释型语言与编译型语言
    C 编译过程浅析
    博客奇谭
  • 原文地址:https://www.cnblogs.com/hualou/p/12133394.html
Copyright © 2020-2023  润新知