go-micro(三) 启动注册源码分析

1,156 阅读3分钟

go-micro启动

// 创建一个服务
service := micro.NewService(
	micro.Name("go.micro.srv.hello"),
	micro.Version("latest"),
	micro.Registry(etcd.NewRegistry()),
)

func newService(opts ...Option) Service {
	service := new(service)
	options := newOptions(opts...)
    // 忽略代码

	// 创建客户端
	options.Client = wrapper.FromService(serviceName, options.Client)
	options.Client = wrapper.TraceCall(serviceName, trace.DefaultTracer, options.Client)

    //忽略代码

	// set opts
	service.opts = options

	return service
}


func newOptions(opts ...Option) Options {
	//初始化组件
	opt := Options{
		Auth:      auth.DefaultAuth,
		Broker:    broker.DefaultBroker,
		Cmd:       cmd.DefaultCmd,
		Client:    client.DefaultClient,
		Server:    server.DefaultServer,
		Registry:  registry.DefaultRegistry,
		Transport: transport.DefaultTransport,
		Context:   context.Background(),
		Signal:    true,
	}
    //循环执行传入的组件 相当于上面的
    //micro.Name("go.micro.srv.hello")
	for _, o := range opts {
		o(&opt)
	}

	return opt
}

上面的代码对配置进行了初始化,创建了服务对象

// 启动服务
if err := service.Run(); err != nil {
		log.Fatal(err)
}
// 启动服务
func (s *service) Run() error {
    // 忽略代码
	log.Infof("Starting [service] %s", s.Name())
    //启动服务
	if err := s.Start(); err != nil {
		return err
	}
    //注册信号量
	ch := make(chan os.Signal, 1)
	if s.opts.Signal {
		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()
}
// 启动
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
}

// 使用grpc提供的服务
func (g *grpcServer) Start() error {
    //...忽略代码
    //启动服务
	if l := g.getListener(); l != nil {
		ts = l
	}
    //...忽略代码
    
	// 注册
	if err := g.Register(); err != nil {
		log.Errorf("Server register error: ", err)
	}

    //...忽略代码

    //启动自动注册
	Loop:
		for {
			select {
			// register self on interval
			case <-t.C:
				if err := g.Register(); err != nil {
					log.Error("Server register error: ", err)
				}
			// wait for exit
			case ch = <-g.exit:
				break Loop
			}
		}
    // 忽略代码

	return nil
}

// 注册服务
func (g *grpcServer) Register() error {
    //...忽略环境判断等代码	    
   
	addr, err := addr.Extract(host)
	if err != nil {
		return err
	}

	// make copy of metadata
	md := make(meta.Metadata)
	for k, v := range config.Metadata {
		md[k] = v
	}

	// 生成注册服务
	node := &registry.Node{
		Id:       config.Name + "-" + config.Id,
		Address:  mnet.HostPort(addr, port),
		Metadata: md,
	}

	node.Metadata["broker"] = config.Broker.String()
	node.Metadata["registry"] = config.Registry.String()
	node.Metadata["server"] = g.String()
	node.Metadata["transport"] = g.String()
	node.Metadata["protocol"] = "grpc"

	g.RLock()
	// 忽略代码
	
	service := &registry.Service{
		Name:      config.Name,
		Version:   config.Version,
		Nodes:     []*registry.Node{node},
		Endpoints: endpoints,
	}

	g.Lock()
	registered := g.registered
	g.Unlock()

	if !registered {
		log.Infof("Registry [%s] Registering node: %s", config.Registry.String(), node.Id)
	}

	// 创建注册服务灿鸟叔
	rOpts := []registry.RegisterOption{registry.RegisterTTL(config.RegisterTTL)}

    //真正的注册调用
	if err := config.Registry.Register(service, rOpts...); err != nil {
		return err
	}

    // ...忽略代码
}

生成各种服务代码参数,检测服务环境,最后组装成服务变量,传递给服务

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
}

只要实现了上面的Registry接口,都可以成为micro的注册服务,这种基于接口的方式,可以让注册服务随意切换

使用memory这种简单的注册服务,了解下注册服务的相关逻辑

func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
	// 循环参数
	for _, o := range opts {
		o(&options)
	}
    // 获取监听对象
	r := serviceToRecord(s, options.TTL)
    // 如果不存在就创建这个变量
	if _, ok := m.records[s.Name]; !ok {
		m.records[s.Name] = make(map[string]*record)
	}
    //如果是第一次的话 启动go m.sendEvent协程
	if _, ok := m.records[s.Name][s.Version]; !ok {
		m.records[s.Name][s.Version] = r
		log.Debugf("Registry added new service: %s, version: %s", s.Name, s.Version)
		go m.sendEvent(&registry.Result{Action: "update", Service: s})
		return nil
	}
    
	// 忽略代码

	return nil
}

func (m *Registry) sendEvent(r *registry.Result) {
	m.RLock()
	watchers := make([]*Watcher, 0, len(m.watchers))
	for _, w := range m.watchers {
		watchers = append(watchers, w)
	}
	m.RUnlock()
    // 监听变量,如果服务退出就取消
	for _, w := range watchers {
		select {
		case <-w.exit:
			m.Lock()
			delete(m.watchers, w.id)
			m.Unlock()
		default:
			select {
			case w.res <- r:
			case <-time.After(sendEventTime):
			}
		}
	}
}

```go

上面分析了服务注册的代码,当服务生成的时候,将服务的信息组合好,传到基于实现Registry的对象中,将服务注册到服务发现服务中,貌似这里面没有服务非主动掉线,心跳检测的代码,后面再慢慢看

> 服务已经注册上了,接下来就是客户端怎么调用的问题了