// main starts a broker instance. // Configuration Options: // - port number // - host:port of register/leader func main() { log.SetVerbose(log.DEBUG) defer func() { if r := recover(); nil != r { log.Error("%v", r) } }() // parse command line args var configFile = flag.String("conf", "conf.json", "configuration file") flag.Parse() log.Info("Initializing broker with options from %s.", *configFile) // init configuration config, err := config.Init(*configFile) checkError(err) log.Info("Options read were: %v", config) port, err := strconv.Atoi(config.Get("port", PORT)) checkError(err) log.SetPrefix(fmt.Sprintf("broker@%d: ", port)) broker, err = brokerimpl.New(config) checkError(err) listenHttp(port) }
// producer handles incoming produce requests. Producers may send multiple // produce requests on the same persistent connection. The function exits when // an `io.EOF` is received on the connection. func producer(conn *websocket.Conn) { defer conn.Close() for { var request protocol.ProduceRequest err := websocket.JSON.Receive(conn, &request) if err == io.EOF { // graceful shutdown break } if nil != err { log.Warn("Ignoring invalid message from %v.", conn.RemoteAddr()) continue } ack := new(protocol.Ack) if err := broker.Publish(request.Topic, request.ID, &request.Message); nil != err { log.Error(err.Error()) ack.Status = protocol.StatusFailure } else { ack.Status = protocol.StatusSuccess } // TODO: should redirect if this node is not the leader websocket.JSON.Send(conn, &ack) } log.Info("Closed producer connection from %v.", conn.RemoteAddr()) }
// initLogs initializes the logs map. func (b *Broker) initLogs() { pattern := filepath.Join(b.config.LogDir(), "*"+EXT) matches, err := filepath.Glob(pattern) if nil != err { log.Panic("Unable to read from log directory: %s", b.config.LogDir()) } for _, name := range matches { topic := filepath.Base(name) topic = topic[0 : len(topic)-len(EXT)] file, err := OpenLog(b.config, topic, -1) if nil != err { log.Error("Ignoring bad log file: %s", name) continue } b.logs[topic] = file log.Info("Found log file for %s.", topic) } }
// Serve blocks until either the websocket connection is closed, or until a // message is received on the `quit` channel. This method may be invoked at // most once; after it returns, the subscription is closed. func (s *Subscription) Serve() error { defer s.log.Close() defer log.Debug("Stopped serving subscription %p.", s) for { select { case <-s.quit: return nil default: if err := s.next(); nil != err { log.Error("Unable to serve subscription: %s", err.Error()) return err } } } return nil }