func (s *SkynetDaemon) Deploy(requestInfo *skynet.RequestInfo, in daemon.DeployRequest, out *daemon.DeployResponse) (err error) { out.UUID = skynet.UUID() s.Log.Trace(fmt.Sprintf("%+v", SubserviceDeployment{ ServicePath: in.ServicePath, Args: in.Args, })) ss, err := NewSubService(s, in.ServicePath, in.Args, out.UUID) if err != nil { return } s.serviceLock.Lock() s.Services[out.UUID] = ss s.serviceLock.Unlock() start, startErr := ss.Start() if startErr != nil { return errors.New("Service failed to start: " + startErr.Error()) } else if !start { return errors.New("Service failed to start") } return }
// ServiceClient.sendToInstance() tries to make an RPC request on a particular connection to an instance func (c *ServiceClient) sendToInstance(sr ServiceResource, requestInfo *skynet.RequestInfo, funcName string, in interface{}) (result []byte, err error) { if requestInfo == nil { requestInfo = &skynet.RequestInfo{ RequestID: skynet.UUID(), } } sin := service.ServiceRPCIn{ RequestInfo: requestInfo, Method: funcName, } sin.In, err = bson.Marshal(in) if err != nil { return } sout := service.ServiceRPCOut{} err = sr.rpcClient.Call(sr.service.Config.Name+".Forward", sin, &sout) if err != nil { sr.Close() c.Log.Item(err) } if sout.ErrString != "" { err = serviceError{sout.ErrString} } result = sout.Out return }
func main() { var err error flag.Parse() log = skynet.NewConsoleSemanticLogger("dashboard", os.Stderr) if *mgoserver == "" { log.Trace(fmt.Sprintf("%+v", skynet.MongoError{ "", "No mongodb server url (both -mgoserver and SKYNET_MGOSERVER missing)", })) } mlogger, err := skynet.NewMongoSemanticLogger(*mgoserver, "skynet", "log", skynet.UUID()) if err != nil { log.Error(fmt.Sprintf("%+v", skynet.MongoError{ Addr: "Could not connect to mongo db for logging", Err: err.Error(), })) } log = skynet.NewMultiSemanticLogger(mlogger, log) DC = Doozer() http.HandleFunc("/", indexHandler) http.HandleFunc("/logs/search", searchHandler) http.Handle("/media/", http.StripPrefix("/media/", http.FileServer(http.Dir(*webroot+"/tmpl")))) http.Handle("/favicon.ico", http.FileServer(http.Dir(*webroot+"/tmpl/images"))) http.Handle("/logs/ws", websocket.Handler(wsHandler)) im := client.NewInstanceMonitor(DC, true) http.Handle("/instances/ws", websocket.Handler(func(ws *websocket.Conn) { NewInstanceSocket(ws, im) })) // Cache templates layoutTmpl = template.Must(template.ParseFiles(*webroot + "/tmpl/layout.html.template")) indexTmpl = template.Must(template.ParseFiles(*webroot + "/tmpl/index.html.template")) searchTmpl = template.Must(template.ParseFiles(*webroot + "/tmpl/search.html.template")) err = http.ListenAndServe(*addr, nil) if err != nil { log.Fatal("ListenAndServe: " + err.Error()) } }
func (s *SkynetDaemon) Deploy(requestInfo *skynet.RequestInfo, in DeployIn, out *DeployOut) (err error) { out.UUID = skynet.UUID() s.Log.Item(SubserviceDeployment{ ServicePath: in.ServicePath, Args: in.Args, }) ss, err := NewSubService(s.Log, in.ServicePath, in.Args, out.UUID) if err != nil { return } s.serviceLock.Lock() s.Services[out.UUID] = ss s.serviceLock.Unlock() return }
func newServiceClient(query *Query, c *Client) (sc *ServiceClient) { sc = &ServiceClient{ client: c, Log: c.Config.Log, cconfig: c.Config, query: query, instances: make(map[string]*servicePool), chooser: NewInstanceChooser(), muxChan: make(chan interface{}), timeoutChan: make(chan timeoutLengths), } sc.listenID = skynet.UUID() sc.instanceListener = c.instanceMonitor.Listen(sc.listenID, query) go sc.mux() return }
func newServiceClient(query *skynet.Query, c *Client) (sc *ServiceClient) { sc = &ServiceClient{ client: c, Log: c.Config.Log, cconfig: c.Config, query: query, instances: make(map[string]*servicePool), chooser: NewInstanceChooser(c), muxChan: make(chan interface{}), timeoutChan: make(chan timeoutLengths), retryTimeout: skynet.DefaultRetryDuration, giveupTimeout: skynet.DefaultTimeoutDuration, } sc.listenID = skynet.UUID() sc.instanceListener = c.instanceMonitor.Listen(sc.listenID, query, false) go sc.mux() return }
func (c *ServiceClient) Send(requestInfo *skynet.RequestInfo, funcName string, in interface{}, outPointer interface{}) (err error) { // TODO: timeout logic s, err := c.getConnection(0) if err != nil { c.Log.Item(err) return } if requestInfo == nil { requestInfo = &skynet.RequestInfo{ RequestID: skynet.UUID(), } } sin := service.ServiceRPCIn{ RequestInfo: requestInfo, Method: funcName, } sin.In, err = bson.Marshal(in) if err != nil { return } sout := service.ServiceRPCOut{} // TODO: Check for connectivity issue so that we can try to get another resource out of the pool err = s.rpcClient.Call(s.service.Config.Name+".Forward", sin, &sout) if err != nil { c.Log.Item(err) } err = bson.Unmarshal(sout.Out, outPointer) if err != nil { return } c.connectionPool.Put(s) return }
func (s *SkynetDaemon) Deploy(requestInfo *skynet.RequestInfo, in M, out *M) (err error) { *out = map[string]interface{}{} uuid := skynet.UUID() (*out)["uuid"] = uuid servicePath := in["service"].(string) args := in["args"].(string) s.Log.Item(SubserviceDeployment{ ServicePath: servicePath, Args: args, }) ss, err := NewSubService(s.Log, servicePath, args, uuid) if err != nil { return } s.serviceLock.Lock() s.Services[uuid] = ss s.serviceLock.Unlock() return }
func (c *ServiceClient) send(retry, giveup time.Duration, ri *skynet.RequestInfo, fn string, in interface{}, out interface{}) (err error) { if ri == nil { ri = &skynet.RequestInfo{ RequestID: skynet.UUID(), } } attempts := make(chan sendAttempt) var ticker <-chan time.Time if retry > 0 { ticker = time.NewTicker(retry).C } var timeout <-chan time.Time if giveup > 0 { timeout = time.NewTimer(giveup).C } doneSignal := make(chan bool) attemptCount := 1 defer func() { go func() { for i := 0; i < attemptCount; i++ { doneSignal <- true } }() }() go c.attemptSend(doneSignal, attempts, ri, fn, in) for { select { case <-ticker: attemptCount++ ri.RetryCount++ go c.attemptSend(doneSignal, attempts, ri, fn, in) case <-timeout: if err == nil { err = ErrRequestTimeout } // otherwise use the last error reported from an attempt return case attempt := <-attempts: err = attempt.err if err != nil { if _, ok := err.(serviceError); !ok { // error during transmition, abort this attempt if giveup == 0 { return } continue } } unmarshallerr := bson.Unmarshal(attempt.result, out) if unmarshallerr != nil { err = unmarshallerr } return } } return }
func NewInstanceSocket(ws *websocket.Conn, im *client.InstanceMonitor) { closeChan := make(chan bool, 1) readChan := make(chan SocketRequest) ticker := time.NewTicker(5 * time.Second) lastHeartbeat := time.Now() go instanceSocketRead(ws, readChan, closeChan) l := im.Listen(skynet.UUID(), &client.Query{}) instances := <-l.NotificationChan err := websocket.JSON.Send(ws, SocketResponse{Action: "List", Data: instances}) if err != nil { closeChan <- true } for { select { case <-closeChan: ticker.Stop() ws.Close() l.Close() case t := <-ticker.C: // Check for timeout if t.Sub(lastHeartbeat) > (15 * time.Second) { closeChan <- true } case request := <-readChan: lastHeartbeat = time.Now() switch request.Action { case "List": err := websocket.JSON.Send(ws, SocketResponse{Action: "List", Data: instances}) if err != nil { closeChan <- true } case "Heartbeat": // this is here more for documentation purposes, setting the lastHeartbeat on read handles the logic here case "Filter": if request.Data != nil { data := request.Data.(map[string]interface{}) if r, ok := data["Reset"]; ok { reset := r.(bool) if reset { l.Query.Reset() } } if r, ok := data["Registered"]; ok { filter := r.(bool) l.Query.Registered = &filter } } instances := l.GetInstances() iln := make(client.InstanceListenerNotification) for _, i := range instances { path := i.GetConfigPath() iln[path] = client.InstanceMonitorNotification{ Path: path, Service: i, Type: client.InstanceAddNotification, } } err := websocket.JSON.Send(ws, SocketResponse{Action: "List", Data: iln}) if err != nil { closeChan <- true } } case notification := <-l.NotificationChan: var err error // Forward message as it stands across the websocket err = websocket.JSON.Send(ws, SocketResponse{Action: "Update", Data: notification}) instances = instances.Join(notification) if err != nil { closeChan <- true } } } }
// this function is the goroutine that owns this service - all thread-sensitive data needs to // be manipulated only through here. func (s *Service) mux() { loop: for { select { case conn := <-s.connectionChan: atomic.AddInt32(&s.Stats.Clients, 1) clientID := skynet.UUID() s.clientMutex.Lock() s.clientInfo[clientID] = ClientInfo{ Address: conn.RemoteAddr(), } s.clientMutex.Unlock() // send the server handshake sh := skynet.ServiceHandshake{ Registered: s.Registered, ClientID: clientID, } encoder := bsonrpc.NewEncoder(conn) err := encoder.Encode(sh) if err != nil { s.Log.Item(err) atomic.AddInt32(&s.Stats.Clients, -1) break } if !s.Registered { conn.Close() atomic.AddInt32(&s.Stats.Clients, -1) break } // read the client handshake var ch skynet.ClientHandshake decoder := bsonrpc.NewDecoder(conn) err = decoder.Decode(&ch) if err != nil { s.Log.Item(err) atomic.AddInt32(&s.Stats.Clients, -1) break } // here do stuff with the client handshake go func() { s.RPCServ.ServeCodec(bsonrpc.NewServerCodec(conn)) atomic.AddInt32(&s.Stats.Clients, -1) }() case register := <-s.registeredChan: if register { s.register() } else { s.unregister() } case _ = <-s.doneChan: go func() { for _ = range s.doneChan { } }() s.RemoveFromCluster() s.doozerChan <- doozerFinish{} break loop case _ = <-s.updateTicker.C: s.UpdateCluster() } } }