go-micro框架简介

1. 框架简介

1.1 Micro 是什么?

官网定义如下:

Micro addresses the key requirements for building services in the cloud. It leverages the microservices architecture pattern and provides a set of services which act as the building blocks of a platform. Micro deals with the complexity of distributed systems and provides simpler programmable abstractions to build on.

看的不清不楚,好像明白了,又好像没有明白,对照其框架并且找了一些文档以后,我认为 Micro 的概念是:

Micro 是一组工具,能帮开发者快速构建并且管理微服务的工具,包含两部分:

  • go-micro:一个 Golang 微服务开发框架。它是核心。 开发者可以利用它快速开发出微服务。这些微服务最常见的形态是 gRPC。

  • micro:一个命令行工具。 虽非必须, 但它可以为 Micro 开发和管理提供很多便利。 例如, 生成模板项目, 查看服务运行状态, 调用服务等。 此工具也是基于 go-micro 开发的。

此外,还有一个重要的组件 pluins,这是一系列插件。涉及服务发现、异步消息、传输协议等方方面面。由于 go-micro 本身被设计成插件式架构, 配合这些插件可以达成非常灵活的组合效果,满足不同需求。 当然, 用户也可以自己开发插件,以便进一步扩展。

1.2 go-micro 架构

官网最新架构如下:

go-micro框架简介_第1张图片

 

以前版本架构图:

go-micro框架简介_第2张图片

 

Service 是系统中最核心的接口, 它负责将其它接口有机地组织在一起, 协调运行。

思考: Service的含义?听罗总在mesh中提过service和applicaiton这些?mesh中都是service而没有application的概念吗?

2. 框架搭建

本教程为搭建 go-micro v3 版本环境,前提go 版本必须为 1.16及以上

2.1 安装 protobuf 编译相关文件

  • protoc 编译器,直接 下载 相关 winzip 压缩文件,内含 protoc.exe 文件,解压后将该文件所在目录加入 Path

  • protoc-gen-go 插件,在 $GOPATH 目录下执行 go get github.com/micro/protobuf/{proto,protoc-gen-go},该命令会在 bin 目录下生成 protoc-gen-go(.exe) 工具,protoc 编译器利用 protoc-gen-go 插件将 .proto 文件转换为 Golang 源文件

  • protoc-gen-micro 插件,在 $GOPATH 目录下执行go get github.com/asim/go-micro/cmd/protoc-gen-micro/v3,该命令会在 bin 目录下生成 protoc-gen-micro(.exe)protoc 编译器利用 protoc-gen-micro 插件将 .proto 文件转换为 micro 代码风格文件

工具都安装完毕后,在 proto 文件夹下新建 proto 文件,文件内容如下:

syntax = "proto3";
option go_package = "./;OrderService";  // 使用该语句主要是有些版本 proto-gen-go不兼容,
                                        //必须要有这条语句才能成功编译生成文件,可以采用老版本解决该问题
package OrderService;
service OrderService {
  rpc CreateOrder (Request) returns(Response){}
}
message Request {
  string goodsId = 1;
  string buyNum = 2;
}
message Response{
  int32  code = 1;
  string msg =2;
  int64 orderID =3;
}

创建好以后,命令行进入 proto 文件夹目录,然后运行下面指令生成 .go 文件

protoc --go_out=. --micro_out=. .\order.go

运行后生成下面两个文件

  • order.pb.gp

  • order.pb.micro.go

注意,上述 proto 文件中,我们有写 option go_package = "./;OrderService";,这是因为新版本的 protoc-gen-go 插件如果不写该语句,那么运行会报下面错误:

protoc-gen-go: unable to determine Go import path for "proto/order.proto"
​
Please specify either:
        • a "go_package" option in the .proto source file, or
        • a "M" argument on the command line.
​
See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information.
​
--go_out: protoc-gen-go: Plugin failed with status code 1.

解决办法:

  1. 加上该语句

  2. proto-gen-go 版本降级,使用1.3.2老版本,go get -u github.com/golang/protobuf/[email protected]

2.2 创建服务端

服务端直接在 server 目录下创建 server.go 文件,该文件中我们启动服务,代码如下:

