func wsWriter(ws *websocket.Conn) { graphIn := broker.Get().Subscribe() pingTicker := time.NewTicker(pingPeriod) defer func() { pingTicker.Stop() broker.Get().Unsubscribe(graphIn) ws.Close() }() // write the current graph state first, before entering loop graphToSock(ws, latestGraph) var g system.CoreGraph for { select { case <-pingTicker.C: // ensure client connection is healthy ws.SetWriteDeadline(time.Now().Add(writeWait)) if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { return } case g = <-graphIn: graphToSock(ws, g) } } }
func init() { startTime := time.Now().UTC() expvar.Publish("Uptime", expvar.Func(func() interface{} { return int64(time.Since(startTime)) })) expvar.Publish("Goroutines", expvar.Func(func() interface{} { return runtime.NumGoroutine() })) // subscribe to the broker in order to report data about current graph c := broker.Get().Subscribe() // Instantiate a real, empty graph to ensure the interface type is never nil when it might be called var g system.CoreGraph = represent.NewGraph() go func() { for latest := range c { g = latest } }() expvar.Publish("MsgId", expvar.Func(func() interface{} { return g.MsgId() })) go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() }
func main() { pflag.Parse() setUpLogging() src, err := schema.Master() if err != nil { log.WithFields(log.Fields{ "system": "main", "err": err, }).Fatal("Could not locate master schema file, exiting") } // The master JSON schema used for validating all incoming messages masterSchema, err := gjs.NewSchema(gjs.NewStringLoader(string(src))) if err != nil { log.WithFields(log.Fields{ "system": "main", "err": err, }).Fatal("Error while creating a schema object from the master schema file, exiting") } // Channel to receive persisted messages from HTTP workers. 1000 cap to allow // some wiggle room if there's a sudden burst of messages and the interpreter // gets behind. interpretChan := make(chan *mlog.Record, 1000) var listenAt string if *bindAll == false { listenAt = "127.0.0.1:" } else { listenAt = ":" } var j mlog.Store switch *mlstore { case "bolt": j, err = boltdb.NewBoltStore(*dbPath + "/mlog.bolt") if err != nil { log.WithFields(log.Fields{ "system": "main", "err": err, }).Fatal("Error while setting up bolt mlog storage, exiting") } case "memory": j = mem.NewMemStore() default: log.WithFields(log.Fields{ "system": "main", "storage": *mlstore, }).Fatal("Invalid storage type requested for mlog, exiting") } // Restore the graph from the mlog (or start from nothing if mlog is empty) // TODO move this down to after ingestor is started g, err := restoreGraph(j) if err != nil { log.WithFields(log.Fields{ "system": "main", "err": err, }).Fatal("Error while rebuilding the graph from the mlog") } // Kick off fanout on the master/singleton graph broker. This will bridge between // the state machine and the listeners interested in the machine's state. brokerChan := make(chan system.CoreGraph, 0) broker.Get().Fanout(brokerChan) brokerChan <- g srv := ingest.New(j, masterSchema, interpretChan, brokerChan, MaxMessageSize) // Kick off the http message ingestor. // TODO let config/params control address go func() { if *ingestKey != "" && *ingestCert == "" { *ingestCert = *ingestKey + ".crt" } err := srv.RunHTTPIngestor(listenAt+strconv.Itoa(DefaultIngestionPort), *ingestKey, *ingestCert) if err != nil { log.WithFields(log.Fields{ "system": "main", "err": err, }).Fatal("Error while starting the ingestion http server") } }() // Kick off the intermediary interpretation goroutine that receives persisted // messages from the ingestor, merges them into the state graph, then passes // them along to the graph broker. go srv.Interpret(g) // And finally, kick off the webapp. // TODO let config/params control address if *webappKey != "" && *webappCert == "" { *webappCert = *webappKey + ".crt" } go RunWebapp(listenAt+strconv.Itoa(DefaultAppPort), *webappKey, *webappCert, j.Get) // Block on goji's graceful waiter, allowing the http connections to shut down nicely. // FIXME using this should be unnecessary if we're crash-only graceful.Wait() }
"github.com/pipeviz/pipeviz/Godeps/_workspace/src/github.com/zenazn/goji/web" "github.com/pipeviz/pipeviz/broker" "github.com/pipeviz/pipeviz/log" "github.com/pipeviz/pipeviz/mlog" "github.com/pipeviz/pipeviz/represent" "github.com/pipeviz/pipeviz/represent/q" "github.com/pipeviz/pipeviz/types/system" ) var ( publicDir = filepath.Join(defaultBase("github.com/pipeviz/pipeviz/webapp/"), "public") ) var ( // Subscribe to the master broker and store latest locally as it comes brokerListen = broker.Get().Subscribe() // Initially set the latestGraph to a new, empty one to avoid nil pointer latestGraph = represent.NewGraph() // Count of active websocket clients (for expvars) clientCount int64 ) const ( // Time allowed to write data to the client. writeWait = 10 * time.Second // Time allowed to read the next pong message from the client. pongWait = 60 * time.Second // Send pings to client with this period; less than pongWait. pingPeriod = (pongWait * 9) / 10 )