Golang之微服务

  软件开发需要分模块,通常要按逻辑进行化分,将代码组织到不同的包里,编译为dll或全部集成到exe,以单进程的方式运行;多个项目公用的代码,直接引用dll或以公共代码库的方式引入。但是,如果项目非常多,每个项目都要去考虑这些公共的功能,将依赖的包导入到项目,还是要直接面对这些代码,编译时间也非常长。为此,将模块进行物理划分,子模块直接部署为独立的进程,进程间采用http、rpc等方式通信,通信内容用json或proto进行编码。新项目启动,直接将通用子模块进行独立部署(或微调后部署),投入全部精力到新的业务中,有效控制新项目的代码量。通过接口使模块间解耦。

  golang提供了go-micro库,实现了对微服务的支持。微服务提供如下优势:

1、服务全局注册,客户端用服务名去发现服务

2、注册多个同类服务,可以实现均衡负载

3、传递的消息进行编解码,精简通信量

4、RPC双向同步通信

5、采用订阅、发布模型,实现异步通信,提供基于事件驱动的架构

6、面向插件开发,易于扩展

 

go-micro安装过程:

1、下载protoc-3.10.1-win64.zip(https://github.com/google/protobuf/releases),提取protoc.exe,拷贝到GOROOT/BIN目录

2、安装protoc-gen-go

 go get github.com/golang/protobuf/protoc-gen-go

3、安装protoc-gen-micro 

go get github.com/micro/protoc-gen-micro

4、安装go-micro

go get -u github.com/micro/go-micro

如果无法科学上网,下载过程报错,请到百度网盘下载已经提取的依赖包(https://pan.baidu.com/s/1chcQaKGX5QW4L61gyVrbRw 提取码f9sc),解压后拷贝到$GOROOT/src目录下,编译过程中如果提示无法中到包,则查看拷贝过来的包,去掉目录包名称中的版本号。

 

环境配置完毕,测试过程参照:

https://micro.mu/docs/framework.html

https://www.cnblogs.com/linguoguo/p/10138715.html

 grpc测试过程简述:

1、创建proto文件,描述接口:

syntax = "proto3";

option objc_class_prefix = "HLW";

package main;

service Greeter{
    rpc SayHello (HelloRequest) returns (HelloReply){}
}

message HelloRequest{
    string name = 1;
}

message HelloReply{
    string message = 1;
}

2、使用protoc.exe将接口描述文件转换为go源码,给服务端和客户端调用

protoc --go_out=plugins=grpc:. helloworld.proto

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: helloworld.proto

package Intf

import (
	context "context"
	fmt "fmt"
	proto "github.com/golang/protobuf/proto"
	grpc "google.golang.org/grpc"
	codes "google.golang.org/grpc/codes"
	status "google.golang.org/grpc/status"
	math "math"
)

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

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package

type HelloRequest struct {
	Name                 string   `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *HelloRequest) Reset()         { *m = HelloRequest{} }
func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
func (*HelloRequest) ProtoMessage()    {}
func (*HelloRequest) Descriptor() ([]byte, []int) {
	return fileDescriptor_17b8c58d586b62f2, []int{0}
}

func (m *HelloRequest) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_HelloRequest.Unmarshal(m, b)
}
func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic)
}
func (m *HelloRequest) XXX_Merge(src proto.Message) {
	xxx_messageInfo_HelloRequest.Merge(m, src)
}
func (m *HelloRequest) XXX_Size() int {
	return xxx_messageInfo_HelloRequest.Size(m)
}
func (m *HelloRequest) XXX_DiscardUnknown() {
	xxx_messageInfo_HelloRequest.DiscardUnknown(m)
}

var xxx_messageInfo_HelloRequest proto.InternalMessageInfo

func (m *HelloRequest) GetName() string {
	if m != nil {
		return m.Name
	}
	return ""
}

type HelloReply struct {
	Message              string   `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *HelloReply) Reset()         { *m = HelloReply{} }
func (m *HelloReply) String() string { return proto.CompactTextString(m) }
func (*HelloReply) ProtoMessage()    {}
func (*HelloReply) Descriptor() ([]byte, []int) {
	return fileDescriptor_17b8c58d586b62f2, []int{1}
}

func (m *HelloReply) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_HelloReply.Unmarshal(m, b)
}
func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic)
}
func (m *HelloReply) XXX_Merge(src proto.Message) {
	xxx_messageInfo_HelloReply.Merge(m, src)
}
func (m *HelloReply) XXX_Size() int {
	return xxx_messageInfo_HelloReply.Size(m)
}
func (m *HelloReply) XXX_DiscardUnknown() {
	xxx_messageInfo_HelloReply.DiscardUnknown(m)
}

var xxx_messageInfo_HelloReply proto.InternalMessageInfo

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

func init() {
	proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest")
	proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply")
}

func init() { proto.RegisterFile("helloworld.proto", fileDescriptor_17b8c58d586b62f2) }

var fileDescriptor_17b8c58d586b62f2 = []byte{
	// 149 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9,
	0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88,
	0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42,
	0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92,
	0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71,
	0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a,
	0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64,
	0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x14, 0xe4, 0x54, 0x2a, 0x31, 0x38, 0xb1, 0x2d, 0x62, 0x62, 0xf6,
	0xf0, 0x09, 0x4f, 0x62, 0x03, 0xbb, 0xd8, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xbe, 0xde, 0x1d,
	0x2e, 0xc5, 0x00, 0x00, 0x00,
}

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn

// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4

// GreeterClient is the client API for Greeter service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type GreeterClient interface {
	SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}

type greeterClient struct {
	cc *grpc.ClientConn
}