package main
​
import (
    "context"
    "fmt"
    "github.com/asim/go-micro/plugins/registry/consul/v3"
    "github.com/asim/go-micro/v3"
    "github.com/asim/go-micro/v3/registry"
    "github.com/syyongx/php2go"
    OrderService "go-micro-v3/proto"
)
​
type OrderServer struct{}
​
// CreateOrder 创建服务接口的实现OrderServiceHandler,后续OrderService就是服务的一个处理方
func (h *OrderServer) CreateOrder(ctx context.Context, req *OrderService.Request, rp *OrderService.Response) error {
    defer func() {
        if err := recover(); err != nil {
            return
        }
    }()
​
    goodsId := req.GoodsId
​
    rp.Code = 200
​
    generateOrderId := php2go.Rand(0, 121212) //github.com/syyongx/php2go
    //创建订单的逻辑
    generateOrderId_ := int64(generateOrderId)
​
    rp.OrderID = generateOrderId_
​
    rp.Msg = fmt.Sprintf("提交订单的goodsid为%s生成的订单id为%d", goodsId, generateOrderId)
​
    return nil
}
​
​
func main() {
    consulRegister := consul.NewRegistry(func(options *registry.Options) {
        options.Addrs = []string{
            "localhost:8500",
        }
    })
    // 创建注册中心客户端,并且在zk根目录创建micro-registry目录,该目录节点不携带任何信息
    //zkRegister := zookeeper.NewRegistry(func(options *registry.Options) {
    //  options.Addrs = []string{
    //      "localhost:2181",
    //  }
    //})
​
    // create a new service
    service := micro.NewService(
        micro.Name("order service v3"),
        micro.Registry(consulRegister),
    )
​
    // initialise flags
    service.Init()
​
    err := OrderService.RegisterOrderServiceHandler(service.Server(), new(OrderServer))
    if err != nil {
        return
    }
​
    // start the service
    err = service.Run()
    if err != nil {
        fmt.Println("service err", err)
    }
}

服务端最主要的几个步骤如下:

// 创建服务
service := micro.NewService(
    ...
)
​
// 初始化服务
service.Init()
​
// 注册 handler(此处 handler 就是我们在 proto 中定义的一个 service)
OrderService.RegisterOrderServiceHandler(service.Server(), new(OrderServer))
​
// 启动服务
err = service.Run()

时序图如下:

go-micro框架简介_第3张图片

 

2.2.1 创建服务

