// NewClientWithConnection returns a new Client to handle requests to the // set of services at the other end of the connection. // An existing connection in the form of a zmq socket together with the zmq // context they were created with is used func NewClientWithConnection(ctx *zmq.Context, conn *zmq.Socket) *Client { // A router socket is the middle-man between client requests and actually sending/receiving on the wire router, err := ctx.NewSocket(zmq.ROUTER) if err != nil { glog.Fatal(err) } if err := router.Bind(RouterURL); err != nil { glog.Fatal(err) } client := &Client{ conn: conn, endpoints: endpoints{ socket: conn, }, router: router, ctx: ctx, } // Start the proxy in an own routine since it is blocking go func() { if err := zmq.Proxy(conn, router, nil); err != nil { switch zmq.AsErrno(err) { case zmq.Errno(zmq.ETERM): glog.Info(err) case zmq.Errno(syscall.EINTR): glog.Info(err) default: glog.Info(zmq.AsErrno(err)) glog.Info(err) } client.Close() } }() // Socket monitor for connect event monitorURL := "inproc://monitor" if err := conn.Monitor(monitorURL, zmq.EVENT_CONNECTED|zmq.EVENT_DISCONNECTED); err != nil { client.Close() glog.Fatal(err) } go client.monitor(monitorURL) return client }
/****************************************************************************** * 概述: 订阅退订 * 函数名: dealCmd * 返回值: * 参数列表: 参数名 参数类型 取值范围 描述 * *******************************************************************************/ func (this *ZmqSocket) dealCmd(cmdSocket *zmq4.Socket) (int, error) { log4.Debug("start deal cmd...") for { //半包处理 cmdsStr, err0 := cmdSocket.Recv(zmq4.DONTWAIT) if err0 != nil { errno1 := zmq4.AsErrno(err0) switch errno1 { case zmq4.Errno(syscall.EAGAIN): return 0, nil case zmq4.Errno(syscall.EINTR): continue default: log4.Debug("zmq req Get err %v, %d!", errno1, errno1) } } if len(cmdsStr) == 0 { log4.Debug("deal cmd return") return 0, nil } ss := strings.Split(cmdsStr, " ") log4.Debug("recv cmd %s", cmdsStr) for i := 0; i < len(ss); i++ { if len(ss[i]) == 0 { continue } if ss[i] == "stop" { // log4.Debug("recv cmd will stop %s %s", this.mreqUrl, this.msubUrl) cmdSocket.Send("0", 0) return 1, nil } if !this.mChoose { // log4.Debug("recv cmd ,but notChoose so return") return 0, nil } if ss[i][0] == 'r' { if err := this.msubSocket.SetSubscribe(ss[i][1:]); err != nil { log4.Error("SetSubscribe(%s) falied, %s", ss[i][1:], err.Error()) return 0, err } log4.Debug("setSubscribe ok %s", ss[i][1:]) continue } if ss[i][0] == 'u' { if err := this.msubSocket.SetUnsubscribe(ss[i][1:]); err != nil { log4.Error("SetUnSubscribe(%s) falied, %s", ss[i][1:], err.Error()) return 0, err } log4.Debug("setUnSubscribe ok %s", ss[i][1:]) continue } } } return 0, nil }
func handleGeneralError(err error) { switch err { case zmq.ErrorSocketClosed, zmq.ErrorContextClosed: return } switch zmq.AsErrno(err) { case zmq.ETERM: return } panic(err) }
func IsEINTR(err error) bool { switch err.(type) { case syscall.Errno: if err == syscall.EINTR { return true } return false case zmq.Errno: if zmq.AsErrno(err) == zmq.Errno(syscall.EINTR) { return true } return false } return false }
func handleIOError(err error) bool { switch err { case zmq.ErrorSocketClosed, zmq.ErrorContextClosed: return false } switch zmq.AsErrno(err) { case zmq.Errno(syscall.EAGAIN): return true case zmq.ETERM: return false } panic(err) }
/****************************************************************************** * 概述: Zmq关闭 * 函数名: Close * 返回值: * 参数列表: 参数名 参数类型 取值范围 描述 * *******************************************************************************/ func (this *Zmq) Close() bool { if this.Mcontext != nil { if err := this.Mcontext.Term(); err != nil { switch zmq4.AsErrno(err) { case zmq4.Errno(syscall.EINTR): this.Mcontext = nil return false case zmq4.ETERM: this.Mcontext = nil return true case zmq4.Errno(syscall.EFAULT): this.Mcontext = nil return true } } this.Mcontext = nil } log4.Info("conetxt term ok") return true }
func (client *Client) monitor(addr string) { s, err := client.ctx.NewSocket(zmq.PAIR) if err != nil { glog.Fatal(err) } defer s.Close() if err := s.Connect(addr); err != nil { glog.Fatal(err) } for { evtType, addr, _, err := s.RecvEvent(0) if err != nil { switch zmq.AsErrno(err) { case zmq.Errno(zmq.ETERM): glog.Info(err) case zmq.Errno(syscall.EINTR): glog.Info(err) default: glog.Error(err) } break } // Only care about events for our added endpoints if end, ok := client.endpoints.get(addr); ok { switch evtType { case zmq.EVENT_CONNECTED: // Flag endpoint as connected if !end.alive { close(end.connected) } else { glog.Info("zrpc: reconnected to ", end.address) } case zmq.EVENT_DISCONNECTED: glog.Infof("zrpc: %s disconnected ", end.address) } } } }
func main() { // Socket to talk to server fmt.Println("Connecting to hello world server...") client, _ := zmq.NewSocket(zmq.REQ) defer client.Close() client.Connect("tcp://localhost:5555") // Without signal handling, Go will exit on signal, even if the signal was caught by ZeroMQ chSignal := make(chan os.Signal, 1) signal.Notify(chSignal, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM) LOOP: for { client.Send("HELLO", 0) fmt.Println("Sent: HELLO") reply, err := client.Recv(0) if err != nil { if zmq.AsErrno(err) == zmq.Errno(syscall.EINTR) { // signal was caught by 0MQ log.Println("Client Recv:", err) break } else { // some error occurred log.Panicln(err) } } fmt.Println("Received:", reply) time.Sleep(time.Second) select { case sig := <-chSignal: // signal was caught by Go log.Println("Signal:", sig) break LOOP default: } } }
/****************************************************************************** * 概述: 事件处理 * 函数名: dealMonitorEvent * 返回值: error * 参数列表: 参数名 参数类型 取值范围 描述 * isReq bool 是否为req事件 * *******************************************************************************/ func (this *ZmqSocket) dealMonitorEvent(s *zmq4.Socket, isReq bool) error { log4.Debug("start dealMonitorEvent...") for { a, b, c, err := s.RecvEvent(0) //if runtime.GOOS == "windows" && len(b) == 0 && c == 0 { // log4.Debug("monitor eagan windows") // return nil //} if err != nil { errno1 := zmq4.AsErrno(err) switch errno1 { case zmq4.Errno(syscall.EAGAIN): log4.Debug("monitor eagan ") return nil case zmq4.Errno(syscall.EINTR): log4.Debug("monitor EINTR") continue default: log4.Debug("zmq req Get err %v, %d!", errno1, errno1) } } if a == 0 { // log4.Debug("monitor return") return nil } switch a { case zmq4.EVENT_CONNECTED: log4.Info("sub or req monitor event CONNECTED, url:%s %s", this.mreqUrl, this.mreqUrl) this.mchooseMutex.Lock() defer this.mchooseMutex.Unlock() if isReq { this.mreqOK = true } else { this.msubOK = true } if this.mstateChFlag && this.mreqOK && this.msubOK { this.mstateChFlag = false select { case this.mstateCh <- true: default: } } // this.mok++ case zmq4.EVENT_DISCONNECTED: log4.Error("sub or req monitor event DISCONNECTED, url:%s %s", this.mreqUrl, this.mreqUrl) this.mchooseMutex.Lock() defer this.mchooseMutex.Unlock() if isReq { this.mreqOK = false } else { this.msubOK = false } // this.mok-- if this.mChoose { this.mChoose = false topics := this.mzmq.MdataCache.GetSdsTopic() for i := 0; i < len(*topics); i++ { if err := this.msubSocket.SetUnsubscribe((*topics)[i]); err != nil { log4.Error("SetUnSubscribe(%s) falied, %s", (*topics)[i], err.Error()) } } this.mzmq.Mevent.UpdateWorkSocket() } case zmq4.EVENT_CLOSED: // this.mchooseMutex.Lock() // this.mok // if this.mChoose{ // this.mChoose = false // this.mevent.UpdateWorkSocket() // log4.Error("sdssdk zmqreq monitor event CLOSED", b, c) // } // this.mchooseMutex.UnLock() default: log4.Debug("zmqreq monitor unknow event err", a, b, c, err) } } return nil }
/****************************************************************************** * 概述: 接收处理推送信息 * 函数名: dealSub * 返回值: * 参数列表: 参数名 参数类型 取值范围 描述 * *******************************************************************************/ func (this *ZmqSocket) dealSub(soc *zmq4.Socket, msgHeadFlag *bool) error { // log4.Debug("start deal sub0...") // this.mchooseMutex.RLocker() if !this.mChoose { // this.mchooseMutex.RUnlock() return nil } log4.Debug("start deal sub...") // this.mchooseMutex.RUnlock() for { if *msgHeadFlag { topic, err := soc.Recv(zmq4.DONTWAIT) if err != nil { errno1 := zmq4.AsErrno(err) switch errno1 { case zmq4.Errno(syscall.EAGAIN): return nil case zmq4.Errno(syscall.EINTR): continue default: log4.Debug("zmq req Get err %v, %d!", errno1, errno1) } } if len(topic) == 0 { // log4.Debug("sub return1") return nil } // log4.Debug("recv sub head =", topic) *msgHeadFlag = false } else { stream, err2 := soc.RecvBytes(zmq4.DONTWAIT) if err2 != nil { errno1 := zmq4.AsErrno(err2) switch errno1 { case zmq4.Errno(syscall.EAGAIN): return nil case zmq4.Errno(syscall.EINTR): continue default: log4.Debug("zmq req Get err %v, %d!", errno1, errno1) } } if len(stream) == 0 { // log4.Debug("sub return2") return nil } *msgHeadFlag = true frm, err3 := common.UnwrapBaseProto(stream) if err3 != nil { log4.Error("UnwrapBaseProto falied, %s", err3.Error()) return err3 } mid := common.UnOffset(*frm.GetBody().Mid, 4) if mid[4-1] == 200 { log4.Error("sdssdk zmqsub get mid == 200 err") continue } res, err4 := common.UnwrapResponse(frm.GetBody().Mdata) if err4 != nil { log4.Error("sdssdk sub UnwrapResponse error:", err4) continue } this.mzmq.MdataCache.UpdatePoints(res) } } return nil }
// Start registers a zmq endpoint at passed address answering requests for registered services func (server *Server) Start(addr string) { // Don't use the global context to avoid package level confusion ctx, err := zmq.NewContext() if err != nil { glog.Fatal(err) } // A router socket handles the actual connection sock, _ := ctx.NewSocket(zmq.ROUTER) server.conn = sock // If no prefix is passed, default to tcp if !strings.HasPrefix(addr, "tcp://") { addr = "tcp://" + addr } server.conn.Bind(addr) glog.Info("Server listening on ", addr) // Socket monitor monitorURL := "inproc://monitor" if err := server.conn.Monitor(monitorURL, zmq.EVENT_ACCEPTED|zmq.EVENT_DISCONNECTED); err != nil { glog.Fatal(err) } go server.monitor(ctx, monitorURL) // A dealer socket multiplexes requests to workers mux, _ := ctx.NewSocket(zmq.DEALER) defer mux.Close() mux.Bind("inproc://mux") // Start backing worker processes for i := 0; i < server.numWorkers; i++ { go func(i int) { worker, _ := ctx.NewSocket(zmq.REP) defer worker.Close() worker.Connect("inproc://mux") glog.V(2).Infof("Started worker #%d", i) for { if server.closing { glog.Warning(ErrShutdown) break } reqBytes, err := worker.RecvBytes(0) if err != nil { switch zmq.AsErrno(err) { // If was interrupted there is no need to log as an error case zmq.Errno(zmq.ETERM): glog.Info(err) default: // Error receiving is usually fatal glog.Error(err) } break } // Decode the request envelope req := &Request{} if err := proto.Unmarshal(reqBytes, req); err != nil { glog.Error(err) sendError(worker, nil, err) continue } // Make sure it's not expired on arrival if req.Expires != nil { if time.Unix(*req.Expires, 0).Before(time.Now()) { glog.Infof("discarding expired message: '%s'", req.UUID) sendError(worker, req, NewExpiredError("message expired on arrival")) continue } } serviceName := path.Dir(strings.TrimPrefix(req.GetPath(), "zrpc://")) methodName := path.Base(req.GetPath()) // Make sure a handler for this request exists server.mu.RLock() service, ok := server.serviceMap[serviceName] server.mu.RUnlock() if !ok { err := fmt.Sprintf("service '%s' is not served", serviceName) if serviceName == "." { err = "no service name passed" } glog.Warning(err) sendError(worker, req, errors.New(err)) continue } // Make sure the message is registered for this server if mType, ok := service.method[methodName]; ok { // Decode the incoming request message var argv reflect.Value argIsValue := false // if true, need to indirect before calling. if mType.ArgType.Kind() == reflect.Ptr { argv = reflect.New(mType.ArgType.Elem()) } else { argv = reflect.New(mType.ArgType) argIsValue = true } // argv guaranteed to be a pointer now. if err := proto.Unmarshal(req.Payload, argv.Interface().(proto.Message)); err != nil { glog.Error(err) sendError(worker, req, err) continue } if argIsValue { argv = reflect.Indirect(argv) } glog.V(3).Infof("Received '%s' (%s)", argv.Type().Elem(), req.UUID) // Invoke the method, providing a new value for the reply (if expected) var ( returnValues []reflect.Value replyv reflect.Value ) if mType.ReplyType != nil { replyv = reflect.New(mType.ReplyType.Elem()) returnValues = mType.method.Func.Call([]reflect.Value{service.rcvr, argv, replyv}) } else { returnValues = mType.method.Func.Call([]reflect.Value{service.rcvr, argv}) } // The return value for the method is an error. errInter := returnValues[0].Interface() if errInter != nil { err := errInter.(error) sendError(worker, req, err) continue } // Envelope the response message envelope := &Response{ Path: req.Path, UUID: req.UUID, } // Marshal the response message (if exists) if mType.ReplyType != nil { replyBytes, err := proto.Marshal(replyv.Interface().(proto.Message)) if err != nil { glog.Error(err) sendError(worker, req, err) continue } envelope.Payload = replyBytes } // Marshal the envelope envBytes, err := proto.Marshal(envelope) if err != nil { glog.Error(err) sendError(worker, req, err) continue } // Send the response if _, err := worker.SendBytes(envBytes, 0); err != nil { // Since we could not send, we could not send an error either, just log glog.Error(err) } if mType.ReplyType != nil { glog.V(3).Infof("Replied '%s' (%s)", mType.ReplyType.Elem(), envelope.UUID) } else { glog.V(3).Infof("Replied nil (%s)", envelope.UUID) } } else { // If reached here, the message was not handled by the server glog.V(1).Infof("message '%s' is not handled by this service", methodName) sendError(worker, req, fmt.Errorf("message '%s' is not handled by this service", methodName)) } } glog.Infof("Closing worker #%d", i) }(i + 1) } // This is blocking so we put it last if err := zmq.Proxy(sock, mux, nil); err != nil { switch zmq.AsErrno(err) { // If was interrupted there is no need to log as an error case zmq.Errno(syscall.EINTR): glog.Info(err) case zmq.Errno(zmq.ETERM): glog.Info(err) default: glog.Error(err) } } // Since it was blocking we could safely close the server if reached here server.Close() }