Go from a PHP Perspective

45
Go from a PHP Perspective and some other languages *cough* ruby *cough* too…but MOSTLY PHP

Transcript of Go from a PHP Perspective

Go from a PHP Perspective

and some other languages *cough* ruby *cough* too…but MOSTLY PHP

Go (or Golang) from Google

From golang.org

“Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.”

Valiant attempt to balance

• Productivity

• Workflow

• Growth

• Portability

• Efficiency

• Concurrency

SURELY YOU’RE NOT SERIOUS…

Another server side language?

Can Go bring balance to the force?

IT IS A GREAT TOOL IN THE BOX

But it’s still not the answer to life, the universe and everything

Let’s talk PHP

• A few months back I gave a presentation called:

What’s the “right” PHP framework?

• With the shocking conclusion that there isn’t one

– It was very “meta” as the kids say

PHP gives 90% of needs

• Frameworks largely just arrange them

– While locking you into them

• The most successful PHP ecosystem is one driven by Composer

– Lot’s of great parts

– Assemble them to your needs

– Skip the frameworks completely

My solution was going to be…

• Routes defined in PHP– For development– But generated as a file for nginx in production– Framework routing is insanely expensive

• PHP is stupid fast when it’s lightweight• Route direct to a PHP file

– Include only what you need for that file– Share templates with straight PHP

• Because APC will hold that in RAM

• Use the power of the PostgreSQL• Let PHP be the only real upgrade path

Random stats

Java takes the top 8 spots before

= 914,749 to 731,583

Go appears

= 348,555

Then more Java, jRuby, Go and Java

= 344,032 to 245,709

Before Node.js

= 228,887

Until we get to PHP at

= 180,147

Impact of frameworksUnderstand how far things fall when frameworks are added to the stack

Leaner is better with dynamics

More than raw requests

When requests have a database involved things change

…a lot

Needed to learn Go for work

• Frameworks largely rejected (exist though)

• Code as interchangeable parts

– There are about 10 different URL routers

• Range from features to speed…depending on your needs

• Even the frameworks are mostly

– “bring your own ORM…if you want”

So I started looking at trade offs

There’s a lot of trade offs with ALMOST

• Serves static files ALMOST as fast as nginx– But better than just about everything else

• Composite objects ALMOST as good as Ruby– But better than just about everything else

• Performance and garbage collection ALMOST as good as Java (portability too)– But better than just about everything else

• Concurrency model ALMOST as good as Erlang– But better than just about everything else

• Save refresh workflow from a statically typed language ALMOST as efficient as PHP– But better than just about everything else

Other perks

• Default, programmatically enforced code style– gofmt

• Compiler won’t allow includes/imports that aren’t used– Avoids bloat

• Won’t even allow declaring VARIABLES that aren’t used– Guardian of your RAM

• Other things to save your team from arguments– Testing built in, standardized– Templates built in, standardized– HTTP and other protocol servers, built in, standardized– Documentation built in, standardized– Sort of a convention over configuration approach to standard

libraries

So…compared to my solution

PHP + nginx routing/static Go for static/routing/logic

• Same result, one binary

• Zero server config

• Assemble parts as needed

SO YOU’RE SAYING WE SHOULD USE GO FOR EVERYTHING!?!

Wait…

NO

It’s not all gophers and rainbows

For “code heavy” projects

• Great performance

• Excellent for development teams

• Code structure works great for iterative development

• Concurrency, computation, API, Services, encapsulation, sharing code between projects, portable scripts for agents running reliably on client machines

For “websites”

• Lot’s of assembly required

• HTML templates are tedious– Rewriting lots of loading,

caching code

– Breaking large templates into pieces is an exercise in pain

• CSS/JS minify/versioning solutions are fairly clunky

• Better for refinement than prototype or MVP dev

“Fortunes” Benchmark

Most closely mirrors read-heavy web requestsAnd PHP…well…dominates

Or at least it did last year…

Unofficial, preliminary, unpublished, disclaimer heavy Round 10 benchmarks

Where does Go “fit” for a PHP dev?

• Services / APIs / JSON

