Summary
In the Go Micro framework, Server is an encapsulation of services such as Broker, Register, Codec, Transort, as you can see in the figure below.
Look again at the interface defined by Server
- Init: Initialization
- Handler: Register rpchandler
- NewHandler: Encapsulated rpchandler
- NewSubscriber: Encapsulating Subscriber to Subscribe Method
- Subscribe://Injection of Subscribe Events
- Start: Start services, listen on ports, process requests
- Stop: Stop service, broker and other resources shut down
- String
type Server interface { Options() Options Init(...Option) error Handle(Handler) error NewHandler(interface{}, ...HandlerOption) Handler NewSubscriber(string, interface{}, ...SubscriberOption) Subscriber Subscribe(Subscriber) error Start() error Stop() error String() string
Main Method Source Code Take RPC Server as an Example
Init
The initialization method of server. After initializing options, Cmd is initialized by using on function.
func (s *service) Init(opts ...Option) { // process options for _, o := range opts { o(&s.opts) } s.once.Do(func() { // Initialise the command flags, overriding new service _ = s.opts.Cmd.Init( cmd.Broker(&s.opts.Broker), cmd.Registry(&s.opts.Registry), cmd.Transport(&s.opts.Transport), cmd.Client(&s.opts.Client), cmd.Server(&s.opts.Server), ) }) }
Handle
Call router's handler method to register the method in server
func (s *rpcServer) Handle(h Handler) error { s.Lock() defer s.Unlock() if err := s.router.Handle(h); err != nil { return err } s.handlers[h.Name()] = h return nil }
NewSubscriber
Subscribe to a topic and call newSubscriber in subscriber
The newSubscriber function we are in Go Micro Broker Source Code Analysis This article has already been analyzed and will not be expanded here.
func (s *rpcServer) NewSubscriber(topic string, sb interface{}, opts ...SubscriberOption) Subscriber { return newSubscriber(topic, sb, opts...) }
Subscribe
The Subscribe function accepts a Subscriber interface. You can see the definition of the interface below.
Subscribe functions are saved to subscribers map in rpcServer after they are accepted into the interface
Call the registered Subscriber() Unsubscribe() function in Deregister, Register, Subscribe methods
type Subscriber interface { Topic() string Subscriber() interface{} Endpoints() []*registry.Endpoint Options() SubscriberOptions } // broker.go type Subscriber interface { Options() SubscribeOptions Topic() string Unsubscribe() error } func (s *rpcServer) Subscribe(sb Subscriber) error { sub, ok := sb.(*subscriber) if !ok { return fmt.Errorf("invalid subscriber: expected *subscriber") } if len(sub.handlers) == 0 { return fmt.Errorf("invalid subscriber: no handler functions") } if err := validateSubscriber(sb); err != nil { return err } s.Lock() defer s.Unlock() _, ok = s.subscribers[sub] if ok { return fmt.Errorf("subscriber %v already exists", s) } s.subscribers[sub] = nil return nil }
Start
Start function is the most important function in server. When service run, it calls back the start method of server to officially open the service.
The code is as follows
stay Go Micro Register Source Code Analysis This article has already analyzed the Start function's entry code for service discovery registration, which is not duplicated here. The main things to look at are listening ports, processing sake, and initializing brokers. Overall, it implements listening ports / processing requests / initializing brokers.
func (s *rpcServer) Start() error { registerDebugHandler(s) config := s.Options() // Start listening for transport to process requests received by clients ts, err := config.Transport.Listen(config.Address) if err != nil { return err } log.Logf("Transport [%s] Listening on %s", config.Transport.String(), ts.Addr()) // swap address s.Lock() addr := s.opts.Address s.opts.Address = ts.Addr() s.Unlock() // Connect broker for subscription services if err := config.Broker.Connect(); err != nil { return err } bname := config.Broker.String() log.Logf("Broker [%s] Connected to %s", bname, config.Broker.Address()) // Call RegisterCheck to check whether the registration service is available for injection of functions from outside 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 { // Registration Service if err = s.Register(); err != nil { log.Logf("Server %s-%s register error: %s", config.Name, config.Id, err) } } exit := make(chan bool) // Handling the Transport.Listen listerer listened on above go func() { for { // Listening for request processing request code will be explained in more detail in Transport err := ts.Accept(s.ServeConn) select { // check if we're supposed to exit case <-exit: return // check the error and backoff default: if err != nil { log.Logf("Accept error: %v", err) time.Sleep(time.Second) continue } } // no error just exit return } }() // Open the goroutine to check whether the service is available, with a set Register Interval interval 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() // Call the RegisterCheck method in the Register component to test whether the service is working properly 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 }
summary
Server is the encapsulation of some underlying methods, such as the opening and closing of services, the registration of nodes and the registration of subscriptions. The same level of encapsulation can be seen in the top figure as well as CLient, but there are services on top of Client and Server, which will be re-analyzed elsewhere.