/** * process subscriber commands * when feed command is received, data must be received within 30 seconds or subscriber will unsubscribe */ func (s *Subscriber) subscriberCommandProcess(h *Hub) { l.If("subscriber process - %s - start", s.id) defer l.If("subscriber process - %s - end", s.id) for { select { case subscriberCommand := <-s.commandListener: switch subscriberCommand.command { case SubscriberStop: l.If("subscriber process - %s - stop", s.id) return case SubscriberFeed: l.If("subscriber process - %s - feed", s.id) select { case s.feedListener <- subscriberCommand.SubscriberFeedCommand: case <-time.After(30 * time.Second): l.Wf("subscriber process - %s - feed timedout", s.id) h.subscriberCommandListener <- HubSubscriberRequest{Unsubscribe, s.id, nil, nil} return } } case <-time.After(30 * time.Second): l.If("subscriber process - %s - alive", s.id) } } }
func NewHubRepository() *HubRepository { var repository = new(HubRepository) repository.channels = make(map[string]*Channel) repository.getChannelListener = make(chan HubRepositoryChannelGetCommand, expectedMaxSubscribers) repository.newDataListener = make(map[string]chan ChannelDataInputCommand, expectedMaxSubscribers) repository.getDataListener = make(map[string]chan ChannelDataRequestCommand, expectedMaxSubscribers) if restoreData { l.I("restoring channels") // read previous data var files, err = ioutil.ReadDir("data") if err == nil { for i := 0; i < len(files); i++ { var file = files[i] if !strings.HasSuffix(file.Name(), ".json") { continue } var data, err = ioutil.ReadFile("data/" + file.Name()) if err == nil { l.If("restoring channel %s", file.Name()) var channel = new(Channel) var err = json.Unmarshal(data, channel) if err == nil && strings.Trim(channel.ChannelName, "") != "" { repository.addCreatedChannel(channel) } } } } l.I("restoring channels completed.") } // start channel process go repository.channelProcess() return repository }
/** * Http request handler */ func (h *Hub) ServeHTTP(w http.ResponseWriter, r *http.Request) { defer func() { if x := recover(); x != nil { l.Ef("request %s caused error from %s {%s}: %v", r.URL.Path, r.RemoteAddr, r.Form.Encode(), x) } }() w.Header().Set("Access-Control-Allow-Origin", "*") parts := strings.Split(r.URL.Path, "/") command := parts[1] r.ParseForm() startTime := time.Now() l.If("http serving request %s from %s {%s}", r.URL.Path, r.RemoteAddr, r.Form.Encode()) mediator := HttpDataMediator{w: w, r: r} h.route(command, &mediator) delay := float64(time.Now().Sub(startTime).Nanoseconds()) / 1000000.0 l.If("http Served request %s from %s {%s} took %f ms", r.URL.Path, r.RemoteAddr, r.Form.Encode(), delay) }
func (m *WebSocketHandler) reader() { l.I("wsreader process - starting") var subscriberId string = "anonymous" for { var message string err := websocket.Message.Receive(m.ws, &message) if err != nil { l.If("wsreader process - %s - error reading from socket: %s", subscriberId, err.Error()) break } l.If("wsreader process - read from socket %s", string(message)) var command = new(WebSocketCommand) err = json.Unmarshal([]byte(message), command) if err != nil { l.Ef("wsreader process - %s - error unmarshal commad %s", subscriberId, err.Error()) break } var mediator = WebSocketDataMediator{send: m.send, command: *command, RequestId: command.RequestId} // special commands - keep alive and subscribe if command.Command == "keepAlive" { var subscriber = m.hub.subscribers[m.subscriber.id] // subscriber not found if subscriber == nil { l.Wf("wsreader process - %s - timed out!", subscriberId) break } else { subscriber.lastRequest = time.Now().Unix() } } else if command.Command == "subscribe" { var channels = strings.Split(mediator.ReadParameter("channels"), ",") var responseListener = make(chan Subscriber) m.hub.subscriberCommandListener <- HubSubscriberRequest{command: Subscribe, channels: channels, responseListener: responseListener} m.subscriber = <-responseListener subscriberId = m.subscriber.id go m.writer() mediator.WriteResponse(map[string]interface{}{"command": "subscribe", "subscriberId": m.subscriber.id}, "json") close(responseListener) } else { m.hub.route(command.Command, &mediator) } } m.closeListener <- true l.If("wsreader process - %s - stopped", subscriberId) m.ws.Close() }
func (r *HubRepository) channelProcess() { l.I("channels process - start") for { var channelRequest = <-r.getChannelListener var channel = r.channels[channelRequest.channelName] if channel == nil { l.If("channels process - add channel %s", channelRequest.channelName) channel = new(Channel) channel.ChannelName = channelRequest.channelName channel.DataVersion = 0 channel.Data = "" r.addCreatedChannel(channel) } l.If("channels process - served channel %s", channelRequest.channelName) channelRequest.resultListener <- HubRepositoryChannelFeeds{newDataListener: r.newDataListener[channel.ChannelName], getDataListener: r.getDataListener[channel.ChannelName]} } l.I("channels process - end") }
func main() { path, err := os.Getwd() if err != nil { panic(err) } l.If("current app path: %s", path) var processes = runtime.NumCPU() runtime.GOMAXPROCS(processes) l.If("using processes %d", processes) l.I("server started.") restartLisnener := make(chan string) hub := comet.NewHub() for { go httpServerProcess(hub, restartLisnener) reason := <-restartLisnener l.Wf("web server restarted: %s", reason) } }
func httpServerProcess(hub *comet.Hub, restartListener chan string) { var ip = flag.String("ip", "0.0.0.0", "ip address to listen on") var port = flag.Int("port", 8080, "port to listen on") flag.Parse() var addr = fmt.Sprintf("%s:%d", *ip, *port) l.If("listening on %s", addr) http.HandleFunc("/", hub.ServeHTTP) http.Handle("/ws", websocket.Handler(hub.ServeWebsocket)) err := http.ListenAndServe(addr, nil) restartListener <- err.Error() }
func (h *Hub) createSubscriber(channels []string) Subscriber { l.If("subscriber creating with channels %s", channels) var id, err = newUUID() if err != nil { l.Ef("Error creation new uuid %s", err.Error()) } subscriber := Subscriber{ id: id, commandListener: make(chan SubscriberControlCommand, 10), feedListener: make(chan SubscriberFeedCommand, subscriberQueueSize), channels: make(map[string]*SubscriberChannel), } channels = append(channels, fmt.Sprintf("private_%s", id)) h.addChannelsToSubscriber(channels, &subscriber) subscriber.lastRequest = time.Now().Unix() go subscriber.subscriberCommandProcess(h) h.subscribers[id] = &subscriber l.If("subscriber %s created with channels %s", id, channels) return subscriber }
func (m *WebSocketHandler) writer() { l.If("wswriter process - %s - starting", m.subscriber.id) defer close(m.send) defer close(m.closeListener) defer m.ws.Close() defer l.If("wswriter process - %s - stopped", m.subscriber.id) for { select { case message := <-m.send: jsonData, _ := json.Marshal(message) l.If("wswriter process - %s - serve json %s", m.subscriber.id, jsonData) err := websocket.Message.Send(m.ws, string(jsonData)) if err != nil { l.Ef("wswriter process - %s - error writing to socket: %s", m.subscriber.id, err.Error()) } case command := <-m.subscriber.feedListener: var response = SubscriberResponse{Status: 1, Commands: command.data} jsonData, _ := json.Marshal(response) l.If("wswriter process - %s - serve data: %s", m.subscriber.id, string(jsonData)) err := websocket.Message.Send(m.ws, string(jsonData)) if err != nil { l.Ef("wswriter process - %s - error writing to socket: %s", m.subscriber.id, err.Error()) } case <-m.closeListener: l.If("wswriter process - %s - received close command", m.subscriber.id) return case <-time.After(30 * time.Second): l.If("wswriter process - %s - alive", m.subscriber.id) } } }
/** * receive and feed data from a channel **/ func (r *HubRepository) dataProcess(channelName string) { l.If("data process - %s - start", channelName) for { select { // on new data case newData := <-r.newDataListener[channelName]: l.If("data process - %s - new data: %s ", channelName, newData.ToJson()) var channel = r.channels[newData.ChannelName] if channel == nil { channel = new(Channel) channel.ChannelName = newData.ChannelName channel.DataVersion = 0 channel.Data = "" r.channels[newData.ChannelName] = channel } channel.addNewData(DataOperation(newData.Command), newData.Data, newData.responseListener) if !strings.HasPrefix("private", channel.ChannelName) { // persist data var js, _ = json.Marshal(channel) var err = ioutil.WriteFile("data/"+channel.ChannelName+".json", js, 0644) if err != nil { log.Panic(err) } } // on data feed request case newRequest := <-r.getDataListener[channelName]: l.If("data process - %s - get data", channelName) var channel = r.channels[newRequest.channelName] var result Channel if channel == nil { result = Channel{newRequest.channelName, 0, "", time.Now(), make([]ChannelData, 0)} } else { result = channel.Copy() } newRequest.responseReceiver <- result } } l.If("data process - %s - stop", channelName) }
/** * handles adding, removing, cleaning and feeding subscribers */ func (h *Hub) subscribersProcess() { l.I("subscribers process - starting") for { select { case newData := <-h.subscriberFeedListener: l.If("subscribers process - newData", newData) for _, subscriber := range h.subscribers { if subscriber.channels[newData.channelData.ChannelName] != nil { select { case subscriber.commandListener <- SubscriberControlCommand{SubscriberFeed, CreateSingleFeedCommad(string(newData.operation), newData.channelData.ChannelName, newData.channelData.Data, newData.channelData.DataVersion)}: default: l.Wf("subscribers process - did not deliver new data to %s - queue full", subscriber.id) } } } case subscriberCommand := <-h.subscriberCommandListener: switch subscriberCommand.command { case Subscribe: l.I("subscribers process - subscribe") newSubscriber := h.createSubscriber(subscriberCommand.channels) subscriberCommand.responseListener <- newSubscriber case Unsubscribe: l.If("subscribers process - %s - unsubscribe", subscriberCommand.subscriberId) h.deleteSubscriber(subscriberCommand.subscriberId) subscriberCommand.responseListener <- Subscriber{} case SubscribeToChannels: l.If("subscribers process - %s - subscribe to channels", subscriberCommand.subscriberId, subscriberCommand.channels) if subscriber, found := h.subscribers[subscriberCommand.subscriberId]; found == true { h.addChannelsToSubscriber(subscriberCommand.channels, subscriber) subscriberCommand.responseListener <- *subscriber } else { subscriberCommand.responseListener <- Subscriber{} } case UnsubscribeFromChannels: l.If("subscribers process - %s - unsubscribe from channels", subscriberCommand.subscriberId, subscriberCommand.channels) if subscriber, found := h.subscribers[subscriberCommand.subscriberId]; found == true { h.removeChannelsFromSubscriber(subscriberCommand.channels, subscriber) subscriberCommand.responseListener <- *subscriber } else { subscriberCommand.responseListener <- Subscriber{} } case CleanupSubscribers: var cleanupStartTime = time.Now().Unix() for id, subscriber := range h.subscribers { if cleanupStartTime-subscriber.lastRequest > secondsToKeepSubscriberAlive { l.If("subscribers process - found dead subscriber %s", id) h.deleteSubscriber(id) } } } case <-time.After(30 * time.Second): l.If("subscribers process - alive") } } l.I("subscribers process - stopped") }