关于micro:go-micro-metrics-接入PrometheusGrafana

本文介绍go micro中退出metric, 并接入Prometheus、Grafana 1.go micro中间件加载prometheus pluginsgo micro中提供了prometheus plugins github.com/micro/go-plugins/wrapper/monitoring/prometheus/ package mainimport ( "net/http" "github.com/micro/go-plugins/wrapper/monitoring/prometheus/v2" "github.com/prometheus/client_golang/prometheus/promhttp" //...)func main() { // New Service service := micro.NewService( micro.Name("go.micro.api.myauth"), micro.Version("latest"), micro.WrapHandler(prometheus.NewHandlerWrapper()), ) // Initialise service service.Init( // create wrap for the Myauth service client micro.WrapHandler(client.MyauthWrapper(service)), ) go PrometheusBoot() // Register Handler myauth.RegisterMyauthHandler(service.Server(), new(handler.Myauth)) // Run service if err := service.Run(); err != nil { log.Fatal(err) }}func PrometheusBoot() { http.Handle("/metrics", promhttp.Handler()) // 启动web服务,监听8085端口 go func() { err := http.ListenAndServe("localhost:8085", nil) if err != nil { log.Fatal("ListenAndServe: ", err) } }()}代码很简略,在micro.NewService中传入micro.WrapHandler(prometheus.NewHandlerWrapper()), ...

August 6, 2020 · 2 min · jiezi

关于micro:go-micro-wrapper-中间件

go micro wrapper 中间件 本篇不波及中间件底层是如何运作的,如有趣味请见[micro server]篇 在options.go中有如下定义 // WrapClient is a convenience method for wrapping a Client with// some middleware component. A list of wrappers can be provided.// Wrappers are applied in reverse order so the last is executed first.func WrapClient(w ...client.Wrapper) Option { return func(o *Options) { // apply in reverse for i := len(w); i > 0; i-- { o.Client = w[i-1](o.Client) } }}// WrapCall is a convenience method for wrapping a Client CallFuncfunc WrapCall(w ...client.CallWrapper) Option { return func(o *Options) { o.Client.Init(client.WrapCall(w...)) }}// WrapHandler adds a handler Wrapper to a list of options passed into the serverfunc WrapHandler(w ...server.HandlerWrapper) Option { return func(o *Options) { var wrappers []server.Option for _, wrap := range w { wrappers = append(wrappers, server.WrapHandler(wrap)) } // Init once o.Server.Init(wrappers...) }}// WrapSubscriber adds a subscriber Wrapper to a list of options passed into the serverfunc WrapSubscriber(w ...server.SubscriberWrapper) Option { return func(o *Options) { var wrappers []server.Option for _, wrap := range w { wrappers = append(wrappers, server.WrapSubscriber(wrap)) } // Init once o.Server.Init(wrappers...) }}这些参数设置用于micro.NewService()参数 ...

August 6, 2020 · 3 min · jiezi

关于micro:go-micro-熔断与限流

本篇文章参考【Micro In Action(七):熔断与限流】 https://medium.com/@dche423/m... @dche423写的太好了,这里仅做局部摘录介绍 熔断go micro 封装了hystrix-go,gobreaker,都在plugins下 上面是hystrix的例子 import ( ... "github.com/micro/go-plugins/wrapper/breaker/hystrix/v2" ...)func main(){ ... // New Service service := micro.NewService( micro.Name("com.foo.breaker.example"), micro.WrapClient(hystrix.NewClientWrapper()), ) // Initialise service service.Init() ...}他的默认值超时工夫是1000毫秒,最大并发数是10 // DefaultTimeout is how long to wait for command to complete, in milliseconds DefaultTimeout = 1000 // DefaultMaxConcurrent is how many commands of the same type can run at the same time DefaultMaxConcurrent = 10其余参数请见官网,https://github.com/afex/hystr... ...

August 6, 2020 · 2 min · jiezi

关于micro:go-micro-链路追踪

本片介绍go micro中应用jaeger作为链路追踪的应用 jaeger相干常识请见官网文档,这里应用docker启动gaeger,作为测试应用 启动jaeger docker run -d -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:latest 拜访地址http://localhost:16686 go micro版本2.x,和1.x有些许不同 样例代码应用examples/greeter中代码批改srv/mian.go // Package mainpackage mainimport ( "context" "io" "time" hello "github.com/micro/examples/greeter/srv/proto/hello" "github.com/micro/go-micro/v2" "github.com/micro/go-micro/v2/util/log" wrapperTrace "github.com/micro/go-plugins/wrapper/trace/opentracing/v2" "github.com/opentracing/opentracing-go" "github.com/uber/jaeger-client-go" jaegercfg "github.com/uber/jaeger-client-go/config")type Say struct{}func (s *Say) Hello(ctx context.Context, req *hello.Request, rsp *hello.Response) error { log.Log("Received Say.Hello request") rsp.Msg = "Hello " + req.Name return nil}func main() { t, io, err := NewTracer("tracer-srv", "127.0.0.1:6831") if err != nil { log.Fatal(err) } defer io.Close() opentracing.SetGlobalTracer(t) service := micro.NewService( micro.Name("go.micro.srv.greeter"), micro.WrapHandler(wrapperTrace.NewHandlerWrapper(opentracing.GlobalTracer())), ) // optionally setup command line usage service.Init() // Register Handlers hello.RegisterSayHandler(service.Server(), new(Say)) // Run server if err := service.Run(); err != nil { log.Fatal(err) }}// NewTracer 创立一个jaeger Tracerfunc 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.Second, }, } sender, err := jaeger.NewUDPTransport(addr, 0) if err != nil { return nil, nil, err } reporter := jaeger.NewRemoteReporter(sender) // Initialize tracer with a logger and a metrics factory tracer, closer, err := cfg.NewTracer( jaegercfg.Reporter(reporter), ) return tracer, closer, err}这里封装了`NewTracer()`创立一个jaeger Tracer ...

August 6, 2020 · 2 min · jiezi

关于micro:go-micro-jwt-网关鉴权

本文介绍go micro中的api拜访权限管制,这里仅仅介绍外部服务间的相互调用鉴权(jwt),不是针对用户零碎的粗疏rbac模式。 指标: 外部服务间调用应在header上带上jwt token,否则返回谬误提示信息有2种形式 在每个服务上各自鉴权在网关上鉴权,各服务只需注册一个wrapper查看header中jwt信息鉴权。这样做有个前提,所有服务需放到同一个网络内,走对立的服务注册核心,这里次要介绍这种形式。先创立一个myauth的服务,对外提供2个接口 GetJwt() 返回jwt token,能够用用户名明码或其余形式验证,依据具体业务灵便决定即可,这里示例采纳服务名和密钥方InspectJwt() 验证jwt token是否无效自定义一个网关,通过插件形式wrapper指定一个鉴权函数,除了第3步中的2个接口外,其余都须要header中有jwt的token其余业务只须要注册一个wrapper查看header中jwt信息鉴权业务调用步骤 调用GetJwt()获取jwt token调用其余接口时,在header退出jwt token首先创立一个api我的项目 micro new --type=api myauth GetJwt()、InspectJwt()相干代码 // Myauth.Call is called by the API as /myauth/call with post body {"name": "foo"}func (e *Myauth) GetJwt(ctx context.Context, req *api.Request, rsp *api.Response) error { log.Info("Received Myauth.GetJwt request") getmap := req.GetGet() log.Info("Received getmap %+v\n", getmap) postmap := req.GetPost() log.Info("Received postmap %+v\n", postmap) // 外部服务调用底层service,通过jwt验证 // 定义服务名和key,通过这2个参数获取jwt access token // serviceName := "order" // serviceKey := "123456" // 比照serviceName\serviceKey 也能够是用户名明码等,这里的示例为了不便硬编码在代码中 // 理论我的项目中应该从数据库或文件读取 serviceName := extractValue(postmap["service"]) serviceKey := extractValue(postmap["key"]) log.Info("serviceName %+v\n", serviceName, serviceKey) if serviceName != "order" || serviceKey != "123456" { Rsp(rsp, 403, "服务名称或key谬误", nil) return nil } //生成jwt // expireTime := time.Now().Add(time.Hour * 24 * 3).Unix() expireTime := time.Now().Add(time.Second * 60 * 60).Unix() token := &token.Token{} token.Init([]byte("key123456")) //理论我的项目需从数据库或文件读取 jwtstring, err := token.Encode("auth jwt", serviceName, expireTime) if err != nil { Rsp(rsp, 403, "jwt 生成谬误", nil) return nil } msg := make(map[string]interface{}) msg["jwt"] = jwtstring Rsp(rsp, 200, "ok", msg) return nil}// 验证jwtfunc (e *Myauth) InspectJwt(ctx context.Context, req *api.Request, rsp *api.Response) error { log.Info("Received Myauth.InspectJwt request") // getmap := req.GetGet() // log.Info("Received getmap %+v\n", getmap) postmap := req.GetPost() // log.Info("Received postmap %+v\n", postmap) jwtString := extractValue(postmap["jwt"]) log.Info("jwtString %+v\n", jwtString) if len(jwtString) == 0 { Rsp(rsp, 403, "jwt参数谬误", nil) return nil } //解析jwt token := &token.Token{} token.Init([]byte("key123456")) info, err := token.Decode(jwtString) if err != nil { Rsp(rsp, 403, "jwt 解析谬误", nil) //过期或jwt有问题 return nil } t := make(map[string]interface{}) t["data"] = info Rsp(rsp, 200, "ok", t) return nil}// 返回funcfunc Rsp(rsp *api.Response, code int, err string, msg map[string]interface{}) error { if msg == nil { msg = make(map[string]interface{}) } r := &rspMsg{ Code: code, Err: err, Msg: msg, } b, err2 := json.Marshal(r) if err2 != nil { log.Info("json.Marshal err", err2) } rsp.StatusCode = int32(code) rsp.Body = string(b) return nil}自定义micro网关代码 ...

August 6, 2020 · 2 min · jiezi

关于micro:go-micro-plugin

这篇文章中的 plugin 次要讲https://github.com/micro/micro 中的插件,次要用于自定义网关中如何加载插件。(如文章[micro auth jwt]) go-micro中的插件请见https://github.com/micro/go-p... 官网README中有一些介绍 https://github.com/micro/micr... 官网示例: 在我的项目目录创立plugin.go package mainimport ( "log" "github.com/micro/cli/v2" "github.com/micro/micro/v2/plugin")func init() { plugin.Register(plugin.NewPlugin( plugin.WithName("example"), plugin.WithFlag(cli.StringFlag{ Name: "example_flag", Usage: "This is an example plugin flag", EnvVars: []string{"EXAMPLE_FLAG"}, Value: "avalue", }), plugin.WithInit(func(ctx *cli.Context) error { log.Println("Got value for example_flag", ctx.String("example_flag")) return nil }), ))}最初编译 `go build -o micro ./main.go ./plugin.go` 一步步看看是怎么注册的,在micro/plugin/manager.go中 type manager struct { sync.Mutex plugins []Plugin registered map[string]bool}var ( // global plugin manager defaultManager = newManager())func newManager() *manager { return &manager{ registered: make(map[string]bool), }}plugin包有默认`defaultManager = newManager()`,是一个manager{}对象 ...

August 6, 2020 · 2 min · jiezi

关于micro:go-micro-registry-插件consul

go micro v2版本中,consul不在默认反对,官网举荐应用etcd,具体起因官网blog有讲https://medium.com/microhq/de... consul被放到go-plugins, 由社区保护github.com/micro/go-plugins/registry/consul 要应用consul,须要从go-plugins引入 import ( "fmt" "os" "context" "github.com/micro/cli/v2" proto "github.com/micro/examples/service/proto" "github.com/micro/go-micro/v2" "github.com/micro/go-micro/v2/transport/grpc" "github.com/micro/go-plugins/registry/consul/v2")/*Example usage of top level service initialisation*/type Greeter struct{}func (g *Greeter) Hello(ctx context.Context, req *proto.Request, rsp *proto.Response) error { rsp.Greeting = "Hello " + req.Name return nil}func main() { //reg := consul.NewRegistry() // Create a new service. Optionally include some options here. service := micro.NewService( micro.Name("greeter"), micro.Version("latest"), micro.Metadata(map[string]string{ "type": "helloworld", }), micro.Registry(reg), micro.Transport(grpc.NewTransport()), // Setup some flags. Specify --run_client to run the client // Add runtime flags // We could do this below too micro.Flags(&cli.BoolFlag{ Name: "run_client", Usage: "Launch the client", }), ) // Init will parse the command line flags. Any flags set will // override the above settings. Options defined here will // override anything set on the command line. service.Init( // Add runtime action // We could actually do this above micro.Action(func(c *cli.Context) error { if c.Bool("run_client") { runClient(service) os.Exit(0) } return nil }), ) // By default we'll run the server unless the flags catch us // Setup the server // Register handler proto.RegisterGreeterHandler(service.Server(), new(Greeter)) // Run the server if err := service.Run(); err != nil { fmt.Println(err) }}两种形式应用插件,具体见[go-plugins应用阐明](https://github.com/micro/go-p... ...

August 6, 2020 · 2 min · jiezi

关于micro:go-micro-registry-插件consul

go micro v2版本中,consul不在默认反对,官网举荐应用etcd,具体起因官网blog有讲https://medium.com/microhq/de... consul被放到go-plugins, 由社区保护github.com/micro/go-plugins/registry/consul 要应用consul,须要从go-plugins引入 import ( "fmt" "os" "context" "github.com/micro/cli/v2" proto "github.com/micro/examples/service/proto" "github.com/micro/go-micro/v2" "github.com/micro/go-micro/v2/transport/grpc" "github.com/micro/go-plugins/registry/consul/v2")/*Example usage of top level service initialisation*/type Greeter struct{}func (g *Greeter) Hello(ctx context.Context, req *proto.Request, rsp *proto.Response) error { rsp.Greeting = "Hello " + req.Name return nil}func main() { //reg := consul.NewRegistry() // Create a new service. Optionally include some options here. service := micro.NewService( micro.Name("greeter"), micro.Version("latest"), micro.Metadata(map[string]string{ "type": "helloworld", }), micro.Registry(reg), micro.Transport(grpc.NewTransport()), // Setup some flags. Specify --run_client to run the client // Add runtime flags // We could do this below too micro.Flags(&cli.BoolFlag{ Name: "run_client", Usage: "Launch the client", }), ) // Init will parse the command line flags. Any flags set will // override the above settings. Options defined here will // override anything set on the command line. service.Init( // Add runtime action // We could actually do this above micro.Action(func(c *cli.Context) error { if c.Bool("run_client") { runClient(service) os.Exit(0) } return nil }), ) // By default we'll run the server unless the flags catch us // Setup the server // Register handler proto.RegisterGreeterHandler(service.Server(), new(Greeter)) // Run the server if err := service.Run(); err != nil { fmt.Println(err) }}两种形式应用插件,具体见[go-plugins应用阐明](https://github.com/micro/go-p... ...

August 6, 2020 · 2 min · jiezi

关于micro:go-micro-web

examples/web 有一个web的例子,这里比较简单 service.HandleFunc("/", helloWorldHandler) 这一行指定处理程序比较简单,第2个参数定义了一个函数,只有满足条件就行 handler func(http.ResponseWriter, *http.Request) 理论我的项目中不太可能只用go micro, 从0开始手撸所有其余轮子,那么可不可以在go micro中引入罕用的框架呢? 当然能够,来看一个引入gin的例子examples/greeter/api/gin/gin.go func main() { // Create service service := web.NewService( web.Name("go.micro.api.greeter"), ) service.Init() // setup Greeter Server Client cl = hello.NewSayService("go.micro.srv.greeter", client.DefaultClient) // Create RESTful handler (using Gin) say := new(Say) router := gin.Default() router.GET("/greeter", say.Anything) router.GET("/greeter/:name", say.Hello) // Register Handler service.Handle("/", router) // Run server if err := service.Run(); err != nil { log.Fatal(err) }}要害是service.Handle("/", router) ...

August 6, 2020 · 3 min · jiezi

关于micro:go-micro-web

examples/web 有一个web的例子,这里比较简单 service.HandleFunc("/", helloWorldHandler) 这一行指定处理程序比较简单,第2个参数定义了一个函数,只有满足条件就行 handler func(http.ResponseWriter, *http.Request) 理论我的项目中不太可能只用go micro, 从0开始手撸所有其余轮子,那么可不可以在go micro中引入罕用的框架呢? 当然能够,来看一个引入gin的例子examples/greeter/api/gin/gin.go func main() { // Create service service := web.NewService( web.Name("go.micro.api.greeter"), ) service.Init() // setup Greeter Server Client cl = hello.NewSayService("go.micro.srv.greeter", client.DefaultClient) // Create RESTful handler (using Gin) say := new(Say) router := gin.Default() router.GET("/greeter", say.Anything) router.GET("/greeter/:name", say.Hello) // Register Handler service.Handle("/", router) // Run server if err := service.Run(); err != nil { log.Fatal(err) }}要害是service.Handle("/", router) ...

August 6, 2020 · 3 min · jiezi

关于micro:go-micro-transport

micro.newService()中newOptions func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt}初始化了一堆根底设置,来看看Transporttransport.DefaultTransport,在transport/transport.go中的DefaultTransport Transport = newHTTPTransport() func NewTransport(opts ...Option) Transport { return newHTTPTransport(opts...)}func newHTTPTransport(opts ...Option) *httpTransport { var options Options for _, o := range opts { o(&options) } return &httpTransport{opts: options}}这里就初始化并设置options,返回了一个httpTransport{} ...

August 6, 2020 · 1 min · jiezi

关于micro:go-micro-runtime

micro.newService()中newOptions func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt}初始化了一堆根底设置,来看看Runtimeruntime.DefaultRuntime,在runtime/runtime.go中的DefaultRuntime Runtime = NewRuntime() // NewRuntime creates new local runtime and returns itfunc NewRuntime(opts ...Option) Runtime { // get default options options := Options{} // apply requested options for _, o := range opts { o(&options) } // make the logs directory path := filepath.Join(os.TempDir(), "micro", "logs") _ = os.MkdirAll(path, 0755) return &runtime{ options: options, closed: make(chan bool), start: make(chan *service, 128), namespaces: make(map[string]map[string]*service), }}这里做了以下事件: ...

August 6, 2020 · 1 min · jiezi

关于micro:go-micro-router

micro.newService()中newOptions func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt}初始化了一堆根底设置,来看看Routerrouter.DefaultRouter,在router/router.go中的DefaultRouter = NewRouter() // NewRouter creates new Router and returns itfunc NewRouter(opts ...Option) Router { return newRouter(opts...)}// newRouter creates new router and returns itfunc newRouter(opts ...Option) Router { // get default options options := DefaultOptions() // apply requested options for _, o := range opts { o(&options) } // construct the router r := &router{ options: options, subscribers: make(map[string]chan *Advert), } // create the new table, passing the fetchRoute method in as a fallback if // the table doesn't contain the result for a query. r.table = newTable(r.fetchRoutes) // start the router and return r.start() return r}这里做了以下事件: ...

