go-micro教程 — 第二章 go-micro v3 使用Gin、Etcd

前言

注意:本文使用的Go版本为 go1.17.6 使用 1.18.x 版本或其他版本在操作时总是碰到各种问题,比如依赖下载异常、版本不一致等问题。当然也可能是我电脑的问题。
参考文档:go 微服务之go-micro v3+gin

本文代码:https://gitee.com/XiMuQi/go-micro-demo,对三、四、五步的操作均打了标签。

1 启动Etcd集群

在使用Etcd作为注册中心前需要先有Etcd节点或者Etcd集群,Etcd集群的安装配置及启动,详见:Etcd教程 — 第四章 Etcd集群安全配置

2 创建项目并安装相关依赖

注意:2.3到 2.6 步骤执行完同时会在 ${GOPATH}\bin 下生成exe文件。
在这里插入图片描述## 2.1 创建项目
本文创建的项目名称为 go-micro-demo

2.2 初始化项目

go mod init go-micro-demo

2.3 安装 proto

见:Go — 相关依赖对应的exe 1、protobuf

2.4 安装 protoc-gen-go

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

2.5 安装 protoc-gen-micro

注意:是安装 asim下的而不是micro下的,因为micro下的始终下载不了,这个也是go micro 3.0 框架。

go get github.com/asim/go-micro/cmd/protoc-gen-micro/v3

2.6 安装micro v3 构建工具

  1. 需要用到Micro 3.0 的micro工具,主要是用于快速构建micro项目,但是不使用这个的配置,用下面2的
go install github.com/micro/micro/v3@latest
  1. 下载go micro 3.0 库,下面库没有上面micro构建工具
go get github.com/asim/go-micro/v3

2.7 安装gin

go get -u github.com/gin-gonic/gin

3 开发项目

3.1 创建web模块

go-micro-demo下创建web文件夹

3.2 进入web文件夹

创建main.go文件

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

const addr = ":9000"

func Index(c *gin.Context) {
	c.JSON(http.StatusOK, map[string]interface{}{
		"message": "Gin访问成功",
	})
}

func main() {
	r := gin.Default()
	r.Handle("GET", "/", Index)
	if err := r.Run(addr); err != nil {
		fmt.Println("err")
	}
}

然后执行 go run .或者在Goland中执行main函数,启动web服务。
启动成功后,在浏览器访问 http://127.0.0.1:9000, 得到如下响应:

// 20220704141435
// http://127.0.0.1:9000/

{
  "message": "Gin访问成功"
}

3.3 创建services下的子模块

go-micro-demo\services下执行micro new test命令,创建后端测试服务模块。

H:\Goland\go-micro-demo\services>micro new test
Creating service test

.
├── micro.mu
├── main.go
├── generate.go
├── handler
│   └── test.go
├── proto
│   └── test.proto
├── Dockerfile
├── Makefile
├── README.md
├── .gitignore
└── go.mod


download protoc zip packages (protoc-$VERSION-$PLATFORM.zip) and install:

visit https://github.com/protocolbuffers/protobuf/releases

compile the proto file test.proto:

cd test
make init
go mod vendor
make proto

3.4 删除go.mod文件

删除services\test模块下的go.mod文件,统一使用go-micro-demo下的go.mod

3.5 根据proto生成pb文件

3.5.1 修改 test.proto

主要是修改go_package指定生成pb文件的路经,这里是将 ./改为 ../即可。

syntax = "proto3";

package test;

//option go_package = "./proto;test";
//将 ./改为 ../
option go_package = "../proto;test";

service Test {
	rpc Call(Request) returns (Response) {}
	rpc Stream(StreamingRequest) returns (stream StreamingResponse) {}
	rpc PingPong(stream Ping) returns (stream Pong) {}
}

message Message {
	string say = 1;
}

message Request {
	string name = 1;
}

message Response {
	string msg = 1;
}

message StreamingRequest {
	int64 count = 1;
}

message StreamingResponse {
	int64 count = 1;
}

