Esempio n. 1
0
func (client *Client) send(ctx context.Context, call *Call) error {
	if client.shutdown || client.closing {
		return ErrShutdown
	}

	// Every call gets its own req socket
	sock, err := client.ctx.NewSocket(zmq.REQ)
	if err != nil {
		return err
	}
	defer sock.Close()

	// Connect it to the router
	if err := sock.Connect(RouterURL); err != nil {
		return err
	}

	// Marshal the outgoing message
	msgBytes, err := proto.Marshal(call.Req)
	if err != nil {
		return err
	}

	// Envelope the message
	reqID := uuid.NewV4()
	envelope := &Request{
		UUID:    reqID.Bytes(),
		Path:    proto.String(fmt.Sprintf("zrpc://%s/%s", call.Service, util.GetMessageName(call.Req))),
		Payload: msgBytes,
	}

	// If request has a timeout, send it in the request envelope
	d, ok := ctx.Deadline()
	if ok {
		envelope.Expires = proto.Int64(d.Unix())
	}

	// Marshal the outgoing envelope
	envBytes, err := proto.Marshal(envelope)
	if err != nil {
		return err
	}

	// Send it
	if _, err := sock.SendBytes(envBytes, 0); err != nil {
		return err
	}

	handleResponse := func(state zmq.State) error {
		respBytes, err := sock.RecvBytes(0)
		if err != nil {
			return err
		}

		// Unmarshal the response envelope
		resp := &Response{}
		if err := proto.Unmarshal(respBytes, resp); err != nil {
			return err
		}

		// Make sure the same message ID was received
		respID, err := uuid.FromBytes(resp.UUID)
		if err != nil || !uuid.Equal(reqID, respID) {
			glog.Errorf("Mismatching message IDs, sent '%s', got '%s'", reqID, respID)
			return ErrMessageMismatch
		}

		// Check if there is an error
		if resp.Error != nil {
			return NewClientError(resp.Error.GetMessage(), int(resp.GetStatusCode()))
		}

		// Decode the actual message (if exists and wanted)
		if call.Resp != nil && len(resp.Payload) > 0 {
			if err := proto.Unmarshal(resp.Payload, call.Resp); err != nil {
				return err
			}
		}
		// This is insane, but the reactor runs until an error is returned...
		return errDone
	}

	// Use a reactor to be able to utilize the calls cancelc
	reactor := zmq.NewReactor()
	reactor.AddSocket(sock, zmq.POLLIN, handleResponse)
	reactor.AddChannel(call.cancelc, 1, func(interface{}) error {
		return ErrCancel
	})
	// Poll for a short interval to be able to return to the channel handling
	if err := reactor.Run(time.Millisecond * 50); err != errDone {
		return err
	}
	return nil
}
Esempio n. 2
0
// suitableMethods returns suitable Rpc methods of typ, it will report
// error using log if reportErr is true.
func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {
	methods := make(map[string]*methodType)
	for m := 0; m < typ.NumMethod(); m++ {
		method := typ.Method(m)
		mtype := method.Type
		mname := method.Name
		// Method must be exported.
		if method.PkgPath != "" {
			continue
		}
		// Method can have 2 or 3 ins: receiver, *args, (optional) *reply.
		if mtype.NumIn() < 2 {
			if reportErr {
				glog.Error("zrpc.Register: method ", mname, " has wrong number of ins: ", mtype.NumIn())
			}
			continue
		}
		// First arg need not be a pointer.
		argType := mtype.In(1)
		if !isExportedOrBuiltinType(argType) {
			if reportErr {
				glog.Error("zrpc.Register: ", mname, " argument type not exported: ", argType)
			}
			continue
		}
		// Second arg is optional
		var replyType reflect.Type
		if mtype.NumIn() > 2 {
			// Second arg must be a pointer.
			replyType = mtype.In(2)
			if replyType.Kind() != reflect.Ptr {
				if reportErr {
					glog.Error("zrpc.Register: method ", mname, " reply type not a pointer: ", replyType)
				}
				continue
			}
			// Reply type must be exported.
			if !isExportedOrBuiltinType(replyType) {
				if reportErr {
					glog.Error("zrpc.Register: method ", mname, " reply type not exported: ", replyType)
				}
				continue
			}
		}
		// Method needs one out.
		if mtype.NumOut() != 1 {
			if reportErr {
				glog.Error("zrpc.Register: method ", mname, " has wrong number of outs: ", mtype.NumOut())
			}
			continue
		}
		// The return type of the method must be error.
		if returnType := mtype.Out(0); returnType != typeOfError {
			if reportErr {
				glog.Error("zrpc.Register: method ", mname, " returns ", returnType.String(), " not error")
			}
			continue
		}
		// Use the request arg as key
		var argv reflect.Value
		if argType.Kind() == reflect.Ptr {
			argv = reflect.New(argType.Elem())
		} else {
			argv = reflect.New(argType)
		}
		methods[util.GetMessageName(argv.Interface().(proto.Message))] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
	}
	return methods
}