Skip to content

Latest commit

 

History

History
373 lines (249 loc) · 8.93 KB

PITCHME.md

File metadata and controls

373 lines (249 loc) · 8.93 KB

Disruptive Go

Building a REST API with fasthttp (GO#04)

This presentation and exercise code available in GitHub repo rpcavaco/disruptivego04

Requirements

In order to reproduce code examples, you should have installed on your system:

  • Go
  • fasthttp library from Aliaksandr Valialkin
  • radix.v2 or other Redis Go Client
  • Redis, an in-memory datastructure store to use as our REST API backend

We can use a a Redis cloud server - like the ones available from Redis Labs


Go can be dowloaded from here, and Redis from here.

Fasthttp library available as GitHub go repository in https://github.com/valyala/fasthttp

To install it, execute this command line (after Go's installation):

go get -u github.com/valyala/fasthttp

Redis Go client available as GitHub go repository in https://github.com/mediocregopher/radix.v2

To install it, execute this command line:

go get github.com/mediocregopher/radix.v2

Why use Golang to program HTTP services?

Other languages might be more popular and effective

@ul

  • Python is very easy to write and maintain
  • C++ is both robust and efficient
  • Java: extremely efficient with a solid knowledge base
  • ASP.NET is as good as Java
  • PHP is hugely popular and simple

@ulend


Why use Golang to program HTTP services?

We may try to get something out of Python 3 or, we are just in time to give new language a try! ;)

Where should we look for it?

---?image=assets/img/fortunes_fasthttp_light.png&size=auto 75%

Some benchmarking

Techempower.com is my favorite benchmark

Results available at https://www.techempower.com/benchmarks/

---?image=assets/img/fortunes_fasthttp.png&position=bottom 50px right 100px&size=auto 45%

Looking at its results (May 2017) I first saw Go as a top performing language.

As I’m a huge fan of PostgreSQL, fasthttp-postgresql immediately caught my attention.

First Python framework comes 46th
First PHP at 56th place ...

(In latest tests, fasthttp is not so well ...)


Nice features of Go and fasthttp lib for HTTP REST

  • efficiency
  • simplicity
  • good straightforward JSON support out-of-the-box

What is fasthttp?

Fasthttp is an improvement to Go’s net/http package.

Package author states it delivers 3x to 4x better perfomance than standard net/http lib. See this Google Groups discussion with the author.


A little smell of net/http

http.Handle("/foo", fooHandler)

http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})

log.Fatal(http.ListenAndServe(":8080", nil))

@[7](ListenAndServe initiates an HTTP server at the given address, this case localhost:8080, and 'multiplexer' function.) @[7](Passing nil as second parameter, internal multiplexer DefaultServeMux (not shown) is used.) @[1-4](Handle and HandleFunc add handlers to DefaultServeMux.)


The 'multiplexer'

http.Handle("/foo", fooHandler)

http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})

log.Fatal(http.ListenAndServe(":8080", nil))

The 'multiplexer' routes the request to a given handler, matching the request path with a pattern like "/foo" or "/bar".

Handle and HandleFunc are two different ways of automatically adding handler functions to the DefaultServeMux multiplexer


Building the first net/http example

Let's create an empty directory (name it as you wish). Inside it create our first Go source named basichttp.go containing the following code:

package main

import (
	"fmt"
	"net/http"
	"html"
	"log"
)

