[golang microservice] 8. The load balancing operation of go-micro, the go Web box (Gin, Beego) calls the go-micro microservice

1. Create the go-micro server first

  1. start consul

Consul needs to be started first, see
[golang microservices] for consol related content 5. Microservice service discovery introduction, installation and use of consul, Consul cluster
[golang microservices] 6. GRPC microservice cluster + Consul cluster + grpc-consul-resolver The case demo
starts the consul command, here, use the dev mode: consul agent -dev, start as follows:

UI interface after startup:

  1. Start the microservice

The goodsinfo microservice in the previous section is used here , see the previous section [golang microservice] 7. Go-micro framework introduction, go-micro scaffolding, go-micro combined with consul to build a microservice case
Here is the optimization of .proto : unify the product model, and increase the related request and response code for getting the product

The code of goodsinfo.proto is as follows:

syntax = "proto3";

package goodsinfo;

option go_package = "./proto;goodsinfo";

//商品相关方法
service Goodsinfo {
    //AddGoods: 定义增加商品的微服务, 这里的写法和gRPC中的写法一致
    rpc AddGoods(AddGoodsRequest) returns (AddGoodsResponse) {}
    //获取商品列表信息
    rpc GetGoods(GetGoodsRequest) returns (GetGoodsResponse) {}
}

//定义GoodsModel结构体,以便增加商品,获取商品数据时使用
message GoodsModel{
    string title = 1;
    double price = 2;
    string content = 3;
}

//增加商品请求: 和gRPC中的写法一致
message AddGoodsRequest {
    GoodsModel parame = 1;
}

//增加商品响应:和gRPC中的写法一致
message AddGoodsResponse {
    string message = 1;
    bool success = 2;
}

//获取商品请求
message GetGoodsRequest {
}
//获取商品响应
message GetGoodsResponse {
    repeated GoodsModel GoodsList = 1;
}
Execute the command under goodsinfo:
protoc --proto_path=. --micro_out=. --go_out=:. proto/goodsinfo.proto
generates the corresponding .pb.go, .micro.go files, and then in handler/goodsinfo.go Perfect corresponding method:

The code in handler/goodsinfo.go is as follows:

package handler

import (
    "context"
    "strconv"
    "go-micro.dev/v4/logger"
    pb "goodsinfo/proto"
)

type Goodsinfo struct{}

//增加商品
func (e *Goodsinfo) AddGoods(ctx context.Context, req *pb.AddGoodsRequest, rsp *pb.AddGoodsResponse) error {
    logger.Infof("request: %v", req)
    //书写返回的逻辑结果
    rsp.Message = "增加成功"
    rsp.Success = true
    return nil
}

//获取商品
func (e *Goodsinfo) GetGoods(ctx context.Context, req *pb.GetGoodsRequest, rsp *pb.GetGoodsResponse) error {
    var tempList []*pb.GoodsModel
    //模拟从数据库中获取商品数据
    for i := 0; i < 10; i++ {
        tempList = append(tempList, &pb.GoodsModel{
            Title:   "第" + strconv.Itoa(i) + "条数据",
            Price:   float64((i + 1) * 2),
            Content: "第" + strconv.Itoa(i) + "内容",
        })
    }
    rsp.GoodsList = tempList
    return nil
}

The code of goodsinfo.pb.go is as follows:

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
//     protoc-gen-go v1.30.0
//     protoc        v3.15.5
// source: proto/goodsinfo.proto

package goodsinfo

import (
    protoreflect "google.golang.org/protobuf/reflect/protoreflect"
    protoimpl "google.golang.org/protobuf/runtime/protoimpl"
    reflect "reflect"
    sync "sync"
)

const (
    // Verify that this generated code is sufficiently up-to-date.
    _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
    // Verify that runtime/protoimpl is sufficiently up-to-date.
    _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

// 定义GoodsModel结构体,以便增加商品,获取商品数据时使用
type GoodsModel struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Title   string  `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
    Price   float64 `protobuf:"fixed64,2,opt,name=price,proto3" json:"price,omitempty"`
    Content string  `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"`
}

func (x *GoodsModel) Reset() {
    *x = GoodsModel{}
    if protoimpl.UnsafeEnabled {
        mi := &file_proto_goodsinfo_proto_msgTypes[0]
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        ms.StoreMessageInfo(mi)
    }
}

func (x *GoodsModel) String() string {
    return protoimpl.X.MessageStringOf(x)
}