August 6, 2020 · 1 min · jiezi

关于micro:go-micro-registry

micro.newService()中newOptions func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt}初始化了一堆根底设置,来看看Registryregistry.DefaultRegistry,在registry/registry.go中的DefaultRegistry = NewRegistry() // NewRegistry returns a new default registry which is mdnsfunc NewRegistry(opts ...Option) Registry { return newRegistry(opts...)}func newRegistry(opts ...Option) Registry { options := Options{ Context: context.Background(), Timeout: time.Millisecond * 100, } for _, o := range opts { o(&options) } // set the domain defaultDomain := DefaultDomain d, ok := options.Context.Value("mdns.domain").(string) if ok { defaultDomain = d } return &mdnsRegistry{ defaultDomain: defaultDomain, globalDomain: globalDomain, opts: options, domains: make(map[string]services), watchers: make(map[string]*mdnsWatcher), }}这里做了以下事件: ...

August 6, 2020 · 7 min · jiezi

关于micro:go-micro-store

micro.newService()中newOptions func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt}初始化了一堆根底设置,来看看Storestore.DefaultStore,这里不要间接去看store/store.go中的DefaultStore Store = new(noopStore),因为在micro/defaults.go中曾经初始化了,默认应用的是store.DefaultStore = memoryStore.NewStore() // NewStore returns a memory storefunc NewStore(opts ...store.Option) store.Store { s := &memoryStore{ options: store.Options{ Database: "micro", Table: "micro", }, store: cache.New(cache.NoExpiration, 5*time.Minute), } for _, o := range opts { o(&s.options) } return s}type memoryStore struct { options store.Options store *cache.Cache}初始化了memoryStore{},store是应用的patrickmn/go-cache设置options,返回memoryStore实例性能比较简单,在store/memory/memory.go封装了读写删改等操作函数 ...

