GoMicro/

directory
v0.0.0-...-e4da755 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 9, 2023 License: Apache-2.0

README

微服务

特点

  • 在分布式环境中,将单体应用拆分为一系列服务,共同组成整个系统。
  • 每个服务都轻量级,单独部署,运行在自己的进程中。
  • 每个微服务注重自己的核心能力的开发,微服务组件之间采用轻量级通信方式进行通信,包括但不限于RESTful API。
  • 按照业务边界进行划分。
  • 微服务是一种编程架构思想,有不同的语言实现。

微服务面临的问题

1、客户端如何访问这些服务?

采用一种叫做网关(英文为API Gateway)的技术方案来解决这些问题,网关的作用主要包括:

提供统一服务入口,让微服务对前台透明
聚合后台的服务,节省流量,提升性能
提供安全,过滤,流控等API管理功能

客户端访问微服务

2、每个服务之间如何进行通信?

所有的微服务都是独立部署,运行在自己的进程容器中,所以微服务与微服务之间的通信就是IPC(Inter Process Communication),翻译为进程间通信。进程间通信的方案已经比较成熟了,现在最常见的有两大类:同步调用、异步消息调用。

  • 同步调用

同步调用比较简单,一致性强,但是容易出调用问题,性能体验上也会差些,特别是调用层次多的时候。同步调用的有两种实现方式:分别是REST和RPC

REST:REST基于HTTP,实现更容易,各种语言都支持,同时能够跨客户端,对客户端没有特殊的要求,只要具备HTTP的网络请求库功能就能使用。

RPC:rpc的特点是传输效率高,安全性可控,在系统内部调用实现时使用的较多。 基于REST和RPC的特点,我们通常采用的原则为:向系统外部暴露采用REST,向系统内部暴露调用采用RPC方式。

  • 异步消息调用

异步消息的方式在分布式系统中有特别广泛的应用,他既能减低调用服务之间的耦合,又能成为调用之间的缓冲,确保消息积压不会冲垮被调用方,同时能保证调用方的服务体验,继续干自己该干的活,不至于被后台性能拖慢。需要付出的代价是一致性的减弱,需要接受数据最终一致性,所谓的最终一致性就是只可能不会立刻同步完成,会有延时,但是最终会完成数据同步;还有就是后台服务一般要实现幂等性,因为消息送出于性能的考虑一般会有重复(保证消息的被收到且仅收到一次对性能是很大的考验)。最后就是必须引入一个独立的 Broker,作为中间代理池。

3、多个微服务,应如何实现?

  • 服务注册与发现

当服务上线时,服务提供者将自己的服务注册信息注册到某个专门的框架中,并通过心跳维持长链接,实时更新链接信息。服务调用者通过服务管理框架进行寻址,根据特定的算法,找到对应的服务,或者将服务的注册信息缓存到本地,这样提高性能。当服务下线时,服务管理框架会发送服务下线的通知给其他服务。

4、如果服务出现异常宕机,该如何解决?

  • 重试机制
  • 限流机制
  • 熔断机制
  • 负载均衡
  • 降级机制(本地缓存)

ProtoBuf

Protobuf是一种结构化数据的存储格式,平台无关,语言无关,可扩展。

安装
  • 安装protobuf

windows平台安装

去网站下载一个protoc.exe,网址:https://github.com/protocolbuffers/protobuf/releases, 同样放在GOPATH/bin下

linux平台安装

下载 protobuf安装包

git clone https://github.com/protocolbuffers/protobuf.git

安装依赖库

sudo yum install autoconf automake libtool curl make g++ unzip libffi-dev -y

安装

  $ cd protobuf/
  $ ./autogen.sh
  $ ./configure
  $ make
  $ sudo make install
  $ sudo ldconfig                  // 刷新共享库,很重要的一步
  • 获取proto包(Go语言的proto API接口)
go get github.com/golang/protobuf/proto
  • 获取protoc-gen-go
go get github.com/golang/protobuf/protoc-gen-go

在GOPATH/bin下生成protoc-gen-go.exe

protobuf格式
syntax = "proto3";
option go_package = "./;prototest";
message Person {
   string name = 1;
   int32 age = 2;
   repeated int32 height = 3;
   string motto = 4;  //格言
}
生成对应客户端代码
protoc --go_out ./ person.proto

Code generated by protoc-gen-go,文件后缀为.pb.go

