Making requests
With Gohandlers provided request builders and response parsers making requests between Go services should feel like RPC. But it is not. You are actually using the good old http
package provided DefaultClient
underneath and your choice of body encoding, like the json
or x-www-form-urlencoded
.
type Desk struct {
pets *pets.Client
}
func (d *Desk) NewPet(w http.ResponseWriter, r *http.Request) {
// ...
bq, err := d.pets.Create(&handlers.CreateRequest{
Name: "Cookie",
Tag: "Fluffy"
})
// ...
fmt.Println(bq.ID)
// ...
}
Host pool
Client constructor expects a Pool
value. Pool
is an interface of types implement Host
method. A Pool
value is expected to return the next available host’s address at each method call. By providing your implementation of the Pool
to the constructor you are abstracting the load balancing logic from rest of your service codebase will make requests. One simple implementation of a Pool
type might be like the below. It either returns an error in case of there is no available host, or returns a random one at each call, impersonating the round robin method.
type MyPool []string
func (p MyPool) Host() (string, error) {
if len(p) == 0 {
return "", fmt.Errorf("no hosts available")
}
return p[rand.IntN(len(p))], nil
}
Initialization
Start with importing the client package. Construct your pool. Then pass the client to your handler’s or app’s struct as a dependency. After that, your handlers must be able to perform service-to-service requests by calling the methods on the client.
package main
import (
pets "petstore/cmd/pets/client"
"petstore/cmd/desk/handlers"
)
func main() {
pool := MyPool([]string{"127.0.0.1:8081"})
pets := pets.NewClient(pool)
_ := handlers.NewDesk(pets)
// ...
}
For the unit testing
Instead of using the concrete Client
implementation, use the Interface
type to declare dependency type to your consumer service handler. Then you can construct and provide the actual Client
in main
and the Mock
in the unit tests. This will enable you to keep testing methods in isolation.
import pets "petstore/cmd/pets/client"
type Desk struct {
pets pets.Interface
}
import pets "petstore/cmd/pets/client"
func TestDesk_NewPet(t *testing.T) {
d := Desk{
Pets: &pets.Mock{}
}
// ...
}