From Node to Go 
John Maxwell / @jmaxyz
The Agile League 
agileleague.com 
@agileleague
Functional Spec 
• Serve static HTML, CSS, and JS assets! 
• /xhr: HTTP endpoint! 
• {"now":"2014-08-12T03:29:23.721Z"}! 
• /ws: Websocket endpoint! 
• {"id":9,"now":"2014-08-12T03:30:03.398Z"}! 
• Sending “delay” param to /xhr or /ws will trigger a 
100ms delay in response
Demo time
"use strict"; 
var http = require('http'); 
var express = require('express'); 
var WebSocketServer = require('ws').Server; 
var app = express(); 
app.use(express.static("./public")); 
app.use(app.router); 
app.get('/xhr', function (req, res) { 
bufferResponse(function () { 
res.writeHead(200, {'Content-Type': 'application/json'}); 
res.end(buildResponse()); 
}, req.query.delay == '1'); 
}); 
var port = process.env.PORT || 3000; 
var server = http.createServer(app).listen(port); 
var wss = new WebSocketServer({server: server, path: "/ws"}); 
wss.on('connection', function(socket) { 
socket.on('message', function(message) { 
message = JSON.parse(message); 
bufferResponse(function () { 
socket.send(buildResponse({id: message.id})); 
}, message.delay == '1'); 
}); 
}); 
console.log("listening on " + port); 
var buildResponse = function (properties) { 
properties = properties || {}; 
properties.now = new Date(); 
return JSON.stringify(properties); 
}; 
var bufferResponse = function (responder, delay) { 
delay ? setTimeout(responder, 100) : responder(); 
}; 
Node Server
"use strict"; 
var http = require('http'); 
var express = require('express'); 
var WebSocketServer = require('ws').Server; 
var app = express(); 
app.use(express.static("./public")); 
app.use(app.router); 
app.get('/xhr', function (req, res) { 
bufferResponse(function () { 
res.writeHead(200, {'Content-Type': 'application/json'}); 
res.end(buildResponse()); 
}, req.query.delay == '1'); 
}); 
var port = process.env.PORT || 3000; 
var server = http.createServer(app).listen(port); 
var wss = new WebSocketServer({server: server, path: "/ws"}); 
wss.on('connection', function(socket) { 
socket.on('message', function(message) { 
message = JSON.parse(message); 
bufferResponse(function () { 
Node Server
"use strict"; 
var http = require('http'); 
var express = require('express'); 
var WebSocketServer = require('ws').Server; 
var app = express(); 
app.use(express.static("./public")); 
app.use(app.router); 
app.get('/xhr', function (req, res) { 
bufferResponse(function () { 
res.writeHead(200, {'Content-Type': 'application/json'}); 
res.end(buildResponse()); 
}, req.query.delay == '1'); 
}); 
var port = process.env.PORT || 3000; 
var server = http.createServer(app).listen(port); 
var wss = new WebSocketServer({server: server, path: "/ws"}); 
wss.on('connection', function(socket) { 
socket.on('message', function(message) { 
message = JSON.parse(message); 
bufferResponse(function () { 
Node Server
app.get('/xhr', function (req, res) { 
bufferResponse(function () { 
res.writeHead(200, {'Content-Type': 'application/Node Server 
json'}); 
res.end(buildResponse()); 
}, req.query.delay == '1'); 
}); 
var port = process.env.PORT || 3000; 
var server = http.createServer(app).listen(port); 
var wss = new WebSocketServer({server: server, path: "/ws"}); 
wss.on('connection', function(socket) { 
socket.on('message', function(message) { 
message = JSON.parse(message); 
bufferResponse(function () { 
socket.send(buildResponse({id: message.id})); 
}, message.delay == '1'); 
}); 
}); 
console.log("listening on " + port); 
var buildResponse = function (properties) { 
properties = properties || {}; 
properties.now = new Date(); 
return JSON.stringify(properties); 
};
message = JSON.parse(message); 
bufferResponse(function () { 
socket.send(buildResponse({id: message.id})); 
}, message.delay == '1'); 
}); 
}); 
console.log("listening on " + port); 
var buildResponse = function (properties) { 
properties = properties || {}; 
properties.now = new Date(); 
return JSON.stringify(properties); 
}; 
var bufferResponse = function (responder, delay) { 
delay ? setTimeout(responder, 100) : responder(); 
}; 
Node Server
Functional Spec 
• Serve static HTML, CSS, and JS assets! 
• /xhr: HTTP endpoint! 
• {"now":"2014-08-12T03:29:23.721Z"}! 
• /ws: Websocket endpoint! 
• {"id":9,"now":"2014-08-12T03:30:03.398Z"}! 
• Sending “delay” param to /xhr or /ws will trigger a 
100ms delay in response
Why Go?
Why Go?
Go Primer
Blocking I/O & 
Synchronous Execution
Capitalization is important 
• Identifiers (functions, methods, and types) are 
only exported if they are capitalized 
• No public/private keywords
Static Typing 
• Type of every identifier is set and cannot be altered! 
• Variables (function parameters and locals)! 
• Return values! 
• Elements of Structs, Maps, and Slices! 
• Channels! 
• Compile time enforcement! 
• Function arguments are passed by value
Concurrency Support 
• Goroutines! 
• Any function can be called under its own 
Goroutine! 
• Go organizes threads/processes internally! 
• Channels! 
• How Goroutines communicate
package main 
import ( 
"net/http" 
"github.com/gorilla/mux" 
"github.com/gorilla/websocket" 
"time" 
"encoding/json" 
"fmt" 
"os" 
) 
func main() { 
r := mux.NewRouter() 
r.HandleFunc("/xhr", handleXHR) 
r.HandleFunc("/ws", handleWS) 
r.PathPrefix("/").Handler( 
Web Server 
http.FileServer(http.Dir("./public/")), 
)
"os" 
) 
func main() { 
r := mux.NewRouter() 
r.HandleFunc("/xhr", handleXHR) 
r.HandleFunc("/ws", handleWS) 
r.PathPrefix("/").Handler( 
Web Server 
http.FileServer(http.Dir("./public/")), 
) 
port := os.Getenv("PORT") 
if port == "" { 
port = "8080" 
} 
fmt.Printf("listening on %sn", port) 
http.ListenAndServe(":" + port, r) 
} 
func handleXHR(w http.ResponseWriter, r *http.Request) { 
if r.FormValue("delay") == "1" { 
time.Sleep(100 * time.Millisecond) 
} 
response, _ := json.Marshal(WebResponse{time.Now()}) 
w.Write(response)
"os" 
) 
func main() { 
r := mux.NewRouter() 
r.HandleFunc("/xhr", handleXHR) 
r.HandleFunc("/ws", handleWS) 
r.PathPrefix("/").Handler( 
main() 
http.FileServer(http.Dir("./public/")), 
) 
port := os.Getenv("PORT") 
if port == "" { 
port = "8080" 
} 
fmt.Printf("listening on %sn", port) 
http.ListenAndServe(":" + port, r) 
} 
func handleXHR(w http.ResponseWriter, r 
*http.Request) {
if port == "" { 
port = "8080" 
} 
HTTP request handler 
fmt.Printf("listening on %sn", port) 
http.ListenAndServe(":" + port, r) 
} 
func handleXHR(w http.ResponseWriter, r *http.Request) { 
if r.FormValue("delay") == "1" { 
time.Sleep(100 * time.Millisecond) 
} 
response, _ := json.Marshal(WebResponse{time.Now()}) 
w.Write(response) 
} 
type WebResponse struct { 
Now time.Time `json:"now"` 
} 
var upgrader = websocket.Upgrader{ 
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
}
if port == "" { 
port = "8080" 
} 
JSON serialization 
fmt.Printf("listening on %sn", port) 
http.ListenAndServe(":" + port, r) 
} 
func handleXHR(w http.ResponseWriter, r *http.Request) { 
if r.FormValue("delay") == "1" { 
time.Sleep(100 * time.Millisecond) 
} 
response, _ := json.Marshal(WebResponse{time.Now()}) 
w.Write(response) 
} 
type WebResponse struct { 
Now time.Time `json:"now"` 
} 
var upgrader = websocket.Upgrader{ 
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
}
if port == "" { 
port = "8080" 
} 
JSON serialization 
fmt.Printf("listening on %sn", port) 
http.ListenAndServe(":" + port, r) 
} 
func handleXHR(w http.ResponseWriter, r *http.Request) { 
if r.FormValue("delay") == "1" { 
time.Sleep(100 * time.Millisecond) 
} 
response, _ := json.Marshal(WebResponse{time.Now()}) 
w.Write(response) 
} 
type WebResponse struct { 
Now time.Time `json:"now"` 
} 
var upgrader = websocket.Upgrader{ 
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
}
"fmt" 
"os" 
) 
func main() { 
r := mux.NewRouter() 
r.HandleFunc("/xhr", handleXHR) 
r.HandleFunc("/ws", handleWS) 
r.PathPrefix("/").Handler( 
http.FileServer(http.Dir("./public/")), 
) 
port := os.Getenv("PORT") 
if port == "" { 
port = "8080" 
} 
fmt.Printf("listening on %sn", port) 
http.ListenAndServe(":" + port, r) 
} 
func handleXHR(w http.ResponseWriter, r *http.Request) { 
if r.FormValue("delay") == "1" { 
time.Sleep(100 * time.Millisecond) 
} 
response, _ := json.Marshal(WebResponse{time.Now()}) 
w.Write(response) 
} 
type WebResponse struct { 
Now time.Time `json:"now"` 
} 
var upgrader = websocket.Upgrader{ 
ReadBufferSize: 1024, 
Web Server
w.Write(response) 
} 
type WebResponse struct { 
Now time.Time `json:"now"` 
} 
var upgrader = websocket.Upgrader{ 
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
} 
func handleWS(w http.ResponseWriter, r *http.Request) { 
conn, err := upgrader.Upgrade(w, r, nil) 
if err != nil { 
return 
} 
writes := make(chan WSResponse) 
go respondWS(conn, writes) 
for { 
message := WSRequest{} 
err := conn.ReadJSON(&message) 
if err != nil { 
close(writes) 
return 
} 
go message.respond(writes) 
} 
} 
func respondWS(conn *websocket.Conn, 
writes chan WSResponse) { 
for { 
content, more := <-writes 
conn.WriteJSON(content) 
if !more { 
return 
} 
} 
} 
type WSRequest struct { 
Id int `json:id` 
Delay bool `json:delay` 
} 
func (request *WSRequest) respond( 
writes chan WSResponse) { 
if request.Delay { 
time.Sleep(100 * time.Millisecond) 
} 
response := WSResponse{time.Now(), request.Id} 
writes <- response 
} 
type WSResponse struct { 
Now time.Time `json:"now"` 
Id int `json:"id"` 
} 
WebSocket Server
"os" 
) 
func main() { 
r := mux.NewRouter() 
r.HandleFunc("/xhr", handleXHR) 
r.HandleFunc("/ws", handleWS) 
r.PathPrefix("/").Handler( 
Integration with Web Server 
http.FileServer(http.Dir("./public/")), 
) 
port := os.Getenv("PORT") 
if port == "" { 
port = "8080" 
} 
fmt.Printf("listening on %sn", port) 
http.ListenAndServe(":" + port, r) 
} 
func handleXHR(w http.ResponseWriter, r 
*http.Request) { 
if r.FormValue("delay") == "1" {
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
} 
func handleWS(w http.ResponseWriter, r *http.Request) { 
conn, err := upgrader.Upgrade(w, r, nil) 
if err != nil { 
return 
} 
writes := make(chan WSResponse) 
go respondWS(conn, writes) 
for { 
message := WSRequest{} 
err := conn.ReadJSON(&message) 
if err != nil { 
WebSocket Server 
close(writes) 
return 
} 
go message.respond(writes) 
} 
} 
func respondWS(conn *websocket.Conn, 
writes chan WSResponse) {
Now time.Time `json:"now"` 
} 
var upgrader = websocket.Upgrader{ 
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
} 
func handleWS(w http.ResponseWriter, r *http.Request) { 
conn, err := upgrader.Upgrade(w, r, nil) 
if err != nil { 
return 
} 
writes := make(chan WSResponse) 
go respondWS(conn, writes) 
for { 
message := WSRequest{} 
err := conn.ReadJSON(&message) 
if err != nil { 
close(writes) 
return 
} 
go message.respond(writes) 
} 
Open Connection
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
} 
func handleWS(w http.ResponseWriter, r *http.Request) { 
conn, err := upgrader.Upgrade(w, r, nil) 
if err != nil { 
return 
} 
writes := make(chan WSResponse) 
go respondWS(conn, writes) 
for { 
message := WSRequest{} 
err := conn.ReadJSON(&message) 
if err != nil { 
WebSocket Server 
close(writes) 
return 
} 
go message.respond(writes) 
} 
} 
func respondWS(conn *websocket.Conn, 
writes chan WSResponse) {
Order of operations 
1. Listen for new messages 
2. Send the responses 
3. Then close the connection
Order of implementation 
1. Send the responses 
2. Listen for new messages 
3. Then close the connection
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
} 
func handleWS(w http.ResponseWriter, r *http.Request) { 
conn, err := upgrader.Upgrade(w, r, nil) 
if err != nil { 
return 
} 
writes := make(chan WSResponse) 
go respondWS(conn, writes) 
for { 
message := WSRequest{} 
err := conn.ReadJSON(&message) 
if err != nil { 
WebSocket Server 
close(writes) 
return 
} 
go message.respond(writes) 
} 
} 
func respondWS(conn *websocket.Conn, 
writes chan WSResponse) {
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
} 
Enable concurrent responses 
func handleWS(w http.ResponseWriter, r *http.Request) { 
conn, err := upgrader.Upgrade(w, r, nil) 
if err != nil { 
return 
} 
writes := make(chan WSResponse) 
go respondWS(conn, writes) 
for { 
message := WSRequest{} 
err := conn.ReadJSON(&message) 
if err != nil { 
close(writes) 
return 
} 
go message.respond(writes) 
} 
} 
func respondWS(conn *websocket.Conn, 
writes chan WSResponse) {
} 
go message.respond(writes) 
} 
} 
func respondWS(conn *websocket.Conn, 
writes chan WSResponse) { 
Receive and Send Responses 
for { 
content, more := <-writes 
conn.WriteJSON(content) 
if !more { 
return 
} 
} 
} 
type WSRequest struct { 
Id int `json:id` 
Delay bool `json:delay`
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
} 
Enable concurrent responses 
func handleWS(w http.ResponseWriter, r *http.Request) { 
conn, err := upgrader.Upgrade(w, r, nil) 
if err != nil { 
return 
} 
writes := make(chan WSResponse) 
go respondWS(conn, writes) 
for { 
message := WSRequest{} 
err := conn.ReadJSON(&message) 
if err != nil { 
close(writes) 
return 
} 
go message.respond(writes) 
} 
} 
func respondWS(conn *websocket.Conn, 
writes chan WSResponse) {
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
} 
Listen on Connection 
func handleWS(w http.ResponseWriter, r *http.Request) { 
conn, err := upgrader.Upgrade(w, r, nil) 
if err != nil { 
return 
} 
writes := make(chan WSResponse) 
go respondWS(conn, writes) 
for { 
message := WSRequest{} 
err := conn.ReadJSON(&message) 
if err != nil { 
close(writes) 
return 
} 
go message.respond(writes) 
} 
} 
func respondWS(conn *websocket.Conn, 
writes chan WSResponse) {
} 
} 
type WSRequest struct { 
Id int `json:id` 
Delay bool `json:delay` 
} 
func (request *WSRequest) respond( 
writes chan WSResponse) { 
if request.Delay { 
time.Sleep(100 * time.Millisecond) 
} 
response := WSResponse{time.Now(), request.Id} 
writes <- response 
} 
type WSResponse struct { 
Now time.Time `json:"now"` 
Id int `json:"id"` 
} 
Build Response
package main 
import ( 
"net/http" 
"github.com/gorilla/mux" 
"github.com/gorilla/websocket" 
"time" 
"encoding/json" 
"fmt" 
"os" 
) 
func main() { 
r := mux.NewRouter() 
r.HandleFunc("/xhr", handleXHR) 
r.HandleFunc("/ws", handleWS) 
r.PathPrefix("/").Handler( 
http.FileServer(http.Dir("./public/")), 
) 
port := os.Getenv("PORT") 
if port == "" { 
port = "8080" 
} 
fmt.Printf("listening on %sn", port) 
http.ListenAndServe(":" + port, r) 
} 
func handleXHR(w http.ResponseWriter, r *http.Request) { 
if r.FormValue("delay") == "1" { 
time.Sleep(100 * time.Millisecond) 
} 
response, _ := json.Marshal(WebResponse{time.Now()}) 
w.Write(response) 
} 
type WebResponse struct { 
Now time.Time `json:"now"` 
} 
var upgrader = websocket.Upgrader{ 
ReadBufferSize: 1024, 
WriteBufferSize: 1024, 
} 
func handleWS(w http.ResponseWriter, r *http.Request) { 
conn, err := upgrader.Upgrade(w, r, nil) 
if err != nil { 
return 
} 
writes := make(chan WSResponse) 
go respondWS(conn, writes) 
for { 
message := WSRequest{} 
err := conn.ReadJSON(&message) 
if err != nil { 
close(writes) 
return 
} 
go message.respond(writes) 
} 
} 
func respondWS(conn *websocket.Conn, 
writes chan WSResponse) { 
for { 
content, more := <-writes 
conn.WriteJSON(content) 
if !more { 
return 
} 
} 
} 
type WSRequest struct { 
Id int `json:id` 
Delay bool `json:delay` 
} 
func (request *WSRequest) respond( 
writes chan WSResponse) { 
if request.Delay { 
time.Sleep(100 * time.Millisecond) 
} 
response := WSResponse{time.Now(), request.Id} 
writes <- response 
} 
type WSResponse struct { 
Now time.Time `json:"now"` 
Id int `json:"id"` 
} 
Go Server
What’s the score? 
• 88 lines of Go vs 39 lines of JavaScript! 
• JSON interaction was kind of clunky! 
• Haven’t figured out how to deploy to Heroku! 
• Doing what the cool kids are doing
“ A straightforward translation of a C++ 
or Java program into Go is unlikely to 
produce a satisfactory result—Java 
programs are written in Java, not Go.” 
–Effective Go
“ A straightforward translation of a C++ 
or Java JavaScript program into Go is 
unlikely to produce a satisfactory result 
—Java JavaScript programs are written 
in Java JavaScript, not Go.”
John Maxwell 
john@agileleague.com 
@jmaxyz 
agileleague.com 
@agileleague

From Node to Go

  • 1.
    From Node toGo John Maxwell / @jmaxyz
  • 2.
    The Agile League agileleague.com @agileleague
  • 5.
    Functional Spec •Serve static HTML, CSS, and JS assets! • /xhr: HTTP endpoint! • {"now":"2014-08-12T03:29:23.721Z"}! • /ws: Websocket endpoint! • {"id":9,"now":"2014-08-12T03:30:03.398Z"}! • Sending “delay” param to /xhr or /ws will trigger a 100ms delay in response
  • 6.
  • 8.
    "use strict"; varhttp = require('http'); var express = require('express'); var WebSocketServer = require('ws').Server; var app = express(); app.use(express.static("./public")); app.use(app.router); app.get('/xhr', function (req, res) { bufferResponse(function () { res.writeHead(200, {'Content-Type': 'application/json'}); res.end(buildResponse()); }, req.query.delay == '1'); }); var port = process.env.PORT || 3000; var server = http.createServer(app).listen(port); var wss = new WebSocketServer({server: server, path: "/ws"}); wss.on('connection', function(socket) { socket.on('message', function(message) { message = JSON.parse(message); bufferResponse(function () { socket.send(buildResponse({id: message.id})); }, message.delay == '1'); }); }); console.log("listening on " + port); var buildResponse = function (properties) { properties = properties || {}; properties.now = new Date(); return JSON.stringify(properties); }; var bufferResponse = function (responder, delay) { delay ? setTimeout(responder, 100) : responder(); }; Node Server
  • 9.
    "use strict"; varhttp = require('http'); var express = require('express'); var WebSocketServer = require('ws').Server; var app = express(); app.use(express.static("./public")); app.use(app.router); app.get('/xhr', function (req, res) { bufferResponse(function () { res.writeHead(200, {'Content-Type': 'application/json'}); res.end(buildResponse()); }, req.query.delay == '1'); }); var port = process.env.PORT || 3000; var server = http.createServer(app).listen(port); var wss = new WebSocketServer({server: server, path: "/ws"}); wss.on('connection', function(socket) { socket.on('message', function(message) { message = JSON.parse(message); bufferResponse(function () { Node Server
  • 10.
    "use strict"; varhttp = require('http'); var express = require('express'); var WebSocketServer = require('ws').Server; var app = express(); app.use(express.static("./public")); app.use(app.router); app.get('/xhr', function (req, res) { bufferResponse(function () { res.writeHead(200, {'Content-Type': 'application/json'}); res.end(buildResponse()); }, req.query.delay == '1'); }); var port = process.env.PORT || 3000; var server = http.createServer(app).listen(port); var wss = new WebSocketServer({server: server, path: "/ws"}); wss.on('connection', function(socket) { socket.on('message', function(message) { message = JSON.parse(message); bufferResponse(function () { Node Server
  • 11.
    app.get('/xhr', function (req,res) { bufferResponse(function () { res.writeHead(200, {'Content-Type': 'application/Node Server json'}); res.end(buildResponse()); }, req.query.delay == '1'); }); var port = process.env.PORT || 3000; var server = http.createServer(app).listen(port); var wss = new WebSocketServer({server: server, path: "/ws"}); wss.on('connection', function(socket) { socket.on('message', function(message) { message = JSON.parse(message); bufferResponse(function () { socket.send(buildResponse({id: message.id})); }, message.delay == '1'); }); }); console.log("listening on " + port); var buildResponse = function (properties) { properties = properties || {}; properties.now = new Date(); return JSON.stringify(properties); };
  • 12.
    message = JSON.parse(message); bufferResponse(function () { socket.send(buildResponse({id: message.id})); }, message.delay == '1'); }); }); console.log("listening on " + port); var buildResponse = function (properties) { properties = properties || {}; properties.now = new Date(); return JSON.stringify(properties); }; var bufferResponse = function (responder, delay) { delay ? setTimeout(responder, 100) : responder(); }; Node Server
  • 13.
    Functional Spec •Serve static HTML, CSS, and JS assets! • /xhr: HTTP endpoint! • {"now":"2014-08-12T03:29:23.721Z"}! • /ws: Websocket endpoint! • {"id":9,"now":"2014-08-12T03:30:03.398Z"}! • Sending “delay” param to /xhr or /ws will trigger a 100ms delay in response
  • 14.
  • 17.
  • 18.
  • 19.
    Blocking I/O & Synchronous Execution
  • 20.
    Capitalization is important • Identifiers (functions, methods, and types) are only exported if they are capitalized • No public/private keywords
  • 21.
    Static Typing •Type of every identifier is set and cannot be altered! • Variables (function parameters and locals)! • Return values! • Elements of Structs, Maps, and Slices! • Channels! • Compile time enforcement! • Function arguments are passed by value
  • 22.
    Concurrency Support •Goroutines! • Any function can be called under its own Goroutine! • Go organizes threads/processes internally! • Channels! • How Goroutines communicate
  • 23.
    package main import( "net/http" "github.com/gorilla/mux" "github.com/gorilla/websocket" "time" "encoding/json" "fmt" "os" ) func main() { r := mux.NewRouter() r.HandleFunc("/xhr", handleXHR) r.HandleFunc("/ws", handleWS) r.PathPrefix("/").Handler( Web Server http.FileServer(http.Dir("./public/")), )
  • 24.
    "os" ) funcmain() { r := mux.NewRouter() r.HandleFunc("/xhr", handleXHR) r.HandleFunc("/ws", handleWS) r.PathPrefix("/").Handler( Web Server http.FileServer(http.Dir("./public/")), ) port := os.Getenv("PORT") if port == "" { port = "8080" } fmt.Printf("listening on %sn", port) http.ListenAndServe(":" + port, r) } func handleXHR(w http.ResponseWriter, r *http.Request) { if r.FormValue("delay") == "1" { time.Sleep(100 * time.Millisecond) } response, _ := json.Marshal(WebResponse{time.Now()}) w.Write(response)
  • 25.
    "os" ) funcmain() { r := mux.NewRouter() r.HandleFunc("/xhr", handleXHR) r.HandleFunc("/ws", handleWS) r.PathPrefix("/").Handler( main() http.FileServer(http.Dir("./public/")), ) port := os.Getenv("PORT") if port == "" { port = "8080" } fmt.Printf("listening on %sn", port) http.ListenAndServe(":" + port, r) } func handleXHR(w http.ResponseWriter, r *http.Request) {
  • 26.
    if port =="" { port = "8080" } HTTP request handler fmt.Printf("listening on %sn", port) http.ListenAndServe(":" + port, r) } func handleXHR(w http.ResponseWriter, r *http.Request) { if r.FormValue("delay") == "1" { time.Sleep(100 * time.Millisecond) } response, _ := json.Marshal(WebResponse{time.Now()}) w.Write(response) } type WebResponse struct { Now time.Time `json:"now"` } var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, }
  • 27.
    if port =="" { port = "8080" } JSON serialization fmt.Printf("listening on %sn", port) http.ListenAndServe(":" + port, r) } func handleXHR(w http.ResponseWriter, r *http.Request) { if r.FormValue("delay") == "1" { time.Sleep(100 * time.Millisecond) } response, _ := json.Marshal(WebResponse{time.Now()}) w.Write(response) } type WebResponse struct { Now time.Time `json:"now"` } var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, }
  • 28.
    if port =="" { port = "8080" } JSON serialization fmt.Printf("listening on %sn", port) http.ListenAndServe(":" + port, r) } func handleXHR(w http.ResponseWriter, r *http.Request) { if r.FormValue("delay") == "1" { time.Sleep(100 * time.Millisecond) } response, _ := json.Marshal(WebResponse{time.Now()}) w.Write(response) } type WebResponse struct { Now time.Time `json:"now"` } var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, }
  • 29.
    "fmt" "os" ) func main() { r := mux.NewRouter() r.HandleFunc("/xhr", handleXHR) r.HandleFunc("/ws", handleWS) r.PathPrefix("/").Handler( http.FileServer(http.Dir("./public/")), ) port := os.Getenv("PORT") if port == "" { port = "8080" } fmt.Printf("listening on %sn", port) http.ListenAndServe(":" + port, r) } func handleXHR(w http.ResponseWriter, r *http.Request) { if r.FormValue("delay") == "1" { time.Sleep(100 * time.Millisecond) } response, _ := json.Marshal(WebResponse{time.Now()}) w.Write(response) } type WebResponse struct { Now time.Time `json:"now"` } var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, Web Server
  • 30.
    w.Write(response) } typeWebResponse struct { Now time.Time `json:"now"` } var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } func handleWS(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } writes := make(chan WSResponse) go respondWS(conn, writes) for { message := WSRequest{} err := conn.ReadJSON(&message) if err != nil { close(writes) return } go message.respond(writes) } } func respondWS(conn *websocket.Conn, writes chan WSResponse) { for { content, more := <-writes conn.WriteJSON(content) if !more { return } } } type WSRequest struct { Id int `json:id` Delay bool `json:delay` } func (request *WSRequest) respond( writes chan WSResponse) { if request.Delay { time.Sleep(100 * time.Millisecond) } response := WSResponse{time.Now(), request.Id} writes <- response } type WSResponse struct { Now time.Time `json:"now"` Id int `json:"id"` } WebSocket Server
  • 31.
    "os" ) funcmain() { r := mux.NewRouter() r.HandleFunc("/xhr", handleXHR) r.HandleFunc("/ws", handleWS) r.PathPrefix("/").Handler( Integration with Web Server http.FileServer(http.Dir("./public/")), ) port := os.Getenv("PORT") if port == "" { port = "8080" } fmt.Printf("listening on %sn", port) http.ListenAndServe(":" + port, r) } func handleXHR(w http.ResponseWriter, r *http.Request) { if r.FormValue("delay") == "1" {
  • 32.
    ReadBufferSize: 1024, WriteBufferSize:1024, } func handleWS(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } writes := make(chan WSResponse) go respondWS(conn, writes) for { message := WSRequest{} err := conn.ReadJSON(&message) if err != nil { WebSocket Server close(writes) return } go message.respond(writes) } } func respondWS(conn *websocket.Conn, writes chan WSResponse) {
  • 33.
    Now time.Time `json:"now"` } var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } func handleWS(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } writes := make(chan WSResponse) go respondWS(conn, writes) for { message := WSRequest{} err := conn.ReadJSON(&message) if err != nil { close(writes) return } go message.respond(writes) } Open Connection
  • 34.
    ReadBufferSize: 1024, WriteBufferSize:1024, } func handleWS(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } writes := make(chan WSResponse) go respondWS(conn, writes) for { message := WSRequest{} err := conn.ReadJSON(&message) if err != nil { WebSocket Server close(writes) return } go message.respond(writes) } } func respondWS(conn *websocket.Conn, writes chan WSResponse) {
  • 35.
    Order of operations 1. Listen for new messages 2. Send the responses 3. Then close the connection
  • 36.
    Order of implementation 1. Send the responses 2. Listen for new messages 3. Then close the connection
  • 37.
    ReadBufferSize: 1024, WriteBufferSize:1024, } func handleWS(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } writes := make(chan WSResponse) go respondWS(conn, writes) for { message := WSRequest{} err := conn.ReadJSON(&message) if err != nil { WebSocket Server close(writes) return } go message.respond(writes) } } func respondWS(conn *websocket.Conn, writes chan WSResponse) {
  • 38.
    ReadBufferSize: 1024, WriteBufferSize:1024, } Enable concurrent responses func handleWS(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } writes := make(chan WSResponse) go respondWS(conn, writes) for { message := WSRequest{} err := conn.ReadJSON(&message) if err != nil { close(writes) return } go message.respond(writes) } } func respondWS(conn *websocket.Conn, writes chan WSResponse) {
  • 39.
    } go message.respond(writes) } } func respondWS(conn *websocket.Conn, writes chan WSResponse) { Receive and Send Responses for { content, more := <-writes conn.WriteJSON(content) if !more { return } } } type WSRequest struct { Id int `json:id` Delay bool `json:delay`
  • 40.
    ReadBufferSize: 1024, WriteBufferSize:1024, } Enable concurrent responses func handleWS(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } writes := make(chan WSResponse) go respondWS(conn, writes) for { message := WSRequest{} err := conn.ReadJSON(&message) if err != nil { close(writes) return } go message.respond(writes) } } func respondWS(conn *websocket.Conn, writes chan WSResponse) {
  • 41.
    ReadBufferSize: 1024, WriteBufferSize:1024, } Listen on Connection func handleWS(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } writes := make(chan WSResponse) go respondWS(conn, writes) for { message := WSRequest{} err := conn.ReadJSON(&message) if err != nil { close(writes) return } go message.respond(writes) } } func respondWS(conn *websocket.Conn, writes chan WSResponse) {
  • 42.
    } } typeWSRequest struct { Id int `json:id` Delay bool `json:delay` } func (request *WSRequest) respond( writes chan WSResponse) { if request.Delay { time.Sleep(100 * time.Millisecond) } response := WSResponse{time.Now(), request.Id} writes <- response } type WSResponse struct { Now time.Time `json:"now"` Id int `json:"id"` } Build Response
  • 43.
    package main import( "net/http" "github.com/gorilla/mux" "github.com/gorilla/websocket" "time" "encoding/json" "fmt" "os" ) func main() { r := mux.NewRouter() r.HandleFunc("/xhr", handleXHR) r.HandleFunc("/ws", handleWS) r.PathPrefix("/").Handler( http.FileServer(http.Dir("./public/")), ) port := os.Getenv("PORT") if port == "" { port = "8080" } fmt.Printf("listening on %sn", port) http.ListenAndServe(":" + port, r) } func handleXHR(w http.ResponseWriter, r *http.Request) { if r.FormValue("delay") == "1" { time.Sleep(100 * time.Millisecond) } response, _ := json.Marshal(WebResponse{time.Now()}) w.Write(response) } type WebResponse struct { Now time.Time `json:"now"` } var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } func handleWS(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } writes := make(chan WSResponse) go respondWS(conn, writes) for { message := WSRequest{} err := conn.ReadJSON(&message) if err != nil { close(writes) return } go message.respond(writes) } } func respondWS(conn *websocket.Conn, writes chan WSResponse) { for { content, more := <-writes conn.WriteJSON(content) if !more { return } } } type WSRequest struct { Id int `json:id` Delay bool `json:delay` } func (request *WSRequest) respond( writes chan WSResponse) { if request.Delay { time.Sleep(100 * time.Millisecond) } response := WSResponse{time.Now(), request.Id} writes <- response } type WSResponse struct { Now time.Time `json:"now"` Id int `json:"id"` } Go Server
  • 44.
    What’s the score? • 88 lines of Go vs 39 lines of JavaScript! • JSON interaction was kind of clunky! • Haven’t figured out how to deploy to Heroku! • Doing what the cool kids are doing
  • 45.
    “ A straightforwardtranslation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go.” –Effective Go
  • 46.
    “ A straightforwardtranslation of a C++ or Java JavaScript program into Go is unlikely to produce a satisfactory result —Java JavaScript programs are written in Java JavaScript, not Go.”
  • 47.
    John Maxwell [email protected] @jmaxyz agileleague.com @agileleague