go-micro 动态加载插件源码分析

go-micro 框架支持动态加载插件,无需修改代码。 源码分析 启动服务前,设定 MICRO_PLUGIN 环境变量指定动态库 .so 文件路径,支持多个插件,逗号分割。程序启动前会读取 MICRO_PLUGIN 环境变量,并完成插件设定。 下面是其内部实现: go-micro/service.go func (s *service) Init(opts ...Option) { ... // setup the plugins for _, p := range strings.Split(os.Getenv("MICRO_PLUGIN"), ",") { if len(p) == 0 { continue } // 加载 .so 文件 c, err := plugin.Load(p) if err != nil { logger.Fatal(err) } // go-micro 初始化插件 if err := plugin.Init(c); err != nil { logger.Fatal(err) } } 从上面的代码可以看出,service 初始化化的时候,读取 MICRO_PLUGIN 环境变量中指定的 ....

March 28, 2020 · 5 min · 徐旭

Go Micro hystrix 熔断

hystrix-go hystrix是Netflix开源的一个JAVA项目,不过GitHub也有golang的实现版本hystrix-go hystrix-dashboard hystrix并没有自带一个仪表盘,无法直观的查看接口的健康状况。所以,我们采用GitHub的一个开源实现hystrix-dashboard。 docker run --name hystrix-dashboard -d -p 8081:9002 mlabouardy/hystrix-dashboard:latest micro API网关插件 关于hystrix的工作原理,可以查阅相关资料,这里只讲解如何封装插件在micro API网关中使用。 err := hystrix.Do("my_command", func() error { // talk to other services return nil }, nil) 使用hystrix.Do() 同步API,第一个参数是command, 应该是与当前请求一一对应的一个名称,如入“GET-/test”。第二个参数传入一个函数,函数包含我我们自己的错误逻辑,当请求失败时应该返回error。hystrix会根据我们的失败率执行熔断策略。 封装Handler // BreakerWrapper hystrix breaker func BreakerWrapper(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { name := r.Method + "-" + r.RequestURI log.Println(name) err := hystrix.Do(name, func() error { sct := &status_code.StatusCodeTracker{ResponseWriter: w, Status: http.StatusOK} h....

June 27, 2019 · 2 min · 徐旭

Go Micro 服务健康检查

服务健康检查 在微服务架构中,每个服务都会存在多个实例,可能部署在不同的主机中。因为网络或者主机等不确定因素,每个服务都可能会出现故障。我们需要能够监控每个服务实例的健康状态,当一个服务故障时,及时将它从注册中心删除。 实现 micro提供两个方法可以直接实现健康检查功能 micro.RegisterTTL(time.Second*30), micro.RegisterInterval(time.Second*20), Interval就是间隔多久服务会重新注册 TTL就是注册服务的过期时间,如果服务挂了,超过过期时间后,注册中心也会将服务删除 micro内部服务注册的流程 当我们执行service.Run() 时内部会执行Start() 在Start函数中又会执行s.opts.Server.Start(),方法的实现在go-micro/server/rpc_server.go中。 我们跳转到内部server的Start方法 可以发现micro使用一个定时器按照间隔时间去自动重新注册。当服务意外故障,无法向注册中心重新注册时,如果超过了设定的TTL时间,注册中心就会将服务删除。 修改源码 service := grpc.NewService( micro.Name("go.micro.srv.hello"), micro.WrapHandler(ocplugin.NewHandlerWrapper(t)), + micro.RegisterTTL(time.Second*15), + micro.RegisterInterval(time.Second*10), // micro.Version("latest"), ) service := web.NewService( web.Name(name), web.Version("lastest"), + web.RegisterTTL(time.Second*15), + web.RegisterInterval(time.Second*10), web.MicroService(grpc.NewService()), )

June 27, 2019 · 1 min · 徐旭

Go Micro API网管增加 JWT 鉴权

micro API网关 micro API网关是基于go-micro开发的,具有服务发现,负载均衡和RPC通信的能力。 业界普遍做法是将鉴权,限流,熔断等功能也纳入API网关。micro API网关本身是可插拔的,可以通过新增插件的方式加入其他功能。 JWT (JSON Web Token) JWT是是微服务中常用的授权技术,关于JWT的技术原理可以参考阮一峰的博文 JWT库封装 lib/token 目录下封装了JWT的库。有一点特殊的是,库中利用consul的KV存储和micro的go-config库实现了动态更新JWT的PrivateKey功能,实际生产中还是应该使用拥有发布和权限管理的配置中心。 go-config 是micro作者实现的一个可动态加载、可插拔的配置库,可以从多种格式文件或者远程服务获取配置。详情可以参考文档中文文档|英文文档 PrivateKey是JWT在编解码时使用的私钥,一旦泄漏,客户端便可以利用这个私钥篡改、伪造Token。所以一般生产环境中都必须具备动态更新私钥的能力,一旦发现泄漏可以立即更改,或者定期更换私钥,提高安全性。 // InitConfig 初始化 func (srv *Token) InitConfig(address string, path ...string) { consulSource := consul.NewSource( consul.WithAddress(address), ) srv.conf = config.NewConfig() err := srv.conf.Load(consulSource) if err != nil { log.Fatal(err) } value := srv.conf.Get(path...).Bytes() if err != nil { log.Fatal(err) } srv.put(value) log.Println("JWT privateKey:", string(srv.get())) srv.enableAutoUpdate(path...) } func (srv *Token) enableAutoUpdate(path ....

June 24, 2019 · 2 min · 徐旭

Go Micro jaeger 分布式链路追踪

安装jaeger jaeger提供一个all in one 的docker镜像,可以快速搭建实验环境 docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 -p 9411:9411 jaegertracing/all-in-one:1.6 OpenTracing OpenTracing通过提供平台无关、厂商无关的API,使得开发人员能够方便的添加(或更换)追踪系统的实现。 OpenTracing提供了用于运营支撑系统的和针对特定平台的辅助程序库。 jaeger兼容OpenTracing API,所以我们使用OpenTracing的程序库可以方便的替换追踪工具。 OpenTracing中文文档 jaeger使用 封住一下jaeger的初始化操作方便使用,详细用法可以查看 jaeger-client-go // lib/tracer // NewTracer 创建一个jaeger Tracer func NewTracer(servicename string, addr string) (opentracing.Tracer, io.Closer, error) { cfg := jaegercfg.Configuration{ ServiceName: servicename, Sampler: &jaegercfg.SamplerConfig{ Type: jaeger.SamplerTypeConst, Param: 1, }, Reporter: &jaegercfg.ReporterConfig{ LogSpans: true, BufferFlushInterval: 1 * time....

June 24, 2019 · 3 min · 徐旭

Go Micro 重试机制

在分布式系统中,经常会有服务出现故障,所以良好的重试机制可以大大的提高系统的可用性。本文主要分析micro的客户端重试机制,以及实例演示。 micro 重试实现 micro框架提供方法设置客户端重试的次数。 Client.Init( client.Retries(3), ) 当client请求失败时,客户端会根据selector的策略选择下一个节点重试请求。这样当一个服务实例故障时,客户端可以自动调用另一个实例。 我们来看看micro 客户端内部重试的实现: go-micro\client\rpc_client.go func (r *rpcClient) Call(ctx context.Context, request Request, response interface{}, opts ...CallOption) error { ... //客户端call 调用函数, 在下面的循环中调用 call := func(i int) error { // call backoff first. Someone may want an initial start delay t, err := callOpts.Backoff(ctx, request, i) if err != nil { return errors.InternalServerError("go.micro.client", "backoff error: %v", err.Error()) } // only sleep if greater than 0 if t....

June 20, 2019 · 3 min · 徐旭