func (*GoodsModel) ProtoMessage() {}

func (x *GoodsModel) ProtoReflect() protoreflect.Message {
    mi := &file_proto_goodsinfo_proto_msgTypes[0]
    if protoimpl.UnsafeEnabled && x != nil {
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        if ms.LoadMessageInfo() == nil {
            ms.StoreMessageInfo(mi)
        }
        return ms
    }
    return mi.MessageOf(x)
}

// Deprecated: Use GoodsModel.ProtoReflect.Descriptor instead.
func (*GoodsModel) Descriptor() ([]byte, []int) {
    return file_proto_goodsinfo_proto_rawDescGZIP(), []int{0}
}

func (x *GoodsModel) GetTitle() string {
    if x != nil {
        return x.Title
    }
    return ""
}

func (x *GoodsModel) GetPrice() float64 {
    if x != nil {
        return x.Price
    }
    return 0
}

func (x *GoodsModel) GetContent() string {
    if x != nil {
        return x.Content
    }
    return ""
}

// 增加商品请求: 和gRPC中的写法一致
type AddGoodsRequest struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Parame *GoodsModel `protobuf:"bytes,1,opt,name=parame,proto3" json:"parame,omitempty"`
}

func (x *AddGoodsRequest) Reset() {
    *x = AddGoodsRequest{}
    if protoimpl.UnsafeEnabled {
        mi := &file_proto_goodsinfo_proto_msgTypes[1]
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        ms.StoreMessageInfo(mi)
    }
}

func (x *AddGoodsRequest) String() string {
    return protoimpl.X.MessageStringOf(x)
}

func (*AddGoodsRequest) ProtoMessage() {}

func (x *AddGoodsRequest) ProtoReflect() protoreflect.Message {
    mi := &file_proto_goodsinfo_proto_msgTypes[1]
    if protoimpl.UnsafeEnabled && x != nil {
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        if ms.LoadMessageInfo() == nil {
            ms.StoreMessageInfo(mi)
        }
        return ms
    }
    return mi.MessageOf(x)
}

// Deprecated: Use AddGoodsRequest.ProtoReflect.Descriptor instead.
func (*AddGoodsRequest) Descriptor() ([]byte, []int) {
    return file_proto_goodsinfo_proto_rawDescGZIP(), []int{1}
}

func (x *AddGoodsRequest) GetParame() *GoodsModel {
    if x != nil {
        return x.Parame
    }
    return nil
}

// 增加商品响应:和gRPC中的写法一致
type AddGoodsResponse struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
    Success bool   `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"`
}

func (x *AddGoodsResponse) Reset() {
    *x = AddGoodsResponse{}
    if protoimpl.UnsafeEnabled {
        mi := &file_proto_goodsinfo_proto_msgTypes[2]
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        ms.StoreMessageInfo(mi)
    }
}

func (x *AddGoodsResponse) String() string {
    return protoimpl.X.MessageStringOf(x)
}

func (*AddGoodsResponse) ProtoMessage() {}

func (x *AddGoodsResponse) ProtoReflect() protoreflect.Message {
    mi := &file_proto_goodsinfo_proto_msgTypes[2]
    if protoimpl.UnsafeEnabled && x != nil {
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        if ms.LoadMessageInfo() == nil {
            ms.StoreMessageInfo(mi)
        }
        return ms
    }
    return mi.MessageOf(x)
}

// Deprecated: Use AddGoodsResponse.ProtoReflect.Descriptor instead.
func (*AddGoodsResponse) Descriptor() ([]byte, []int) {
    return file_proto_goodsinfo_proto_rawDescGZIP(), []int{2}
}

func (x *AddGoodsResponse) GetMessage() string {
    if x != nil {
        return x.Message
    }
    return ""
}

func (x *AddGoodsResponse) GetSuccess() bool {
    if x != nil {
        return x.Success
    }
    return false
}

// 获取商品请求
type GetGoodsRequest struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields
}

func (x *GetGoodsRequest) Reset() {
    *x = GetGoodsRequest{}
    if protoimpl.UnsafeEnabled {
        mi := &file_proto_goodsinfo_proto_msgTypes[3]
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        ms.StoreMessageInfo(mi)
    }
}

func (x *GetGoodsRequest) String() string {
    return protoimpl.X.MessageStringOf(x)
}

func (*GetGoodsRequest) ProtoMessage() {}

