-
Notifications
You must be signed in to change notification settings - Fork 325
Services
Services are the heart of your Skynet cluster, they serve all RPC requests and return responses back to clients.
The service delegate is your service, that has your callback methods and exported RPC methods.
All services must implement the service.ServiceDelegate interface
type ServiceDelegate interface { Started(s *Service) Stopped(s *Service) Registered(s *Service) Unregistered(s *Service) }
and any number of methods to be exported via RPC. In order for a method to be available to the skynet network it must meet the following criteria
func Foo(ri *skynet.RequestInfo, req interface{}, resp interface{}) error {
- The method must be exported
- It must have 3 parameters
- a pointer to skynet.RequestInfo which will hold the unique UUID for this request, it's passed to each machine that touches a request.
- a map/array or struct of your own that will be the request sent from the client. This can be a pointer
- a map/array, or a pointer to struct of your own that will be the response to send back to the client
- It must have a single return value of the type error for failures when a response cannot be sent
Valid
func (s *MyService) Foo(ri *skynet.RequestInfo, req *MyRequest, resp *MyResponse) error { func (s *MyService) Foo(ri *skynet.RequestInfo, req MyRequest, resp *MyResponse) error { func (s *MyService) Foo(ri *skynet.RequestInfo, in map[string]interface{}, out map[string]interface{}) (err error) {
In order to turn your ServiceDelegate into a Skynet service, you will need to create a ServiceConfig.
type ServiceConfig struct { Log Logger `json:"-"` UUID string Name string Version string Region string ServiceAddr *BindAddr AdminAddr *BindAddr DoozerConfig *DoozerConfig `json:"-"` DoozerUpdateInterval time.Duration `json:"-"` }
- Log - anything that implements the Logger interface, this will be used internally for all logging
- UUID - a custom UUID for this instance, so that it can be identified later.
- Name - a unique name for this specific service
- Version - the version of this service
- Region - we generally refer to region as a data center, but you could use this to group things however you'd like
- ServiceAddr - This is an object that represents the ip address and port to bind to for serving rpc requests
- AdminAddr - This is an object that represents the ip address and port to bind to for serving admin (stop/unregister/register) requests
- DoozerConfig - Configuration for how this service should connect to doozer
- DoozerUpdateInterval - this is the frequency in which the service will report it's number of connections and average response time etc to doozer
type Logger interface { Item(item interface{}) Panic(item interface{}) Println(items ...interface{})
This is pretty self explanatory, aside from the MaxPort. If given a Port and MaxPort it will hunt for an open port in that range.
type BindAddr struct { IPAddress string Port int MaxPort int }
type DoozerConfig struct { Uri string BootUri string AutoDiscover bool }
- Uri - doozer uri to connect to
- BootUri - if you're using a DZNS cluster this will be the uri for that
- AutoDiscover - whether or not new instances of doozer that come online should be added to our internal list of instances to connect to in case of connection problems to our current instance.
To save a bit of repetitive effort, and help encourage consistency. There are some helpers available to you.
This will parse everything you need for a ServiceConfig from the command line flags, and Environment Variables set as defaults. We recommend taking this approach, and then setting the config values on the config object that are returned that you would like to be hardcoded and not overrideable.
config, _ := skynet.GetServiceConfig()
Doozer Flags:
- --doozer - ip:port of doozer instance
- --doozerboot - ip:port of doozer dzns cluster
- --autodiscover - use new doozer instances that come online
Service Flags
- --uuid - unique id for the current instance of the service
- --region - the region the current instance of the service is running in
- --version - the version of the service
- --dzupdate - the interval that the service should update doozer
To create a skynet service that can be run you just need to pass in your ServiceConfig, and ServiceDelegate to service.CreateService
service := service.CreateService(delegate, config)
Now you have a Skynet Service wrapper, which will delegate RPC calls to the delegate that you created, and handle all startup/shutdown/register/unregister, and connectivity with clients.
Only 1 thing left to do. We need to call Start so that the service will bind its ip's and notify the cluster. Passing true will register the service to take requests once initiaization has finished, if you pass false, you will need to call service.Register() later in order for your service to start taking requests.
Start() returns a sync.WaitGroup for you to wait on to keep your app up and taking requests, when the service is killed or interrupted it will finish all current requests and shutdown, and Wait() will return.
waiter := service.Start(true) waiter.Wait()
Be sure that in your main function, and anywhere else that is neccessary that you call
service.Shutdown()
This will ensure your service cleans up after itself, stops taking new requests, finishes current requests, and removes itself from the cluster. Don't worry if something terribly bad happens the daemon that started this service will clean up after it, but we prefer services to cleanup after themselves, and not loose requests if at all possible.