作业:grpc 远程调用。

  • 服务端 grpc
    1. 初始一个 grpc 对象
    2. 注册服务
    3. 设置监听, 指定 IP、port
    4. 启动服务。—— serve()
  • 客户端 grpc
    1. 连接 grpc 服务
      1. 给 grpc.Dial() 传参2 : grpc.WithInsecure() . 表示:以安全的方式操作。
  1. 初始化 grpc 客户端
  2. 调用远程服务。
    1. 给 grpc.SayHello() 传参1: context.TODO() 表示:空对象。

go-mirco

micro 简介

  • go-micro:微服务开发使用资源库。添加 RCP、GRPC
  • micro:微服务开发的生态。包含 微服务命令、API、插件。。。
  • go-plugins:微服务使用的插件。(新版微服务,舍弃了旧版的内容。)
  • 服务发现:微服务开发的核心

服务发现

  • 是微服务开发中,必须的 核心技术

1581734008422.png

  • 服务发现,也可以看做一个 “服务”, 是给 “服务” 提供服务的。

有服务发现后,client、server工作流程:

  1. 每个server启动时,都将自己的IP、port 和 服务名 注册给 ”服务发现“
  2. 当 client 向服务发现发起服务请求时, “服务发现” 会自动找一个可用的 服务,将其 IP/port/服务名返回给 client
  3. client 再借助服务发现,访问 server。

服务发现的种类:

  • consul: 常应用于 go-micro 中。
  • mdns:go-micro中默认自带的服务发现。
  • etcd:k8s 内嵌的服务发现
  • zookeeper:java中较常用。

consul 关键特性

  1. 服务发现: consul 提供服务, 服务端 主动向 consul 发起注册。
  2. 健康检查: 定时发送消息,类似于 “心跳包”,保证 客户端获得到的 一定是 健康的服务。
  3. 键值存储: consul 提供,但是我们使用 redis
  4. 多数据中心:可以轻松搭建集群。

consul 安装说明:

  1. 下载 consul: https://releases.hashicorp.com/consul/ 从中选择版本下载。得到 zip 压缩包。
  2. 解压缩 : 命令:unzip。
  3. 将解压缩的文件 拷贝至 /usr/local/bin/ —— 可执行文件。
  4. 终端使用 consul -h 验证 安装是否成功。

consul 常用命令

  • consul agent
    • -bind=0.0.0.0 指定 consul所在机器的 IP地址。 默认值:0.0.0.0
    • -http-port=8500 consul 自带一个web访问的默认端口:8500
    • -client=127.0.0.1 表明哪些机器可以访问consul 。 默认本机。0.0.0.0 所有机器均可访问。
    • -config-dir=foo 所有主动注册服务的 描述信息
    • -data-dir=path 储存所有注册过来的srv机器的详细信息。
    • -dev 开发者模式,直接以默认配置启动 consul
    • -node=hostname 服务发现的名字。
    • -rejoin consul 启动的时候,加入到的 consul集群
    • -server 以服务方式开启consul, 允许其他的consul 连接到开启的 consul上 (形成集群)。如果不加 -server, 表示以 “客户端” 的方式开启。不能被连接。
    • -ui 可以使用 web 页面 来查看服务发现的详情
  • 测试上述 命令:
    启动 Google 浏览器, 测试:
    1581737388273.png ```shell

    在终端中,键入:

    consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -node=n1 -bind=192.168.6.108 -ui -rejoin -config-dir=/etc/consul.d/ -client 0.0.0.0

看到提示:

==> Consul agent running!

  1. - consul members 查看集群中有多少个成员。
  2. - ![1581737508458.png](https://cdn.nlark.com/yuque/0/2021/png/2606456/1623244768656-84703112-0a19-46dd-98f9-d61430dcf508.png#clientId=ue286c265-ba01-4&from=ui&id=u9f00a49c&margin=%5Bobject%20Object%5D&name=1581737508458.png&originHeight=54&originWidth=683&originalType=binary&ratio=2&size=13987&status=done&style=none&taskId=u00b62b3d-afb9-4004-aa31-a1458a0a29e)
  3. - consul info 查看当前 consul IP 信息。
  4. - consul leave 优雅的关闭 consul 。—— 不优雅!Ctrl -c
  5. <a name="a184966a"></a>
  6. ### 注册服务到 consul 并查看
  7. 步骤:
  8. 1. 进入配置文件路径 cd /etc/consul.d/
  9. 2. 创建 json 文件。 sudo vim web.json
  10. 3. json 的语法,填写 服务信息。<br />![](%E8%AF%BE%E5%A0%82%E7%AC%94%E8%AE%B0-%E7%AC%AC2%E5%A4%A9.assets/1581738360648.png#id=Zjlsv&originalType=binary&ratio=1&status=done&style=none)
  11. 4. 重新启动 consul
  12. ```shell
  13. consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -node=n1 -bind=192.168.6.108 -ui -rejoin -config-dir=/etc/consul.d/ -client 0.0.0.0
  1. 查询 服务
    1. 浏览器查看:
      day02笔记 - 图3
    2. 终端命令查看:
      day02笔记 - 图4
      将 终端终端的 json数据,粘贴至 json在线网站中,查看消息。
      day02笔记 - 图5