func (x *GetGoodsRequest) ProtoReflect() protoreflect.Message {
    mi := &file_proto_goodsinfo_proto_msgTypes[3]
    if protoimpl.UnsafeEnabled && x != nil {
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        if ms.LoadMessageInfo() == nil {
            ms.StoreMessageInfo(mi)
        }
        return ms
    }
    return mi.MessageOf(x)
}

// Deprecated: Use GetGoodsRequest.ProtoReflect.Descriptor instead.
func (*GetGoodsRequest) Descriptor() ([]byte, []int) {
    return file_proto_goodsinfo_proto_rawDescGZIP(), []int{3}
}

// 获取商品响应
type GetGoodsResponse struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    GoodsList []*GoodsModel `protobuf:"bytes,1,rep,name=GoodsList,proto3" json:"GoodsList,omitempty"`
}

func (x *GetGoodsResponse) Reset() {
    *x = GetGoodsResponse{}
    if protoimpl.UnsafeEnabled {
        mi := &file_proto_goodsinfo_proto_msgTypes[4]
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        ms.StoreMessageInfo(mi)
    }
}

func (x *GetGoodsResponse) String() string {
    return protoimpl.X.MessageStringOf(x)
}

func (*GetGoodsResponse) ProtoMessage() {}

func (x *GetGoodsResponse) ProtoReflect() protoreflect.Message {
    mi := &file_proto_goodsinfo_proto_msgTypes[4]
    if protoimpl.UnsafeEnabled && x != nil {
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        if ms.LoadMessageInfo() == nil {
            ms.StoreMessageInfo(mi)
        }
        return ms
    }
    return mi.MessageOf(x)
}

// Deprecated: Use GetGoodsResponse.ProtoReflect.Descriptor instead.
func (*GetGoodsResponse) Descriptor() ([]byte, []int) {
    return file_proto_goodsinfo_proto_rawDescGZIP(), []int{4}
}

func (x *GetGoodsResponse) GetGoodsList() []*GoodsModel {
    if x != nil {
        return x.GoodsList
    }
    return nil
}

var File_proto_goodsinfo_proto protoreflect.FileDescriptor

var file_proto_goodsinfo_proto_rawDesc = []byte{
    0x0a, 0x15, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x69, 0x6e, 0x66,
    0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x69, 0x6e,
    0x66, 0x6f, 0x22, 0x52, 0x0a, 0x0a, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x4d, 0x6f, 0x64, 0x65, 0x6c,
    0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
    0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18,
    0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07,
    0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63,
    0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x40, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f,
    0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x70, 0x61, 0x72,
    0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x6f, 0x6f, 0x64,
    0x73, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x4d, 0x6f, 0x64, 0x65, 0x6c,
    0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x22, 0x46, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x47,
    0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07,
    0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d,
    0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73,
    0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
    0x22, 0x11, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75,
    0x65, 0x73, 0x74, 0x22, 0x47, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52,
    0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x09, 0x47, 0x6f, 0x6f, 0x64, 0x73,
    0x4c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x6f, 0x6f,
    0x64, 0x73, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x4d, 0x6f, 0x64, 0x65,
    0x6c, 0x52, 0x09, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x32, 0x99, 0x01, 0x0a,
    0x09, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x45, 0x0a, 0x08, 0x41, 0x64,
    0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x12, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x69, 0x6e,
    0x66, 0x6f, 0x2e, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
    0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x41,
    0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
    0x00, 0x12, 0x45, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x12, 0x1a, 0x2e,
    0x67, 0x6f, 0x6f, 0x64, 0x73, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f,
    0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x64,
    0x73, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65,
    0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x13, 0x5a, 0x11, 0x2e, 0x2f, 0x70, 0x72,
    0x6f, 0x74, 0x6f, 0x3b, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x69, 0x6e, 0x66, 0x6f, 0x62, 0x06, 0x70,
    0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
    file_proto_goodsinfo_proto_rawDescOnce sync.Once
    file_proto_goodsinfo_proto_rawDescData = file_proto_goodsinfo_proto_rawDesc
)

func file_proto_goodsinfo_proto_rawDescGZIP() []byte {
    file_proto_goodsinfo_proto_rawDescOnce.Do(func() {
        file_proto_goodsinfo_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_goodsinfo_proto_rawDescData)
    })
    return file_proto_goodsinfo_proto_rawDescData
}

