前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go-micro In Action

go-micro In Action

原创
作者头像
xiaojunzhou
修改2019-07-04 10:05:49
2.1K0
修改2019-07-04 10:05:49
举报
文章被收录于专栏:后台开发学习后台开发学习

什么是 go-micro

go-micro是一个后台微服务开发框架,它提供了一个分布式系统开发所需的核心要求;其最大的特点是它是一个可插拔的架构,它对分布式系统的各个组成部分都抽象成接口,例如:

  • 客户端/服务端(Client/Server)
  • 服务发现(Registry)
  • 异步消息通信(Broker)
  • 负载均衡(Selector)
  • 传输层(Transport)
  • ...
go-micro 官方架构图
go-micro 官方架构图

什么是可插拔?就是你可以直接使用go-micro的默认实现或者在go-plugins中选择基于不同组件实现的插件库;甚至你可以基于go-micro框架抽象的接口来实现自己的插件库。

以上就是关于go-micro框架的简单说明,下面的内容会记录个人在具体项目中使用go-micro框架的心得以及问题总结(持续更新),希望能为大家提供一些帮助:

gprc.NewService() vs micro.NewService()

go-micro中主要提供了两种创建micro.Service的方式:

  1. gprc.NewService()
  2. micro.NewService()

第一种方式下底层传输协议使用的是grpc;第二种方式下底层传输协议使用的是http + protobuf。从实际比较来看,gprc的性能明显好于micro。关于两者的差异可参考这篇stackoverflow提问

micro.Service 全局化

在一个微服务中,一般会以Server的身份来提供服务,但是很多时候还会以Client的身份来调用其他微服务。我们通过示例代码来详细看一下:

以下Server的示例代码(一般为main.go中),会创建一个micro.Service实例:

代码语言:txt
复制
service := gprc.NewService()
service.Init()
// ...
proto.RegisterXXXServiceHandler(service.Server(), handler)
// ....
sevice.Run()

以下Client的示例代码,每次都创建一个micro.Service实例:

代码语言:txt
复制
service := gprc.NewService()
service.Init()
// ...
client := proto.NewXXXService("XXX", service.Client())
// ...

综上,其实我们可以在main.go中创建一个全局的Service来进行复用,而不是每次来新创建一个。

micro.Service初始化

使用命令行参数或者环境变量

micro web 工具

go-micro为我们提供了一个叫micro web的工具,它会以页面的形式为我们提供以下三个功能:

  1. CLI:一个命令行工具,你可以通过输入help来使用;
  2. Registry:列出了所有微服务,并可查看某个微服务的详情,例如Nodes,Endpoints等信息;
  3. Call: 直接在页面上调试某个微服务的接口。

以上这些方便我们调试某个微服务,以及查看微服务的健康情况等;不过有一点要注意的是,Call功能默认使用micro.NewService方式来调用的,如果你的微服务使用的是grpc.NewService的话,需要在执行命令micro web时增加相关参数。

问题总结

一. "go.micro.client", "request timeout", 408 问题原因总结

原因一:全局环境变量设置了http代理

go-micro中你可以使用micro.NewService或者grpc.NewService来创建micro.Service,这两种方式的区别是底层传输协议的不一样,前者是http,后者用的是grpc,但是不管怎么样,最终都是基于http的。所以如果你设置了http代理,就会出现408 Request Timeout的问题。以micro.NewService为例,我们来追溯一下源代码:

代码语言:txt
复制
// 代码调用流程:Client.Call -----> Transport.Dial
// 涉及代码文件:rpc_client.go, transport.go http_transport.go http_proxy.go
// 源代码位于 micro/transport/http_proxy.go
func getURL(addr string) (*url.URL, error) {
	r := &http.Request{
		URL: &url.URL{
			Scheme: "https",
			Host:   addr,
		},
	}
	return http.ProxyFromEnvironment(r)
}

上面注释中的ClientTransport都是go-micro抽象出来的接口,请注意最后一行http.ProxyFromEnvironment(r),它的作用是:如果你设置了http代理,那么它就会把代理地址返回,这样就导致Transport.Dial接口调用超时,go-micro框架会返回408错误。

代码语言:txt
复制
// 源代码位于 net/http/transport.go
func ProxyFromEnvironment(req *Request) (*url.URL, error) {
	return envProxyFunc()(req.URL)
}

那么golangnet/http包是如何获取代理地址的呢?请看下面的代码(golang version >= 1.11),就是根据几个环境变量来的:

代码语言:txt
复制
// 源代码位于 x/net/http/httpproxy/proxy.go
func FromEnvironment() *Config {
	return &Config{
		HTTPProxy:  getEnvAny("HTTP_PROXY", "http_proxy"),
		HTTPSProxy: getEnvAny("HTTPS_PROXY", "https_proxy"),
		NoProxy:    getEnvAny("NO_PROXY", "no_proxy"),
		CGI:        os.Getenv("REQUEST_METHOD") != "",
	}
}

在golang 1.10中没有使用上述几个环境变量,通过查阅1.10版本的proxy.go源代码文件可以确认。

原因二: context.Context 使用不规范

代码语言:txt
复制
func Hello(ctx context.Context, req *Request, rsp *Response) {
  go func() {
      // use ctx for other rpc call
  }()
}

出现408问题的情况除了第一种之外,还有一个情况。如上述示例代码所示,Hello是一个rpc接口:在其内部实现中起了一个goroutine,而这个goroutine内部又使用外部的ctx去调用了另外rpc接口。这种情况下,只有在grpc.NewService这种方式下来创建micro.Service才会出现该问题。所以这个估计与grpc-gogo-micro grpc的实现有关系,有兴趣的话,可以追一下它们的代码实现。

另外,再推荐三篇关于Context的文章,其中摘录了一段,可能是上述情况的原因:

当顶层的Request请求处理结束,或者外部取消了这次请求,就可以cancel掉顶层context,从而使整个请求的routine树得以退出。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是 go-micro
  • gprc.NewService() vs micro.NewService()
  • micro.Service 全局化
  • micro.Service初始化
  • micro web 工具
  • 问题总结
    • 一. "go.micro.client", "request timeout", 408 问题原因总结
    相关产品与服务
    负载均衡
    负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档