健康检查

  1. sudo vim /etc/consul.d/web.json 打开配置文件
  2. 写入 服务的配置 信息。
    day02笔记 - 图6
  3. 执行命令,consul reload。 或者,关闭consul 再重启。
  4. 使用 浏览器 键入 192.168.6.108:8500 查看 “bj38” 这个服务 的 健康状况
    • 不健康!没有服务bj38 给 consul 实时回复!
  1. 除了 http 实现健康检查外,还可以使用 “脚本”、“tcp”、“ttl” 方式进行健康检查。

consul 和 grpc 结合

安装 consul 源码包:

  1. $ go get -u -v github.com/hashicorp/consul

使用整体流程

  1. 创建 proto文件 , 指定 rpc 服务
  2. 启动 consul 服务发现 consul agent -dev
  3. 启动server
    1. 获取consul 对象。
    2. 使用 consul对象,将 server 信息,注册给 consul
    3. 启动服务
  1. 启动client
    1. 获取consul 对象。
    2. 使用consul对象,从consul 上获取健康的 服务。
    3. 再访问服务 (grpc远程调用)

编码实现:

  • 用到的函数:
    ```go // 从 consul 服务发送上获取 健康服务 func (h Health) Service(service, tag string, passingOnly bool, q QueryOptions) ([]ServiceEntry, QueryMeta, error)

// 参数: service: 服务名。 — 注册服务时,指定该string tag:外名/别名。 如果有多个, 任选一个 passingOnly:是否通过健康检查。 true q:查询参数。 通常传 nil // 返回值: ServiceEntry: 存储服务的切片。 QueryMeta:额外查询返回值。 nil error: 错误信息

  1. - proto文件 <br />protoc --go_out=plugins=grpc:./ *.proto
  2. ```protobuf
  3. syntax = "proto3";
  4. package pb;
  5. message Person {
  6. string name = 1;
  7. int32 age = 2;
  8. }
  9. // 添加 rpc服务
  10. service hello {
  11. rpc sayHello (Person) returns (Person);
  12. }
  • 服务端
    ```go package main

import ( “google.golang.org/grpc” “day02/pb” “context” “net” “fmt” “github.com/hashicorp/consul/api” )

// 定义类 type Children struct { }

// 绑定类方法, 实现借口 func (this Children)SayHello(ctx context.Context, p pb.Person) (*pb.Person, error) { p.Name = “hello “ + p.Name return p, nil }

func main() { // 把grpc服务,注册到consul上. // 1. 初始化 consul 配置 consulConfig := api.DefaultConfig()

  1. // 2. 创建 consul 对象
  2. consulClient, err := api.NewClient(consulConfig)
  3. if err != nil {
  4. fmt.Println("api.NewClient err:", err)
  5. return
  6. }
  7. // 3. 告诉consul, 即将注册的服务的配置信息
  8. reg := api.AgentServiceRegistration {
  9. ID:"bj38",
  10. Tags:[]string{"grcp", "consul"},
  11. Name:"grpc And Consul",
  12. Address:"127.0.0.1",
  13. Port:8800,
  14. Check:&api.AgentServiceCheck{
  15. CheckID:"consul grpc test",
  16. TCP:"127.0.0.1:8800",
  17. Timeout:"1s",
  18. Interval:"5s",
  19. },
  20. }
  21. // 4. 注册 grpc 服务到 consul 上
  22. consulClient.Agent().ServiceRegister(&reg)

//////////////////////以下为 grpc 服务远程调用////////////////////////

  1. // 1.初始化 grpc 对象,
  2. grpcServer := grpc.NewServer()
  3. // 2.注册服务
  4. pb.RegisterHelloServer(grpcServer, new(Children))
  5. // 3.设置监听, 指定 IP/port
  6. listener, err := net.Listen("tcp", "127.0.0.1:8800")
  7. if err != nil {
  8. fmt.Println("Listen err:", err)
  9. return
  10. }
  11. defer listener.Close()
  12. fmt.Println("服务启动... ")
  13. // 4. 启动服务
  14. grpcServer.Serve(listener)

}

  1. - 客户端
  2. ```go
  3. package main
  4. import (
  5. "google.golang.org/grpc"
  6. "day02/pb"
  7. "context"
  8. "fmt"
  9. "github.com/hashicorp/consul/api"
  10. "strconv"
  11. )
  12. func main() {
  13. // 初始化 consul 配置
  14. consulConfig := api.DefaultConfig()
  15. // 创建consul对象 -- (可以重新指定 consul 属性: IP/Port , 也可以使用默认)
  16. consulClient, err := api.NewClient(consulConfig)
  17. // 服务发现. 从consuL上, 获取健康的服务
  18. services, _, err := consulClient.Health().Service("grpc And Consul", "grcp", true, nil)
  19. // 简单的负载均衡.
  20. addr := services[0].Service.Address + ":" + strconv.Itoa(services[0].Service.Port)
  21. //////////////////////以下为 grpc 服务远程调用///////////////////////////
  22. // 1. 链接服务
  23. //grpcConn, _ := grpc.Dial("127.0.0.1:8800", grpc.WithInsecure())
  24. // 使用 服务发现consul 上的 IP/port 来与服务建立链接
  25. grpcConn, _ := grpc.Dial(addr, grpc.WithInsecure())
  26. // 2. 初始化 grpc 客户端
  27. grpcClient := pb.NewHelloClient(grpcConn)
  28. var person pb.Person
  29. person.Name = "Andy"
  30. person.Age = 18
  31. // 3. 调用远程函数
  32. p, err := grpcClient.SayHello(context.TODO(), &person)
  33. fmt.Println(p, err)
  34. }