创建服务主要是配置服务的 optionsoptionsservice 的核心,里面存储了 service 需要的各种配置项,go-micro v3 原生提供31个函数帮助我们配置 options,如下所示:

  1. micro.Name(n string) Option , 指定服务名称。命名规则一般是“$namespace.$type.$name”。其中namespace 代表项目的名称空间, type代表服务类型(例如gRPC 和 web),一般会把gRPC service类型缩写成 srv。服务实例运行后, 此名称将自动注册到 Registry, 成为服务发现的依据。默认为“go.micro.server”。 :因此此项必须要指定, 否则所有节点使用相同的默认名称,会导致调用混乱

  2. micro.Version(v string) Option,指定服务版本。默认为启动时间格式化的字符串。恰当地选择版本号再配合相应的Selector, 可以实现优雅的轮转升级、灰度发布、A/B 测试等功能。

  3. micro.Address(addr string) Option,指定gRPC 服务地址。 默认为随机端口。由于客户端是通过注册中心来定位服务, 所以随机端口并不影响使用。 但实践中经常是指定固定端口号的, 这会有利于运维管理和安全控制

  4. micro.RegisterTTL(t time.Duration) Option,指定服务注册信息在注册中心的有效期。 默认为一分种

  5. micro.RegisterInterval(t time.Duration) Option,指定服务主动向注册中心报告健康状态的时间间隔, 默认为30秒。 这两个注册中心相关的Option结合起来用,可以避免因服务意外宕机而未通知注册中心,产生“无效注册信息”

  6. micro.WrapHandler(w …server.HandlerWrapper) Option,包装服务Handler, 概念上类似于 Gin Middleware, 集中控制Handler行为。可包装多层,执行顺序由外到内(后续会有实例)

  7. micro.WrapSubscriber(w …server.SubscriberWrapper) Option,与WrapHandler相似,不同之处在于它用来包装异步消费处理中的“订阅者”。

  8. micro.WrapCall(w …client.CallWrapper) Option,包装客户端发起的每一次方法调用。

  9. micro.WrapClient(w …client.Wrapper) Option,包装客户端,可包装多层, 执行顺序由内到外。

  10. micro.BeforeStart(fn func() error) Option,设置服务启动前回调函数,可设置多个。

  11. micro.BeforeStop(fn func() error) Option,设置服务关闭前回调函数,可设置多个。

  12. micro.AfterStart(fn func() error) Option,设置服务启动后回调函数,可设置多个。

  13. micro.AfterStop(fn func() error) Option,设置服务关闭后回调函数,可设置多个。

  14. micro.Action(a func(*cli.Context)) Option,处理命令行参数。 支持子命令及控制标记。 详情请见 micro/cli

  15. micro.Flags(flags …cli.Flag) Option,快捷支持命令行控制标记, 详情请见 micro/cli

  16. micro.Cmd(c cmd.Cmd) Option, 指定命令行处理对象。 默认由 newCmd生成,此对象包含了一系列默认的环境变量、命令行参数支持。 可以看作是多个内置cli.Flag的集合。: go-micro 框架对命令行处理的设计方案有利有弊。 利是提供大量默认选项,可以节省开发者时间。 弊是此设计对用户程序的有强烈的侵入性: 框架要求开发者必须以 micro/cli 统一要求的方式来处理命令行参数。如若不然, 程序会报错无法运行。 例如,我们运行 ./hello-service --foo=bar 就会报出“Incorrect Usage. flag provided but not defined: -foo=bar”的错误。 好在有这个Option,可以弥补这种强侵入性带来的弊端。假如一个现存项目想引入Micro ,而它已经有自己的参数处理机制, 那么就需要使用此Option覆盖默认行为(同时丢掉一些默认的参数处理能力)。 关于命令行参数, 本文后面部分有进一步解释。

  17. micro.Metadata(md map[string]string) Option,指定服务元数据。 元数据时常被用来为服务标记与分组, 实现特定的负载策略等

  18. micro.Transport(t transport.Transport) Option,指定传输协议, 默认为http协议

  19. micro.Selector(s selector.Selector) Option ,指定节点选择器, 实现不同负载策略。默认为随机Selector

  20. micro.Registry(r registry.Registry) Option,指定用于服务发现的注册机制, 默认为基于 mDNS 的注册机制

  21. micro.Server(s server.Server) Option, 指定自定义Server, 用于默认Server不满足业务要求的情况。默认为rpcServer

  22. micro.HandleSignal(b bool) Option, 是否允许服务自动响应 TERM, INT, QUIT 等信号。默认为true

  23. micro.Context(ctx context.Context) Option,指定服务初始Context,默认为context.BackGround(),可用于控制服务生存期及其它

  24. micro.Client(c client.Client) Option,指定对外调用的客户端。 默认为rpcClient

  25. micro.Broker(b broker.Broker) Option, 指定用于 发布/订阅 消息通讯的Broker。默认为http broker

  26. micro.Profile(p profile.Profile) Option,指定Profile对象,用于性能调优

  27. micro.Tracer(t trace.Tracer) Option,指定Trace对象,方便性能跟踪

  28. micro.Auth(a auth.Auth) Option,指定Auth对象, 用于自定义认证。 (根据官方Slack的说法,API尚不稳定,不建议v2.4.0中使用)

  29. micro.Config(c config.Config) Option,批定Config对象, 用于自定义配置。

  30. micro.Store(s store.Store) Option

  31. micro.Runtime(r runtime.Runtime)

服务创建后会包装其 clientserver ,包装完后,服务创建流程完毕

2.2.2 初始化服务

除了在创建服务的时候,可以传递上述31中函数对服务进行初始化,在服务初始化方法中,也接受上述函数对服务进行初始化,并且会读取命令行参数来加载插件和初始化cmd(用于命令行)(这一块儿还不是很懂)

2.2.3 注册 handler

注册 handler 主要是将我们实现的接口的处理方法的结构,注册到 router 中,主要流程为:创建 handler -> router 中保存 service -> server 中保存 handler

2.2.4 启动服务

服务启动时,会优先调用启动前函数(通过 option 函数进行配置),启动后会调用启动后函数(也是通过 option 函数进行配置),启动时,会启动一个端口(用户客户端连接的端口,如果不指定,端口随机),然后尝试连接 broker(默认为 http),然后检查注册信息是否有误,如果无误,则进行服务注册,服务注册完毕以后,开一个 goroutine 监听端口连接请求,然后再开一个 goroutine 定时更新(注册)服务,至此服务启动完成

2.3 创建客户端

客户端创建过程主要有三个步骤:

// 创建服务
service := micro.NewService(
    ...
)
​
// 初始化服务
service.Init()
​
// 创建 OrderService,类似 grpc 中客户端 stub 的概念,使用该 stub 发起请求调用,后续使用 stub 进行表示
order := OrderService.NewOrderService("order service v3", service.Client())

前两个步骤跟服务端创建一摸一样,此处不再赘述,我们看下创建 stub 的流程如下:

func NewOrderService(name string, c client.Client) OrderService {
    return &orderService{
        c:    c,
        name: name,
    }
}

如上所示,创建过程很简单,将 name 和 client 直接赋值即可,在请求时,会使用 name 查找服务,然后使用 client 对特定服务发送请求

2.4 请求调用流程

请求的调用跟调用本地方法一样,使用上面的 stub 直接调用即可,如下:

response, err := order.CreateOrder(context.TODO(), &OrderService.Request{
    GoodsId: php2go.Md5("123"),
    BuyNum:  "1",
})

请求调用流程如下:

go-micro框架简介_第4张图片

 

2.5 micro安装以及使用

2.5.1 安装

go get github.com/micro/micro/v3

2.5.2 运行服务

micro 现成的服务为例,首先运行 micro server,命令行信息如下:

C:\Users\802612523>micro server
2021-08-16 19:39:57  file=server/server.go:86 level=info Starting server
2021-08-16 19:39:57  file=server/server.go:114 level=info Registering registry
2021-08-16 19:39:57  file=server/server.go:114 level=info Registering broker
2021-08-16 19:39:57  file=server/server.go:114 level=info Registering network
2021-08-16 19:39:57  file=server/server.go:114 level=info Registering runtime
2021-08-16 19:39:58  file=server/server.go:114 level=info Registering config
2021-08-16 19:39:58  file=server/server.go:114 level=info Registering store
2021-08-16 19:39:58  file=server/server.go:114 level=info Registering events
2021-08-16 19:39:58  file=server/server.go:114 level=info Registering auth
2021-08-16 19:39:58  file=server/server.go:114 level=info Registering proxy
2021-08-16 19:39:58  file=server/server.go:114 level=info Registering api
2021-08-16 19:39:58  file=server/server.go:201 level=info Starting server runtime
2021-08-16 19:39:58  file=service/service.go:195 level=info Starting [service] server
2021-08-16 19:39:58  file=grpc/grpc.go:938 level=info Server [grpc] Listening on [::]:10001
2021-08-16 19:39:59  file=grpc/grpc.go:768 level=info Registry [service] Registering node: server-b463c702-d1ce-4aec-a158-e28de2caf98d

这表明服务启动成功,然后在新的命令行窗口,首先执行登录指令(不然后续指令会提示没有操作权限) micro login,用户名为:admin,密码为:micro,登陆成功以后,如下所示:

C:\Users\802612523>micro login
Enter username: admin
Enter password:
Successfully logged in.

接下来可以使用指令 micro services 查看所有运行的服务,如下:

C:\Users\802612523>micro services
api
auth
broker
config
events
network
proxy
registry
runtime
server
store

然后尝试启动一个 micro 服务,直接启动 micro 作者写的例子,运行指令 micro run github.com/micro/services/helloworld,结果报错

C:\Users\802612523>micro run github.com/micro/services/helloworld
zip: not a valid zip file

尝试将源码下载下来,然后进入 helloworld 所在文件夹,直接在本地启动服务,运行指令micro run .,结果仍就报错:

D:\>cd go\pkg\mod\github.com\micro\[email protected]
​
D:\go\pkg\mod\github.com\micro\[email protected]>micro run .
transport is closing

暂为找到解决办法,后续指令无法展示其效果,下面简单列一下后续指令的用法以及作用:

  • micro status:查看所有运行中的服务的信息

  • micro logs [service]:查看指定 service 日志信息

  • micro [service] [command]:调用服务

  • micro new [name]:创建一个 micro 项目,3.0创建结果有问题,需要修改后才能执行

  • micro cli:micro 命令行界面,进入后指令发生变化,使用 help 查看 micro 命令行支持的指令

3. go相关知识分享

3.1 go包管理

Go modulesGo 语言的依赖解决方案,发布于 Go1.11Go1.14 上已经明确建议生产上使用了。而 Go modules 之前,Go 项目使用 GOPATH 模式,先讨论下 GOPATH 模式的问题。

3.1.1 GOPATH

GOPATH 是布置 GO 开发环境时设置的一个环境变量。旧版本 go 项目开发过程中,需要将源码放在 GOPATHsrc 目录下,并且在使用 go get 指令获取依赖时,也会将代码直接下载到 GOPATHsrc 目录下,比如:

go get github.com/foo/bar

该指令会将代码下载到 $GOPATH/src/github.com/foo/bar

上面的 $GOPATH 可以通过指令 go env 获取,GOPATH 结构中必须包含下面三个文件夹:

GOPATH
├── bin              //编译生成的二进制文件
├── pkg              //预编译文件,以加快程序的后续编译速度
|── src              //所有源代码
    ├── github.com
    ...
    ...

GOPATH 模式的问题是在 GOPATH 模式下,go get 命令使用时,没有版本选择机制,拉下来的代码都是默认当前最新版本的代码,这回有一个非常严重的问题,项目 A 依赖项目 B 和 C,项目 B 依赖项目 C,如果 A 和 B 中 C 的版本不一致,并且是不兼容的,那此时由于 GOPATH 路径下只会有一个版本的 C 存在,会导致 C 无法同时满足 A 和 B 的要求,这可以说是致命的缺陷,因而 Go1.13 起,官方就不再推荐使用 GOPATH 的模式了。

3.1.2 GOPATH 衍生依赖管理工具

既然 GOPATH 有这么重大的缺陷,但是开发还要继续,那怎么办呢?GO 官方在 Go 1.5 的时候提出了实验性质的 vendor 机制:每个项目都可以有一个 vendor/ 目录来存放项目所需版本依赖的拷贝。(依赖冗余?)

社区中基于官方给的机制,开发出了各种版本管理工具。比较流行的比如 govendor,以及之前曾被官方认定的 godep 工具等。

这些工具的思路基本都是为每个项目单独维护一份对应版本依赖的拷贝

管理工具虽然丰富了起来,但是不同版本工具之间不兼容,无法协作,各种工具还都有学习成本。这时候在 Go 官方扶持下成立的 dep 项目被大家认为是未来一统江湖的版本管理工具,被称作 official experiment。

但是没过多久,Go 社区的核心人物 rsc 提出了 vgo 方案。一时间竟然出现了两个所谓的 Go 官方的版本管理方案。最终官方采用了 vgo 方案,随着 vgo 的逐渐成熟,Go 1.11 发布了该功能,并集成到了 Go 的官方工具中,也就是当前的 Go modules。

3.1.3 Go modules

使用 Go modules,一般涉及到如下 6 个环境变量的配置,go env 可以列出,摘录如下:

GO111MODULE="auto"
GOPROXY="https://goproxy.io,direct" 
GONOPROXY=""
GOSUMDB="sum.golang.org"
GONOSUMDB=""
GOPRIVATE=""

3.1.3.1 GO111MODULE

是开启使用 Go modules 的开关,可用参数值如下:

含义
auto 在 GOPATH/src 之外,将自动使用 Go Modules 模式。否则还是用 GOPATH 模式。目前在最新的 Go1.14 中是默认值。
on 启用 Go modules,将不使用 GOPATH,推荐设置,将会是未来版本中的默认值。
off 或者不设置 Go 将使用 GOPATH 和沿用老的 vendor 机制,禁用 Go modules。不推荐设置。

设置开启 on 的方法

go env -w GO111MODULE=on

也可在 .bash_profile 文件中直接环境变量方式设置,增加

export GO111MODULE=on

3.1.3.2 GOPROXY

用于设置 Go 模块代理,其作用是使 Go 在拉取模块版本时直接通过镜像站点来快速拉取默认值是这个https://proxy.golang.org,direct/,国内无法访问,所以开启 Go Modules 必需设置镜像代理地址

列举国内常用的镜像代理地址如下:

地址 简介
https://goproxy.io 一个全球代理为 Go 模块而生
https://mirrors.aliyun.com/goproxy/ 阿里镜像代理
https://goproxy.cn 七牛云赞助支持的

GOPROXY 的值是一个以英文逗号 “,” 分割的 Go 模块代理列表,允许设置多个模块代理,不使用,可设为 “off”

默认值中有个 direct,用来告诉 Go 将直接从依赖的源地址进行下载操作(比如 GitHub 等),例如当值列表中上一个 Go 代理返回 404 或 410 时,Go 自动尝试列表中的下一个,遇见 “direct” 时回到源地址去抓取。

最终:设置代理方法一般如下:如使用七牛网代理

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

也可在 .bash_profile 文件中直接环境变量方式设置,增加

export GOPROXY=https://goproxy.cn,direct

3.1.3.3 GOSUMDB

Go checksum database 的缩写,含义如其名字,用于在拉取模块版本时保证拉取到的模块版本数据未经过篡改,若发现不一致,也就是可能存在篡改,将会立即中止。

GOSUMDB 的默认值为:sum.golang.org,在国内也是无法访问的,但是 GOSUMDB 可以被 Go 模块代理所代理,即设置 GOPROXY 自然就解决了。

若设置为“off”,就禁止 Go 在后续操作中校验模块版本。

3.1.3.4 GONOPROXY / GONOSUMDB / GOPRIVATE

如果当前项目依赖了私有模块,则配置会涉及这三个环境变量。例如公司的私有 git 仓库,又或是 github 中的私有库,都是属于私有模块,都是要进行设置的,否则会拉取失败。简单来说就是应对 GOPROXY 设置的代理或 GOSUMDB 设定的 Go checksum database 代理无法访问模块时的情形。

建议直接设置 GOPRIVATE,它的值将作为 GONOPROXY 和 GONOSUMDB 的默认值,所以建议的最佳设置是直接使用 GOPRIVATE。

它们的值都是一个以英文逗号 “,” 分割的模块路径前缀,也就是可以设置多个,例如:

go env -w GOPRIVATE="*.my.com,github.com/eabc/def"

设置后,前缀为 *.my.com 和 github.com/eabc/def 的模块都会被认为是私有模块。

将不经过 Go module proxy 和 Go checksum database,需要注意的是不包括 my.com 本身。

3.1.3.5 Go modules 操作命令

go mod 支持的指令如下:

download    download modules to local cache 常用,下载依赖包
edit        edit go.mod from tools or scripts   ide编辑就行
graph       print module requirement graph  查看使用而已
init        initialize new module in current directory  常用
tidy        add missing and remove unused modules   常用
vendor      make vendored copy of dependencies  从mod cache中拷贝到项目的vendor
verify      verify dependencies have expected content
why         explain why packages or modules are needed

至于命令的具体使用,此处不再延申讲解

3.2 go 中值类型和引用类型

在 go 中,一个值分为两种类型:值类型和引用类型,go 里面的引用类型有如下几种:

  • 指针

  • slice 切片

  • chan 管道

  • interface 接口

  • map

  • 函数

golang没有引用传递,都是值传递

  • 如果函数形参是值类型,则会对值类型做一份拷贝作为函数形参。在函数内对形参变量做的修改,不会影响函数外的那个被传入的变量。

  • 如果函数形参是引用类型,则会对引用类型变量做一次拷贝。但是拷贝得到的引用类型变量的值,和被传入调用函数的原始引用类型变量的值,是一样的,即指向的是同一个变量的地址(参考前面值类型变量和引用类型变量图)。所以在函数里面的修改,会影响原始引用变量指向的变量的值。

3.3 duck typing(Structural type)

鸭子类型(英语:duck typing)在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。

鸭子类型,是动态编程语言的一种对象推断策略,它更关注对象能如何被使用,而不是对象的类型本身。Go 语言作为一门静态语言,它通过通过接口的方式完美支持鸭子类型。

3.4 闭包

闭包是指可以包含自由变量(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内活在任何全局上下文中定义的,而是在定义代码块的环境中定义的(局部变量),当在这个代码块所在环境的外部调用该代码块时,代码块和它所引用的自由变量构成闭包。如下图所示:

go-micro框架简介_第5张图片

 

当使用闭包方式访问某个局部变量时,该局部变量会常驻内存,访问速度会特别快,并且我们始终操作的都是这个变量的本身,而不是其副本,修改时也特别方便

(1) 通常是通过嵌套的匿名函数的形式实现的; (2) 匿名函数内部引用了外部函数的参数或变量; (3) 被引用的参数和变量的生命周期和外部调用者的生命周期相同。

我的理解是闭包的作用是动态的生成函数

3.5 值接收者和指针接收者

方法能给用户自定义的类型添加新的行为。它和函数的区别在于方法有一个接收者,给一个函数添加一个接收者,那么它就变成了方法。接收者可以是值接收者,也可以是指针接收者

在调用方法的时候,值类型既可以调用值接收者的方法,也可以调用指针接收者的方法;指针类型既可以调用指针接收者的方法,也可以调用值接收者的方法。也就是说,不管方法的接收者是什么类型,该类型的值和指针都可以调用,不必严格符合接收者的类型。

**如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法。

4. esa registry go sdk

开发 go 版本 sdk,首相需要将请求进行抽象,用户配置 esa registry,生成一个 esa registry 的配置对象,然后将该对象转换为 HTTP.request 进行发送

你可能感兴趣的:(golang,java,微服务)