var file_proto_goodsinfo_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_proto_goodsinfo_proto_goTypes = []interface{}{
    (*GoodsModel)(nil),       // 0: goodsinfo.GoodsModel
    (*AddGoodsRequest)(nil),  // 1: goodsinfo.AddGoodsRequest
    (*AddGoodsResponse)(nil), // 2: goodsinfo.AddGoodsResponse
    (*GetGoodsRequest)(nil),  // 3: goodsinfo.GetGoodsRequest
    (*GetGoodsResponse)(nil), // 4: goodsinfo.GetGoodsResponse
}
var file_proto_goodsinfo_proto_depIdxs = []int32{
    0, // 0: goodsinfo.AddGoodsRequest.parame:type_name -> goodsinfo.GoodsModel
    0, // 1: goodsinfo.GetGoodsResponse.GoodsList:type_name -> goodsinfo.GoodsModel
    1, // 2: goodsinfo.Goodsinfo.AddGoods:input_type -> goodsinfo.AddGoodsRequest
    3, // 3: goodsinfo.Goodsinfo.GetGoods:input_type -> goodsinfo.GetGoodsRequest
    2, // 4: goodsinfo.Goodsinfo.AddGoods:output_type -> goodsinfo.AddGoodsResponse
    4, // 5: goodsinfo.Goodsinfo.GetGoods:output_type -> goodsinfo.GetGoodsResponse
    4, // [4:6] is the sub-list for method output_type
    2, // [2:4] is the sub-list for method input_type
    2, // [2:2] is the sub-list for extension type_name
    2, // [2:2] is the sub-list for extension extendee
    0, // [0:2] is the sub-list for field type_name
}

func init() { file_proto_goodsinfo_proto_init() }
func file_proto_goodsinfo_proto_init() {
    if File_proto_goodsinfo_proto != nil {
        return
    }
    if !protoimpl.UnsafeEnabled {
        file_proto_goodsinfo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
            switch v := v.(*GoodsModel); i {
            case 0:
                return &v.state
            case 1:
                return &v.sizeCache
            case 2:
                return &v.unknownFields
            default:
                return nil
            }
        }
        file_proto_goodsinfo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
            switch v := v.(*AddGoodsRequest); i {
            case 0:
                return &v.state
            case 1:
                return &v.sizeCache
            case 2:
                return &v.unknownFields
            default:
                return nil
            }
        }
        file_proto_goodsinfo_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
            switch v := v.(*AddGoodsResponse); i {
            case 0:
                return &v.state
            case 1:
                return &v.sizeCache
            case 2:
                return &v.unknownFields
            default:
                return nil
            }
        }
        file_proto_goodsinfo_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
            switch v := v.(*GetGoodsRequest); i {
            case 0:
                return &v.state
            case 1:
                return &v.sizeCache
            case 2:
                return &v.unknownFields
            default:
                return nil
            }
        }
        file_proto_goodsinfo_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
            switch v := v.(*GetGoodsResponse); i {
            case 0:
                return &v.state
            case 1:
                return &v.sizeCache
            case 2:
                return &v.unknownFields
            default:
                return nil
            }
        }
    }
    type x struct{}
    out := protoimpl.TypeBuilder{
        File: protoimpl.DescBuilder{
            GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
            RawDescriptor: file_proto_goodsinfo_proto_rawDesc,
            NumEnums:      0,
            NumMessages:   5,
            NumExtensions: 0,
            NumServices:   1,
        },
        GoTypes:           file_proto_goodsinfo_proto_goTypes,
        DependencyIndexes: file_proto_goodsinfo_proto_depIdxs,
        MessageInfos:      file_proto_goodsinfo_proto_msgTypes,
    }.Build()
    File_proto_goodsinfo_proto = out.File
    file_proto_goodsinfo_proto_rawDesc = nil
    file_proto_goodsinfo_proto_goTypes = nil
    file_proto_goodsinfo_proto_depIdxs = nil
}

The code of goodsinfo.pb.micro.go is as follows:

// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: proto/goodsinfo.proto

package goodsinfo

import (
    fmt "fmt"
    proto "google.golang.org/protobuf/proto"
    math "math"
)