服务注销

  1. package main
  2. import "github.com/hashicorp/consul/api"
  3. func main() {
  4. // 1. 初始化 consul 配置
  5. consuConfig := api.DefaultConfig()
  6. // 2. 创建 consul 对象
  7. consulClient, _ := api.NewClient(consuConfig)
  8. // 3. 注销服务
  9. consulClient.Agent().ServiceDeregister("bj38")
  10. }

go-micro 安装

  • 在线安装: 参考讲义。
  • docker 镜像安装: 参考讲义。
  • 测试:micro 命令!

go-micro 的使用

new命令

  • 参数:
    • —namespace: 命名空间 == 包名
    • —type : 微服务类型。
      • srv: 微服务
      • web:基于微服务的 web 网站。

创建服务

  • micro new —type srv bj38 —— 创建一个微服务项目!

day02笔记 - 图7

  • main.go : 项目的入口文件。
  • handler/: 处理 grpc 实现的接口。对应实现接口的子类,都放置在 handler 中。
  • proto/: 预生成的 protobuf 文件。
  • Dockerfile:部署微服务使用的 Dockerfile
  • Makefile:编译文件。—— 快速编译 protobuf 文件。

查看创建的项目

  1. makefile 编译 proto
  2. 查看 make proto生 成 的 文件:
    1. xxx.pb.go
    2. xxx.micro.go
      • RegisterBj38Handler()【168行】服务端用 ———— 对应 grpc RegisterXXXService()
      • NewBj38Service()【47行】客户端用 —— 对应 grpc NewXXXClient() —— 对应自己封装的 IintXXX
  1. 查看 main.go

    1. func main() {
    2. // New Service -- 初始化服务器对象.
    3. service := micro.NewService(
    4. micro.Name("go.micro.srv.bj38"), // 服务器名
    5. micro.Version("latest"), // 版本
    6. )
    7. // Initialise service 与newService作用一致,但优先级高.后续代码运行期,初始化才有使用的必要.
    8. //service.Init()
    9. // Register Handler --- 注册服务
    10. bj38.RegisterBj38Handler(service.Server(), new(handler.Bj38))
    11. // Register Struct as Subscriber -- redis 发布订阅.
    12. //micro.RegisterSubscriber("go.micro.srv.bj38", service.Server(), new(subscriber.Bj38))
    13. // Register Function as Subscriber
    14. //micro.RegisterSubscriber("go.micro.srv.bj38", service.Server(), subscriber.Handler)
    15. // Run service --- 运行服务
    16. if err := service.Run(); err != nil {
    17. log.Fatal(err)
    18. }
    19. }
  2. 查看 handler/ xxx.go 文件
    包含 与 Interface 严格对应的 3 个函数实现!!

  3. XXX.micro.pb.go文件中客户端应该有三个实现

    1. XXXService的结构体
    2. NewXXXService的函数实现
    3. 实现调用的方法

服务端的实现

  1. XXXService的接口和结构体,结构体实现了该接口
  2. RegisterXXXHandler()函数,在grpc中是RegisterXXXService()函数
  3. 结构体所实现的接口的方法