Ejemplo n.º 1
0
func (c *ServiceClient) attemptSend(timeout chan bool,
	attempts chan sendAttempt, ri *skynet.RequestInfo,
	fn string, in interface{}) {

	// first find an available instance
	var r pools.Resource
	var err error
	var sp *servicePool

	for r == nil {
		if len(c.instances) < 1 {
			attempts <- sendAttempt{err: errors.New("No instances found")}
			return
		}

		sp = <-c.servicePool

		log.Println(log.TRACE, "Sending request to: "+sp.service.UUID)

		// then, get a connection to that instance
		r, err = sp.pool.Acquire()
		defer sp.pool.Release(r)

		if err != nil {
			if r != nil {
				r.Close()
			}

			failed := FailedConnection{err}
			log.Printf(log.ERROR, "%T: %+v", failed, failed)
			c.instanceFailureChan <- *sp.service
		}
	}

	if err != nil {
		log.Printf(log.ERROR, "Error: %v", err)

		attempts <- sendAttempt{err: err}
		return
	}

	sr := r.(ServiceResource)

	result, serviceErr, err := c.sendToInstance(sr, ri, fn, in)
	if err != nil {
		// some communication error happened, shut down this connection and remove it from the pool
		failed := FailedConnection{err}
		log.Printf(log.ERROR, "%T: %+v", failed, failed)

		c.instanceFailureChan <- *sp.service
		sr.Close()
		return
	}

	attempts <- sendAttempt{
		result: result,
		err:    serviceErr,
	}
}
Ejemplo n.º 2
0
// 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, serviceErr, err error) {

	sin := skynet.ServiceRPCIn{
		RequestInfo: requestInfo,
		Method:      funcName,
		ClientID:    sr.clientID,
	}

	sin.In, err = bson.Marshal(in)
	if err != nil {
		err = fmt.Errorf("Error calling bson.Marshal: %v", err)
		return
	}

	sout := skynet.ServiceRPCOut{}

	err = sr.rpcClient.Call(sr.service.Name+".Forward", sin, &sout)
	if err != nil {
		sr.Close()

		// Log failure
		log.Printf(log.ERROR, "Error calling sr.rpcClient.Call: "+err.Error())
	}

	if sout.ErrString != "" {
		serviceErr = serviceError{sout.ErrString}
	}

	result = sout.Out

	return
}
Ejemplo n.º 3
0
func (s *Service) listen(addr skynet.BindAddr, bindWait *sync.WaitGroup) {
	var err error
	s.rpcListener, err = addr.Listen()
	if err != nil {
		panic(err)
	}

	log.Printf(log.INFO, "%+v\n", ServiceListening{
		Addr:        &addr,
		ServiceInfo: s.ServiceInfo,
	})

	// We may have changed port due to conflict, ensure config has the correct port now
	a, _ := skynet.BindAddrFromString(addr.String())
	s.ServiceAddr.IPAddress = a.IPAddress
	s.ServiceAddr.Port = a.Port

	bindWait.Done()

	for {
		conn, err := s.rpcListener.AcceptTCP()

		if s.shuttingDown {
			break
		}

		if err != nil && !s.shuttingDown {
			log.Println(log.ERROR, "AcceptTCP failed", err)
			continue
		}
		s.connectionChan <- conn
	}
}
Ejemplo n.º 4
0
func (s *Service) register() {
	// this version must be run from the mux() goroutine
	if s.Registered {
		return
	}

	err := skynet.GetServiceManager().Register(s.ServiceInfo.UUID)
	if err != nil {
		log.Println(log.ERROR, "Failed to register service: "+err.Error())
	}

	s.Registered = true
	log.Printf(log.INFO, "%+v\n", ServiceRegistered{s.ServiceInfo})
	s.Delegate.Registered(s) // Call user defined callback
}
Ejemplo n.º 5
0
func watchSignals(c chan os.Signal, s *Service) {
	signal.Notify(c, syscall.SIGINT, syscall.SIGKILL, syscall.SIGSEGV, syscall.SIGSTOP, syscall.SIGTERM)

	for {
		select {
		case sig := <-c:
			switch sig.(syscall.Signal) {
			// Trap signals for clean shutdown
			case syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT,
				syscall.SIGSEGV, syscall.SIGSTOP, syscall.SIGTERM:
				log.Printf(log.INFO, "%+v", KillSignal{sig.(syscall.Signal)})
				s.Shutdown()
				return
			}
		}
	}
}
Ejemplo n.º 6
0
func (s *Service) serveAdminRequests() {
	rId := os.Stderr.Fd() + 2
	wId := os.Stderr.Fd() + 3

	pipeReader := os.NewFile(uintptr(rId), "")
	pipeWriter := os.NewFile(uintptr(wId), "")
	s.pipe = daemon.NewPipe(pipeReader, pipeWriter)

	b := make([]byte, daemon.MAX_PIPE_BYTES)
	for {
		n, err := s.pipe.Read(b)

		if err != nil {
			if err != io.EOF {
				log.Printf(log.ERROR, "Error reading from admin pipe "+err.Error())
			} else {
				// We received EOF, ensure we shutdown (if daemon died we could be orphaned)
				s.Shutdown()
			}

			return
		}

		cmd := string(b[:n])
		log.Println(log.TRACE, "Received "+cmd+" from daemon")

		switch cmd {
		case "SHUTDOWN":
			s.Shutdown()
			s.pipe.Write([]byte("ACK"))
			break
		case "REGISTER":
			s.Register()
			s.pipe.Write([]byte("ACK"))
		case "UNREGISTER":
			s.Unregister()
			s.pipe.Write([]byte("ACK"))
		case "LOG DEBUG", "LOG TRACE", "LOG INFO", "LOG WARN", "LOG ERROR", "LOG FATAL", "LOG PANIC":
			parts := strings.Split(cmd, " ")
			log.SetLogLevel(log.LevelFromString(parts[1]))
			log.Println(log.INFO, "Setting log level to "+parts[1])

			s.pipe.Write([]byte("ACK"))
		}
	}
}
Ejemplo n.º 7
0
func (s *SkynetDaemon) StartSubService(requestInfo *skynet.RequestInfo, in daemon.StartSubServiceRequest, out *daemon.StartSubServiceResponse) (err error) {
	out.UUID = config.NewUUID()

	log.Printf(log.TRACE, "%+v", SubserviceStart{
		BinaryName: in.BinaryName,
		Args:       in.Args,
	})

	ss, err := NewSubService(in.BinaryName, in.Args, out.UUID, in.Registered)
	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")
	}

	tc := time.Tick(RerunWait * 2)

	go func() {
		// Wait for startup timer to see if we're still running
		// We want to avoid keeping a state of a large list of services that failed to start
		<-tc

		if ss.IsRunning() {
			s.saveState()
		}
	}()

	return
}
Ejemplo n.º 8
0
func NewServiceRPC(s *Service) (srpc *ServiceRPC) {
	srpc = &ServiceRPC{
		service: s,
		methods: make(map[string]reflect.Value),
	}

	// scan through methods looking for a method (RequestInfo,
	// something, something) error
	typ := reflect.TypeOf(srpc.service.Delegate)
	for i := 0; i < typ.NumMethod(); i++ {
		m := typ.Method(i)

		if reservedMethodNames[m.Name] {
			continue
		}

		// this is the check to see if something is exported
		if m.PkgPath != "" {
			continue
		}

		f := m.Func
		ftyp := f.Type()

		// must have four parameters: (receiver, RequestInfo,
		// somethingIn, somethingOut)
		if ftyp.NumIn() != 4 {
			goto problem
		}

		// don't have to check for the receiver

		// check the second parameter
		if ftyp.In(1) != RequestInfoPtrType {
			goto problem
		}

		// the somethingIn can be anything

		// somethingOut must be a pointer or a map
		switch ftyp.In(3).Kind() {
		case reflect.Ptr, reflect.Map:
		default:
			goto problem
		}

		// must have one return value that is an error
		if ftyp.NumOut() != 1 {
			goto problem
		}
		if ftyp.Out(0) != ErrorType {
			goto problem
		}

		// we've got a method!
		srpc.methods[m.Name] = f
		srpc.MethodNames = append(srpc.MethodNames, m.Name)
		continue

	problem:
		log.Printf(log.WARN, "Bad RPC method for %T: %q %v\n", s.Delegate, m.Name, f)
	}

	return
}
Ejemplo n.º 9
0
// ServiceRPC.Forward is the entry point for RPC calls. It wraps actual RPC calls
// and provides a slot for the RequestInfo. The parameters to the actual RPC
// calls are transmitted in a []byte, and are then marshalled/unmarshalled on
// either end.
func (srpc *ServiceRPC) Forward(in skynet.ServiceRPCIn, out *skynet.ServiceRPCOut) (err error) {
	srpc.service.activeRequests.Add(1)
	defer srpc.service.activeRequests.Done()

	go stats.MethodCalled(in.Method)

	clientInfo, ok := srpc.service.getClientInfo(in.ClientID)
	if !ok {
		err = errors.New("did not provide the ClientID")
		return
	}

	in.RequestInfo.ConnectionAddress = clientInfo.Address.String()
	if in.RequestInfo.OriginAddress == "" || !srpc.service.IsTrusted(clientInfo.Address) {
		in.RequestInfo.OriginAddress = in.RequestInfo.ConnectionAddress
	}

	mc := MethodCall{
		MethodName:  in.Method,
		RequestInfo: in.RequestInfo,
	}

	log.Printf(log.INFO, "%+v", mc)

	m, ok := srpc.methods[in.Method]
	if !ok {
		err = errors.New(fmt.Sprintf("No such method %q", in.Method))
		return
	}

	inValuePtr := reflect.New(m.Type().In(2))

	err = bson.Unmarshal(in.In, inValuePtr.Interface())
	if err != nil {
		return
	}

	// Allocate the out parameter of the RPC call.
	outType := m.Type().In(3)
	var outValue reflect.Value

	switch outType.Kind() {
	case reflect.Ptr:
		outValue = reflect.New(m.Type().In(3).Elem())
	case reflect.Map:
		outValue = reflect.MakeMap(outType)
	default:
		panic("illegal out param type")
	}

	startTime := time.Now().UnixNano()

	params := []reflect.Value{
		reflect.ValueOf(srpc.service.Delegate),
		reflect.ValueOf(in.RequestInfo),
		inValuePtr.Elem(),
		outValue,
	}

	returns := m.Call(params)

	duration := time.Now().UnixNano() - startTime

	mcp := MethodCompletion{
		MethodName:  in.Method,
		RequestInfo: in.RequestInfo,
		Duration:    duration,
	}

	log.Printf(log.INFO, "%+v", mcp)

	out.Out, err = bson.Marshal(outValue.Interface())
	if err != nil {
		return
	}

	var rerr error = nil
	erri := returns[0].Interface()
	if erri != nil {
		rerr, _ = erri.(error)
		out.ErrString = rerr.Error()
	}

	go stats.MethodCompleted(in.Method, duration, rerr)

	return
}