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,请求成功会得到商品信息

浙公网安备 33010602011771号