编译protobuf常见bug
  • package xxx is not in GOROOT or GOPATH

原因:1.开启了Go mod,没有对应模块 2.没开启Go mod,但是GOPATH中没有对应模块

  • Missing ‘go_package‘ option in “person.proto“

原因:是因为在 proto3 的语法中缺少了 option go_package

option go_package = "aaa;bbb";

aaa 表示生成的go文件的存放地址,会自动生成目录的,建议使用当前目录:./。

bbb 表示生成的go文件所属的包名

Go Module

开启Go mod,并设置代理

 go env -w GO111MODULE=on
 go env -w GOPROXY=https://goproxy.cn,direct

常用命令

go mod init [模块名]  //在当前目录下初始化新的模块,此命令会在当前目录中初始化和创建一个新的go.mod文件
go mod tidy        //添加缺失的模块以及移除无用的模块,此动作会执行下载动作,类似于npm install.执行后会生成go.sum文件
go mod download  //使用此命令来下载指定的模块,模块的格式可以根据主模块依赖的形式或者path@version形式指定。如果没有指定参数,此命令会将主模块下的所有依赖下载下来,类似于npm install.一般使用go get替代
go mod vendor   //此命令会将build阶段需要的所有依赖包放到主模块所在的vendor目录中

RPC

golang实现rpc必备条件

golang写RPC程序,必须符合4个基本条件,不然RPC用不了

结构体字段首字母要大写,可以别人调用

函数名必须首字母大写

函数第一参数是接收参数,第二个参数是返回给客户端的参数,必须是指针类型

函数还必须有一个返回值error
go实现rpc
  • golang官方的net/rpc库使用encoding/gob进行编解码,支持tcp和http数据传输方式,由于其他语言不支持gob编解码方式,所以golang的RPC只支持golang开发的服务器与客户端之间的交互.[gob流]
Server端
package main

import (
	"io"
	"net/http"
	"net/rpc"
)

// 参数
type Params struct {
	Width  int
	Height int
}

// 矩形
type React struct{}

func (this *React) ZhouChang(params Params, ret *int) error {
	*ret = (params.Width + params.Height) * 2
	return nil
}

func (this *React) MianJi(params Params, ret *int) error {
	*ret = params.Width * params.Height
	return nil
}

func main() {
	http.HandleFunc("/panda", pandaFunc)
	//注册服务
	react := new(React)
	rpc.Register(react)
	//HTTP绑定
	rpc.HandleHTTP()
	http.ListenAndServe("127.0.0.1:8888", nil)
}

func pandaFunc(writer http.ResponseWriter, request *http.Request) {
	io.WriteString(writer, "Hello,World!!!")
}
客户端
package main

import (
	"fmt"
	"net/rpc"
)

// 传的参数
type Params struct {
	Width, Height int
}

// rpc客户端
func main() {
	client, e := rpc.DialHTTP("tcp", "127.0.0.1:8888")
	if e != nil {
		fmt.Println("Dial HTTP Error:", e)
		return
	}
	zhouchang := 0
	e = client.Call("React.ZhouChang", Params{10, 20}, &zhouchang)
	if e != nil {
		fmt.Println("React.ZhouChang Error:", e)
		return
	}
	fmt.Println("矩形的周长是:", zhouchang)
	mianji := 0
	e = client.Call("React.MianJi", Params{10, 20}, &mianji)
	if e != nil {
		fmt.Println("React.MianJi Error:", e)
		return
	}
	fmt.Println("矩形的面积是:", mianji)
}
  • 官方还提供了net/rpc/jsonrpc库实现RPC方法,jsonrpc采用encoding/json进行数据编解码,因而支持跨语言调用,目前jsonrpc库是基于tcp协议实现的,暂不支持http传输方式。[json字符串]
服务端
package main

import (
	"fmt"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
)

// 参数
type Params struct {
	Width  int
	Height int
}

// 矩形
type React struct{}

func (this *React) ZhouChang(params Params, ret *int) error {
	*ret = (params.Width + params.Height) * 2
	return nil
}

func (this *React) MianJi(params Params, ret *int) error {
	*ret = params.Width * params.Height
	return nil
}

func main() {
	//注册服务
	react := new(React)
	rpc.Register(react)
	listener, e := net.Listen("tcp", "127.0.0.1:9999")
	if e != nil {
		fmt.Println("Net Listen Error:", e)
		return
	}
	for {
		// 监听客户端连接
		conn, e := listener.Accept()
		if e != nil {
			fmt.Println("Listener Accept Error:", e)
			return
		}
		go jsonrpc.ServeConn(conn)
	}
}
客户端
package main