• SPA Backend

• Background jobs

• Queue workers

• Portability

• Client installed agent code

• High concurrency workload

• Server level code– Drop uploads after seeing

bad headers

• Long polling / websockets

Basically, Go is good at the things that PHP is really bad at without the development time requirement of doing it in Java.

PHP is really good at the things that Go is really bad at, efficiently developing complete web sites

LET’S LOOK AT SOME GO EXAMPLES

It’s about dang time

* Most of the examples come directly from the free golang-book.com

Compile Time

This 1 minute video explains it best

https://youtu.be/wwoWei-GAPo

Data Types

• uint = uint8 (byte), uint16, uint32, uint64

• int = int8, int16, int32 (rune), int64

• float32, float64

• complex64, complex128 (for imaginaries)

• string (byte[])

• Use := to infer type automatically– x := “some cool text”

– var x string = “some cool text”

Array, Map, Interface

• Array = Numerically indexed set of Type

• Map = Key of Type and Value of Type

private / Public

Starts with capital letter = Public

Starts with lower case letter = private

Yes. Really.

Functions

func methodName(argument type) returnType {

}

// From the Go doc’s template tutorial

func loadPage(title string) *Page {

filename := title + ".txt"

body, _ := ioutil.ReadFile(filename)

return &Page{Title: title, Body: body}

}

Structs

type Circle struct {

x float64

y float64

r float64

}

// OR

type Circle struct {

x, y, r float64

}

var c Circle

// OR

c := new(Circle)

// OR with values

c := Circle{x: 0, y: 0,

r: 5}

// OR by definition order

c := Circle{0, 0, 5}

Compositional Object

type Circle struct {

x, y, r float64

} // Look…a closing bracket

func (c *Circle) area() float64 {

return math.Pi * c.r*c.r

}

• (c *Circle) means Circles can call this function.

• Meaning object data and methods are SEPARATE

• Data in the struct, methods on the type

• So you can keep adding methods as your application requires

without inheriting, extending, and reworking the entire

inheritance chain

• Similar to object Includes and Monkey Patching in Rails

– One of the strengths of the language for productivity

Interfaces

• Java / most Object Oriented languages

– Object defined to implement an interface

• Go

– Object just needs to match an interface

– Solves a big pain point of OO development

– Create interfaces for existing objects

• Without modifying or extending the objects

Interface Example

type Circle struct {

x, y, r float64

}

func (c *Circle) area() float64 {

return math.Pi * c.r*c.r

}

type Rectangle struct {

x1, y1, x2, y2 float64

}

func (r *Rectangle) area() float64 {

l := distance(r.x1, r.y1, r.x1, r.y2)

w := distance(r.x1, r.y1, r.x2, r.y1)

return l * w

}

// Any type with an area() float64 method is a Shape

type Shape interface {

area() float64

}

Interface Functions

type Shape interface {

area() float64

}

func totalArea(shapes ...Shape) float64 {

var area float64

for _, s := range shapes {

area += s.area()

}

return area

}

fmt.Println(totalArea(&c, &r))

Interfaces as Fields

type MultiShape struct {

shapes []Shape

}

// This actually makes MultiShape into a Shape

func (m *MultiShape) area() float64 {

var area float64

for _, s := range m.shapes {

area += s.area()

}

return area

}

Defer / Panic / Recover

func readFile(filename string) string {

f, _ := os.Open(filename)

defer f.Close() // Will always run before end

if filename == “SOME CONDITION” {

return f.getDataSpecialWay

} else {

return f.getDataNormalWay

}

} // Exception handling

defer func() {

str := recover()

fmt.Println(str)

}()

