go微服务demo

本 Demo 以获取商品信息为例子

分为 Server 端和 Client 端

Server 端使用 go-micro 实现简单业务逻辑

Client 端使用 rpc 调用 Server 端的处理函数并使用 gin 向外暴露 HTTP 接口

Demo 依赖

  • gin
  • go-micro
  • protobuf
  • consul
  • docker
  • protoc-go-inject-tag
  • protoc-gen-go

Docker 容器启动 consul

终端输入

docker run -p 8500:8500/tcp consul agent -server -ui -bootstrap-expect=1 -client=0.0.0.0

-server consul 支持以 server 或 client 的模式运行, server 是服务发现模块的核心, client 主要用于转发请求

-bootstrap-expect 指定 consul 将等待几个节点连通,成为一个完整的集群

-client 0.0.0.0 表示任何地址可以访问。

-ui 提供图形化的界面。

更多参数请查阅相关文档

浏览器访问 localhost:8500可以看到 consul 的图形化界面

Server 端 Demo

目录结构

go-micro-grpc/
├── serviceImpl
│	├── productService.go
├── services
│	├── protos
│	│	├── models.proto
│	│	├── productService.proto
│   ├── models.pb.go
│   ├── productService.pb.go
│   ├── productService.pb.micro.go
├── go.mod
├── go.sum
├── main.go
  • serviceImpl:业务逻辑实现
  • services:proto 文件及生成文件

proto 编写及自动生成 pb.go

models.proto

syntax="proto3";
package services;

message ProductModel{
  // @inject_tag: json:"product_id"
  int32 ProductID=1;
  // @inject_tag: json:"product_name"
  string ProductName=2;
}

productService.proto

syntax="proto3";
package services;
import "models.proto";

message ProductRequest{
  // @inject_tag: json:"size" form:"size"
  int32 size=1;
}
message ProductListResponse{
  repeated ProductModel ProductList=1;
}

service ProductService{
  rpc GetProductsList(ProductRequest) returns(ProductListResponse);
}

进入 proto 文件的路径,终端输入

#生成pb.go文件
protoc  --go_out=../ models.proto
#生成pb.go和pb.micro.go文件
protoc --micro_out=../ --go_out=../ productService.proto
#在pb.go文件生成结构体验证标记
protoc-go-inject-tag -input=../models.pb.go
protoc-go-inject-tag -input=../productService.pb.go

执行完毕后,上一级目录会生成models.pb.go、productService.pb.go、productService.pb.micro.go三个文件

main.go

将服务注册到 consul

package main

import (
	"go-micro-grpc/serviceImpl"
	"go-micro-grpc/services"

	"github.com/micro/go-micro"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/registry/consul"

)

func main() {
	//consul注册件
	consulReg := consul.NewRegistry(
		registry.Addrs("localhost:8500"),
	)
	//创建微服务实例,并注册到consul
	microService := micro.NewService(
		micro.Name("grpcProductService"),
		micro.Address(":8080"),
		micro.Registry(consulReg),
	)
	//接收命令行参数 如--server_address
	microService.Init()

	//注册商品服务
	services.RegisterProductServiceHandler(microService.Server(), new(serviceImpl.ProductService))
	microService.Run()

}

serviceImpl/productService.go

简单的业务逻辑

package serviceImpl

import (
	"context"
	"go-micro-grpc/services"
	"strconv"
)

func NewProduct(id int32, name string) *services.ProductModel {
	return &services.ProductModel{ProductID: id, ProductName: name}
}


type ProductService struct {
}

//实现商品服务接口
func (*ProductService) GetProductsList(ctx context.Context, in *services.ProductRequest, res *services.ProductListResponse) error {
	productList := make([]*services.ProductModel, 0)
	var i int32
	for i = 0; i < in.Size; i++ {
		productList = append(productList, NewProduct(i, "ProductService"+strconv.Itoa(100+int(i))))
	}
	res.ProductList = productList
	return nil
}

Client 端 Demo

目录结构

go-micro-ginClient/
├── services
│	├── protos
│	│	├── models.proto
│	│	├── productService.proto
│   ├── models.pb.go
│   ├── productService.pb.go
│   ├── productService.pb.micro.go
├── webLib
│	├── handlers.go
│	├── middlewares.go
│	├── router.go
├── go.mod
├── go.sum
├── main.go
  • services:proto 文件及生成文件
  • webLib:gin 路由和处理函数封装

proto 编写及自动生成 pb.go

具体操作和前面 server 端一样

main.go

package main

import (
	"go-micro-ginClient/services"
	"go-micro-ginClient/webLib"

	"github.com/micro/go-micro"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/web"
	"github.com/micro/go-plugins/registry/consul"

)

func main() {
	//consul注册件
	consulReq := consul.NewRegistry(
		registry.Addrs("localhost:8500"),
	)

	myService := micro.NewService(micro.Name("productService.client"))
	//服务调用实例
	productService := services.NewProductService("grpcProductService", myService.Client())

	//创建微服务实例,使用gin暴露http接口并注册到consul
	server := web.NewService(
		web.Name("httpProductService"),
		web.Address(":8000"),
		//将服务调用实例使用gin处理
		web.Handler(webLib.NewRouter(productService)),
		web.Registry(consulReq),
	)
	//接收命令行参数
	server.Init()
	server.Run()

}

webLib/router.go

package webLib

import (
	"go-micro-ginClient/services"

	"github.com/gin-gonic/gin"
)

//接收服务调用实例的参数
func NewRouter(productService services.ProductService) *gin.Engine {
	ginRouter := gin.Default()
	//使用中间件,接收服务调用实例
	ginRouter.Use(InitMiddleware(productService))
	//向外暴露http接口,本Demo使用Post请求
	v1 := ginRouter.Group("/api/v1")
	{
		v1.POST("/products", GetProductsList)
	}
	return ginRouter
}

webLib/middlewares.go

gin 的中间件

package webLib

import (
	"go-micro-ginClient/services"

	"github.com/gin-gonic/gin"
)

func InitMiddleware(productService services.ProductService) gin.HandlerFunc {
	return func(context *gin.Context) {
		//将实例存到context.Keys里
		context.Keys = make(map[string]interface{})
		context.Keys["productService"] = productService
		context.Next()
	}
}

webLib/handlers.go

封装调用函数

package webLib

import (
	"context"
	"go-micro-ginClient/services"

	"github.com/gin-gonic/gin"
)

//使用rpc调用服务端的处理函数
func GetProductsList(ginCtx *gin.Context) {
	productService := ginCtx.Keys["productService"].(services.ProductService)
	var productReq services.ProductRequest
	//解析请求
	err := ginCtx.Bind(&productReq)
	if err != nil {
		ginCtx.JSON(500, gin.H{"status": err.Error()})
	} else {
		//调用服务端的函数
		productRes, _ := productService.GetProductsList(context.Background(), &productReq)
		ginCtx.JSON(200, gin.H{"data": productRes.ProductList})
	}
}

Postman 测试接口

POST 请求访问 localhost:8000/api/v1/products 并带 form 或 json 参数"size":1,请求成功会得到商品信息

posted @ 2021-02-04 11:11  liuxinyu123  阅读(499)  评论(0)    收藏  举报