import (
	"fmt"
	"net/rpc/jsonrpc"
)

// 传的参数
type Params struct {
	Width, Height int
}

// rpc客户端
func main() {
	client, e := jsonrpc.Dial("tcp", "127.0.0.1:9999")
	if e != nil {
		fmt.Println("Dial HTTP Error:", e)
		return
	}
	zhouchang := 0
	e = client.Call("React.ZhouChang", Params{50, 20}, &zhouchang)
	if e != nil {
		fmt.Println("React.ZhouChang Error:", e)
		return
	}
	fmt.Println("矩形的周长是:", zhouchang)
	mianji := 0
	e = client.Call("React.MianJi", Params{50, 20}, &mianji)
	if e != nil {
		fmt.Println("React.MianJi Error:", e)
		return
	}
	fmt.Println("矩形的面积是:", mianji)
}

###gRPC

gRPC是一个高性能、开源、通用的RPC框架,底层是通讯协议,采用Protobuf数据序列化协议[protobuf]。

安装gRPC
go get google.golang.org/grpc
gRPC调用流程
1.编写.proto描述文件
2.编译生成.pb.go文件
3.服务端实现约定的接口并提供服务
4.客户端按照约定调用.pb.go文件中的方法请求服务
项目结构
    |—— hello/
        |—— client/
            |—— main.go   // 客户端
        |—— server/
            |—— main.go   // 服务端
    |—— proto/
        |—— hello/
            |—— hello.proto   // proto描述文件
            |—— hello.pb.go   // proto编译后文件
编写.proto描述文件
    syntax = "proto3";
    option go_package = "./;hello";
    
    service Hello{
       rpc SayHello(HelloRequest) returns (HelloResponse){}
    }
    
    message HelloRequest{
       string name = 1;
    }
    
    message HelloResponse{
       string message = 1;
    }
编译生成.pb.go文件

生成.pb.go时,编译命令使用grpc的插件

protoc --go_out=plugins=grpc:. hello.proto
实现服务端接口 server/main.go
package main

import (
	"fmt"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"grpc-demo/proto/hello"
	"net"
)

type HelloService struct{}

func (h HelloService) SayHello(ctx context.Context, in *hello.HelloRequest) (*hello.HelloResponse, error) {
	response := new(hello.HelloResponse)
	response.Message = fmt.Sprintf("Hello %s.", in.Name)
	return response, nil
}

func main() {
	listener, e := net.Listen("tcp", "127.0.0.1:8989")
	if e != nil {
		fmt.Println("TCP Listen Error!!!!")
		return
	}

	helloService := HelloService{}

	//实例化grpc Server
	server := grpc.NewServer()
	// 注册服务
	hello.RegisterHelloServer(server, helloService)

	server.Serve(listener)
}
实现客户端调用 client/main.go
package main

import (
	"fmt"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"grpc-demo/proto/hello"
)

func main() {
	conn, e := grpc.Dial( "127.0.0.1:8989",grpc.WithInsecure())
	if e != nil {
		fmt.Println("Grpc Dial Error:",e)
		return
	}
	defer conn.Close()
    //初始化客户端
	client:= hello.NewHelloClient(conn)
    //调用方法
	response, e := client.SayHello(context.Background(), &hello.HelloRequest{Name: "Gopher"})
	if e != nil {
		fmt.Println("Client SayHello Error:",e)
		return
	}

	fmt.Println("Info:",response.Message)

}

consul

consul注册中心

consul注册中心

1、当 Producer 启动的时候,会向 Consul 发送一个 post 请求,告诉 Consul 自己的 IP 和 Port

2、Consul 接收到 Producer 的注册后,每隔10s(默认)会向 Producer 发送一个健康检查的请求,检验Producer是否健康

3、当 Consumer 发送 GET 方式请求 /api/address 到 Producer 时,会先从 Consul 中拿到一个存储服务 IP 和 Port 的临时表,从表中拿到 Producer 的 IP 和 Port 后再发送 GET 方式请求 /api/address

4、该临时表每隔10s会更新,只包含有通过了健康检查的 Producer

consul集群架构图

consul集群架构图

consul集群架构图

consul单机版
consul agent -dev
consul集群版
consul服务端1
consul agent -server -bootstrap-expect 3 -data-dir /tmp/consul -node=n1 -bind=192.168.1.110 -ui -config-dir /etc/config/cosul -rejoin -join 192.168.1.110 -client 127.0.0.1/0.0.0.0
consul服务端2
consul agent -server -bootstrap-expect 3 -data-dir /tmp/consul -node=n2 -bind=192.168.1.111 -config-dir /etc/config/consul -rejoin -join 192.168.1.110 -client 127.0.0.1/0.0.0.0
consul服务端3
consul agent -server -bootstrap-expect 3 -data-dir /tmp/consul -node=n3 -bind=192.168.1.112 -config-dir /etc/config/consul -rejoin -join 192.168.1.110 -client 127.0.0.1/0.0.0.0
consul客户端1
consul agent -data-dir /tmp/consul -node=n4 -bind=192.168.1.113 -config-dir /etc/config/consul -rejoin -join 192.168.1.110
consul客户端2
consul agent -data-dir /tmp/consul -node=n5 -bind=192.168.1.114 -config-dir /etc/config/consul -rejoin -join 192.168.1.110

Go-Micro微服务框架

环境安装
  • 安装go-micro微服务RPC框架
go get github.com/micro/go-micro
  • 安装Protobuf
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
  • 安装protoc-gen-micro可执行文件

v1版本:

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

v2版本:

go get github.com/micro/micro/v2/cmd/protoc-gen-micro
  • 安装micro工具包,生成micro可执行文件

方式1:

go get github.com/micro/micro

方式2:使用源码包进行go build编译安装

安装出现的问题
  • 使用micro/v2时,protoc生成micro.protoc文件导致的版本冲突

可将生成的*.pb.micro.go文件中的v1依赖改为v2依赖即可

import (
	context "context"
	client "github.com/micro/go-micro/v2/client"
	server "github.com/micro/go-micro/v2/server"
)
  • micro/v2版本使用consul作为注册中心,默认使用mdns
2021-12-02 09:40:28.278823 I | Transport [http] Listening on [::]:65258

2021-12-02 09:40:28.278823 I | Broker [http] Connected to [::]:65259

2021-12-02 09:40:28.451945 I | Registry [consul] Registering node: go.micro.service.hello-c79e1fee-5f0d-4461-a56b-ca9552be4ad3

2021-12-02 09:40:29.150437 I | Subscribing go.micro.service.hello-c79e1fee-5f0d-4461-a56b-ca9552be4ad3 to topic: go.micro.service.hello

微服务小案例
  1. go-micro v2 默认是 GRPC 通信

  2. 生成服务端骨架、客户端骨架

  • v1版本
micro new --gopath=false --type="srv" hello
micro new --gopath=false --type="web" web
  • v2版本
micro new --gopath=false --type="service" hello
micro new --gopath=false --type="web" web

2.生成protobuf

protoc --micro_out=. --go_out=. proto/hello/hello.proto

3.启用web面板查看服务【服务治理】

micro --registry=etcd --registry_address=127.0.0.1:2379 web

4.查看服务列表

micro --registry etcd --registry_address 127.0.0.1:2379 list services
使用etcd做注册中心
启动etcd
./etcd  --data-dir ./data.etcd/  --listen-client-urls http://yourip:2379 --advertise-client-urls http://yourip:2379 & >./log/etcd.log

-listen-client-urls用于指定etcd和客户端的连接端口

-advertise-client-urls用于指定etcd服务器之间通讯的端口

etcd有要求,如果-listen-client-urls被设置了,那么就必须同时设置-advertise-client-urls,所以即使设置和默认相同,也必须显式设置.

2021-12-06 15:02:30  file=v2@v2.9.1/service.go:200 level=info Starting [service] go.micro.service.hello
2021-12-06 15:02:30  file=grpc/grpc.go:864 level=info Server [grpc] Listening on [::]:63232
2021-12-06 15:02:30  file=grpc/grpc.go:881 level=info Broker [http] Connected to 127.0.0.1:63233
2021-12-06 15:02:30  file=grpc/grpc.go:697 level=info Registry [etcd] Registering node: go.micro.service.hello-8bebc6e1-107f-49db-a4c6-063e7ba9509e
2021-12-06 15:02:30  file=grpc/grpc.go:730 level=info Subscribing to topic: go.micro.service.hello

Directories

Path Synopsis
day1
day2

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL