Top Banner
Scaling FastAGI Applications with Go Lefteris Zafiris NTA Ltd Astricon 2014 Las Vegas
30

Scaling FastAGI Applications with Go

Jul 08, 2015

Download

Technology

Digium

A presentation about combining the powers of the Go language and Asterisk in order to provide fast, reliable and hugely scalable voice applications. A brief introduction about why Go presents a big opportunity for the asterisk community, a primer into developing FastAGI applications, demonstration of the AGI package [1] and a walk through the idioms and challenges of developing in Go. The talk is focused mainly on our experience of porting existing code into Go with the aim to scale our services to larger numbers accompanied with benchmarks and an introduction to some tools [2] we developed to help us test, debug and benchmark AGI applications.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Scaling FastAGI Applications with Go

Scaling FastAGI Applications with Go

Lefteris Zafiris NTA LtdAstricon 2014 Las Vegas

Page 3: Scaling FastAGI Applications with Go

Who we are

NTA Ltd - nta.co.uk

- UK based VoIP provider since 2001- Hosted PBX services- SIP- FOSS- Kamailio / Asterisk- Perl

Page 4: Scaling FastAGI Applications with Go

Perl stack

- FastAGI server

- Net::Server

- fork / prefork

- Memory

- Capacity - calls / server

Page 5: Scaling FastAGI Applications with Go

Enter Go

2007 Google

Rob Pike, Ken Thompson, Robert Griesemer

- Statically typed- Garbage-collected- Natively compiled- Concurrency- C family- Pragmatic, minimalistic

Page 6: Scaling FastAGI Applications with Go

Enter Go

Do not communicate by sharing memory.

Share memory by communicating.

- Goroutines

lightweight threads

cheap

multiplexed in OS threads

- Channels

communication

synchronization

Page 7: Scaling FastAGI Applications with Go

Enter Go

/* A simple example in go*/

package main

import "fmt"

func main() { var msg string msg = "Hello Astricon"

// Retroactively say hello to all Astricons! for i := 0; i <= 10; i++ { fmt.Println(msg, 2004+i) }}

Page 8: Scaling FastAGI Applications with Go

Go toolset

- go command

testfmtbuildinstall

- Debugging

gdb- Package management

go get

Page 9: Scaling FastAGI Applications with Go

AGI Package

- AGI and FastAGI support

- Simple familiar interface

- BSD Licence

- Install:

go get github.com/zaf/agi

Page 10: Scaling FastAGI Applications with Go

AGI Package

- Session structure

AGI environment variables

Env map[string]string

- Reply structure

Numeric result of the AGI command

Res intAdditional returned data

Dat string

Page 11: Scaling FastAGI Applications with Go

AGI Package

- AGI commands as methods of the Session struct

func (a *Session) Answer() (Reply, error)

func (a *Session) SendText(text string) (Reply, error)

func (a *Session) Hangup(channel ...string) (Reply, error)

func (a *Session) SayDigits(digit int,escape string) (Reply, error)

Page 12: Scaling FastAGI Applications with Go

AGI Package usage

- Create a new session:

myAgi := agi.New()

- Initialize, read and parse AGI environment:

myAgi.Init(nil)

- Execute AGI commands:

myAgi.StreamFile("hello-world", "#")

Page 13: Scaling FastAGI Applications with Go

AGI examplepackage main

import ( "log" "github.com/zaf/agi")

func main() { // Create a new AGI session and Parse the AGI environment. myAgi := agi.New() err := myAgi.Init(nil) if err != nil { log.Fatalf("Error Parsing AGI environment: %v\n", err) } // Print a message on the asterisk console using Verbose. _, err := myAgi.Verbose("Hello World") if err != nil { log.Fatalf("AGI reply error: %v\n", err) }}

Page 14: Scaling FastAGI Applications with Go

FastAGI Example

func main() { // Create a tcp listener on port 4573 and start a new goroutine for each connection. ln, err := net.Listen("tcp", ":4573") if err != nil { log.Fatal(err) } defer ln.Close() for { conn, err := ln.Accept() if err != nil { log.Println(err) continue } go connHandle(conn) }}

Page 15: Scaling FastAGI Applications with Go

FastAGI Examplefunc connHandle(c net.Conn) { defer c.Close() myAgi := agi.New() // Create I/O buffers rw := bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c)) err := myAgi.Init(rw) if err != nil { log.Println(err) return } rep, err := myAgi.StreamFile("hello", "1234567890#*") if err != nil { log.Println(err) return }

// Check AGI command return value if rep.Res == -1 { log.Println("Failed to playback file") }}

Page 16: Scaling FastAGI Applications with Go

Error Handlingfunc connHandle(c net.Conn) { defer func() { c.Close() if err := recover(); err != nil { log.Println("Session terminated:", err) } }() myAgi := agi.New() rw := bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c)) err := myAgi.Init(rw) checkErr(err) _, err := myAgi.Verbose("Hello World") checkErr(err)}//Check for AGI Protocol errors or hangupsfunc checkErr(e error) { if e != nil { panic(e) }}

Page 17: Scaling FastAGI Applications with Go

AGI Debug

- Error testing- Performance testing- Security testing

Eliminate the use of Asterisk

Custom AGI Payloads

Page 18: Scaling FastAGI Applications with Go

Agistress

A client that can connect to servers using the Asterisk Gateway Interface

- user defined payloads- user controlled session parameters- fast - parallel session spawning- performance measuring- written in Go

go get github.com/zaf/agistress

Page 19: Scaling FastAGI Applications with Go

Agistress

Run once and display full debug output of the AGI session:

agistress -host 127.0.0.1 -single

Stress test the AGI server by spawning 10 sessions 10 times per second and display performance details:

agistress -host 127.0.0.1 -sess 10 -runs 10

Run once using custom AGI payload from config file:agistress -host 127.0.0.1 -single -conf payload.conf

Page 20: Scaling FastAGI Applications with Go

Agistress

Config file:

- JSON format

- Environment variables

- AGI Command replies

- Reply delay

Page 21: Scaling FastAGI Applications with Go

Agistress

Config file sample:

{"AgiPayload": [

{"Msg": "200 result=0", "Delay": 10}, {"Msg": "200 result=0", "Delay": 10}, {"Msg": "200 result=1 endpos=1234", "Delay": 3000}, {"Msg": "HANGUP"}

]}

Page 22: Scaling FastAGI Applications with Go

Agistress

Sample output:Running FastAGI bench against: 192.168.1.10:4573Press Enter to stop.

A new run each: 100msSessions per run: 10Reply delay: 50ms

FastAGI SessionsActive: 20Completed: 520Duration: 206121479 ns (last 10000 sessions average)Failed: 0

Page 23: Scaling FastAGI Applications with Go

Measuring

Comparison between FastAGI implementations:

- Perl:

Asterisk::AGI

Net::Server::PreFork

- Go:

agi package

net package

Page 24: Scaling FastAGI Applications with Go

Measuring

Simple sound file Playback application:

- Start session and parse AGI Env- Parse FastAGI request URI

agi://10.11.12.13/myagi?file=echo-test

- Check channel status- Answer channel- Playback given file

Perl: 73 loc (http://goo.gl/hdsxuw)

Go: 90 loc (http://goo.gl/uxezg7)

Page 25: Scaling FastAGI Applications with Go

Benchmark

Stress test using agistress:- 1 session/sec

agistress -delay=0- 25 sessions/sec

agistress -runs=5 -sess=5 -delay=0- 50 sessions/sec

agistress -runs=10 -sess=5 -delay=0- 100 sessions/sec

agistress -runs=10 -sess=10 -delay=0- 200 and 400 sessions/sec

Page 26: Scaling FastAGI Applications with Go
Page 27: Scaling FastAGI Applications with Go

Benchmark

Memory usage:

Many active sessions

Custom AGI Payload, 5 sec playback duration

- 12 active sessions:

agistress -sess=2 -conf=sample.conf

- 24 active sessions:

agistress -sess=4 -conf=sample.conf

- 48, 96 and 192 sessions

Page 28: Scaling FastAGI Applications with Go
Page 29: Scaling FastAGI Applications with Go

Results

- Up to 3 times less CPU usage

- Up to 2.5 times shorter runtime

- Enormous memory savings

Page 30: Scaling FastAGI Applications with Go

Thank You!