message Ping {
	int64 stroke = 1;
}

message Pong {
	int64 stroke = 1;
}

3.5.2 生成pb文件

进入到 go-micro-demo\services\test\proto 。执行生成命令:

protoc --proto_path=. --micro_out=. --go_out=. *.proto

执行完后可以在go-micro-demo\services\test\proto看到生成的test.pb.gotest.pb.micro.go文件。

3.6 修改handler/test.go文件

修改 micro-demo\services\test\handler下的 test.go 主要是修改引入的 pb文件位置。

import (
	"context"

	log "github.com/micro/micro/v3/service/logger"

	test "go-micro-demo/services/test/proto"//test "test/proto"
)

3.7 修改services/test/main.go文件

  1. 修改引入的pb文件位置。
  2. 将原来 micro下的包替换成asim下的。
package main

import (
	//修改 1
	"go-micro-demo/services/test/handler"  //"test/handler"
	pb "go-micro-demo/services/test/proto" //pb "test/proto"

	//修改 2 替换micro为asim
	service "github.com/asim/go-micro/v3" //"github.com/micro/micro/v3/service"
	"github.com/asim/go-micro/v3/logger"  //"github.com/micro/micro/v3/service/logger"
)

func main() {
	//修改 3
	srv := service.NewService( // service.New
		service.Name("test"),
		service.Version("latest"),
	)

	// Register handler
	//修改 4
	_ = pb.RegisterTestHandler(srv.Server(), new(handler.Test))

	// Run service
	if err := srv.Run(); err != nil {
		logger.Fatal(err)
	}
}

然后执行 go run .或者在Goland中执行main函数,启动名为test的服务。
如果启动时报:

handler\test.go:6:2: no required module provides package github.com/micro/micro/v3/service/logger; to add it:
	go get github.com/micro/micro/v3/service/logger

执行go mod tidy后重启。

启动成功后,控制台显示内容:

API server listening at: 127.0.0.1:58596
2022-07-04 14:50:44  file=v3@v3.7.1/service.go:206 level=info Starting [service] test
2022-07-04 14:50:44  file=server/rpc_server.go:820 level=info Transport [http] Listening on [::]:58603
2022-07-04 14:50:44  file=server/rpc_server.go:840 level=info Broker [http] Connected to 127.0.0.1:58604
2022-07-04 14:50:45  file=server/rpc_server.go:654 level=info Registry [mdns] Registering node: test-69072bf3-9123-4177-88cb-9d898a8219e5

※3.8 通过web模块直接调用services下的test服务

3.8.1 创建web/handler/testHandler.go文件

package handler

import (
	"github.com/asim/go-micro/v3"
	"github.com/gin-gonic/gin"
	testpb "go-micro-demo/services/test/proto"
	"net/http"
)

func Index(c *gin.Context) {
	c.JSON(http.StatusOK, map[string]interface{}{
		"message": "index",
	})
}

func ServiceOne(c *gin.Context) {

	service := micro.NewService()

	service.Init()

	// 创建微服务客户端
	client := testpb.NewTestService("test", service.Client())

	// 调用服务
	rsp, err := client.Call(c, &testpb.Request{
		Name: c.Query("key"),
	})

	if err != nil {
		c.JSON(200, gin.H{"code": 500, "msg": err.Error()})
		return
	}

	c.JSON(200, gin.H{"code": 200, "msg": rsp.Msg})
}

3.8.2 修改web/main.go文件

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"go-micro-demo/web/handler"
	"net/http"
)

const addr = ":9000"

func Index(c *gin.Context) {
	c.JSON(http.StatusOK, map[string]interface{}{
		"message": "Gin访问成功",
	})
}

func main() {
	r := gin.Default()
	r.Handle("GET", "/", Index)
	r.Handle("GET", "/test-req", handler.ServiceOne)
	if err := r.Run(addr); err != nil {
		fmt.Println("err")
	}
}

然后执行 go run .或者在Goland中执行main函数,重新启动web服务。
启动结果:

