Skip to content

Instantly share code, notes, and snippets.

@bertoort
Last active March 19, 2016 20:45
Show Gist options
  • Save bertoort/7aa1e0003ac8f2704c4c to your computer and use it in GitHub Desktop.
Save bertoort/7aa1e0003ac8f2704c4c to your computer and use it in GitHub Desktop.
Building web sockets with goroutines
<style type="text/css"> section, p, h1, h2, h3 { text-align: left; } </style>

Goroutines and Channels


Objectives

  • Goroutines
    • Race Conditions
    • Wait Groups
  • Channels
    • Unbuffered vs Buffered
  • Select
    • Set a timeout

Goroutines

The scheduler runs a goroutine in the most opportune time


Syntax

type food struct {
	seeds int
}

func main() {
	food := food{seeds: 5}
	go eat(&food)
	time.Sleep(100 * time.Millisecond)
}

func eat(f *food) {
	f.seeds--
	fmt.Println(f.seeds)
}

Race Conditions

When two or more goroutines attempt to read and write to the same resource at the same time.

type food struct {
	seeds int
	sync.Mutex
}

func main() {
	food := food{seeds: 5}
	go eat(&food)
	go eat(&food)
	time.Sleep(100 * time.Millisecond)
}

func eat(f *food) {
	f.Lock()
	{
		f.seeds--
	}
	f.Unlock()
	fmt.Println(f.seeds)
}

Note: Goroutines need to be coordinated and synchronized.


Wait Groups

Waits for a collection of goroutines to finish.

func main() {
	food := food{seeds: 5}
	var wg sync.WaitGroup
	wg.Add(2)
	go eat(&food, &wg)
	go eat(&food, &wg)
	wg.Wait()
}

func eat(f *food, wg *sync.WaitGroup) {
	f.Lock()
	{
		f.seeds--
	}
	f.Unlock()
	wg.Done()
	fmt.Println(f.seeds)
}

Channels

Channels figure out when a goroutine is done


Syntax

type food struct {
	seeds chan int
	sync.Mutex
}

func eat(f *food, wg *sync.WaitGroup) {
	defer wg.Done()
	f.Lock()
	{
		seeds := <-f.seeds
		seeds--
		fmt.Println(seeds)
	}
	f.Unlock()
}

func main() {
	food := food{seeds: make(chan int)}
	var wg sync.WaitGroup
	wg.Add(1)
	go eat(&food, &wg)
	food.seeds <- 2
	wg.Wait()
}

Unbuffered Channels

Synchronous channel to make sure a channel finishes.

Note: Receivers always block until there is data to receive


Buffered Channels

Don't force goroutines to be ready at the same instant to perform sends and receives

func main() {
	food := food{seeds: make(chan int, 1)}
	var wg sync.WaitGroup
	wg.Add(1)
	food.seeds <- 2
	go eat(&food, &wg)
	wg.Wait()
}

Select

A statement like switch for goroutines. It picks the first channel that is ready and receives from it


Syntax

func eat(f *food, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-f.seed:
			fmt.Println("eating a seed")
			return
		case <-f.berry:
			fmt.Println("eating a berry")
		}
	}
}

func main() {
	food := food{
		seed:  make(chan bool),
		berry: make(chan bool),
	}
	var wg sync.WaitGroup
	wg.Add(1)
	go eat(&food, &wg)
	food.berry <- true
	food.seed <- true
	wg.Wait()
}

Timeout

Setting a time limit!

func eat(f *food, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-f.seed:
			fmt.Println("eating a seed")
		case <-f.berry:
			fmt.Println("eating a berry")
		case <-time.After(1000 * time.Millisecond):
			fmt.Println("gopher ran home!")
			return
		}
	}
}
<style type="text/css"> section, p, h1, h2, h3 { text-align: left; } img { width: 400px; height: 400px; } div.image { width: 500px; margin: 0 auto; background: white; padding-left: 100px; } </style>

Building WebSockets with Go


  • Introduction
  • How to use goroutines and Channels
  • What are websockets
  • How to build a a simple app

About me

me := RobertoOrtega{
  Instructor: "@galvanize",
  JavaScript: []string{"front-end", "back-end"},
  GoProgrammer: time.Date(2015, 9, ...),
}

Sockets

Socket Diagram

Goroutines and Channels

Other Slide


WebSockets

  • Handler - Interface to a websocket browser client
  • Conn - websocket connection
  • Send - message to the client
  • Receive - message from the client

Handler and Conn

func (s *Server) Listen() {
  onConnected := func(ws *websocket.Conn) {
  	defer func() {
  		ws.Close()
  	}()

  	connection := NewConnection(ws, s)
  	s.addCh <- connection
  	connection.Listen()
  }
  http.Handle(s.path, websocket.Handler(onConnected))
}

Send and Receive

websocket.JSON.Send(c.ws, &msg)
websocket.JSON.Receive(c.ws, &msg)

Structure

Two main files:

  • Server handles connections (broadcast, remove)
  • Connections individually read and write

Server

type Server struct {
	path        string
	connections map[int]*Connection
	addCh       chan *Connection
	delCh       chan *Connection
	sendCh      chan *Message
	doneCh      chan bool
}

Connection

type Connection struct {
	id     int
	ws     *websocket.Conn
	server *Server
	ch     chan *Message
	doneCh chan bool
}

Demo

https://github.com/BertoOrt/websocket-intro


Next Steps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment