本文是Micro系列文章的第三篇。我们将以实际开发微服务为主线,顺带解析相关功能。从最基本的话题开始,逐步转到高级特性。

今天将讨论如何调用服务。

在前面文章中我们创建并运行了一个服务, 接下来就可以调用它了。

最简单的方式是利用micro这个多功能的命令行工具。

使用 micro 命令行工具调用服务

首先用list子命令列出可用服务:

$ micro list servicescom.foo.srv.hello
go.micro.http.broker

其中com.foo.srv.hello 是我们的目标服务,而 go.micro.http.broker 是 message broker 的默认实现,这里暂不作展开讨论。

调用前可以先用get子命令查看这个服务的详细情况,其中包含了服务版本、接口定义、运行节点等信息:

$ micro get service com.foo.srv.hello
service  com.foo.srv.helloversion latestID      Address Metadata
com.foo.srv.hello-7c5ef5ef-22d5-4a10-a5c9-1408d16562f9  10.0.1.6:56004  broker=http,protocol=mucp,registry=mdns,server=mucp,transport=httpEndpoint: Hello.CallRequest: {
        name string
}Response: {
        msg string
}...

然后用call子命令发起调用:

$ micro call com.foo.srv.hello Hello.Call '{"name": "Bill"}' { "msg": "Hello Bill" }

我们以 JSON 形式构建了请求(需要使用单引号转义), 并得到了期望的响应。

除上述形式, micro 也支持交互模式。

使用 micro cli 对服务进行交互式访问

进入交互模式很容易:

$ micro cli
micro>

调用cli子命令后, micro 将进入交互模式。

注:交互模式与非交互模式的命令不完全相同。前者的命令更简洁。例如,交互模式下列出所有服务的命令为list而不是list services。究其原因, 是因为非交互模式下需要支持更丰富的语义(例如,可以用list nodes, 列出所有网络中的节点)。个人认为这个设计不好, 带来一些概念上的不一致,不利于理解。

$ micro cli
micro> list
com.foo.srv.hello
go.micro.http.brokermicro>
micro> get com.foo.srv.hello
service  com.foo.srv.helloversion latestID      Address Metadata
com.foo.srv.hello-7c5ef5ef-22d5-4a10-a5c9-1408d16562f9
...
micro>
micro> call com.foo.srv.hello Hello.Call {"name": "Bill2"}
{ "msg": "Hello Bill2" }
micro>

注意,在交互模式下构建清求时无需单引号转义。

除了micro cli以外, 也可以通过浏览器访问交互命令工具。先在本机运行 micro web ,然后访问用浏览器访问 http://127.0.0.1:8082/terminal , 将得到一个功能相似的交互环境:

micro web, terminal

:虽然此界面试图提供与micro cli 相同的功能。但实际情况是, 至少在 v1.18.0 它的实现并不完整,且没有得到完备的测试。并且在 micro v2.0.0 后布后,此功能已被从 master 分支中移除。因此,不要使用此功能,避免浪费时间

使用 micro web 客户端调用服务

Micro 也提供的网页客户端,允许用户在浏览器中调用服务。

运行micro web以后, 访问 http://127.0.0.1:8082/client 地址将看到如下界面。

选择服务和 Endpoint 后输入请求内容,就可以通过“Execute”按钮调用服务了, 右侧区域以 JSON 格式显示了响应内容。



用代码调用服务

前面谈到几种方式只是方便了运行时调试。micro 服务真正的调用者, 还是代码。

下面我们创建一个客户端项目, 其结构如下:

.
├── main.go
├── plugin.go
├── proto/hello
│   └── hello.proto
│   └── hello.pb.go
│   └── hello.pb.micro.go
├── go.mod
├── go.sum

其中除了main.go 内容有所不同以外,其它文件的内容与含义均与上篇文章所描述的一致,此处不再赘述。main.go 中代码如下:

package main import ( "context" "fmt" "github.com/micro/go-micro" hello "hello/proto/hello" ) func main() { // New Service service := micro.NewService(
		micro.Name("com.foo.srv.hello.client"), //name the client service ) // Initialise service service.Init() //create hello service client helloClient := hello.NewHelloService("", service.Client()) //invoke hello service method resp, err := helloClient.Call(context.TODO(), &hello.Request{Name: "Bill 4"}) if err != nil {
		fmt.Println(err) return }

	fmt.Println(resp.Msg)
}
  • 首先创建并初始化 micro.Service 实例, 将其命名为 com.foo.srv.hello.client。这个名字并没有特殊意义,在真实项目中很可能完全不同。
  • 然后创建 HelloSevice 的客户端实例, 注意我们为 hello.NewHelloService方法传入的第一个参数是空字符串, 这是 micro 的一项约定。如果不指定服务名称, 那么客户端将使用服务的默认名(即 com.foo.srv.hello)进行查找和调用。方法的第二个参数是从 service 实例中取得的 github.com/micro/go-micro/client.Client 实例。
  • 接下来就是调用服务方法了。先传入context.Context实例 , 此处选择context.TODO()作了简化处理,在真实项目中你需要传入实际的 Context;然后传入入请求内容。

准备好代码以后即可编译运行:

$ go run main.go plugin.go
Hello Bill 4
$

将看到期望的响应结果。

如果此时服务并没有运行, 则会报出错误:

$ go run main.go plugin.go
{"id":"go.micro.client","code":500,"detail":"service com.foo.srv.hello: not found","status":"Internal Server Error"}
$

至此, 大功告成。

总结

本文讨论了如何调用一个已经存在服务。

micro 提供了丰富的工具, 可以用来在运行时查看服务状态及调用服务方法。这其中既有命令行工具, 也有基于 web 界面工具。

最后,我们创建了一个客户端项目,用代码真正完成了服务的调用。

推荐阅读

  • Micro In Action(二):项目结构与启动过程