浏览代码

Updates realtime-advanced demo

Manu Mtz-Almeida 10 年之前
父节点
当前提交
a8b9e2d8d6

+ 0 - 21
examples/realtime-advanced/limit.go

@@ -1,21 +0,0 @@
-package main
-
-import (
-	"log"
-
-	"github.com/gin-gonic/gin"
-	"github.com/manucorporat/stats"
-)
-
-var ips = stats.New()
-
-func ratelimit(c *gin.Context) {
-	ip := c.ClientIP()
-	value := uint64(ips.Add(ip, 1))
-	if value >= 1000 {
-		if value%1000 == 0 {
-			log.Printf("BlockedIP:%s Requests:%d\n", ip, value)
-		}
-		c.AbortWithStatus(401)
-	}
-}

+ 13 - 70
examples/realtime-advanced/main.go

@@ -2,9 +2,7 @@ package main
 
 import (
 	"fmt"
-	"io"
 	"runtime"
-	"time"
 
 	"github.com/gin-gonic/gin"
 	"github.com/manucorporat/stats"
@@ -13,86 +11,31 @@ import (
 var messages = stats.New()
 
 func main() {
+	ConfigRuntime()
+	StartWorkers()
+	StartGin()
+}
+
+func ConfigRuntime() {
 	nuCPU := runtime.NumCPU()
 	runtime.GOMAXPROCS(nuCPU)
 	fmt.Printf("Running with %d CPUs\n", nuCPU)
+}
 
-	gin.SetMode(gin.ReleaseMode)
+func StartWorkers() {
+	go statsWorker()
+}
 
-	router := gin.New()
-	router.Use(ratelimit, gin.Recovery(), gin.Logger())
+func StartGin() {
+	gin.SetMode(gin.ReleaseMode)
 
+	router := gin.Default()
 	router.LoadHTMLGlob("resources/*.templ.html")
 	router.Static("/static", "resources/static")
 	router.GET("/", index)
 	router.GET("/room/:roomid", roomGET)
 	router.POST("/room-post/:roomid", roomPOST)
-	//router.DELETE("/room/:roomid", roomDELETE)
 	router.GET("/stream/:roomid", streamRoom)
 
 	router.Run("127.0.0.1:8080")
 }
-
-func index(c *gin.Context) {
-	c.Redirect(301, "/room/hn")
-}
-
-func roomGET(c *gin.Context) {
-	roomid := c.ParamValue("roomid")
-	userid := c.FormValue("nick")
-	if len(userid) > 13 {
-		userid = userid[0:12] + "..."
-	}
-	c.HTML(200, "room_login.templ.html", gin.H{
-		"roomid":    roomid,
-		"nick":      userid,
-		"timestamp": time.Now().Unix(),
-	})
-
-}
-
-func roomPOST(c *gin.Context) {
-	roomid := c.ParamValue("roomid")
-	nick := c.FormValue("nick")
-	message := c.PostFormValue("message")
-
-	if len(message) > 200 || len(nick) > 13 {
-		c.JSON(400, gin.H{
-			"status": "failed",
-			"error":  "the message or nickname is too long",
-		})
-		return
-	}
-
-	post := gin.H{
-		"nick":    nick,
-		"message": message,
-	}
-	messages.Add("inbound", 1)
-	room(roomid).Submit(post)
-	c.JSON(200, post)
-}
-
-func roomDELETE(c *gin.Context) {
-	roomid := c.ParamValue("roomid")
-	deleteBroadcast(roomid)
-}
-
-func streamRoom(c *gin.Context) {
-	roomid := c.ParamValue("roomid")
-	listener := openListener(roomid)
-	ticker := time.NewTicker(1 * time.Second)
-	defer closeListener(roomid, listener)
-	defer ticker.Stop()
-
-	c.Stream(func(w io.Writer) bool {
-		select {
-		case msg := <-listener:
-			messages.Add("outbound", 1)
-			c.SSEvent("message", msg)
-		case <-ticker.C:
-			c.SSEvent("stats", Stats())
-		}
-		return true
-	})
-}

+ 49 - 4
examples/realtime-advanced/resources/room_login.templ.html

@@ -4,7 +4,7 @@
         <meta charset="utf-8">
         <meta http-equiv="X-UA-Compatible" content="IE=edge">
         <meta name="viewport" content="width=device-width, initial-scale=1">
-        <title>Login in Room "{{.roomid}}"</title>
+        <title>Server-Sent Events. Room "{{.roomid}}"</title>
         <!-- jQuery -->
         <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
         <script src="http://malsup.github.com/jquery.form.js"></script> 
@@ -32,6 +32,15 @@
         </style>
     </head>
     <body>
+    <script>
+        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+        ga('create', 'UA-62943585-1', 'auto');
+        ga('send', 'pageview');
+    </script>
     <nav class="navbar navbar-fixed-top navbar-inverse">
       <div class="container">
         <div class="navbar-header">
@@ -58,7 +67,7 @@
         <div class="jumbotron">
             <div class="container">
                 <h1>Server-Sent Events in Go</h1>
-                <p><a href="http://www.html5rocks.com/en/tutorials/eventsource/basics/">Server-sent events (SSE)</a> is a technology where a browser receives automatic updates from a server via HTTP connection.</p>
+                <p>Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. It is not websockets. <a href="http://www.html5rocks.com/en/tutorials/eventsource/basics/">Learn more.</a></p>
                 <p>The chat and the charts data is provided in realtime using the SSE implemention of <a href="https://github.com/gin-gonic/gin/blob/15b0c49da556d58a3d934b86e3aa552ff224026d/examples/realtime-chat/main.go#L23-L32">Gin Framework</a>.</p>
                 <div class="row">
                     <div class="col-md-8">
@@ -95,7 +104,7 @@
                             <legend>Join the SSE real-time chat</legend>
                             <div class="form-group">
                                 <label for="nick">Your Name</label>
-                                <input value='' name="nick" id="nick" placeholder="Your name" type="text" class="form-control" />
+                                <input value='' name="nick" id="nick" placeholder="John" type="text" class="form-control" />
                             </div>
                             <div class="form-group text-center">
                                 <input type="submit" class="btn btn-success btn-login-submit" value="Join" />
@@ -129,7 +138,12 @@
                 </div>
             </div>
             <div class="row">
-            <h2>Source code</h2>
+                <h2>MIT Open Sourced</h2>
+                <ul>
+                    <li><a href="https://github.com/gin-gonic/gin/tree/develop/examples/realtime-advanced">This demo website (JS and Go)</a></li>
+                    <li><a href="https://github.com/manucorporat/sse">The SSE implementation in Go</a></li>
+                    <li><a href="https://github.com/gin-gonic/gin">The Web Framework (Gin)</a></li>
+                </ul>
                 <div class="col-md-6">
                 <script src="/static/prismjs.min.js"></script>
                     <h3>Server-side (Go)</h3>
@@ -160,6 +174,37 @@
 }</code></pre>
                 </div>
             </div>
+            <div class="row">
+                <div class="col-md-12">
+                    <h3>SSE package</h3>
+                    <pre><code class="language-go">import &quot;github.com/manucorporat/sse&quot;
+
+func httpHandler(w http.ResponseWriter, req *http.Request) {
+    // data can be a primitive like a string, an integer or a float
+    sse.Encode(w, sse.Event{
+        Event: &quot;message&quot;,
+        Data:  &quot;some data\nmore data&quot;,
+    })
+
+    // also a complex type, like a map, a struct or a slice
+    sse.Encode(w, sse.Event{
+        Id:    &quot;124&quot;,
+        Event: &quot;message&quot;,
+        Data: map[string]interface{}{
+            &quot;user&quot;:    &quot;manu&quot;,
+            &quot;date&quot;:    time.Now().Unix(),
+            &quot;content&quot;: &quot;hi!&quot;,
+        },
+    })
+}</code></pre>
+<pre>event: message
+data: some data\\nmore data
+
+id: 124
+event: message
+data: {&quot;content&quot;:&quot;hi!&quot;,&quot;date&quot;:1431540810,&quot;user&quot;:&quot;manu&quot;}</pre>
+                </div>
+            </div>
             <hr>
             <footer>
                 <p>Created with <span class="glyphicon glyphicon-heart"></span> by <a href="https://github.com/manucorporat">Manu Martinez-Almeida</a></p>

+ 0 - 8
examples/realtime-advanced/rooms.go

@@ -15,14 +15,6 @@ func closeListener(roomid string, listener chan interface{}) {
 	close(listener)
 }
 
-func deleteBroadcast(roomid string) {
-	b, ok := roomChannels[roomid]
-	if ok {
-		b.Close()
-		delete(roomChannels, roomid)
-	}
-}
-
 func room(roomid string) broadcast.Broadcaster {
 	b, ok := roomChannels[roomid]
 	if !ok {

+ 73 - 0
examples/realtime-advanced/routes.go

@@ -0,0 +1,73 @@
+package main
+
+import (
+	"html"
+	"io"
+	"time"
+
+	"github.com/gin-gonic/gin"
+)
+
+func index(c *gin.Context) {
+	c.Redirect(301, "/room/hn")
+}
+
+func roomGET(c *gin.Context) {
+	roomid := c.ParamValue("roomid")
+	nick := c.FormValue("nick")
+	if len(nick) < 2 {
+		nick = ""
+	}
+	if len(nick) > 13 {
+		nick = nick[0:12] + "..."
+	}
+	c.HTML(200, "room_login.templ.html", gin.H{
+		"roomid":    roomid,
+		"nick":      nick,
+		"timestamp": time.Now().Unix(),
+	})
+
+}
+
+func roomPOST(c *gin.Context) {
+	roomid := c.ParamValue("roomid")
+	nick := c.FormValue("nick")
+	message := c.PostFormValue("message")
+
+	validMessage := len(message) > 1 && len(message) < 200
+	validNick := len(nick) > 1 && len(nick) < 14
+	if !validMessage || !validNick {
+		c.JSON(400, gin.H{
+			"status": "failed",
+			"error":  "the message or nickname is too long",
+		})
+		return
+	}
+
+	post := gin.H{
+		"nick":    html.EscapeString(nick),
+		"message": html.EscapeString(message),
+	}
+	messages.Add("inbound", 1)
+	room(roomid).Submit(post)
+	c.JSON(200, post)
+}
+
+func streamRoom(c *gin.Context) {
+	roomid := c.ParamValue("roomid")
+	listener := openListener(roomid)
+	ticker := time.NewTicker(1 * time.Second)
+	defer closeListener(roomid, listener)
+	defer ticker.Stop()
+
+	c.Stream(func(w io.Writer) bool {
+		select {
+		case msg := <-listener:
+			messages.Add("outbound", 1)
+			c.SSEvent("message", msg)
+		case <-ticker.C:
+			c.SSEvent("stats", Stats())
+		}
+		return true
+	})
+}

+ 28 - 12
examples/realtime-advanced/stats.go

@@ -2,21 +2,37 @@ package main
 
 import (
 	"runtime"
+	"sync"
 	"time"
 )
 
-func Stats() map[string]uint64 {
-	var stats runtime.MemStats
-	runtime.ReadMemStats(&stats)
+var mutexStats sync.RWMutex
+var savedStats map[string]uint64
+
+func statsWorker() {
+	c := time.Tick(1 * time.Second)
+	for range c {
+		var stats runtime.MemStats
+		runtime.ReadMemStats(&stats)
 
-	return map[string]uint64{
-		"timestamp":    uint64(time.Now().Unix()),
-		"HeapInuse":    stats.HeapInuse,
-		"StackInuse":   stats.StackInuse,
-		"NuGoroutines": uint64(runtime.NumGoroutine()),
-		"Mallocs":      stats.Mallocs,
-		"Frees":        stats.Mallocs,
-		"Inbound":      uint64(messages.Get("inbound")),
-		"Outbound":     uint64(messages.Get("outbound")),
+		mutexStats.Lock()
+		savedStats = map[string]uint64{
+			"timestamp":    uint64(time.Now().Unix()),
+			"HeapInuse":    stats.HeapInuse,
+			"StackInuse":   stats.StackInuse,
+			"NuGoroutines": uint64(runtime.NumGoroutine()),
+			"Mallocs":      stats.Mallocs,
+			"Frees":        stats.Mallocs,
+			"Inbound":      uint64(messages.Get("inbound")),
+			"Outbound":     uint64(messages.Get("outbound")),
+		}
+		mutexStats.Unlock()
 	}
 }
+
+func Stats() map[string]uint64 {
+	mutexStats.RLock()
+	defer mutexStats.RUnlock()
+
+	return savedStats
+}