import (
    context "context"
    api "go-micro.dev/v4/api"
    client "go-micro.dev/v4/client"
    server "go-micro.dev/v4/server"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option

// Api Endpoints for Goodsinfo service

func NewGoodsinfoEndpoints() []*api.Endpoint {
    return []*api.Endpoint{}
}

// Client API for Goodsinfo service

type GoodsinfoService interface {
    // AddGoods: 定义增加商品的微服务, 这里的写法和gRPC中的写法一致
    AddGoods(ctx context.Context, in *AddGoodsRequest, opts ...client.CallOption) (*AddGoodsResponse, error)
    GetGoods(ctx context.Context, in *GetGoodsRequest, opts ...client.CallOption) (*GetGoodsResponse, error)
}

type goodsinfoService struct {
    c    client.Client
    name string
}

func NewGoodsinfoService(name string, c client.Client) GoodsinfoService {
    return &goodsinfoService{
        c:    c,
        name: name,
    }
}

func (c *goodsinfoService) AddGoods(ctx context.Context, in *AddGoodsRequest, opts ...client.CallOption) (*AddGoodsResponse, error) {
    req := c.c.NewRequest(c.name, "Goodsinfo.AddGoods", in)
    out := new(AddGoodsResponse)
    err := c.c.Call(ctx, req, out, opts...)
    if err != nil {
        return nil, err
    }
    return out, nil
}

func (c *goodsinfoService) GetGoods(ctx context.Context, in *GetGoodsRequest, opts ...client.CallOption) (*GetGoodsResponse, error) {
    req := c.c.NewRequest(c.name, "Goodsinfo.GetGoods", in)
    out := new(GetGoodsResponse)
    err := c.c.Call(ctx, req, out, opts...)
    if err != nil {
        return nil, err
    }
    return out, nil
}

// Server API for Goodsinfo service

type GoodsinfoHandler interface {
    // AddGoods: 定义增加商品的微服务, 这里的写法和gRPC中的写法一致
    AddGoods(context.Context, *AddGoodsRequest, *AddGoodsResponse) error
    GetGoods(context.Context, *GetGoodsRequest, *GetGoodsResponse) error
}

func RegisterGoodsinfoHandler(s server.Server, hdlr GoodsinfoHandler, opts ...server.HandlerOption) error {
    type goodsinfo interface {
        AddGoods(ctx context.Context, in *AddGoodsRequest, out *AddGoodsResponse) error
        GetGoods(ctx context.Context, in *GetGoodsRequest, out *GetGoodsResponse) error
    }
    type Goodsinfo struct {
        goodsinfo
    }
    h := &goodsinfoHandler{hdlr}
    return s.Handle(s.NewHandler(&Goodsinfo{h}, opts...))
}

type goodsinfoHandler struct {
    GoodsinfoHandler
}

func (h *goodsinfoHandler) AddGoods(ctx context.Context, in *AddGoodsRequest, out *AddGoodsResponse) error {
    return h.GoodsinfoHandler.AddGoods(ctx, in, out)
}

func (h *goodsinfoHandler) GetGoods(ctx context.Context, in *GetGoodsRequest, out *GetGoodsResponse) error {
    return h.GoodsinfoHandler.GetGoods(ctx, in, out)
}

Start the goodsinfo server:

After startup, the consul UI is displayed as follows:

See that the goodsinfo service is registered

Goodsinfo details interface :

2. Create a microservice client using the Go Web framework Gin

  1. Build the Gin framework

The microservice in the previous section is directly called in main.go. Here, the Go Web framework Gin is used to create the microservice client.
The Gin framework needs to be built by yourself. Here, refer to [golang gin framework] 1. Gin environment construction, program Hot loading, routing content in GET, POST, PUT, DELETE for development

After building, import related packages:

Test whether the Gin framework is ok:

  1. Integrate microservice client-proto related in Gin framework

Assign the proto folder of the above microservice server to the project root directory, as follows:
Then check the relevant packages to see if they are imported. If not, run in the directory where the project main.go is located: go mod tidy
  1. Integrate microservice clients in the Gin framework

Still take the above goodsinfo microservice as a case, integrate the goodsinfo microservice client

(1). Encapsulate the service of creating consul

Refer to the goodsinfo client code in the previous section

The client code in the previous section is as follows:

package main

import (
    "context"
    "go-micro.dev/v4/registry"
    pb "goodsinfo-client/proto"
    "go-micro.dev/v4"
    "go-micro.dev/v4/logger"
    "github.com/go-micro/plugins/v4/registry/consul"
)

var (
    service = "goodsinfo"
    version = "latest"
)

func main() {
    //集成consul
    consulReg := consul.NewRegistry(
        //指定微服务的ip:  选择注册服务器地址,默认为本机,也可以选择consul集群中的client
        registry.Addrs("127.0.0.1:8500"),
        )
    // Create service
    srv := micro.NewService(
        //注册consul
        micro.Registry(consulReg),
    )

    srv.Init()

    // 创建客户端服务
    c := pb.NewGoodsinfoService(service, srv.Client())

    // Call service
    rsp, err := c.AddGoods(context.Background(), &pb.AddRequest{
        Title: "我是一个商品",
        Price: "20.22",
        Content: "内容展示",
    })
    if err != nil {
        logger.Fatal(err)
    }

    logger.Info(rsp)
}
上述代码分为两部分:第一部分是 集成consul, 第二部分是 创建客户端服务, 当一个项目中存在 多个微服务 时, 都要 使用到第一部分,所以第一部分是 公共的 ,可以把它 封装 一下,然后在各个微服务客户端中调用
在项目的models下创建initGoodsMicro.go,封装集成consul相关逻辑,代码如下:
package models

//微服务客户端配置: 初始化consul配置,当一个项目中多个微服务时,就很方便了
//建议:一个微服务对应一个客户端,这样好管理

import (
    "github.com/go-micro/plugins/v4/registry/consul"
    "go-micro.dev/v4"
    "go-micro.dev/v4/client"
    "go-micro.dev/v4/registry"
)

//MicroClient: 全局变量 在外部的包中可以调用
var MicroClient client.Client

//init 方法: 当程序运行时就会自动执行
func init() {
    consulRegistry := consul.NewRegistry(
        //指定微服务的ip:  选择注册服务器地址,默认为本机,也可以选择consul集群中的client,建议一个微服务对应一个consul集群的client
        registry.Addrs("127.0.0.1:8500"),
    )
    // Create service
    srv := micro.NewService(
        micro.Registry(consulRegistry),
    )
    srv.Init()

    MicroClient = srv.Client()
}

(2).创建Goods相关方法

在controllers/frontend下创建GoodsController,里面有两个方法:Index(获取商品列表),Add(添加商品),代码如下:
package frontend

import (
    "context"
    "gindemo/models"
    pb "gindemo/proto"
    "github.com/gin-gonic/gin"
    log "go-micro.dev/v4/logger"
)

type GoodsController struct{}

//获取商品列表
func (con GoodsController) Index(c *gin.Context) {
    // Create client: 这里的服务名称需要和服务端注册的名称一致
    microClient := pb.NewGoodsinfoService("goodsinfo", models.MicroClient)
    // Call service
    rsp, err := microClient.GetGoods(context.Background(), &pb.GetGoodsRequest{})
    //判断是否获取商品成功: 这里会调用服务端handler/goodsinfo.go中的方法GetGoods()
    if err != nil {
        log.Fatal(err)
    }
    //记录log
    log.Info(rsp)
    //返回
    c.JSON(200, gin.H{
        "result": rsp.GoodsList,
    })
}

//添加商品
func (con GoodsController) Add(c *gin.Context) {
    // Create client
    microClient := pb.NewGoodsinfoService("goodsinfo", models.MicroClient)
    // Call service
    rsp, err := microClient.AddGoods(context.Background(), &pb.AddGoodsRequest{
        Parame: &pb.GoodsModel{
            Title:   "我是一个商品",  //这里的商品数据是模拟数据, 一般项目中是从前端获取
            Price:   12.0,
            Content: "我是一个内容",
        },
    })
    //判断是否增加成功
    if err != nil {
        log.Fatal(err)
    }
    //记录log
    log.Info(rsp)
    //返回
    c.JSON(200, gin.H{
        "message": rsp.Message,
        "success": rsp.Success,
    })
}

(3).在routers/frontendRouters.go中增加路由

package routers

import (
    "gindemo/controllers/frontend"
    "github.com/gin-gonic/gin"
)

func DefaultRoutersInit(r *gin.Engine) {
    defaultRouters := r.Group("/")
    {
        defaultRouters.GET("/", frontend.DefaultController{}.Index)
        //获取商品列表
        defaultRouters.GET("/goods", frontend.GoodsController{}.Index)
        //添加商品
        defaultRouters.GET("/goods/add", frontend.GoodsController{}.Add)
    }
}

(4).查看微服务客户端是否成功

浏览器中访问 http://localhost:8080/goods,http://localhost:8080/goods/add看看是否返回数据
返回数据成功,说明微服务客户端搭建成功

三.go-micro的负载均衡在项目中的使用

  1. 把goodsinfo微服务放置到另一台服务器上

复制server/goodsinfo微服务服务端代码,把代码放置到 另一台服务器上,然后运行go run main.go把goodsinfo微服务注册到 consul集群中对应的 服务发现里面就可以了,为了区分是来自不同服务器的同一个微服务,这里在handler/goodsinfo.go修改一下,返回的Title进行一个标识,表示调用的是不同服务器上的微服务,达到 负载均衡的效果,代码如下:
package handler

import (
    "context"
    "strconv"
    "go-micro.dev/v4/logger"
    pb "goodsinfo/proto"
)

type Goodsinfo struct{}

//增加商品
func (e *Goodsinfo) AddGoods(ctx context.Context, req *pb.AddGoodsRequest, rsp *pb.AddGoodsResponse) error {
    logger.Infof("request: %v", req)
    //书写返回的逻辑结果
    rsp.Message = "增加成功"
    rsp.Success = true
    return nil
}

//获取商品
func (e *Goodsinfo) GetGoods(ctx context.Context, req *pb.GetGoodsRequest, rsp *pb.GetGoodsResponse) error {
    var tempList []*pb.GoodsModel
    //模拟从数据库中获取商品数据
    for i := 0; i < 10; i++ {
        tempList = append(tempList, &pb.GoodsModel{
            Title:   "goodsinfo1第" + strconv.Itoa(i) + "条数据",  // goodsinfo1或者goodsinfo2代表不同服务器的同一个微服务
            Price:   float64((i + 1) * 2),
            Content: "goodsinfo1第" + strconv.Itoa(i) + "内容",
        })
    }
    rsp.GoodsList = tempList
    return nil
}

main.go中代码:

修改 micro.Address("127.0.0.1:8080"), 这里面的ip表示不同consul集群服务器ip,在这里模拟不同服务器负载均衡,把 micro.Address("127.0.0.1:8080")修改为 micro.Address("127.0.0.1:8081"),表示在不同服务器上的同一个微服务
package main

import (
    "goodsinfo/handler"
    pb "goodsinfo/proto"
    "go-micro.dev/v4"
    "go-micro.dev/v4/logger"
    "github.com/go-micro/plugins/v4/registry/consul"
)

var (
    service = "goodsinfo"
    version = "latest"
)

func main() {
    //集成consul
    consulReg := consul.NewRegistry()
    // Create service
    srv := micro.NewService(
        micro.Address("127.0.0.1:8081"),  //指定微服务的ip:  选择注册服务器地址,也可以不配置,默认为本机,也可以选择consul集群中的client
        micro.Name(service),
        micro.Version(version),
        //注册consul
        micro.Registry(consulReg),
    )
    srv.Init(
        micro.Name(service),
        micro.Version(version),
    )

    // Register handler
    if err := pb.RegisterGoodsinfoHandler(srv.Server(), new(handler.Goodsinfo)); err != nil {
        logger.Fatal(err)
    }
    // Run service
    if err := srv.Run(); err != nil {
        logger.Fatal(err)
    }
}
  1. 启动goodsinfo微服务

启动不同服务器上的goodsinfo微服务
  1. consul UI上查看效果

说明微服务启动成功了
  1. 运行客户端代码

运行项目代码: go run main.go,访问http://localhost:8080/goods,这时客户端就会通过轮询的方式查询consul服务器的goodsinfo服务端微服务,可以通过刷新的方式来检测,看看是否轮询查询
通过上面的方式检测,发现客户端获取数据是通过轮询的方式从consul的不同微服务服务器上获取数据的,负载均衡操作就完成了

四.Go Web框架(Beego)调用go-micro微服务

  1. 新建beego项目

在新建beego目录之前,需要按照go以及配置其环境变量,见 [go学习笔记.第二章] 2.go语言的开发工具以及安装和配置SDK, 以及bee 工具,bee工具的安装见 Beego之Bee安装(Windows)以及创建,运行项目
在client目录下创建beego项目,使用命令 bee new beegodemo创建一个beego项目,如下图:

生成的项目目录:

  1. 初始化项目

先删除go.mod文件,然后通过 go mod init beegodemo, go mod tidy 引入包,重新初始化项目
  1. 测试项目是否正常

(1).在controllers下新建goods.go

代码如下:
package controllers

//商品相关

import (
    beego "github.com/beego/beego/v2/server/web"
)

type GoodsController struct {
    beego.Controller
}

func (c *GoodsController) Get() {
    c.Data["json"] =map[string]interface{} {
        "message": "get",
    }

    c.ServeJSON()
}

func (c *GoodsController) Add() {
    c.Data["json"] =map[string]interface{} {
        "message": "add",
    }

    c.ServeJSON()
}

(2).设置路由

在routers/routers.go中完善控制器goods.go中的路由方法,代码如下:
package routers

import (
    "beegodemo/controllers"
    beego "github.com/beego/beego/v2/server/web"
)

func init() {
    beego.Router("/", &controllers.MainController{})

    //goods相关路由
    beego.Router("/goods", &controllers.GoodsController{})
    beego.Router("/goods/add", &controllers.GoodsController{}, "get:Add") //这里测试使用get方式,正式项目使用restful模式操作
}

(3).运行

在项目目录下,运行 bee run,然后浏览器中访问:http://127.0.0.1:8080/goods,看看是否正常显示相关数据
项目运行正常
  1. 搭建goodsinfo商品微服务beego客户端

(1).引入proto文件

把上面gin项目中的 proto文件复制到beegodemo项目下

(2).引入initGoodsMicro.go

把上面gin项目中的models/ initGoodsMicro.go文件复制到beegodemo项目models下

(3).编写controllers/goods.go代码

完善goods.go下的方法:商品列表方法Get(), 增加商品方法Add(),代码如下:
package controllers

//商品相关

import (
    "context"
    "beegodemo/models"
    pb "beegodemo/proto"
    log "go-micro.dev/v4/logger"
    beego "github.com/beego/beego/v2/server/web"

)

type GoodsController struct {
    beego.Controller
}

//商品列表
func (c *GoodsController) Get() {
    // Create client: 这里的服务名称需要和服务端注册的名称一致
    microClient := pb.NewGoodsinfoService("goodsinfo", models.MicroClient)
    // Call service
    rsp, err := microClient.GetGoods(context.Background(), &pb.GetGoodsRequest{})
    //判断是否获取商品成功: 这里会调用服务端handler/goodsinfo.go中的方法GetGoods()
    if err != nil {
        log.Fatal(err)
    }
    //记录log
    log.Info(rsp)
    //返回
    c.Data["json"] =map[string]interface{} {
        "result": rsp.GoodsList,
    }

    c.ServeJSON()
}

//添加商品
func (c *GoodsController) Add() {
    // Create client
    microClient := pb.NewGoodsinfoService("goodsinfo", models.MicroClient)
    // Call service
    rsp, err := microClient.AddGoods(context.Background(), &pb.AddGoodsRequest{
        Parame: &pb.GoodsModel{
            Title:   "增加一个商品beego",  //这里的商品数据是模拟数据, 一般项目中是从前端获取
            Price:   12.0,
            Content: "我是一个内容beego",
        },
    })
    //判断是否增加成功
    if err != nil {
        log.Fatal(err)
    }
    //记录log
    log.Info(rsp)
    //返回
    c.Data["json"] =map[string]interface{} {
        "message": rsp.Message,
        "success": rsp.Success,
    }
    c.ServeJSON()
}

(4).引入项目所需的包

再次执行命令 go mod tidy引入项目需要的包,在这里可以会出现不能引入包的情况,则需要 手动执行,命令为 go get xxx,也可以参考 https://blog.csdn.net/zhoupenghui168/article/details/131252892#t9

(5).启动微服务服务端

参考第三步骤: 三.go-micro的 负载均衡在项目中的使用
查看consul UI,看看goodsinfo微服务是否启动成功

(6).查看是否能够访问,是否可以负载均衡操作

1).先运行bee run,启动项目

2).访问

访问http://127.0.0.1:8080/goods,以及http://127.0.0.1:8080/goods/add,看看是否成功

刷新页面,查看:

好了,Beego框架调用go-micro微服务功能完成
  1. 总结

Beego框架调用go-micro微服务其实和Gin框架调用go-micro微服务差不多,主要的区别就是需要下载bee工具,以及要引入beego框架所需要的包,然后就是框架的路由操作,返回操作略有不同

[上一节][golang 微服务] 7. go-micro框架介绍,go-micro脚手架,go-micro结合consul搭建微服务案例

[下一节][golang 微服务] 9.go-micro + gorm实现商品微服务的分页查询

Guess you like

Origin blog.csdn.net/zhoupenghui168/article/details/131385755