├── .github └── workflows │ └── go.yml ├── LICENSE ├── README.md └── product-api ├── 1_initial └── main.go ├── 2_two_handlers └── main.go ├── 3_reading_and_writing └── main.go ├── 4_handlers ├── handlers │ ├── goodbye.go │ └── hello.go └── main.go ├── 5_pattern ├── handlers │ ├── goodbye.go │ └── hello.go └── main.go ├── 6_REST ├── data │ └── products.go ├── handlers │ └── products.go └── main.go ├── go.mod └── go.sum /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: [push] 3 | jobs: 4 | 5 | build: 6 | name: Build 7 | runs-on: ubuntu-latest 8 | steps: 9 | 10 | - name: Set up Go 1.13 11 | uses: actions/setup-go@v1 12 | with: 13 | go-version: 1.13 14 | id: go 15 | 16 | - name: Check out code into the Go module directory 17 | uses: actions/checkout@v1 18 | 19 | - name: Get dependencies 20 | run: | 21 | go get -v -t -d ./... 22 | if [ -f Gopkg.toml ]; then 23 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 24 | dep ensure 25 | fi 26 | 27 | - name: Build 28 | run: go build -v . 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Building-Microservices-with-Go-Second-Edition 2 | Building Microservices with Go, Second Edition, published by Packt 3 | -------------------------------------------------------------------------------- /product-api/1_initial/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | http.HandleFunc("/", func(http.ResponseWriter, *http.Request) { 10 | log.Println("Hello World") 11 | }) 12 | 13 | 14 | // Listen for connections on all ip addresses (0.0.0.0) 15 | // port 9090 16 | log.Println("Starting Server") 17 | err := http.ListenAndServe(":9090", nil) 18 | log.Fatal(err) 19 | } -------------------------------------------------------------------------------- /product-api/2_two_handlers/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | // reqeusts to the path /goodbye with be handled by this function 10 | http.HandleFunc("/goodbye", func(http.ResponseWriter, *http.Request) { 11 | log.Println("Goodbye World") 12 | }) 13 | 14 | // any other request will be handled by this function 15 | http.HandleFunc("/", func(http.ResponseWriter, *http.Request) { 16 | log.Println("Hello World") 17 | }) 18 | 19 | 20 | // Listen for connections on all ip addresses (0.0.0.0) 21 | // port 9090 22 | log.Println("Starting Server") 23 | err := http.ListenAndServe(":9090", nil) 24 | log.Fatal(err) 25 | } -------------------------------------------------------------------------------- /product-api/3_reading_and_writing/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "fmt" 6 | "net/http" 7 | "io/ioutil" 8 | ) 9 | 10 | func main() { 11 | // reqeusts to the path /goodbye with be handled by this function 12 | http.HandleFunc("/goodbye", func(http.ResponseWriter, *http.Request) { 13 | log.Println("Goodbye World") 14 | }) 15 | 16 | // any other request will be handled by this function 17 | http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { 18 | log.Println("Running Hello Handler") 19 | 20 | // read the body 21 | b, err := ioutil.ReadAll(r.Body) 22 | if err != nil { 23 | log.Println("Error reading body", err) 24 | 25 | http.Error(rw, "Unable to read request body", http.StatusBadRequest) 26 | return 27 | } 28 | 29 | // write the response 30 | fmt.Fprintf(rw, "Hello %s", b) 31 | }) 32 | 33 | 34 | // Listen for connections on all ip addresses (0.0.0.0) 35 | // port 9090 36 | log.Println("Starting Server") 37 | err := http.ListenAndServe(":9090", nil) 38 | log.Fatal(err) 39 | } -------------------------------------------------------------------------------- /product-api/4_handlers/handlers/goodbye.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | // Goodbye is a simple handler 10 | type Goodbye struct { 11 | l *log.Logger 12 | } 13 | 14 | // NewHGoodbye creates a new goodbye handler with the given logger 15 | func NewGoodbye(l *log.Logger) *Goodbye { 16 | return &Goodbye{l} 17 | } 18 | 19 | // ServeHTTP implements the go http.Handler interface 20 | // https://golang.org/pkg/net/http/#Handler 21 | func (h *Goodbye) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 22 | h.l.Println("Handle Goodbye request") 23 | 24 | // write the response 25 | fmt.Fprintf(rw, "Goodbye") 26 | } 27 | -------------------------------------------------------------------------------- /product-api/4_handlers/handlers/hello.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | ) 9 | 10 | // Hello is a simple handler 11 | type Hello struct { 12 | l *log.Logger 13 | } 14 | 15 | // NewHello creates a new hello handler with the given logger 16 | func NewHello(l *log.Logger) *Hello { 17 | return &Hello{l} 18 | } 19 | 20 | // ServeHTTP implements the go http.Handler interface 21 | // https://golang.org/pkg/net/http/#Handler 22 | func (h *Hello) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 23 | h.l.Println("Handle requests") 24 | 25 | // read the body 26 | b, err := ioutil.ReadAll(r.Body) 27 | if err != nil { 28 | h.l.Println("Error reading body", err) 29 | 30 | http.Error(rw, "Unable to read request body", http.StatusBadRequest) 31 | return 32 | } 33 | 34 | // write the response 35 | fmt.Fprintf(rw, "Hello %s", b) 36 | } 37 | -------------------------------------------------------------------------------- /product-api/4_handlers/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "log" 6 | "net/http" 7 | "github.com/PacktPublishing/Building-Microservices-with-Go-Second-Edition/products-api/final/handlers" 8 | ) 9 | 10 | func main() { 11 | l := log.New(os.Stdout, "products-api", log.LstdFlags) 12 | 13 | // create the handlers 14 | hh := handlers.NewHello(l) 15 | gh := handlers.NewGoodbye(l) 16 | 17 | // create a new serve mux and register the handlers 18 | sm := http.NewServeMux() 19 | sm.Handle("/", hh) 20 | sm.Handle("/goodbye", gh) 21 | 22 | // Listen for connections on all ip addresses (0.0.0.0) 23 | // port 9090 24 | log.Println("Starting Server") 25 | err := http.ListenAndServe(":9090", sm) 26 | log.Fatal(err) 27 | } -------------------------------------------------------------------------------- /product-api/5_pattern/handlers/goodbye.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | // Goodbye is a simple handler 10 | type Goodbye struct { 11 | l *log.Logger 12 | } 13 | 14 | // NewHGoodbye creates a new goodbye handler with the given logger 15 | func NewGoodbye(l *log.Logger) *Goodbye { 16 | return &Goodbye{l} 17 | } 18 | 19 | // ServeHTTP implements the go http.Handler interface 20 | // https://golang.org/pkg/net/http/#Handler 21 | func (h *Goodbye) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 22 | h.l.Println("Handle Goodbye request") 23 | 24 | // write the response 25 | fmt.Fprintf(rw, "Goodbye") 26 | } 27 | -------------------------------------------------------------------------------- /product-api/5_pattern/handlers/hello.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | ) 9 | 10 | // Hello is a simple handler 11 | type Hello struct { 12 | l *log.Logger 13 | } 14 | 15 | // NewHello creates a new hello handler with the given logger 16 | func NewHello(l *log.Logger) *Hello { 17 | return &Hello{l} 18 | } 19 | 20 | // ServeHTTP implements the go http.Handler interface 21 | // https://golang.org/pkg/net/http/#Handler 22 | func (h *Hello) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 23 | h.l.Println("Handle Hello requests") 24 | 25 | // read the body 26 | b, err := ioutil.ReadAll(r.Body) 27 | if err != nil { 28 | h.l.Println("Error reading body", err) 29 | 30 | http.Error(rw, "Unable to read request body", http.StatusBadRequest) 31 | return 32 | } 33 | 34 | // write the response 35 | fmt.Fprintf(rw, "Hello %s", b) 36 | } 37 | -------------------------------------------------------------------------------- /product-api/5_pattern/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net/http" 7 | "os" 8 | "os/signal" 9 | "time" 10 | 11 | "github.com/PacktPublishing/Building-Microservices-with-Go-Second-Edition/products-api/5_pattern/handlers" 12 | "github.com/nicholasjackson/env" 13 | ) 14 | 15 | var bindAddress = env.String("BIND_ADDRESS", false, ":9090", "Bind address for the server") 16 | 17 | func main() { 18 | 19 | env.Parse() 20 | 21 | l := log.New(os.Stdout, "products-api ", log.LstdFlags) 22 | 23 | // create the handlers 24 | hh := handlers.NewHello(l) 25 | gh := handlers.NewGoodbye(l) 26 | 27 | // create a new serve mux and register the handlers 28 | sm := http.NewServeMux() 29 | sm.Handle("/", hh) 30 | sm.Handle("/goodbye", gh) 31 | 32 | // create a new server 33 | s := http.Server{ 34 | Addr: *bindAddress, // configure the bind address 35 | Handler: sm, // set the default handler 36 | ErrorLog: l, // set the logger for the server 37 | ReadTimeout: 5 * time.Second, // max time to read request from the client 38 | WriteTimeout: 10 * time.Second, // max time to write response to the client 39 | IdleTimeout: 120 * time.Second, // max time for connections using TCP Keep-Alive 40 | } 41 | 42 | // start the server 43 | go func() { 44 | l.Println("Starting server on port 9090") 45 | 46 | err := s.ListenAndServe() 47 | if err != nil { 48 | l.Printf("Error starting server: %s\n", err) 49 | os.Exit(1) 50 | } 51 | }() 52 | 53 | // trap sigterm or interupt and gracefully shutdown the server 54 | c := make(chan os.Signal, 1) 55 | signal.Notify(c, os.Interrupt) 56 | signal.Notify(c, os.Kill) 57 | 58 | // Block until a signal is received. 59 | sig := <-c 60 | log.Println("Got signal:", sig) 61 | 62 | // gracefully shutdown the server, waiting max 30 seconds for current operations to complete 63 | ctx, _ := context.WithTimeout(context.Background(), 30*time.Second) 64 | s.Shutdown(ctx) 65 | } 66 | -------------------------------------------------------------------------------- /product-api/6_REST/data/products.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | ) 7 | 8 | // Product defines the structure for an API product 9 | type Product struct { 10 | ID int `json:"id"` 11 | Name string `json:"name"` 12 | Description string `json:"description"` 13 | Price float32 `json:"price"` 14 | SKU string `json:"sku"` 15 | } 16 | 17 | type Products []Product 18 | 19 | func (p *Products) ToJSON(w io.Writer) error { 20 | e := json.NewEncoder(w) 21 | return e.Encode(p) 22 | } 23 | 24 | func GetProducts() Products { 25 | return productList 26 | } 27 | 28 | var productList = []Product{ 29 | Product{ 30 | ID: 1, 31 | Name: "A", 32 | Description: "B", 33 | Price: 12.34, 34 | SKU: "abc323", 35 | }, 36 | Product{ 37 | ID: 2, 38 | Name: "b", 39 | Description: "b", 40 | Price: 2.29, 41 | SKU: "fjd34", 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /product-api/6_REST/handlers/products.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "github.com/PacktPublishing/Building-Microservices-with-Go-Second-Edition/product-api/6_REST/data" 7 | ) 8 | 9 | // Products handler for getting and updating products 10 | type Products struct { 11 | l *log.Logger 12 | } 13 | 14 | // NewProducts returns a new products handler with the given logger 15 | func NewProducts(l *log.Logger) *Products { 16 | return &Products{l} 17 | } 18 | 19 | // ServeHTTP implements the http.Handler interface 20 | func (p*Products) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 21 | prods := data.GetProducts() 22 | 23 | err := prods.ToJSON(rw) 24 | if err != nil { 25 | http.Error(rw, "Error serialzing products", http.StatusInternalServerError) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /product-api/6_REST/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net/http" 7 | "os" 8 | "os/signal" 9 | "time" 10 | 11 | "github.com/PacktPublishing/Building-Microservices-with-Go-Second-Edition/product-api/6_REST/handlers" 12 | "github.com/nicholasjackson/env" 13 | ) 14 | 15 | var bindAddress = env.String("BIND_ADDRESS", false, ":9090", "Bind address for the server") 16 | 17 | func main() { 18 | 19 | env.Parse() 20 | 21 | l := log.New(os.Stdout, "products-api ", log.LstdFlags) 22 | 23 | // create the handlers 24 | ph := handlers.NewProducts(l) 25 | 26 | // create a new serve mux and register the handlers 27 | sm := http.NewServeMux() 28 | sm.Handle("/products", ph) 29 | 30 | // create a new server 31 | s := http.Server{ 32 | Addr: *bindAddress, // configure the bind address 33 | Handler: sm, // set the default handler 34 | ErrorLog: l, // set the logger for the server 35 | ReadTimeout: 5 * time.Second, // max time to read request from the client 36 | WriteTimeout: 10 * time.Second, // max time to write response to the client 37 | IdleTimeout: 120 * time.Second, // max time for connections using TCP Keep-Alive 38 | } 39 | 40 | // start the server 41 | go func() { 42 | l.Println("Starting server on port 9090") 43 | 44 | err := s.ListenAndServe() 45 | if err != nil { 46 | l.Printf("Error starting server: %s\n", err) 47 | os.Exit(1) 48 | } 49 | }() 50 | 51 | // trap sigterm or interupt and gracefully shutdown the server 52 | c := make(chan os.Signal, 1) 53 | signal.Notify(c, os.Interrupt) 54 | signal.Notify(c, os.Kill) 55 | 56 | // Block until a signal is received. 57 | sig := <-c 58 | log.Println("Got signal:", sig) 59 | 60 | // gracefully shutdown the server, waiting max 30 seconds for current operations to complete 61 | ctx, _ := context.WithTimeout(context.Background(), 30*time.Second) 62 | s.Shutdown(ctx) 63 | } 64 | -------------------------------------------------------------------------------- /product-api/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/PacktPublishing/Building-Microservices-with-Go-Second-Edition/product-api 2 | 3 | go 1.13 4 | 5 | require github.com/nicholasjackson/env v0.6.0 6 | -------------------------------------------------------------------------------- /product-api/go.sum: -------------------------------------------------------------------------------- 1 | github.com/PacktPublishing/Building-Microservices-with-Go-Second-Edition v0.0.0-20200112154811-11ac31f987fe h1:h/CK8LSgamqNr56hlrfODPU3Ec0lxAt39tsrAyimG0U= 2 | github.com/PacktPublishing/Building-Microservices-with-Go-Second-Edition v0.0.0-20200114090946-8b8fca2b7f26 h1:VSSckbUJ+7lKE9kiJkSpsLjjkrof4U3zykiNQX2kRuM= 3 | github.com/PacktPublishing/Building-Microservices-with-Go-Second-Edition/product-api v0.0.0-20200114090946-8b8fca2b7f26 h1:N1hxTjviEEz55uEBvSZtqZXMBPjUMsou/ue9XHJPPNg= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/nicholasjackson/env v0.5.0 h1:AmVgGcvc/OjOw5JLQBQyyIYaBFy4eqtG0PyF+PAf9e4= 7 | github.com/nicholasjackson/env v0.5.0/go.mod h1:8PvK2K2IBkG9ANQ9TJq7D0EGIblM5hS5SglTXLkX/04= 8 | github.com/nicholasjackson/env v0.6.0 h1:6xdio52m7cKRtgZPER6NFeBZxicR88rx5a+5Jl4/qus= 9 | github.com/nicholasjackson/env v0.6.0/go.mod h1:/GtSb9a/BDUCLpcnpauN0d/Bw5ekSI1vLC1b9Lw0Vyk= 10 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 11 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 12 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 13 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 14 | --------------------------------------------------------------------------------