Scaling FastAGI Applications with Go

Post on 08-Jul-2015

174 Views

Category:

Technology

6 Downloads

Preview:

Click to see full reader

DESCRIPTION

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.

Transcript

Scaling FastAGI Applications with Go

Lefteris Zafiris NTA LtdAstricon 2014 Las Vegas

Who we are

NTA Ltd - nta.co.uk

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

Perl stack

- FastAGI server

- Net::Server

- fork / prefork

- Memory

- Capacity - calls / server

Enter Go

2007 Google

Rob Pike, Ken Thompson, Robert Griesemer

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

Enter Go

Do not communicate by sharing memory.

Share memory by communicating.

- Goroutines

lightweight threads

cheap

multiplexed in OS threads

- Channels

communication

synchronization

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) }}

Go toolset

- go command

testfmtbuildinstall

- Debugging

gdb- Package management

go get

AGI Package

- AGI and FastAGI support

- Simple familiar interface

- BSD Licence

- Install:

go get github.com/zaf/agi

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

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)

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", "#")

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) }}

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) }}

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") }}

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) }}

AGI Debug

- Error testing- Performance testing- Security testing

Eliminate the use of Asterisk

Custom AGI Payloads

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

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

Agistress

Config file:

- JSON format

- Environment variables

- AGI Command replies

- Reply delay

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"}

]}

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

Measuring

Comparison between FastAGI implementations:

- Perl:

Asterisk::AGI

Net::Server::PreFork

- Go:

agi package

net package

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)

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

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

Results

- Up to 3 times less CPU usage

- Up to 2.5 times shorter runtime

- Enormous memory savings

Thank You!

top related