github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/service.go (about) 1 package service 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "os/signal" 9 "runtime" 10 "syscall" 11 12 "github.com/urfave/cli/v2" 13 14 "github.com/tickoalcantara12/micro/v3/cmd" 15 "github.com/tickoalcantara12/micro/v3/service/client" 16 mudebug "github.com/tickoalcantara12/micro/v3/service/debug" 17 debug "github.com/tickoalcantara12/micro/v3/service/debug/handler" 18 "github.com/tickoalcantara12/micro/v3/service/logger" 19 "github.com/tickoalcantara12/micro/v3/service/model" 20 "github.com/tickoalcantara12/micro/v3/service/server" 21 ) 22 23 var ( 24 // errMissingName is returned by service.Run when a service is run 25 // prior to it's name being set. 26 errMissingName = errors.New("missing service name") 27 ) 28 29 // Service is a Micro Service which honours the go-micro/service interface 30 type Service struct { 31 opts Options 32 } 33 34 // Run the default service and waits for it to exist 35 func Run() { 36 // setup a new service, calling New() will trigger the cmd package 37 // to parse the command line and 38 srv := New() 39 40 if err := srv.Run(); err == errMissingName { 41 fmt.Println("Micro services must be run using \"micro run\"") 42 os.Exit(1) 43 } else if err != nil { 44 logger.Fatalf("Error running %v service: %v", srv.Name(), err) 45 } 46 } 47 48 // New returns a new Micro Service 49 func New(opts ...Option) *Service { 50 // before extracts service options from the CLI flags. These 51 // aren't set by the cmd package to prevent a circular dependancy. 52 // prepend them to the array so options passed by the user to this 53 // function are applied after (taking precedence) 54 before := func(ctx *cli.Context) error { 55 if n := ctx.String("service_name"); len(n) > 0 { 56 opts = append(opts, Name(n)) 57 } 58 if v := ctx.String("service_version"); len(v) > 0 { 59 opts = append(opts, Version(v)) 60 } 61 // service address injected by the runtime takes priority as the service port must match the 62 // port the server is running on 63 if a := ctx.String("service_address"); len(a) > 0 { 64 opts = append(opts, Address(a)) 65 } 66 return nil 67 } 68 69 // setup micro, this triggers the Before 70 // function which parses CLI flags. 71 cmd.New(cmd.SetupOnly(), cmd.Before(before)).Run() 72 73 // return a new service 74 return &Service{opts: newOptions(opts...)} 75 } 76 77 // Name of the service 78 func (s *Service) Name() string { 79 return s.opts.Name 80 } 81 82 // Version of the service 83 func (s *Service) Version() string { 84 return s.opts.Version 85 } 86 87 // Handle registers a handler 88 func (s *Service) Handle(v interface{}) error { 89 return s.Server().Handle(s.Server().NewHandler(v)) 90 } 91 92 // Subscribe registers a subscriber 93 func (s *Service) Subscribe(topic string, v interface{}) error { 94 return s.Server().Subscribe(s.Server().NewSubscriber(topic, v)) 95 } 96 97 func (s *Service) Init(opts ...Option) { 98 for _, o := range opts { 99 o(&s.opts) 100 } 101 } 102 103 func (s *Service) Options() Options { 104 return s.opts 105 } 106 107 func (s *Service) Client() client.Client { 108 return client.DefaultClient 109 } 110 111 func (s *Service) Model() model.Model { 112 return model.DefaultModel 113 } 114 115 func (s *Service) Server() server.Server { 116 return server.DefaultServer 117 } 118 119 func (s *Service) String() string { 120 return "micro" 121 } 122 123 func (s *Service) Start() error { 124 for _, fn := range s.opts.BeforeStart { 125 if err := fn(); err != nil { 126 return err 127 } 128 } 129 130 if err := s.Server().Start(); err != nil { 131 return err 132 } 133 134 for _, fn := range s.opts.AfterStart { 135 if err := fn(); err != nil { 136 return err 137 } 138 } 139 140 return nil 141 } 142 143 func (s *Service) Stop() error { 144 var gerr error 145 146 for _, fn := range s.opts.BeforeStop { 147 if err := fn(); err != nil { 148 gerr = err 149 } 150 } 151 152 if err := server.DefaultServer.Stop(); err != nil { 153 return err 154 } 155 156 for _, fn := range s.opts.AfterStop { 157 if err := fn(); err != nil { 158 gerr = err 159 } 160 } 161 162 return gerr 163 } 164 165 // Run the service 166 func (s *Service) Run() error { 167 // ensure service's have a name, this is injected by the runtime manager 168 if len(s.Name()) == 0 { 169 return errMissingName 170 } 171 172 // register the debug handler 173 s.Server().Handle( 174 s.Server().NewHandler( 175 debug.NewHandler(), 176 server.InternalHandler(true), 177 ), 178 ) 179 180 // start the profiler 181 if mudebug.DefaultProfiler != nil { 182 // to view mutex contention 183 runtime.SetMutexProfileFraction(5) 184 // to view blocking profile 185 runtime.SetBlockProfileRate(1) 186 187 if err := mudebug.DefaultProfiler.Start(); err != nil { 188 return err 189 } 190 191 defer mudebug.DefaultProfiler.Stop() 192 } 193 194 if logger.V(logger.InfoLevel, logger.DefaultLogger) { 195 logger.Infof("Starting [service] %s", s.Name()) 196 } 197 198 if err := s.Start(); err != nil { 199 return err 200 } 201 202 ch := make(chan os.Signal, 1) 203 if s.opts.Signal { 204 signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL) 205 } 206 207 // wait on kill signal 208 <-ch 209 return s.Stop() 210 } 211 212 // Handle is syntactic sugar for registering a handler 213 func Handle(h interface{}, opts ...server.HandlerOption) error { 214 return server.DefaultServer.Handle(server.DefaultServer.NewHandler(h, opts...)) 215 } 216 217 // Subscribe is syntactic sugar for registering a subscriber 218 func Subscribe(topic string, h interface{}, opts ...server.SubscriberOption) error { 219 return server.DefaultServer.Subscribe(server.DefaultServer.NewSubscriber(topic, h, opts...)) 220 } 221 222 // Event is an object messages are published to 223 type Event struct { 224 topic string 225 } 226 227 // Publish a message to an event 228 func (e *Event) Publish(ctx context.Context, msg interface{}) error { 229 return client.Publish(ctx, client.NewMessage(e.topic, msg)) 230 } 231 232 // NewEvent creates a new event publisher 233 func NewEvent(topic string) *Event { 234 return &Event{topic} 235 }