Golang之微服务

Posted henreash

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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, []int0


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, []int1


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 &greeterClientcc


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 &HelloReplyMessage:"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.HelloRequestName: 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.RequestName:"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.RequestName:"john")
	if err != nil
		fmt.Println(err)
	

	fmt.Println(rsq.Greeting)

以上是关于Golang之微服务的主要内容,如果未能解决你的问题,请参考以下文章

微服务实践系列一之微服务架构

微服务实践系列一之微服务架构

.net之微信企业号开发 所使用的环境与工具以及准备工作

Chris Richardson微服务翻译:构建微服务之微服务架构的进程通讯

重学SpringCloud系列八之微服务网关安全认证-JWT篇

掌握系列之微服务-1.概念