panic("PANIC”)

Goroutines

go methodName(argument)

Mo Concurrency Mo Problems

• Concurrent / threaded operations fight for variables

– Mutex locks

– Halt threads and wait

• Go uses channels to avoid this

Channels

func pinger(c chan string) {

for i := 0; ; i++ {

c <- "ping"

}

}

func ponger(c chan string) {

for i := 0; ; i++ {

c <- "pong"

}

}

func printer(c chan string) {

for {

msg := <- c

fmt.Println(msg)

time.Sleep(time.Second * 1)

}

}

func main() {

var c chan string = make(chan string)

go pinger(c)

go ponger(c)

go printer(c)

}

Send a message

c <- “ping”

Receive a message

msg := <- c

OR

fmt.Println(<-c)

Pinger and Ponger won’t send until something is waiting to receive

- Unless it’s a buffered channel

- Only waits when buffer is full

Printer will wait to receive until something sends it a message

More on Channels

// Send only

func pinger(c chan<- string)

// Receive only

func printer(c <-chan string)

// Listen to multiple

select {

case msg1 := <- c1:

fmt.Println(msg1)

case msg2 := <- c2:

fmt.Println(msg2)

case <- time.After(time.Second):

fmt.Println("timeout")

default:

fmt.Println("nothing ready")

}

Define channel direction

- Send only

- Receive only

Listen to multiple with select/case

- Whichever is ready first

- OR random

- OR set a timeout

- OR default if neither are ready

Fake Real World Example

type Job interface {

process()

complete()

failure()

}

func redisListener(c chan<- Job) {

c <- RedisJob()

}

func postrgresListener(c chan<- Job) {

c <- PostgresJob()

}

func awsSqsListener(c chan<- Job) {

c <- SqsJob()

}

func jobRunner(c <-chan Job) {

job := <- c

go job.process()

}

func main() {

// channel of Jobs with a buffer of 50

var c chan Job = make(chan Job, 50)

go redisListener(c)

go postrgresListener(c)

go awsSqsListener(c)

go jobRunner(c)

}

Multi-Queue Worker

- This is purely for insanity sake

- Create infinite job go routines

- Listen on 1 connection per queue

- Use an interface to define common execution needs

Example could continue by

- Create a result channel per queue

- Use a single connection per to report ALL job results in their own way

TCP Servers and Routing

package main

import ("net/http" ; "io")

func hello(res http.ResponseWriter, req *http.Request) {

res.Header().Set(

"Content-Type",

"text/html",

)

io.WriteString(

res,

`<doctype html><html>

<head><title>Hello World</title></head>

<body>Hello World!</body>

</html>`,

)

}

func main() {

http.HandleFunc("/hello", hello)

http.ListenAndServe(":9000", nil)

}

Gorilla Mux Router

func main() {

r := mux.NewRouter()

r.HandleFunc("/", HomeHandler)

r.HandleFunc("/products", ProductsHandler)

r.HandleFunc("/articles", ArticlesHandler)

r.HandleFunc("/products/{key}", ProductHandler)

r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)

r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)

// You could use multiple routers too

http.Handle("/", r)

}

Dependency Management

go get github.com/myname/mypackage

• Copies it locally• Bundles it with your deployed binary• Sits right next to your local packages• Single “src” directory with all packages• Makes code sharing between multiple packages

very simple• Never depend on “remote library host” being

down to deploy again

Gopher It!

References / Credits• Go

– http://golang.org/

• Golang Book– http://www.golang-book.com/

• Gorilla Web Toolkit– http://www.gorillatoolkit.org/

• Techempower Benchmarks– Official Round 9

• http://www.techempower.com/benchmarks/#section=data-r9

– Preview Round 10• http://www.techempower.com/benchmarks/previews/round10/

*Hulkbuster written in Go

Additional Resources

Who’s using Go?• Sendgrid

– Convice Your Company

– Intro and Love

• IronMQ– From 30 Servers to 2

– Go after 2 Years in Prod

• Heroku– Go at Heroku

• Digital Ocean– Super Fast Console. Thanks Go!

• Others– Dropbox, StatHat, Hailo,

Soundcloud, TimeHop

Other good reading• Concurrency is not Parallelism• Scaling VividCortex• Stability at Timehop• Simplicity and Maintenance• Worker Queues in Go• A RESTful Microframework• Monte Carlo Benchmarks• Taking Cloud Dev by Storm• Structuring Applications in Go• Go by Example• Let’s Learn Go!• Going Go – Playground• GopherCasts

Thanks to James, Josh and ACS Technologies