需要先启动consul, consol相关内容见
[golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群
[golang 微服务] 6. GRPC微服务集群+Consul集群+grpc-consul-resolver案例演示
启动consul命令,这里,使用dev模式: consul agent -dev,启动如下:
启动后UI界面:
这里使用 上一节的goodsinfo微服务,见上一节 [golang 微服务] 7. go-micro框架介绍,go-micro脚手架,go-micro结合consul搭建微服务案例
这里对 .proto进行优化: 统一商品模型,以及增加获取商品相关请求以及响应代码
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;
}
在goodsinfo下执行命令:
protoc --proto_path=. --micro_out=. --go_out=:. proto/goodsinfo.proto
生成对应的.pb.go,.micro.go文件,然后在handler/goodsinfo.go中完善对应的方法:
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
}
goodsinfo.pb.go代码如下:
// 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
}
// 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)
}
看到 注册了goodsinfo服务
上一节的微服务是直接在 main.go中调用的,这里使用使用 Go Web框架Gin来 创建微服务客户端,
Gin框架需要自己搭建,这里参考 [golang gin框架] 1.Gin环境搭建,程序的热加载,路由GET,POST,PUT,DELETE 里面的内容来进行开发
搭建好了后,引入相关包:
测试Gin框架是否ok:
把上面微服务服务端的proto文件夹赋值到项目根目录,如下:
然后查看相关包,看看是否引入,没有引入的话,在项目main.go所在的目录运行: go mod tidy 即可
还是以上面goodsinfo微服务为案例,集成goodsinfo微服务客户端
参考上一节 goodsinfo客户端代码
上一节客户端代码如下:
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()
}
在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,
})
}
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看看是否返回数据
返回数据成功,说明微服务客户端搭建成功
复制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)
}
}
启动不同服务器上的goodsinfo微服务
说明微服务启动成功了
运行项目代码: go run main.go,访问http://localhost:8080/goods,这时客户端就会通过轮询的方式查询consul服务器的goodsinfo服务端微服务,可以通过刷新的方式来检测,看看是否轮询查询
通过上面的方式检测,发现客户端获取数据是通过轮询的方式从consul的不同微服务服务器上获取数据的,负载均衡操作就完成了
在新建beego目录之前,需要按照go以及配置其环境变量,见 [go学习笔记.第二章] 2.go语言的开发工具以及安装和配置SDK, 以及bee 工具,bee工具的安装见 Beego之Bee安装(Windows)以及创建,运行项目
在client目录下创建beego项目,使用命令 bee new beegodemo创建一个beego项目,如下图:
生成的项目目录:
先删除go.mod文件,然后通过 go mod init beegodemo, go mod tidy 引入包,重新初始化项目
代码如下:
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()
}
在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模式操作
}
在项目目录下,运行 bee run,然后浏览器中访问:http://127.0.0.1:8080/goods,看看是否正常显示相关数据
项目运行正常
把上面gin项目中的 proto文件复制到beegodemo项目下
把上面gin项目中的models/ initGoodsMicro.go文件复制到beegodemo项目models下
完善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()
}
再次执行命令 go mod tidy引入项目需要的包,在这里可以会出现不能引入包的情况,则需要 手动执行,命令为 go get xxx,也可以参考 https://blog.csdn.net/zhoupenghui168/article/details/131252892#t9
参考第三步骤: 三.go-micro的 负载均衡在项目中的使用
查看consul UI,看看goodsinfo微服务是否启动成功
访问http://127.0.0.1:8080/goods,以及http://127.0.0.1:8080/goods/add,看看是否成功
刷新页面,查看:
好了,Beego框架调用go-micro微服务功能完成
Beego框架调用go-micro微服务其实和Gin框架调用go-micro微服务差不多,主要的区别就是需要下载bee工具,以及要引入beego框架所需要的包,然后就是框架的路由操作,返回操作略有不同
[上一节][golang 微服务] 7. go-micro框架介绍,go-micro脚手架,go-micro结合consul搭建微服务案例
[下一节][golang 微服务] 9.go-micro + gorm实现商品微服务的分页查询