Live: update centrifuge and the ChannelHandler api (#28843)

This commit is contained in:
Ryan McKinley
2020-11-05 10:37:04 -08:00
committed by GitHub
parent d736fcdd1d
commit b91e9faccf
10 changed files with 109 additions and 206 deletions
+21 -15
View File
@@ -1,32 +1,38 @@
package features
import (
"time"
"github.com/centrifugal/centrifuge"
"github.com/grafana/grafana/pkg/models"
)
// BroadcastRunner will simply broadcast all events to `grafana/broadcast/*` channels
// This assumes that data is a JSON object
type BroadcastRunner struct {
}
type BroadcastRunner struct{}
// GetHandlerForPath called on init
func (b *BroadcastRunner) GetHandlerForPath(path string) (models.ChannelHandler, error) {
return b, nil // for now all channels share config
return b, nil // all dashboards share the same handler
}
// GetChannelOptions called fast and often
func (b *BroadcastRunner) GetChannelOptions(id string) centrifuge.ChannelOptions {
return centrifuge.ChannelOptions{}
// OnSubscribe will let anyone connect to the path
func (b *BroadcastRunner) OnSubscribe(c *centrifuge.Client, e centrifuge.SubscribeEvent) (centrifuge.SubscribeReply, error) {
return centrifuge.SubscribeReply{
Options: centrifuge.SubscribeOptions{
Presence: true,
JoinLeave: true,
Recover: true, // loads the saved value from history
},
}, nil
}
// OnSubscribe for now allows anyone to subscribe to any dashboard
func (b *BroadcastRunner) OnSubscribe(c *centrifuge.Client, e centrifuge.SubscribeEvent) error {
// anyone can subscribe
return nil
}
// AllowBroadcast checks if a message can be broadcast on this channel
func (b *BroadcastRunner) AllowBroadcast(c *centrifuge.Client, e centrifuge.PublishEvent) error {
return nil
// OnPublish is called when a client wants to broadcast on the websocket
func (b *BroadcastRunner) OnPublish(c *centrifuge.Client, e centrifuge.PublishEvent) (centrifuge.PublishReply, error) {
return centrifuge.PublishReply{
Options: centrifuge.PublishOptions{
HistorySize: 1, // The last message is saved for 10 mins
HistoryTTL: 10 * time.Minute,
},
}, nil
}
+20 -23
View File
@@ -21,42 +21,39 @@ type DashboardHandler struct {
}
// GetHandlerForPath called on init
func (g *DashboardHandler) GetHandlerForPath(path string) (models.ChannelHandler, error) {
return g, nil // all dashboards share the same handler
}
// GetChannelOptions called fast and often
func (g *DashboardHandler) GetChannelOptions(id string) centrifuge.ChannelOptions {
return centrifuge.ChannelOptions{
Presence: true,
JoinLeave: true, // if enterprise?
}
func (h *DashboardHandler) GetHandlerForPath(path string) (models.ChannelHandler, error) {
return h, nil // all dashboards share the same handler
}
// OnSubscribe for now allows anyone to subscribe to any dashboard
func (g *DashboardHandler) OnSubscribe(c *centrifuge.Client, e centrifuge.SubscribeEvent) error {
// TODO? check authentication
return nil
func (h *DashboardHandler) OnSubscribe(c *centrifuge.Client, e centrifuge.SubscribeEvent) (centrifuge.SubscribeReply, error) {
return centrifuge.SubscribeReply{
Options: centrifuge.SubscribeOptions{
Presence: true,
JoinLeave: true,
},
}, nil
}
// AllowBroadcast checks if a message from the websocket can be broadcast on this channel
// currently messages are sent when a dashboard starts editing
func (g *DashboardHandler) AllowBroadcast(c *centrifuge.Client, e centrifuge.PublishEvent) error {
return nil
// OnPublish is called when someone begins to edit a dashoard
func (h *DashboardHandler) OnPublish(c *centrifuge.Client, e centrifuge.PublishEvent) (centrifuge.PublishReply, error) {
return centrifuge.PublishReply{
Options: centrifuge.PublishOptions{},
}, nil
}
// DashboardSaved should broadcast to the appropriate stream
func (g *DashboardHandler) publish(event dashboardEvent) error {
func (h *DashboardHandler) publish(event dashboardEvent) error {
msg, err := json.Marshal(event)
if err != nil {
return err
}
return g.Publisher("grafana/dashboard/"+event.UID, msg)
return h.Publisher("grafana/dashboard/"+event.UID, msg)
}
// DashboardSaved will broadcast to all connected dashboards
func (g *DashboardHandler) DashboardSaved(uid string, userID int64) error {
return g.publish(dashboardEvent{
func (h *DashboardHandler) DashboardSaved(uid string, userID int64) error {
return h.publish(dashboardEvent{
UID: uid,
Action: "saved",
UserID: userID,
@@ -64,8 +61,8 @@ func (g *DashboardHandler) DashboardSaved(uid string, userID int64) error {
}
// DashboardDeleted will broadcast to all connected dashboards
func (g *DashboardHandler) DashboardDeleted(uid string, userID int64) error {
return g.publish(dashboardEvent{
func (h *DashboardHandler) DashboardDeleted(uid string, userID int64) error {
return h.publish(dashboardEvent{
UID: uid,
Action: "deleted",
UserID: userID,
+8 -13
View File
@@ -21,20 +21,15 @@ func (m *MeasurementsRunner) GetHandlerForPath(path string) (models.ChannelHandl
return m, nil // for now all channels share config
}
// GetChannelOptions gets channel options.
// It gets called fast and often.
func (m *MeasurementsRunner) GetChannelOptions(id string) centrifuge.ChannelOptions {
return centrifuge.ChannelOptions{}
// OnSubscribe will let anyone connect to the path
func (m *MeasurementsRunner) OnSubscribe(c *centrifuge.Client, e centrifuge.SubscribeEvent) (centrifuge.SubscribeReply, error) {
return centrifuge.SubscribeReply{}, nil
}
// OnSubscribe for now allows anyone to subscribe to any dashboard.
func (m *MeasurementsRunner) OnSubscribe(c *centrifuge.Client, e centrifuge.SubscribeEvent) error {
// anyone can subscribe
return nil
}
// AllowBroadcast checks if a message from the websocket can be broadcast on this channel
// OnPublish is called when a client wants to broadcast on the websocket
// Currently this sends measurements over websocket -- should be replaced with the HTTP interface
func (m *MeasurementsRunner) AllowBroadcast(c *centrifuge.Client, e centrifuge.PublishEvent) error {
return nil
func (m *MeasurementsRunner) OnPublish(c *centrifuge.Client, e centrifuge.PublishEvent) (centrifuge.PublishReply, error) {
return centrifuge.PublishReply{
Options: centrifuge.PublishOptions{},
}, nil
}
+18 -25
View File
@@ -27,12 +27,12 @@ type TestDataSupplier struct {
// GetHandlerForPath gets the channel handler for a path.
// Called on init.
func (g *TestDataSupplier) GetHandlerForPath(path string) (models.ChannelHandler, error) {
func (s *TestDataSupplier) GetHandlerForPath(path string) (models.ChannelHandler, error) {
channel := "grafana/testdata/" + path
if path == "random-2s-stream" {
return &testDataRunner{
publisher: g.Publisher,
publisher: s.Publisher,
running: false,
speedMillis: 2000,
dropPercent: 0,
@@ -43,7 +43,7 @@ func (g *TestDataSupplier) GetHandlerForPath(path string) (models.ChannelHandler
if path == "random-flakey-stream" {
return &testDataRunner{
publisher: g.Publisher,
publisher: s.Publisher,
running: false,
speedMillis: 400,
dropPercent: .6,
@@ -54,39 +54,32 @@ func (g *TestDataSupplier) GetHandlerForPath(path string) (models.ChannelHandler
return nil, fmt.Errorf("unknown channel")
}
// GetChannelOptions gets channel options.
// Called fast and often.
func (g *testDataRunner) GetChannelOptions(id string) centrifuge.ChannelOptions {
return centrifuge.ChannelOptions{}
}
// OnSubscribe for now allows anyone to subscribe to any dashboard.
func (g *testDataRunner) OnSubscribe(c *centrifuge.Client, e centrifuge.SubscribeEvent) error {
if !g.running {
g.running = true
// OnSubscribe will let anyone connect to the path
func (r *testDataRunner) OnSubscribe(c *centrifuge.Client, e centrifuge.SubscribeEvent) (centrifuge.SubscribeReply, error) {
if !r.running {
r.running = true
// Run in the background
go g.runRandomCSV()
go r.runRandomCSV()
}
// TODO? check authentication
return nil
return centrifuge.SubscribeReply{}, nil
}
// AllowBroadcast checks if a message from the websocket can be broadcast on this channel
func (g *testDataRunner) AllowBroadcast(c *centrifuge.Client, e centrifuge.PublishEvent) error {
return fmt.Errorf("can not publish to testdata")
// OnPublish checks if a message from the websocket can be broadcast on this channel
func (r *testDataRunner) OnPublish(c *centrifuge.Client, e centrifuge.PublishEvent) (centrifuge.PublishReply, error) {
return centrifuge.PublishReply{}, fmt.Errorf("can not publish to testdata")
}
// runRandomCSV is just for an example.
func (g *testDataRunner) runRandomCSV() {
func (r *testDataRunner) runRandomCSV() {
spread := 50.0
walker := rand.Float64() * 100
ticker := time.NewTicker(time.Duration(g.speedMillis) * time.Millisecond)
ticker := time.NewTicker(time.Duration(r.speedMillis) * time.Millisecond)
measurement := models.Measurement{
Name: g.name,
Name: r.name,
Time: 0,
Values: make(map[string]interface{}, 5),
}
@@ -95,7 +88,7 @@ func (g *testDataRunner) runRandomCSV() {
}
for t := range ticker.C {
if rand.Float64() <= g.dropPercent {
if rand.Float64() <= r.dropPercent {
continue
}
delta := rand.Float64() - 0.5
@@ -112,9 +105,9 @@ func (g *testDataRunner) runRandomCSV() {
continue
}
err = g.publisher(g.channel, bytes)
err = r.publisher(r.channel, bytes)
if err != nil {
logger.Warn("write", "channel", g.channel, "measurement", measurement)
logger.Warn("write", "channel", r.channel, "measurement", measurement)
}
}
}