func main() {
	http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

To run the previous code, justo do:

> go build basichttp.go
> ./basichttp

---?image=assets/img/output_basic.png&position=bottom 20px right 100px&size=auto 35%

Running first example

Posting http://localhost:8080 on a browser's address bar, you should get something as the image shows.

Our first Golang web server is running!


Now, let's go a little faster


A simple HTTP server on fasthttp

package main

import (
	"flag"
	"fmt"
	"log"

	"github.com/valyala/fasthttp"
)

var (
	addr     = flag.String("addr", ":8080", "TCP address to listen to")
)

func main() {
	flag.Parse()

	h := requestMux

	if err := fasthttp.ListenAndServe(*addr, h); err != nil {
		log.Fatalf("Error in ListenAndServe: %s", err)
	}
}

@[1-9](Define package and imports -- including Aleksandr's library) @[11-13](Get command line argument containing TCP port to use, default 8080) @[15-23](main function) @[16](Actual command line argument parsing) @[18-22](Defining ListenAndServe, similarly to net/http's)


A multiplexer associating paths with handlers
func requestMux(ctx *fasthttp.RequestCtx) {
	path := ctx.Path()
	switch string(path) {
		case "/hello":
			helloHandler(ctx)
		case "/raw":
			rawHandler(ctx)
		default:
			fmt.Fprintf(ctx, "HTTP not found: %s", ctx.Path())
			ctx.SetContentType("text/plain; charset=utf8")
	}
}

The handlers
func helloHandler(ctx *fasthttp.RequestCtx) {
	fmt.Fprintf(ctx, "Hello, world!\n\n")

	fmt.Fprintf(ctx, "Request method is %q\n", ctx.Method())
	fmt.Fprintf(ctx, "RequestURI is %q\n", ctx.RequestURI())
	fmt.Fprintf(ctx, "Requested path is %q\n", ctx.Path())
	fmt.Fprintf(ctx, "Host is %q\n", ctx.Host())
	fmt.Fprintf(ctx, "Query string is %q\n", ctx.QueryArgs())
	fmt.Fprintf(ctx, "User-Agent is %q\n", ctx.UserAgent())
	fmt.Fprintf(ctx, "Connection has been established at %s\n", ctx.ConnTime())
	fmt.Fprintf(ctx, "Request has been started at %s\n", ctx.Time())
	fmt.Fprintf(ctx, "Serial request number for the current connection is %d\n", ctx.ConnRequestNum())
	fmt.Fprintf(ctx, "Your ip is %q\n\n", ctx.RemoteIP())

	ctx.SetContentType("text/plain; charset=utf8")
}

func rawHandler(ctx *fasthttp.RequestCtx) {

	fmt.Fprintf(ctx, "Raw request is:\n---CUT---\n%s\n---CUT---", &ctx.Request)

	ctx.SetContentType("text/plain; charset=utf8")
}

@1-16 @18-23


Testing

Full example source available in rpcavaco/disruptivego04/exercises

To run the previous code, justo do:

> go build basicfhttp.go
> ./basicfhttp

A REST API example

Let's expand the previous example to build a very simple REST API.

It would be nicer if we achieve some server persistence for information items bouncing from client to server and back.

We'll use here Redis, an in memory key-value store very easy to use.

---?image=assets/img/redis_downloads.png&position=bottom 20px right 100px&size=auto 35%

A fresh REDIS installation

In case you haven't done it previously, now is the time to have a REDIS server installed in your system.

redis.io/download

You can use a cloud-based Redis server instead


REST API

Now, go for the final example, a simple REST API

Complete example code in exercises/restapi.go


Initialization
package main

import (
	"flag"
	"fmt"
	"log"
	"strings"
	"strconv"
	"encoding/json"

	"github.com/valyala/fasthttp"
	"github.com/mediocregopher/radix.v2/redis"
)

var (
	addr     = flag.String("addr", ":8080", "TCP address to listen to")
)

func main() {
	flag.Parse()

	h := requestMux

	if err := fasthttp.ListenAndServe(*addr, h); err != nil {
		log.Fatalf("Error in ListenAndServe: %s", err)
	}
}

@[1-13](Define package and imports -- including Aleksandr's library) @[15-20](Parsing command line parameter -- TCP port) @[22-27](setting and starting the server)


Multiplexer, handlers
func requestMux(ctx *fasthttp.RequestCtx) {

	path := string(ctx.Path())

	switch {
		case strings.Contains(path, "/docs"):
			docsHandler(ctx)
		case strings.Contains(path, "/test"):
			testHandler(ctx)
		default:
			ctx.Error(fmt.Sprintf("HTTP not found: %s", ctx.Path()), fasthttp.StatusNotFound)
	}
}

func testHandler(ctx *fasthttp.RequestCtx) {

	fmt.Fprintf(ctx, "Request has been started at %s\n", ctx.Time())

	ctx.SetContentType("text/plain; charset=utf8")
}

@[1-13](DMultiplexer, two handlers: one for "docs" and other for testing) @[15-20](Handler for a simple test)