August 6, 2020 · 1 min · jiezi

关于micro:go-micro-store

micro.newService()中newOptions func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt}初始化了一堆根底设置,来看看Storestore.DefaultStore,这里不要间接去看store/store.go中的DefaultStore Store = new(noopStore),因为在micro/defaults.go中曾经初始化了,默认应用的是store.DefaultStore = memoryStore.NewStore() // NewStore returns a memory storefunc NewStore(opts ...store.Option) store.Store { s := &memoryStore{ options: store.Options{ Database: "micro", Table: "micro", }, store: cache.New(cache.NoExpiration, 5*time.Minute), } for _, o := range opts { o(&s.options) } return s}type memoryStore struct { options store.Options store *cache.Cache}初始化了memoryStore{},store是应用的patrickmn/go-cache设置options,返回memoryStore实例性能比较简单,在store/memory/memory.go封装了读写删改等操作函数 ...

August 6, 2020 · 1 min · jiezi

关于micro:go-micro-config

micro.newService()中newOptions func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt}初始化了一堆根底设置,来看看configconfig.DefaultConfig,在config/config.go中 var ( // Default Config Manager DefaultConfig, _ = NewConfig())// NewConfig returns new configfunc NewConfig(opts ...Option) (Config, error) { return newConfig(opts...)}func newConfig(opts ...Option) (Config, error) { var c config c.Init(opts...) go c.run() return &c, nil}func (c *config) Init(opts ...Option) error { c.opts = Options{ Reader: json.NewReader(), } c.exit = make(chan bool) for _, o := range opts { o(&c.opts) } // default loader uses the configured reader if c.opts.Loader == nil { c.opts.Loader = memory.NewLoader(memory.WithReader(c.opts.Reader)) } err := c.opts.Loader.Load(c.opts.Source...) if err != nil { return err } c.snap, err = c.opts.Loader.Snapshot() if err != nil { return err } c.vals, err = c.opts.Reader.Values(c.snap.ChangeSet) if err != nil { return err } return nil}func (c *config) run() { watch := func(w loader.Watcher) error { for { // get changeset snap, err := w.Next() if err != nil { return err } c.Lock() if c.snap.Version >= snap.Version { c.Unlock() continue } // save c.snap = snap // set values c.vals, _ = c.opts.Reader.Values(snap.ChangeSet) c.Unlock() } } for { w, err := c.opts.Loader.Watch() if err != nil { time.Sleep(time.Second) continue } done := make(chan bool) // the stop watch func go func() { select { case <-done: case <-c.exit: } w.Stop() }() // block watch if err := watch(w); err != nil { // do something better time.Sleep(time.Second) } // close done chan close(done) // if the config is closed exit select { case <-c.exit: return default: } }}看看Init()左做了什么 ...

August 6, 2020 · 2 min · jiezi

关于micro:go-micro-cmd

micro.newService()中newOptions func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt}初始化了一堆根底设置,先来看看cmd cmd.DefaultCmd,在config/cmd/cmd.go DefaultCmd = newCmd() type Cmd interface { // The cli app within this cmd App() *cli.App // Adds options, parses flags and initialise // exits on error Init(opts ...Option) error // Options set within this command Options() Options}type cmd struct { opts Options app *cli.App}type Option func(o *Options)func newCmd(opts ...Option) Cmd { options := Options{ Auth: &auth.DefaultAuth, Broker: &broker.DefaultBroker, Client: &client.DefaultClient, Registry: &registry.DefaultRegistry, Server: &server.DefaultServer, Selector: &selector.DefaultSelector, Transport: &transport.DefaultTransport, Router: &router.DefaultRouter, Runtime: &runtime.DefaultRuntime, Store: &store.DefaultStore, Tracer: &trace.DefaultTracer, Profile: &profile.DefaultProfile, Config: &config.DefaultConfig, Brokers: DefaultBrokers, Clients: DefaultClients, Registries: DefaultRegistries, Selectors: DefaultSelectors, Servers: DefaultServers, Transports: DefaultTransports, Routers: DefaultRouters, Runtimes: DefaultRuntimes, Stores: DefaultStores, Tracers: DefaultTracers, Auths: DefaultAuths, Profiles: DefaultProfiles, Configs: DefaultConfigs, } for _, o := range opts { o(&options) } if len(options.Description) == 0 { options.Description = "a go-micro service" } cmd := new(cmd) cmd.opts = options cmd.app = cli.NewApp() cmd.app.Name = cmd.opts.Name cmd.app.Version = cmd.opts.Version cmd.app.Usage = cmd.opts.Description cmd.app.Before = cmd.Before cmd.app.Flags = DefaultFlags cmd.app.Action = func(c *cli.Context) error { return nil } if len(options.Version) == 0 { cmd.app.HideVersion = true } return cmd}这里的cmd构造体中 ...

August 6, 2020 · 2 min · jiezi

关于micro:go-micro-cmd

micro.newService()中newOptions func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt}初始化了一堆根底设置,先来看看cmd cmd.DefaultCmd,在config/cmd/cmd.go DefaultCmd = newCmd() type Cmd interface { // The cli app within this cmd App() *cli.App // Adds options, parses flags and initialise // exits on error Init(opts ...Option) error // Options set within this command Options() Options}type cmd struct { opts Options app *cli.App}type Option func(o *Options)func newCmd(opts ...Option) Cmd { options := Options{ Auth: &auth.DefaultAuth, Broker: &broker.DefaultBroker, Client: &client.DefaultClient, Registry: &registry.DefaultRegistry, Server: &server.DefaultServer, Selector: &selector.DefaultSelector, Transport: &transport.DefaultTransport, Router: &router.DefaultRouter, Runtime: &runtime.DefaultRuntime, Store: &store.DefaultStore, Tracer: &trace.DefaultTracer, Profile: &profile.DefaultProfile, Config: &config.DefaultConfig, Brokers: DefaultBrokers, Clients: DefaultClients, Registries: DefaultRegistries, Selectors: DefaultSelectors, Servers: DefaultServers, Transports: DefaultTransports, Routers: DefaultRouters, Runtimes: DefaultRuntimes, Stores: DefaultStores, Tracers: DefaultTracers, Auths: DefaultAuths, Profiles: DefaultProfiles, Configs: DefaultConfigs, } for _, o := range opts { o(&options) } if len(options.Description) == 0 { options.Description = "a go-micro service" } cmd := new(cmd) cmd.opts = options cmd.app = cli.NewApp() cmd.app.Name = cmd.opts.Name cmd.app.Version = cmd.opts.Version cmd.app.Usage = cmd.opts.Description cmd.app.Before = cmd.Before cmd.app.Flags = DefaultFlags cmd.app.Action = func(c *cli.Context) error { return nil } if len(options.Version) == 0 { cmd.app.HideVersion = true } return cmd}这里的cmd构造体中 ...

August 6, 2020 · 2 min · jiezi

关于micro:go-micro-broker

micro.newService()中newOptions func newOptions(opts ...Option) Options { opt := Options{ Auth: auth.DefaultAuth, Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Config: config.DefaultConfig, Client: client.DefaultClient, Server: server.DefaultServer, Store: store.DefaultStore, Registry: registry.DefaultRegistry, Router: router.DefaultRouter, Runtime: runtime.DefaultRuntime, Transport: transport.DefaultTransport, Context: context.Background(), Signal: true, } for _, o := range opts { o(&opt) } return opt}初始化了一堆根底设置,先来看看Broker broker.DefaultBroker,在broker/broker.go中 DefaultBroker Broker = NewBroker() // NewBroker returns a new http brokerfunc NewBroker(opts ...Option) Broker { return newHttpBroker(opts...)}func newHttpBroker(opts ...Option) Broker { options := Options{ Codec: json.Marshaler{}, Context: context.TODO(), Registry: registry.DefaultRegistry, } for _, o := range opts { o(&options) } // set address addr := DefaultAddress if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 { addr = options.Addrs[0] } h := &httpBroker{ id: uuid.New().String(), address: addr, opts: options, r: options.Registry, c: &http.Client{Transport: newTransport(options.TLSConfig)}, subscribers: make(map[string][]*httpSubscriber), exit: make(chan chan error), mux: http.NewServeMux(), inbox: make(map[string][][]byte), } // specify the message handler h.mux.Handle(DefaultPath, h) // get optional handlers if h.opts.Context != nil { handlers, ok := h.opts.Context.Value("http_handlers").(map[string]http.Handler) if ok { for pattern, handler := range handlers { h.mux.Handle(pattern, handler) } } } return h}这里做了几件事 ...

August 6, 2020 · 7 min · jiezi

关于micro:go-micro-server-启动分析

基于go-micro 2.9.1版本, 样例代码example/greeter,git commit:3b3de68cded8879ca3dde5d81192f2881619aabd 一个微服务server的外围只有3步 service := micro.NewService()service.Init()service.Run()先看micro.NewService() service := micro.NewService( micro.Name("greeter"), micro.Version("latest"), micro.Metadata(map[string]string{ "type": "helloworld", }),)micro.NewService的参数都会返回一个Option,这些参数没有做任何事件,只是返回了一些设置用的函数, 这种写法是“函数选项模式”,可参考https://segmentfault.com/a/11... 先看NewService()外面做了什么 // Name of the servicefunc Name(n string) Option { return func(o *Options) { o.Server.Init(server.Name(n)) }}//Option函数在micro.go中定义//Options构造体在options.go中定义type Option func(*Options)// Options for micro servicetype Options struct { Auth auth.Auth Broker broker.Broker Cmd cmd.Cmd Config config.Config Client client.Client Server server.Server Store store.Store Registry registry.Registry Router router.Router Runtime runtime.Runtime Transport transport.Transport Profile profile.Profile // Before and After funcs BeforeStart []func() error BeforeStop []func() error AfterStart []func() error AfterStop []func() error // Other options for implementations of the interface // can be stored in a context Context context.Context Signal bool}server.Name(n)实现这样,通过micro包提供的函数设置micro.options ...

August 6, 2020 · 5 min · jiezi

安装micro以及处理go-get出现的问题

因为试了很多方法都没能成功的使用go get,最后试出了这一个方法。 国内,请架梯子,全局,下面以这个方法进行。当然如果你有足够的耐心,git clone应该也是可以的,但我没有这个胆量,毕竟一个go get就已经拉下来300+MB的代码,具体有几个库我没数。把GOPROXY改回来默认的:export GOPROXY=在执行go get前,我已经把第三方库全清了,如果你没有清,请加参数-u第一次执行go get执行命令go get -v github.com/micro/micro 耐心等候...(下面是我跑命令的结果) [yangzhao@yangzhaodeMacBook-Pro:] ~/Dev/Gopath/src $go get -v github.com/micro/microgithub.com/micro/go-micro (download)github.com/google/uuid (download)github.com/micro/mdns (download)github.com/miekg/dns (download)get "golang.org/x/crypto/ed25519": found meta tag get.metaImport{Prefix:"golang.org/x/crypto", VCS:"git", RepoRoot:"https://go.googlesource.com/crypto"} at //golang.org/x/crypto/ed25519?go-get=1get "golang.org/x/crypto/ed25519": verifying non-authoritative meta taggolang.org/x/crypto (download)get "golang.org/x/net/ipv4": found meta tag get.metaImport{Prefix:"golang.org/x/net", VCS:"git", RepoRoot:"https://go.googlesource.com/net"} at //golang.org/x/net/ipv4?go-get=1get "golang.org/x/net/ipv4": verifying non-authoritative meta taggolang.org/x/net (download)get "golang.org/x/sys/unix": found meta tag get.metaImport{Prefix:"golang.org/x/sys", VCS:"git", RepoRoot:"https://go.googlesource.com/sys"} at //golang.org/x/sys/unix?go-get=1get "golang.org/x/sys/unix": verifying non-authoritative meta taggolang.org/x/sys (download)get "golang.org/x/net/ipv6": found meta tag get.metaImport{Prefix:"golang.org/x/net", VCS:"git", RepoRoot:"https://go.googlesource.com/net"} at //golang.org/x/net/ipv6?go-get=1get "golang.org/x/net/ipv6": verifying non-authoritative meta taggithub.com/go-log/log (download)get "golang.org/x/net/http2": found meta tag get.metaImport{Prefix:"golang.org/x/net", VCS:"git", RepoRoot:"https://go.googlesource.com/net"} at //golang.org/x/net/http2?go-get=1get "golang.org/x/net/http2": verifying non-authoritative meta tagget "golang.org/x/text/secure/bidirule": found meta tag get.metaImport{Prefix:"golang.org/x/text", VCS:"git", RepoRoot:"https://go.googlesource.com/text"} at //golang.org/x/text/secure/bidirule?go-get=1get "golang.org/x/text/secure/bidirule": verifying non-authoritative meta taggolang.org/x/text (download)get "golang.org/x/text/unicode/bidi": found meta tag get.metaImport{Prefix:"golang.org/x/text", VCS:"git", RepoRoot:"https://go.googlesource.com/text"} at //golang.org/x/text/unicode/bidi?go-get=1get "golang.org/x/text/unicode/bidi": verifying non-authoritative meta tagget "golang.org/x/text/unicode/norm": found meta tag get.metaImport{Prefix:"golang.org/x/text", VCS:"git", RepoRoot:"https://go.googlesource.com/text"} at //golang.org/x/text/unicode/norm?go-get=1get "golang.org/x/text/unicode/norm": verifying non-authoritative meta taggithub.com/nats-io/nats.go (download)github.com/nats-io/jwt (download)github.com/nats-io/nkeys (download)github.com/nats-io/nuid (download)github.com/pkg/errors (download)get "google.golang.org/grpc": found meta tag get.metaImport{Prefix:"google.golang.org/grpc", VCS:"git", RepoRoot:"https://github.com/grpc/grpc-go"} at //google.golang.org/grpc?go-get=1google.golang.org/grpc (download)get "google.golang.org/grpc/credentials": found meta tag get.metaImport{Prefix:"google.golang.org/grpc", VCS:"git", RepoRoot:"https://github.com/grpc/grpc-go"} at //google.golang.org/grpc/credentials?go-get=1get "google.golang.org/grpc/credentials": verifying non-authoritative meta tagget "google.golang.org/grpc/metadata": found meta tag get.metaImport{Prefix:"google.golang.org/grpc", VCS:"git", RepoRoot:"https://github.com/grpc/grpc-go"} at //google.golang.org/grpc/metadata?go-get=1get "google.golang.org/grpc/metadata": verifying non-authoritative meta tagget "google.golang.org/grpc/encoding": found meta tag get.metaImport{Prefix:"google.golang.org/grpc", VCS:"git", RepoRoot:"https://github.com/grpc/grpc-go"} at //google.golang.org/grpc/encoding?go-get=1get "google.golang.org/grpc/encoding": verifying non-authoritative meta tagget "google.golang.org/grpc/status": found meta tag get.metaImport{Prefix:"google.golang.org/grpc", VCS:"git", RepoRoot:"https://github.com/grpc/grpc-go"} at //google.golang.org/grpc/status?go-get=1get "google.golang.org/grpc/status": verifying non-authoritative meta tagget "google.golang.org/genproto/googleapis/rpc/status": found meta tag get.metaImport{Prefix:"google.golang.org/genproto", VCS:"git", RepoRoot:"https://github.com/google/go-genproto"} at //google.golang.org/genproto/googleapis/rpc/status?go-get=1get "google.golang.org/genproto/googleapis/rpc/status": verifying non-authoritative meta taggoogle.golang.org/genproto (download)github.com/json-iterator/go (download)github.com/modern-go/concurrent (download)github.com/modern-go/reflect2 (download)github.com/coreos/etcd (download)get "go.etcd.io/etcd/auth/authpb": found meta tag get.metaImport{Prefix:"go.etcd.io/etcd", VCS:"git", RepoRoot:"https://github.com/etcd-io/etcd"} at //go.etcd.io/etcd/auth/authpb?go-get=1get "go.etcd.io/etcd/auth/authpb": verifying non-authoritative meta taggo.etcd.io/etcd (download)get "go.etcd.io/etcd/clientv3/balancer": found meta tag get.metaImport{Prefix:"go.etcd.io/etcd", VCS:"git", RepoRoot:"https://github.com/etcd-io/etcd"} at //go.etcd.io/etcd/clientv3/balancer?go-get=1get "go.etcd.io/etcd/clientv3/balancer": verifying non-authoritative meta tagget "go.etcd.io/etcd/clientv3/balancer/picker": found meta tag get.metaImport{Prefix:"go.etcd.io/etcd", VCS:"git", RepoRoot:"https://github.com/etcd-io/etcd"} at //go.etcd.io/etcd/clientv3/balancer/picker?go-get=1get "go.etcd.io/etcd/clientv3/balancer/picker": verifying non-authoritative meta tagget "go.etcd.io/etcd/clientv3/balancer/resolver/endpoint": found meta tag get.metaImport{Prefix:"go.etcd.io/etcd", VCS:"git", RepoRoot:"https://github.com/etcd-io/etcd"} at //go.etcd.io/etcd/clientv3/balancer/resolver/endpoint?go-get=1get "go.etcd.io/etcd/clientv3/balancer/resolver/endpoint": verifying non-authoritative meta tagget "go.etcd.io/etcd/clientv3/credentials": found meta tag get.metaImport{Prefix:"go.etcd.io/etcd", VCS:"git", RepoRoot:"https://github.com/etcd-io/etcd"} at //go.etcd.io/etcd/clientv3/credentials?go-get=1get "go.etcd.io/etcd/clientv3/credentials": verifying non-authoritative meta tagget "go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes": found meta tag get.metaImport{Prefix:"go.etcd.io/etcd", VCS:"git", RepoRoot:"https://github.com/etcd-io/etcd"} at //go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes?go-get=1get "go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes": verifying non-authoritative meta tagget "go.etcd.io/etcd/etcdserver/etcdserverpb": found meta tag get.metaImport{Prefix:"go.etcd.io/etcd", VCS:"git", RepoRoot:"https://github.com/etcd-io/etcd"} at //go.etcd.io/etcd/etcdserver/etcdserverpb?go-get=1get "go.etcd.io/etcd/etcdserver/etcdserverpb": verifying non-authoritative meta tagget "go.etcd.io/etcd/mvcc/mvccpb": found meta tag get.metaImport{Prefix:"go.etcd.io/etcd", VCS:"git", RepoRoot:"https://github.com/etcd-io/etcd"} at //go.etcd.io/etcd/mvcc/mvccpb?go-get=1get "go.etcd.io/etcd/mvcc/mvccpb": verifying non-authoritative meta tagget "go.etcd.io/etcd/pkg/logutil": found meta tag get.metaImport{Prefix:"go.etcd.io/etcd", VCS:"git", RepoRoot:"https://github.com/etcd-io/etcd"} at //go.etcd.io/etcd/pkg/logutil?go-get=1get "go.etcd.io/etcd/pkg/logutil": verifying non-authoritative meta tagget "go.etcd.io/etcd/pkg/types": found meta tag get.metaImport{Prefix:"go.etcd.io/etcd", VCS:"git", RepoRoot:"https://github.com/etcd-io/etcd"} at //go.etcd.io/etcd/pkg/types?go-get=1get "go.etcd.io/etcd/pkg/types": verifying non-authoritative meta taggithub.com/mitchellh/hashstructure (download)github.com/lucas-clemente/quic-go (download)github.com/cheekybits/genny (download)github.com/marten-seemann/qtls (download)github.com/marten-seemann/chacha20 (download)github.com/go-acme/lego (download)github.com/cloudflare/cloudflare-go (download)github.com/cenkalti/backoff (download)get "gopkg.in/square/go-jose.v2": found meta tag get.metaImport{Prefix:"gopkg.in/square/go-jose.v2", VCS:"git", RepoRoot:"https://gopkg.in/square/go-jose.v2"} at //gopkg.in/square/go-jose.v2?go-get=1gopkg.in/square/go-jose.v2 (download)github.com/gorilla/mux (download)github.com/joncalhoun/qson (download)github.com/mholt/certmagic (download)github.com/klauspost/cpuid (download)github.com/gorilla/handlers (download)github.com/bwmarrin/discordgo (download)github.com/gorilla/websocket (download)github.com/nlopes/slack (download)github.com/forestgiant/sliceutil (download)get "gopkg.in/telegram-bot-api.v4": found meta tag get.metaImport{Prefix:"gopkg.in/telegram-bot-api.v4", VCS:"git", RepoRoot:"https://gopkg.in/telegram-bot-api.v4"} at //gopkg.in/telegram-bot-api.v4?go-get=1gopkg.in/telegram-bot-api.v4 (download)github.com/technoweenie/multipartstreamer (download)github.com/olekukonko/tablewriter (download)github.com/mattn/go-runewidth (download)github.com/serenize/snaker (download)github.com/chzyer/readline (download)get "golang.org/x/net/context": found meta tag get.metaImport{Prefix:"golang.org/x/net", VCS:"git", RepoRoot:"https://go.googlesource.com/net"} at //golang.org/x/net/context?go-get=1get "golang.org/x/net/context": verifying non-authoritative meta taggithub.com/xlab/treeprint (download)github.com/hako/branca (download)github.com/eknkc/basex (download)github.com/pborman/uuid (download)golang.org/x/text/unicode/bidigolang.org/x/text/unicode/normgithub.com/micro/go-micro/registry/cachegithub.com/nats-io/nkeysgithub.com/micro/go-micro/client/selectorgithub.com/nats-io/jwtgithub.com/micro/go-micro/client/selector/dnsgithub.com/json-iterator/gogithub.com/nats-io/nats.gogolang.org/x/net/idnagithub.com/micro/go-micro/brokergoogle.golang.org/grpc/internal/transportgithub.com/micro/go-micro/servergithub.com/micro/go-micro/broker/natsgithub.com/coreos/etcd/clientv3# github.com/coreos/etcd/clientv3../github.com/coreos/etcd/clientv3/auth.go:121:72: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"go.etcd.io/etcd/vendor/google.golang.org/grpc".CallOption in argument to auth.remote.AuthEnable../github.com/coreos/etcd/clientv3/auth.go:126:74: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"go.etcd.io/etcd/vendor/google.golang.org/grpc".CallOption in argument to auth.remote.AuthDisable../github.com/coreos/etcd/clientv3/auth.go:131:152: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"go.etcd.io/etcd/vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserAdd../github.com/coreos/etcd/clientv3/auth.go:136:144: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"go.etcd.io/etcd/vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserAdd../github.com/coreos/etcd/clientv3/auth.go:141:86: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"go.etcd.io/etcd/vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserDelete../github.com/coreos/etcd/clientv3/auth.go:146:122: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"go.etcd.io/etcd/vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserChangePassword../github.com/coreos/etcd/clientv3/auth.go:151:104: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"go.etcd.io/etcd/vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserGrantRole../github.com/coreos/etcd/clientv3/auth.go:156:80: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"go.etcd.io/etcd/vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserGet../github.com/coreos/etcd/clientv3/auth.go:161:72: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"go.etcd.io/etcd/vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserList../github.com/coreos/etcd/clientv3/auth.go:166:106: cannot use auth.callOpts (type []"github.com/coreos/etcd/vendor/google.golang.org/grpc".CallOption) as type []"go.etcd.io/etcd/vendor/google.golang.org/grpc".CallOption in argument to auth.remote.UserRevokeRole../github.com/coreos/etcd/clientv3/auth.go:166:106: too many errorsgithub.com/micro/go-micro/runtimegithub.com/lucas-clemente/quic-go/internal/handshakegolang.org/x/crypto/acme/autocertgithub.com/lucas-clemente/quic-gogithub.com/micro/go-micro/api/server/acme/autocertgoogle.golang.org/grpcgithub.com/micro/go-micro/storegithub.com/micro/go-micro/api/server/acme/certmagicgithub.com/micro/go-micro/store/cloudflaregithub.com/micro/micro/network/dns/proto/dnsgithub.com/micro/go-micro/proxy/mucpgithub.com/micro/micro/network/dns/providergithub.com/micro/micro/network/dns/handlergithub.com/micro/micro/network/dns/provider/cloudflaregithub.com/micro/go-micro/util/httpgithub.com/micro/go-micro/proxy/httpgithub.com/micro/go-micro/router/servicegithub.com/micro/go-micro/store/memorygithub.com/eknkc/basexgithub.com/micro/micro/internal/token/protogithub.com/hako/brancagithub.com/pborman/uuidgithub.com/micro/micro/internal/tokengithub.com/micro/micro/tokengithub.com/micro/go-micro/transport/quicgithub.com/micro/go-micro/tunnelgithub.com/micro/go-micro/broker/service/protogithub.com/micro/go-micro/client/grpcgithub.com/micro/go-micro/registry/service/protogithub.com/micro/go-micro/broker/servicegithub.com/micro/go-micro/server/grpcgithub.com/micro/go-micro/transport/grpc/protogithub.com/micro/go-micro/registry/servicegithub.com/micro/go-micro/debug/protogithub.com/micro/go-micro/broker/service/handlergithub.com/micro/go-micro/transport/grpcgithub.com/micro/micro/monitor/protogithub.com/micro/go-micro/network/protogithub.com/micro/go-micro/debug/handlergithub.com/micro/go-micro/monitorgithub.com/micro/go-micro/tunnel/transportgithub.com/micro/micro/monitor/handlergithub.com/micro/go-micro/proxy/grpcgithub.com/micro/go-micro/registry/service/handlergithub.com/micro/go-micro/store/service/protogithub.com/micro/go-micro/networkgithub.com/micro/go-micro/store/service/handlergithub.com/micro/go-micro/network/service/handler[yangzhao@yangzhaodeMacBook-Pro:] ~/Dev/Gopath/src $前面出现了错误信息,跑了太多的命令,我也懒得回去找了,再运行一遍go get -v github.com/micro/micro看看是什么错误 ...

November 2, 2019 · 4 min · jiezi

Go-Micro-Broker-源码分析

概述在第一篇概述文章中已经提到了在Micro中 Broker的作用,Go Micro 总体设计。我们也知道Micro是一个可插拔的分布式框架,我们可以使用kafka,rabbitmq,cache,redis,nats等各种实现具体可以在git上的插件库中找到go-plugins我们再来看一下接口: type Broker interface { Init(...Option) error Options() Options Address() string Connect() error Disconnect() error Publish(topic string, m *Message, opts ...PublishOption) error Subscribe(topic string, h Handler, opts ...SubscribeOption) (Subscriber, error) String() string}Connect 开启brokerSubsribe 注册对某个topic的监听Publish 发布某个topic的消息Disconnect 关闭broker 简单使用本片文章使用的是默认broker实现httpbroker分析,是一个比较简单和容易理解的方式,代码地址。 PubTopic函数为发布函数SubTopic函数为订阅函数下面的例子其实不需要很多注释,就是简单的使用一个发布一个订阅函数来实现。package mainimport ( "fmt" "github.com/micro/go-micro/broker" "log" "time")func main() { if err := broker.Connect(); err != nil { fmt.Println("Broker Connect error: %v", err) } go PubTopic("SubServerName") go SubTopic("SubServerName") time.Sleep(time.Second * 10)}func PubTopic(topic string) { tick := time.NewTicker(time.Second) i := 0 for _ = range tick.C { msg := &broker.Message{ Header: map[string]string{ "id": fmt.Sprintf("%d", i), }, Body: []byte(fmt.Sprintf("%d: %s", i, time.Now().String())), } if err := broker.Publish(topic, msg); err != nil { log.Printf("[pub] failed: %v", err) } else { fmt.Println("[pub] pubbed message:", string(msg.Body)) } i++ }}func SubTopic(topic string) { _, err := broker.Subscribe(topic, func(p broker.Event) error { fmt.Println("[sub] received message:", string(p.Message().Body), "header", p.Message().Header) return nil }) if err != nil { fmt.Println(err) }}结果[pub] pubbed message: 0: 2019-07-15 15:48:00.377247298 +0800 CST m=+1.005764860[sub] received message: 0: 2019-07-15 15:48:00.377247298 +0800 CST m=+1.005764860 header map[id:0][pub] pubbed message: 1: 2019-07-15 15:48:01.376383294 +0800 CST m=+2.004891632[sub] received message: 1: 2019-07-15 15:48:01.376383294 +0800 CST m=+2.004891632 header map[id:1][pub] pubbed message: 2: 2019-07-15 15:48:02.377595797 +0800 CST m=+3.006094892[sub] received message: 2: 2019-07-15 15:48:02.377595797 +0800 CST m=+3.006094892 header map[id:2][pub] pubbed message: 3: 2019-07-15 15:48:03.376685455 +0800 CST m=+4.005175327[sub] received message: 3: 2019-07-15 15:48:03.376685455 +0800 CST m=+4.005175327 header map[id:3][pub] pubbed message: 4: 2019-07-15 15:48:04.377715895 +0800 CST m=+5.006196526[sub] received message: 4: 2019-07-15 15:48:04.377715895 +0800 CST m=+5.006196526 header map[id:4]源码分析Connect开启tcp监听启一个goroutine,在registerInterval间隔对subscriber就行注册,类似心跳设置服务发现注册服务设置缓存对象设置running = truefunc (h *httpBroker) Connect() error { h.RLock() if h.running { h.RUnlock() return nil } h.RUnlock() h.Lock() defer h.Unlock() var l net.Listener var err error // 创建监听函数 判断是否有配置 if h.opts.Secure || h.opts.TLSConfig != nil { config := h.opts.TLSConfig fn := func(addr string) (net.Listener, error) { if config == nil { hosts := []string{addr} // check if its a valid host:port if host, _, err := net.SplitHostPort(addr); err == nil { if len(host) == 0 { hosts = maddr.IPs() } else { hosts = []string{host} } } // generate a certificate cert, err := mls.Certificate(hosts...) if err != nil { return nil, err } config = &tls.Config{Certificates: []tls.Certificate{cert}} } return tls.Listen("tcp", addr, config) } l, err = mnet.Listen(h.address, fn) } else { fn := func(addr string) (net.Listener, error) { return net.Listen("tcp", addr) } l, err = mnet.Listen(h.address, fn) } if err != nil { return err } addr := h.address h.address = l.Addr().String() go http.Serve(l, h.mux) go func() { // 根据设置的registerInterval 心跳时间,检测服务是否存活 h.run(l) h.Lock() h.opts.Addrs = []string{addr} h.address = addr h.Unlock() }() // get registry reg, ok := h.opts.Context.Value(registryKey).(registry.Registry) if !ok { reg = registry.DefaultRegistry } // set cache h.r = cache.New(reg) // set running h.running = true return nil}Subscribe解析address创建唯一id拼装服务信息 最后的服务信息如下图调用Register(默认的是mdns)注册服务把service放到subscribers map[string][]*httpSubscriber中 ...

July 15, 2019 · 5 min · jiezi

Go-Micro-Register-源码分析

概述Go Micro是一个微服务框架分布式框架,既然是分布式那服务的注册和发现就是不可避免的。Micro又是一个可插拔插件的框架,只要实现下面代码中的接口就可以使用各种不同的服务注册发现。现在代码库中已经可以支持consul,etcd,zk等各种。下面我们来看一下Micro框架是如何注册和发现服务的。 流程服务端把服务的地址信息保存到Registry, 然后定时的心跳检查,或者定时的重新注册服务。客户端监听Registry,最好是把服务信息保存到本地,监听服务的变动,更新缓存。当调用服务端的接口是时,根据客户端的服务列表和负载算法选择服务端进行通信。 Register() 服务端服务注册Deregister() 服务端服务注销GetService() 客户端获取服务节点ListServices() 获取所有服务节点Watch() 客户端获取watcher监听Registry的服务节点信息type Registry interface { Init(...Option) error Options() Options Register(*Service, ...RegisterOption) error Deregister(*Service) error GetService(string) ([]*Service, error) ListServices() ([]*Service, error) Watch(...WatchOption) (Watcher, error) String() string}以下我们就以consul作为服务注册发现来分析源码服务端注册服务当服务开启时,调用consul插件作为服务发现注册。准备服务对象,包括服务名等信息。往consul中写入services节点。根据设置的RegisterInterval时间间隔参数,循环检测服务是否可用监听已注册服务。调用consul设置服务存货时间TTL。consul添加完节点如下图(下图展示了同一个服务名 开启了2个服务,相当于分布式的两台机器): 源码分析 // 服务开启 调用run方法func (s *service) Run() error { // 调用start函数 if err := s.Start(); err != nil { return err } ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT) select { // wait on kill signal case <-ch: // wait on context cancel case <-s.opts.Context.Done(): } return s.Stop()}// 调用服务start方法 func (s *service) Start() error { // 执行服务开始之前函数列表 for _, fn := range s.opts.BeforeStart { if err := fn(); err != nil { return err } } // 服务开始 if err := s.opts.Server.Start(); err != nil { return err } // 执行服务结束之后函数列表 for _, fn := range s.opts.AfterStart { if err := fn(); err != nil { return err } } return nil}// 服务开启函数 func (s *rpcServer) Start() error { // 省略其他逻辑代码。。。 // 调用RegisterCheck检查注册服务是否可用 可从外部注入函数 if err = s.opts.RegisterCheck(s.opts.Context); err != nil { log.Logf("Server %s-%s register check error: %s", config.Name, config.Id, err) } else { // 注册服务 if err = s.Register(); err != nil { log.Logf("Server %s-%s register error: %s", config.Name, config.Id, err) } } exit := make(chan bool) // 省略broker 代码 // 开启goroutine 检查服务是否可用,间隔时间为设置的RegisterInterval go func() { t := new(time.Ticker) // only process if it exists if s.opts.RegisterInterval > time.Duration(0) { // new ticker t = time.NewTicker(s.opts.RegisterInterval) } // return error chan var ch chan error Loop: for { select { // register self on interval case <-t.C: s.RLock() registered := s.registered s.RUnlock() if err = s.opts.RegisterCheck(s.opts.Context); err != nil && registered { log.Logf("Server %s-%s register check error: %s, deregister it", config.Name, config.Id, err) // deregister self in case of error if err := s.Deregister(); err != nil { log.Logf("Server %s-%s deregister error: %s", config.Name, config.Id, err) } } else { if err := s.Register(); err != nil { log.Logf("Server %s-%s register error: %s", config.Name, config.Id, err) } } // wait for exit case ch = <-s.exit: t.Stop() close(exit) break Loop } } // deregister self if err := s.Deregister(); err != nil { log.Logf("Server %s-%s deregister error: %s", config.Name, config.Id, err) } // wait for requests to finish if s.wg != nil { s.wg.Wait() } // close transport listener ch <- ts.Close() // disconnect the broker config.Broker.Disconnect() // swap back address s.Lock() s.opts.Address = addr s.Unlock() }() return nil}// 调用consul插件中的Register函数func (c *consulRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error { // 省略组成注册服务和判断是否错误代码 // 向consul中写入服务 if err := c.Client.Agent().ServiceRegister(asr); err != nil { return err } // save our hash and time check of the service c.Lock() c.register[s.Name] = h c.lastChecked[s.Name] = time.Now() c.Unlock() // if the TTL is 0 we don't mess with the checks if options.TTL == time.Duration(0) { return nil } // pass the healthcheck return c.Client.Agent().PassTTL("service:"+node.Id, "")}// 使用http方式往consul中添加服务func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error { r := a.c.newRequest("PUT", "/v1/agent/service/register") r.obj = service _, resp, err := requireOK(a.c.doRequest(r)) if err != nil { return err } resp.Body.Close() return nil}订阅服务注册调用broker的Subscribe(订阅方法)调用Register Register函数往consul添加服务节点如下图 ...

July 15, 2019 · 4 min · jiezi

Go-Micro-总体设计

Go-micro 是什么Go-micro框架是一套微服务分布式的框架,可以大幅度的提高开发效率。源码地址:https://github.com/micro/go-microGo-micro拥有很多特性: 服务注册、发现负载均衡消息解码,并默认支持json以及protobuf基于rpc的请求响应异步的消息通讯接口可插拔其中最值得一提的是最后一个特性,接口可插拔。只要实现上图的8个关键interface,就可以随意的根据需求重新时间这8个接口的功能。 这8个接口一实现了go-micro的整体架构。这些接口都有默认的实现方式,意味着你不需要写任何的插件就可以使用这个微服务架构。 主要interface整个Go Micro 都是有这8个interface构成的,换而言之只要理解了这8个接口,并仔细研究其中一个实现基本就能了解整个框架的实现和架构。下面先来看看这8个接口 Transort服务之间通信的接口。也就是服务发送和接收的最终实现方式,是由这些接口定制的。 type Socket interface { Recv(*Message) error Send(*Message) error Close() error}type Client interface { Socket}type Listener interface { Addr() string Close() error Accept(func(Socket)) error}type Transport interface { Dial(addr string, opts ...DialOption) (Client, error) Listen(addr string, opts ...ListenOption) (Listener, error) String() string}Codec有了传输方式,下面要解决的就是传输编码和解码问题,go-micro有很多种编码解码方式,默认的实现方式是protobuf,当然也有其他的实现方式,json、protobuf、jsonrpc、mercury等等。 源码 type Codec interface { ReadHeader(*Message, MessageType) error ReadBody(interface{}) error Write(*Message, interface{}) error Close() error String() string}type Message struct { Id uint64 Type MessageType Target string Method string Error string Header map[string]string}Codec接口的Write方法就是编码过程,两个Read是解码过程。 ...

July 10, 2019 · 2 min · jiezi

Go-Micro-Options-函数选项模式

函数选项 Functimional Options在Go语言中是没有默认函数的,但是我们可以使用函数选项模式来优雅的解决这个问题。函数选项模式不仅仅可以解决默认函数的问题还可以解决大量参数造成的代码复杂的问题。使用这个模式的有点: 支持默认参数:不必像结构体参数那样代码简介:即使想go-micro中 像Broker Cmd Client Server Registry 和BefroeStart等等都可以优雅的传入。扩展性好:如果有新增的参数,可以少量代码打到效果。函数选项模式在Go中的应用1. 先看几个模式使用的实力对象Option 封装了一个函数 函数接受一个Options的指针参数 Options 是具体的Micro 的具体选项实体// Option封装了一个函数type Option func(*Options)// 函数选项的具体对象// 保存了注册 客户端 服务以及 服务开始的方法列表 服务开启之后的方法列表等等type Options struct { Broker broker.Broker Cmd cmd.Cmd Client client.Client Server server.Server Registry registry.Registry Transport transport.Transport // Before and After funcs BeforeStart []func() error BeforeStop []func() error AfterStart []func() error AfterStop []func() error // Other options for implementations of the interface // can be stored in a context Context context.Context}2. Micro如何使用函数选项NewService 方法调用了内部方法newServicenewServices 调用了内部方法newOptions newOptions 1. 方法先给了默认的实现方式2. 循环传入的参数Options函数 执行方法 传入opt指针对象 3. 最终返回services对象func NewService(opts ...Option) Service { return newService(opts...)}func newService(opts ...Option) Service { options := newOptions(opts...) options.Client = &clientWrapper{ options.Client, metadata.Metadata{ HeaderPrefix + "From-Service": options.Server.Options().Name, }, } return &service{ opts: options, }}func newOptions(opts ...Option) Options { opt := Options{ Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Client: client.DefaultClient, Server: server.DefaultServer, Registry: registry.DefaultRegistry, Transport: transport.DefaultTransport, Context: context.Background(), } for _, o := range opts { o(&opt) } return opt}例子可以看到Name函数 接受一个string 返回一个Option 函数内部接受一个Options指针参数 内部给server复制了那么属性剩下的RegisterTTL 给server对象复制了time to live(生存时间)RegisterInterval函数设置了server的注册间隔时间可以看到 想Micro框架这么复杂的对象和这么多的设置,在不能使用默认参数的情况下,使用了函数选项模式,很优雅的实现了功能同事代码也很清楚和优雅。 ...

July 10, 2019 · 2 min · jiezi

cmdr-04-简单微服务-daemon

cmdr 04 - simple micro-service based on cmdr v0.2.21My ado is too much. 所以这次直入主题,谢绝吐槽。不知道 cmdr 干嘛用的,无妨看看前文 另一个go命令行参数处理器 - cmdrcmdr 02 - 复刻一个 wgetcmdr 03 - 用流式接口定义命令行参数处理选项那么,golang适合做后端开发,无论是 gRPC 还是 RESTful 都是它的强项。 一旦我们想要开发一个微服务时,抛开核心逻辑不谈,也不论 DevOps 方面究竟是 K8s,还是 Docker,还是裸机,总要面对一个启动、调试、测试的日常问题。 cmdr 除了提供命令行参数的解释能力之外,也额外提供了一个daemon插件,它可以帮助你简化日常开发工作,也令你不必关心 pid 文件、日志、退出信号等等问题,也无需重复编排 daemon 相关的命令行指令。 下面介绍怎么使用 daemon 插件,怎么编写实际的业务逻辑。我们以 demo 为例编写一个简单的示例性微服务,并解释具体的做法。 使用 Daemon 插件启用 Daemon 插件启用 Daemon 插件只需一行代码: // Entry is app main entryfunc Entry() { logrus.SetLevel(logrus.DebugLevel) logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) daemon.Enable(svr.NewDaemon(), nil, nil, nil) if err := cmdr.Exec(rootCmd); err != nil { logrus.Errorf("Error: %v", err) }}实现 daemon.Daemon 接口启用 daemon 插件,需要你实现 daemon.Daemon 接口,并编写一定的包装代码来连接 cmdr, daemon 以及你的业务逻辑。 ...

June 3, 2019 · 3 min · jiezi