func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
	return &greeterClient{cc}
}

func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
	out := new(HelloReply)
	err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// GreeterServer is the server API for Greeter service.
type GreeterServer interface {
	SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}

// UnimplementedGreeterServer can be embedded to have forward compatible implementations.
type UnimplementedGreeterServer struct {
}

func (*UnimplementedGreeterServer) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) {
	return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
}

func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
	s.RegisterService(&_Greeter_serviceDesc, srv)
}

func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(HelloRequest)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(GreeterServer).SayHello(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/helloworld.Greeter/SayHello",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
	}
	return interceptor(ctx, in, info, handler)
}

var _Greeter_serviceDesc = grpc.ServiceDesc{
	ServiceName: "helloworld.Greeter",
	HandlerType: (*GreeterServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "SayHello",
			Handler:    _Greeter_SayHello_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "helloworld.proto",
}

3、服务端:

package main

import(
	"context"
	"google.golang.org/grpc"
	"log"
	"net"
)

const (
	PORT = ":50001"
)

type server struct {}

func (s *server) SayHello(ctx context.Context, in *HelloRequest) (*HelloReply, error)  {
	log.Println("request:", in.Name)
	return &HelloReply{Message:"Hello " + in.Name}, nil
}

func main(){
	lis, err := net.Listen("tcp", PORT)
	if err != nil{
		log.Fatalf("failed to listen:%v", err)
	}

	s := grpc.NewServer()
	RegisterGreeterServer(s, &server{})
	log.Print("rpc服务已经启动")
	s.Serve(lis)
}

4、客户端:

package main

import (
	"../Intf"
	"context"
	"fmt"
	"google.golang.org/grpc"
	"log"
)

const (
    //还需要指定服务地址
	address = "localhost:50001"
)

func main(){
	conn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil{
		log.Fatalf("did not connect: %v", err)
	}

	defer conn.Close()

	c := Intf.NewGreeterClient(conn)
	for i := 0; i < 100; i++ {
		name := fmt.Sprintf("lin %d", i)
		r, err := c.SayHello(context.Background(), &Intf.HelloRequest{Name: name})

		if err != nil {
			log.Fatalf("could not greet:%v", err)
		}
		log.Println(r.Message)
	}
}



go-micro测试过程简述:

按照https://micro.mu/docs/framework.html过程创建proto文件,生成go接口,编写服务端、客户端代码,启动测试。

服务端代码:

package main

import (
	"context"
	"fmt"
	"github.com/micro/go-micro"
)

type Greeter struct {}

func (g *Greeter) Hello(ctx context.Context, req *Request, rsq *Response)  error {
	rsq.Greeting = "hello " + req.Name
	return nil
}

func main()  {
	//创建一个服务,传递的参数指定服务的名称,客户端根据这个名称查找服务
	service := micro.NewService(micro.Name("greeter"))
	service.Init()
	RegisterGreeterHandler(service.Server(), new(Greeter))
	if err := service.Run(); err != nil{
		fmt.Println(err)
	}
}

客户端代码:

package main

import (
	proto "./proto"
	"context"
	"fmt"
	"github.com/micro/go-micro"
)

func main(){
	service := micro.NewService(micro.Name("greeter.client"))
	service.Init()
	//NewGreeterService的第一个参数name为服务名称,根据这个名称在ngs服务中查找服务实例
	greeterClt := proto.NewGreeterService("greeter", service.Client())
	rsq, err := greeterClt.Hello(context.TODO(), &proto.Request{Name:"john"})
	if err != nil{
		fmt.Println(err)
	}

	fmt.Println(rsq.Greeting)
}

与grpc做比较,发现客户端、服务端都没有指定端口号、ip地址相关信息。完全依靠NewService函数的name参数来注册、查找服务,代码非常简洁。调用远程服务和调用本地服务的方式完全一样。

如要使用consul注册服务,需要下载go-micro的go-plugin插件,将其中的registry/consul目录拷贝到go-micro的registry/consul。修改服务端和客户端代码,指定consul服务地址。

package main

import (
	"context"
	"fmt"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/registry/consul"
)

type Greeter struct {}

func (g *Greeter) Hello(ctx context.Context, req *Request, rsq *Response)  error {
	rsq.Greeting = "hello " + req.Name
	return nil
}

func main()  {
	//创建一个服务,传递的参数指定服务的名称,客户端根据这个名称查找服务
	reg := consul.NewRegistry(func(op *registry.Options) {
		op.Addrs = []string{
			"127.0.0.1:8500",
		}
	})
	service := micro.NewService(
		micro.Registry(reg), micro.Name("greeter"), )
	service.Init()
	RegisterGreeterHandler(service.Server(), new(Greeter))
	if err := service.Run(); err != nil{
		fmt.Println(err)
	}
}
package main

import (
	proto "./proto"
	"context"
	"fmt"
	"github.com/micro/go-micro"
	"github.com/micro/go-micro/registry"
	"github.com/micro/go-plugins/registry/consul"
)

func main(){
	reg := consul.NewRegistry(func(op *registry.Options) {
		op.Addrs = []string{
			"127.0.0.1:8500",
		}
	})
	service := micro.NewService(micro.Registry(reg), micro.Name("greeter.client"))
	service.Init()
	//NewGreeterService的第一个参数name为服务名称,根据这个名称在ngs服务中查找服务实例
	greeterClt := proto.NewGreeterService("greeter", service.Client())
	rsq, err := greeterClt.Hello(context.TODO(), &proto.Request{Name:"john"})
	if err != nil{
		fmt.Println(err)
	}

	fmt.Println(rsq.Greeting)
}

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页