Browse Source

Updates realtime-advanced demo

Manu Mtz-Almeida 10 years ago
parent
commit
1f11541011

+ 3 - 4
examples/realtime-advanced/limit.go

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

+ 18 - 2
examples/realtime-advanced/main.go

@@ -1,13 +1,24 @@
 package main
 
 import (
+	"fmt"
 	"io"
+	"runtime"
 	"time"
 
 	"github.com/gin-gonic/gin"
+	"github.com/manucorporat/stats"
 )
 
+var messages = stats.New()
+
 func main() {
+	nuCPU := runtime.NumCPU()
+	runtime.GOMAXPROCS(nuCPU)
+	fmt.Printf("Running with %d CPUs\n", nuCPU)
+
+	gin.SetMode(gin.ReleaseMode)
+
 	router := gin.New()
 	router.Use(ratelimit, gin.Recovery(), gin.Logger())
 
@@ -19,7 +30,7 @@ func main() {
 	//router.DELETE("/room/:roomid", roomDELETE)
 	router.GET("/stream/:roomid", streamRoom)
 
-	router.Run(":8080")
+	router.Run("127.0.0.1:8080")
 }
 
 func index(c *gin.Context) {
@@ -29,6 +40,9 @@ func index(c *gin.Context) {
 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,
@@ -42,7 +56,7 @@ func roomPOST(c *gin.Context) {
 	nick := c.FormValue("nick")
 	message := c.PostFormValue("message")
 
-	if len(message) > 200 || len(nick) > 20 {
+	if len(message) > 200 || len(nick) > 13 {
 		c.JSON(400, gin.H{
 			"status": "failed",
 			"error":  "the message or nickname is too long",
@@ -54,6 +68,7 @@ func roomPOST(c *gin.Context) {
 		"nick":    nick,
 		"message": message,
 	}
+	messages.Add("inbound", 1)
 	room(roomid).Submit(post)
 	c.JSON(200, post)
 }
@@ -73,6 +88,7 @@ func streamRoom(c *gin.Context) {
 	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())

+ 75 - 11
examples/realtime-advanced/resources/room_login.templ.html

@@ -19,13 +19,41 @@
         <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css">
         <!-- Latest compiled and minified JavaScript -->
         <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
+        <!-- Primjs -->
+        <link href="/static/prismjs.min.css" rel="stylesheet" />
+
         <script type="text/javascript">
             $(document).ready(function() { 
               StartRealtime({{.roomid}}, {{.timestamp}});
             });
         </script>
+        <style>
+        body { padding-top: 50px; }
+        </style>
     </head>
     <body>
+    <nav class="navbar navbar-fixed-top navbar-inverse">
+      <div class="container">
+        <div class="navbar-header">
+          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+            <span class="sr-only">Toggle navigation</span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+          </button>
+          <a class="navbar-brand" href="#">Server-Sent Events</a>
+        </div>
+        <div id="navbar" class="collapse navbar-collapse">
+          <ul class="nav navbar-nav">
+            <li class="active"><a href="#">Demo</a></li>
+            <li><a href="http://www.w3.org/TR/2009/WD-eventsource-20091029/">W3 Standard</a></li>
+            <li><a href="http://caniuse.com/#feat=eventsource">Browser Support</a></li>
+            <li><a href="http://gin-gonic.github.io/gin/">Gin Framework</a></li>
+            <li><a href="https://github.com/gin-gonic/gin/tree/develop/examples/realtime-advanced">Github</a></li>
+          </ul>
+        </div><!-- /.nav-collapse -->
+      </div><!-- /.container -->
+    </nav><!-- /.navbar -->
         <!-- Main jumbotron for a primary marketing message or call to action -->
         <div class="jumbotron">
             <div class="container">
@@ -33,10 +61,8 @@
                 <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>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">
-                    {{if not .nick}}
                     <div class="col-md-8">
-                        {{end}}
-                        <div id="chat-scroll" style="overflow-y:scroll; overflow-x:scroll; height:200px">
+                        <div id="chat-scroll" style="overflow-y:scroll; overflow-x:scroll; height:270px">
                             <table id="table-style" class="table">
                                 <thead>
                                     <tr>
@@ -49,17 +75,22 @@
                         </div>
                         {{if .nick}}
                         <form class="form-inline" id="chat-form" action="/room/{{.roomid}}?nick={{.nick}}" method="post">
-                            <div class="form-group col-md-6">
-                                <input name="message" id="chat-message" placeholder="a message" type="text" class="form-control" style="width:100%" />
-                            </div>
-                            <div class="form-group col-md-2">
-                                <input type="submit" class="btn btn-primary" value="Send" />
+                            <div class="form-group">
+                                <label class="sr-only" for="chat-message">Message</label>
+                                <div class="input-group">
+                                    <div class="input-group-addon">{{.nick}}</div>
+                                    <input name="message" id="chat-message" placeholder="a message" type="text" class="form-control" />
+                                </div>
                             </div>
+                            <input type="submit" class="btn btn-primary" value="Send" />
                         </form>
                         {{end}}
-                        {{if not .nick}}
                     </div>
                     <div class="col-md-4">
+                        {{if .nick}}
+                        <h3>Inbound/Outbound</h3>
+                        <div id="messagesChart" class="epoch category20c"></div>
+                        {{else}}
                         <form action="" method="get">
                             <legend>Join the SSE real-time chat</legend>
                             <div class="form-group">
@@ -70,13 +101,14 @@
                                 <input type="submit" class="btn btn-success btn-login-submit" value="Join" />
                             </div>
                         </form>
+                        {{end}}
                     </div>
-                    {{end}}
                 </div>
             </div>
         </div>
         <div class="container">
             <div class="row">
+                <h2>Realtime server Go stats</h2>
                 <div class="col-md-4">
                     <h3>Number of Goroutines</h3>
                     <p>
@@ -96,9 +128,41 @@
                     </p>
                 </div>
             </div>
+            <div class="row">
+            <h2>Source code</h2>
+                <div class="col-md-6">
+                <script src="/static/prismjs.min.js"></script>
+                    <h3>Server-side (Go)</h3>
+                    <pre><code class="language-go">func streamRoom(c *gin.Context) {
+    roomid := c.ParamValue(&quot;roomid&quot;)
+    listener := openListener(roomid)
+    statsTicker := time.NewTicker(1 * time.Second)
+    defer closeListener(roomid, listener)
+    defer statsTicker.Stop()
+
+    c.Stream(func(w io.Writer) bool {
+        select {
+        case msg := &lt;-listener:
+            c.SSEvent(&quot;message&quot;, msg)
+        case &lt;-statsTicker.C:
+            c.SSEvent(&quot;stats&quot;, Stats())
+        }
+        return true
+    })
+}</code></pre>
+                </div>
+                <div class="col-md-6">
+                    <h3>Client-side (JS)</h3>
+                    <pre><code class="language-javascript">function StartSSE(roomid) {
+    var source = new EventSource('/stream/'+roomid);
+    source.addEventListener('message', newChatMessage, false);
+    source.addEventListener('stats', stats, false);
+}</code></pre>
+                </div>
+            </div>
             <hr>
             <footer>
-                <p>&copy; Company 2014</p>
+                <p>Created with <span class="glyphicon glyphicon-heart"></span> by <a href="https://github.com/manucorporat">Manu Martinez-Almeida</a></p>
             </footer>
         </div>
     </body>

+ 137 - 0
examples/realtime-advanced/resources/static/prismjs.min.css

@@ -0,0 +1,137 @@
+/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+go */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+
+code[class*="language-"],
+pre[class*="language-"] {
+	color: black;
+	text-shadow: 0 1px white;
+	font-family: Consolas, Monaco, 'Andale Mono', monospace;
+	direction: ltr;
+	text-align: left;
+	white-space: pre;
+	word-spacing: normal;
+	word-break: normal;
+	line-height: 1.5;
+
+	-moz-tab-size: 4;
+	-o-tab-size: 4;
+	tab-size: 4;
+
+	-webkit-hyphens: none;
+	-moz-hyphens: none;
+	-ms-hyphens: none;
+	hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
+	text-shadow: none;
+	background: #b3d4fc;
+}
+
+pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
+code[class*="language-"]::selection, code[class*="language-"] ::selection {
+	text-shadow: none;
+	background: #b3d4fc;
+}
+
+@media print {
+	code[class*="language-"],
+	pre[class*="language-"] {
+		text-shadow: none;
+	}
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+	padding: 1em;
+	margin: .5em 0;
+	overflow: auto;
+}
+
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+	background: #f5f2f0;
+}
+
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+	padding: .1em;
+	border-radius: .3em;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+	color: slategray;
+}
+
+.token.punctuation {
+	color: #999;
+}
+
+.namespace {
+	opacity: .7;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+	color: #905;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+	color: #690;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+	color: #a67f59;
+	background: hsla(0, 0%, 100%, .5);
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+	color: #07a;
+}
+
+.token.function {
+	color: #DD4A68;
+}
+
+.token.regex,
+.token.important,
+.token.variable {
+	color: #e90;
+}
+
+.token.important,
+.token.bold {
+	font-weight: bold;
+}
+.token.italic {
+	font-style: italic;
+}
+
+.token.entity {
+	cursor: help;
+}
+

File diff suppressed because it is too large
+ 1 - 0
examples/realtime-advanced/resources/static/prismjs.min.js


+ 21 - 1
examples/realtime-advanced/resources/static/realtime.js

@@ -46,6 +46,18 @@ function StartEpoch(timestamp) {
             {values: defaultData}
         ]
     });
+
+    if($('#messagesChart').length ) {
+        window.messagesChart = $('#messagesChart').epoch({
+            type: 'time.area',
+            axes: ['bottom', 'left'],
+            height: 250,
+            data: [
+                {values: defaultData},
+                {values: defaultData}
+            ]
+        });
+    }
 }
 
 function StartSSE(roomid) {
@@ -63,6 +75,9 @@ function stats(e) {
     heapChart.push(data.heap)
     mallocsChart.push(data.mallocs)
     goroutinesChart.push(data.goroutines)
+    if(messagesChart) {
+        messagesChart.push(data.messages)
+    }
 }
 
 function parseJSONStats(e) {
@@ -78,13 +93,18 @@ function parseJSONStats(e) {
         {time: timestamp, y: data.Mallocs},
         {time: timestamp, y: data.Frees}
     ];
+    var messages = [
+        {time: timestamp, y: data.Inbound},
+        {time: timestamp, y: data.Outbound}
+    ];
     var goroutines = [
         {time: timestamp, y: data.NuGoroutines},
     ]
     return {
         heap: heap,
         mallocs: mallocs,
-        goroutines: goroutines
+        goroutines: goroutines,
+        messages: messages
     }
 }
 

+ 4 - 7
examples/realtime-advanced/stats.go

@@ -14,12 +14,9 @@ func Stats() map[string]uint64 {
 		"HeapInuse":    stats.HeapInuse,
 		"StackInuse":   stats.StackInuse,
 		"NuGoroutines": uint64(runtime.NumGoroutine()),
-		//"Latency":      latency,
-		"Mallocs": stats.Mallocs,
-		"Frees":   stats.Mallocs,
-		// "HeapIdle":     stats.HeapIdle,
-		// "HeapInuse":    stats.HeapInuse,
-		// "HeapReleased": stats.HeapReleased,
-		// "HeapObjects":  stats.HeapObjects,
+		"Mallocs":      stats.Mallocs,
+		"Frees":        stats.Mallocs,
+		"Inbound":      uint64(messages.Get("inbound")),
+		"Outbound":     uint64(messages.Get("outbound")),
 	}
 }

Some files were not shown because too many files changed in this diff