[GIN-debug] GET    /                         --> main.Index (3 handlers)
[GIN-debug] GET    /test-req                 --> go-micro-demo/web/handler.ServiceOne (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :9000

浏览器访问http://127.0.0.1:9000/test-req?key=哈哈,得到如下响应:

// 20220704150031
// http://127.0.0.1:9000/test-req?key=%E5%93%88%E5%93%88

{
  "code": 200,
  "msg": "Hello 哈哈"
}

4 使用consul作为注册中心注册服务

4.1 安装配置consul

安装配置 consul,详见 Consul安装

启动consul后,使用浏览器访问http://192.168.1.222:8500,如果能访问成功则说明consul正常。

4.2 项目加入consul包

go get -u github.com/asim/go-micro/plugins/registry/consul/v3

4.3 修改services/test/main.go

  1. 加入 consul包。
  2. 配置 consul 的地址及注册的名称。
  3. 修改 注册服务的方式为consul方式。

主要修改或加入的地方 consul 1consul 2consul 3

package main

import (
	//修改 1
	"go-micro-demo/services/test/handler"  //"test/handler"
	pb "go-micro-demo/services/test/proto" //pb "test/proto"

	//修改 2 替换micro为asim
	service "github.com/asim/go-micro/v3" //"github.com/micro/micro/v3/service"
	"github.com/asim/go-micro/v3/logger"  //"github.com/micro/micro/v3/service/logger"

	//consul 1
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	"github.com/asim/go-micro/v3/registry"
)

//consul 2
const (
	ServerName = "consul-test"
	ConsulAddr = "192.168.1.222:8500"
)

func main() {
	// consul 3
	consulReg := consul.NewRegistry(
		registry.Addrs(ConsulAddr),
	)

	srv := service.NewService(
		service.Name(ServerName),	// 服务名字
		service.Registry(consulReg),// 注册中心
	)

	// Register handler
	//修改 4
	_ = pb.RegisterTestHandler(srv.Server(), new(handler.Test))

	// Run service
	if err := srv.Run(); err != nil {
		logger.Fatal(err)
	}
}

如果引入的包报红,执行go mod tidy即可。
执行go run .,重新启动services/test模块的名为consul-test的服务。然后访问Consul 的UI界面:http://192.168.1.222:8500,可以看到服务名称为consul-test的节点已经注册到了consul注册中心上。
在这里插入图片描述

4.4 修改web/handler/testHandler.go

主要修改或加入的地方 consul 1consul 2consul 3

package handler

import (
	"github.com/asim/go-micro/v3"
	"github.com/gin-gonic/gin"
	testpb "go-micro-demo/services/test/proto"
	"net/http"

	//consul 1
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	"github.com/asim/go-micro/v3/registry"
)

func Index(c *gin.Context) {
	c.JSON(http.StatusOK, map[string]interface{}{
		"message": "index",
	})
}

func ServiceOne(c *gin.Context) {
	//consul 2
	consulReg := consul.NewRegistry(
		registry.Addrs("192.168.1.222:8500"),
	)

	service := micro.NewService(
		micro.Registry(consulReg), //设置注册中心
	)

	service.Init()

	//consul 3 创建微服务客户端【将服务名称从test改为为consul-test】
	client := testpb.NewTestService("consul-test", service.Client())

	// 调用服务
	rsp, err := client.Call(c, &testpb.Request{
		Name: c.Query("key"),
	})

	if err != nil {
		c.JSON(200, gin.H{"code": 500, "msg": err.Error()})
		return
	}

	c.JSON(200, gin.H{"code": 200, "msg": rsp.Msg})
}

执行go run .,重启web。

4.5 验证

浏览器访问http://127.0.0.1:9000/test-req?key=哈哈,得到如下响应:

// 20220704212337
// http://127.0.0.1:9000/test-req?key=%E5%93%88%E5%93%88

{
  "code": 200,
  "msg": "Hello 哈哈"
}

4.6 关停 Consul

如果关停Consul服务,再重新访问http://127.0.0.1:9000/test-req?key=哈哈,会得到如下响应:

// 20220704212448
// http://127.0.0.1:9000/test-req?key=%E5%93%88%E5%93%88

{
  "code": 500,
  "msg": "{\"id\":\"go.micro.client\",\"code\":500,\"detail\":\"error selecting consul-test node: Get \\\"http: //192.168.1.222: 8500/v1/health/service/consul-test?stale=\\\": dial tcp 192.168.1.222:8500: connectex: No connection could be made because the target machine actively refused it.\",\"status\":\"Internal Server Error\"}"
}

客户端和服务端的控制台同样显示如下信息,等到Consul恢复正常即会停止报错:

2022-07-04T21:26:03.837+0800 [ERROR] watch: Watch errored: type=services error="Get "http://192.168.1.222:8500/v1/catalog/services": dial tcp 192.168.1.222:8500: connectex: No conn
ection could be made because the target machine actively refused it." retry=1m20s
2022-07-04T21:26:54.163+0800 [ERROR] watch: Watch errored: type=service error="Get "http://192.168.1.222:8500/v1/health/service/consul-test": dial tcp 192.168.1.222:8500: connectex
: No connection could be made because the target machine actively refused it." retry=2m5s
2022-07-04T21:27:25.895+0800 [ERROR] watch: Watch errored: type=services error="Get "http://192.168.1.222:8500/v1/catalog/services": dial tcp 192.168.1.222:8500: connectex: No conn
ection could be made because the target machine actively refused it." retry=2m5s

5 使用etcd作为注册中心

5.1 项目加入etcd包

go get -u "github.com/asim/go-micro/plugins/registry/etcd/v3"

5.2 修改services/test/main.go

consul替换成etcd

  1. 加入etcd 包。
  2. 配置 etcd 的地址及注册的名称。
  3. 修改注册服务的方式为etcd方式。

主要修改或加入的地方 etcd 1etcd 2etcd 3

package main

import (
	//修改 1
	"go-micro-demo/services/test/handler"  //"test/handler"
	pb "go-micro-demo/services/test/proto" //pb "test/proto"

	//修改 2 替换micro为asim
	service "github.com/asim/go-micro/v3" //"github.com/micro/micro/v3/service"
	"github.com/asim/go-micro/v3/logger"  //"github.com/micro/micro/v3/service/logger"

	//etcd 1
	"github.com/asim/go-micro/plugins/registry/etcd/v3"
	"github.com/asim/go-micro/v3/registry"
)

//etcd 2
const (
	ServerName = "etcd-test"
	EtcdAddr   = "192.168.1.221:2379"
)
func main() {
	//etcd 3
	etcdReg := etcd.NewRegistry(
		registry.Addrs(EtcdAddr),
	)

	srv := service.NewService(
		service.Name(ServerName),  // 服务名字
		service.Registry(etcdReg), // 注册中心
	)

	// Register handler
	//修改 4
	_ = pb.RegisterTestHandler(srv.Server(), new(handler.Test))

	// Run service
	if err := srv.Run(); err != nil {
		logger.Fatal(err)
	}
}

5.3 修改web/handler/testHandler.go

package handler

import (
	"github.com/asim/go-micro/plugins/registry/consul/v3"
	"github.com/asim/go-micro/v3"
	"github.com/gin-gonic/gin"
	testpb "go-micro-demo/services/test/proto"
	"net/http"

	//etcd 1
	"github.com/asim/go-micro/plugins/registry/etcd/v3"
	"github.com/asim/go-micro/v3/registry"
)

func Index(c *gin.Context) {
	c.JSON(http.StatusOK, map[string]interface{}{
		"message": "index",
	})
}

func ServiceOne(c *gin.Context) {
	// etcd 2
	etcdReg := etcd.NewRegistry(
		registry.Addrs("192.168.1.221:2379"),
	)
	service := micro.NewService(
		micro.Registry(etcdReg), //设置注册中心
	)

	service.Init()

	//etcd 3 创建微服务客户端【将服务名称从consul-test改为etcd-test】
	client := testpb.NewTestService("etcd-test", service.Client())

	// 调用服务
	rsp, err := client.Call(c, &testpb.Request{
		Name: c.Query("key"),
	})

	if err != nil {
		c.JSON(200, gin.H{"code": 500, "msg": err.Error()})
		return
	}

	c.JSON(200, gin.H{"code": 200, "msg": rsp.Msg})
}

执行go run .,重启web。

5.4 验证

浏览器端发送 http://127.0.0.1:9000/test-req?key=哈哈,返回结果:

// 20220704215208
// http://127.0.0.1:9000/test-req?key=%E5%93%88%E5%93%88

{
  "code": 200,
  "msg": "Hello 哈哈"
}

5.5 关停 Etcd

如果关停Etcd服务,再重新访问 http://127.0.0.1:9000/test-req?key=哈哈,会得到如下响应:

// 20220704215457
// http://127.0.0.1:9000/test-req?key=%E5%93%88%E5%93%88

{
  "code": 500,
  "msg": "{\"id\":\"go.micro.client\",\"code\":500,\"detail\":\"error selecting etcd-test node: context deadline exceeded\",\"status\":\"Internal Server Error\"}"
}

客户端的控制台同样显示如下信息:

{"level":"warn","ts":"2022-07-04T21:54:57.129+0800","logger":"etcd-client","caller":"v3@v3.5.0/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"etcd-endp
oints://0xc000024c40/#initially=[192.168.1.221:2379]","attempt":0,"error":"rpc error: code = DeadlineExceeded desc = latest balancer error: last connection error: connection error:
 desc = \"transport: Error while dialing dial tcp 192.168.1.221:2379: connectex: No connection could be made because the target machine actively refused it.\""}

注意:go-micro需要等到Etcd恢复正常后重启相关服务才能正常使用。

5.6 查看服务注册情况

Etcd虽然没有和Consul一样的UI界面查看服务注册情况。但是可以通过查看指定前缀的所有键值对来查看go-micro的注册信息:

etcdctl get --prefix /micro
#或
etcdctl get --prefix /micro/registry

go-micro 注册时的节点ID信息:

/micro/registry/etcd-test/etcd-test-3bbd2f13-d24e-41dd-9b4e-e8f5b601a549

注意:后面的etcd-test-3bbd2f13-d24e-41dd-9b4e-e8f5b601a549是和services/test/main.go运行后在控制台显示的节点ID完全一致。

API server listening at: 127.0.0.1:58099
2022-07-04 23:11:48  file=v3@v3.7.1/service.go:206 level=info Starting [service] etcd-test
2022-07-04 23:11:48  file=server/rpc_server.go:820 level=info Transport [http] Listening on [::]:58104
2022-07-04 23:11:48  file=server/rpc_server.go:840 level=info Broker [http] Connected to 127.0.0.1:58105
2022-07-04 23:11:48  file=server/rpc_server.go:654 level=info Registry [etcd] Registering node: etcd-test-3bbd2f13-d24e-41dd-9b4e-e8
f5b601a549

其余注册信息:

/micro/registry/etcd-test/etcd-test-3bbd2f13-d24e-41dd-9b4e-e8f5b601a549
{
    "name":"etcd-test",
    "version":"latest",
    "metadata":null,
    "endpoints":[
        {
            "name":"Test.Call",
            "request":{
                "name":"Request",
                "ty pe":"Request",
                "values":[
                    {
                        "name":"MessageState",
                        "type":"MessageState",
                        "values":[
                            {
                                "name":"NoUnkeyedLiterals",
                                "type":"NoUnkeyedLiterals",
                                "values":null
                            },
                            {
                                "name":"DoNotCompare",
                                "type":"DoNotCompare",
                                "values":null
                            },
                            {
                                "name":"DoNotCopy",
                                "type":"DoNotCopy",
                                "values":null
                            },
                            {
                                "name":"MessageInfo",
                                "type":"MessageInfo",
                                "values":null
                            }
                        ]
                    },
                    {
                        "name":"int32",
                        "type":"int32",
                        "va lues":null
                    },
                    {
                        "name":"unknownFields",
                        "type":"[]uint8",
                        "values":null
                    },
                    {
                        "name":"name",
                        "type":"string",
                        "values":null
                    }
                ]
            },
            "re sponse":{
                "name":"Response",
                "type":"Response",
                "values":[
                    {
                        "name":"MessageState",
                        "type":"MessageState",
                        "values":[
                            {
                                "name":"NoUnkeyedLiterals",
                                "type":"NoUnkeyedLiterals",
                                "values":null
                            },
                            {
                                "name":"DoNotCompare",
                                "type":"DoNotCompare",
                                "values":null
                            },
                            {
                                "name":"DoNotCopy",
                                "type":"DoNotCopy",
                                "values":null
                            },
                            {
                                "name":"MessageInfo",
                                "type":"MessageInfo",
                                "values":null
                            }
                        ]
                    },
                    {
                        "n ame":"int32",
                        "type":"int32",
                        "values":null
                    },
                    {
                        "name":"unknownFields",
                        "type":"[]uint8",
                        "values":null
                    },
                    {
                        "name":"msg",
                        "type":"string",
                        "values":null
                    }
                ]
            },
            "metadata":{

            }
        },
        {
            "name":"Test.PingPong",
            "request":{
                "name":"Context",
                "type":"Context",
                "values":null
            },
            "response":{
                "name":"Stream",
                "type":"Stream",
                "values":null
            },
            "metadata":{
                "stream":"true"
            }
        },
        {
            "name":"Test.Stream",
            " request":{
                "name":"Context",
                "type":"Context",
                "values":null
            },
            "response":{
                "name":"Stream",
                "type":"Stream",
                "values":null
            },
            " metadata":{
                "stream":"true"
            }
        }
    ],
    "nodes":[
        {
            "id":"etcd-test-3bbd2f13-d24e-41dd-9b4e-e8f5b601a549",
            "address":"192.168.91.1:58104",
            "metadata":{
                "broker":"http",
                "protocol":"mucp",
                "registry":"etcd",
                "server":"mucp",
                "transport":"http"
            }
        }
    ]
}

※6 go-micro v3服务注册发现源码探究

参见:Go语言微服务实战之再探服务发现

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go微服务开发是利用Go语言进行微服务架构的开发方式。在这个问题中,使用gin、grpc和etcd进行重构grpc-todolist项目。 Gin是一个轻量级的Web框架,使用它可以快速构建高性能的Web应用程序。它具有简单易用、性能出色和灵活的特点。在微服务开发中,Gin可以作为HTTP服务器框架,处理和响应客户端的HTTP请求。 gRPC是一种高性能、开源的远程过程调用(RPC)框架。它支持多种编程语言,并使用带有协议缓冲区的Google Protocol Buffers进行数据交换。在微服务架构中,gRPC可以用于服务之间的通信,通过定义接口和消息格式,实现服务间的数据传输和调用。 Etcd是一个高可靠、分布式的键值存储系统。它使用Raft一致性算法来保证数据的可靠性和一致性。在微服务开发中,Etcd可以作为服务发现和配置管理的工具,用于注册和发现各个微服务的信息。 对于重构grpc-todolist项目来说,使用gin可以将原有的HTTP接口改写为更加高性能的接口,提高整个系统的性能。通过使用gRPC,可以将原有的接口定义为gRPC接口,实现服务间的高效通信,并且易于扩展和维护。同时,借助Etcd实现服务注册和发现,提高系统的可用性和灵活性。 总而言之,通过使用gin、grpc和etcd对grpc-todolist项目进行重构,可以提高系统性能、扩展性和可维护性。这种微服务开发方式能够更好地适应大规模分布式系统的需求,使得系统更加稳定和可靠。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值