Microservices with Go
Microservices architecture is a pattern where a system is broken down into smaller, independently deployable services.
Typically each service has its own database and communicates with others using HTTP or messaging queues.
(though i have worked on many production systems where a database is shared between the microservices.. but please don’t tell anyone..
it will be our little secret…)
SO for fun and profit- lets build a simple microservices architecture using Go.
Its amazingly simple honestly..
As I Was working on this i rememeber the first time I got C++ DCOM working.. haha only took a few long days …
this is much simpler..
so lets..
GO GET STARTED!~!!!!!
(I crack me up :) )
Prerequisites - Stuff you need before we go camping
(or containering ??? )
- Go 1.18 or higher
- Basic understanding of Go and HTTP
- Docker (optional, for containerizing the services)
Step 1: Setting up the Go Project
Create a project directory for your microservices:
1
| mkdir go-microservices cd go-microservices
|
Inside this folder, now create directories for each microservice.
For example, let’s create user-service
and order-service
:
1
2
| mkdir user-service
mkdir order-service
|
Step 2: Creating the User Service
In the user-service
directory, create a Go file main.go
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| // user-service/main.go
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}
var users = []User{
{ID: "1", Name: "Alice"},
{ID: "2", Name: "Bob"},
}
func getUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(users)
}
func main() {
http.HandleFunc("/users", getUsers)
fmt.Println("User service listening on port 8080...")
http.ListenAndServe(":8080", nil)
}
|
This is a simple HTTP server that returns a list of users when accessing the /users
endpoint.
To run the service:
1
| cd user-service go run main.go
|
GO DO IT NOW!
The service will be available at http://localhost:8080/users
.
Step 3: Creating the Order Service
Now, let’s create an order-service
in the order-service
directory.
The order service will simulate creating an order and fetching orders for a specific user.
Create the main.go
file in the order-service
folder:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| // order-service/main.go
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
)
type Order struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Amount float64 `json:"amount"`
}
var orders = []Order{
{ID: "1", UserID: "1", Amount: 100.50},
{ID: "2", UserID: "1", Amount: 200.75},
}
func getOrders(w http.ResponseWriter, r *http.Request) {
userID := r.URL.Query().Get("user_id")
var filteredOrders []Order
for _, order := range orders {
if order.UserID == userID {
filteredOrders = append(filteredOrders, order)
}
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(filteredOrders)
}
func main() {
http.HandleFunc("/orders", getOrders)
fmt.Println("Order service listening on port 8081...")
http.ListenAndServe(":8081", nil)
}
|
The order-service
has an endpoint /orders?user_id=1
to fetch orders for a specific user.
Run the service by navigating to the order-service
folder and executing:
GO GO GO TEST IT NOW!!!!
1
2
| cd order-service
go run main.go
|
Now the order service will be available at http://localhost:8081/orders?user_id=1
.
Step 4: Setting Up Communication Between Services
The user service provides data about users, and the order service uses the user ID to filter orders.
The proverbial “Hair CLub For Men” .. needs some clients…
Let’s modify the order-service
to fetch the user details from the user-service
.
Update the getOrders
function in order-service/main.go
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| func getOrders(w http.ResponseWriter, r *http.Request) {
userID := r.URL.Query().Get("user_id")
var filteredOrders []Order
// Fetch user details from user service
userResp, err := http.Get("http://localhost:8080/users")
if err != nil {
http.Error(w, "Could not fetch users", http.StatusInternalServerError)
return
}
defer userResp.Body.Close()
var users []User
if err := json.NewDecoder(userResp.Body).Decode(&users); err != nil {
http.Error(w, "Could not decode users", http.StatusInternalServerError)
return
}
// Find the user by userID
var user *User
for _, u := range users {
if u.ID == userID {
user = &u
break
}
}
// If user is found, filter orders by userID
if user != nil {
for _, order := range orders {
if order.UserID == user.ID {
filteredOrders = append(filteredOrders, order)
}
}
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(filteredOrders)
}
|
In this update, the order-service
makes an HTTP request to the user-service
to get all users and filter orders based on the user_id
.
cool eh?
I love how small the code is honestly…
Step 5: Running the Microservices
Run both services:
- Start the
user-service
:
1
2
| cd user-service
go run main.go
|
- Start the
order-service
:
1
2
| cd order-service
go run main.go
|
Now, you can request orders from the order service for a particular user. For example:
1
| curl http://localhost:8081/orders?user_id=1`
|
This should return the list of orders for the user with ID 1
from the order service, which internally communicates with the user service.
(clap)(clap)(clap)
OPTIONAL:
Step 6: Dockerizing the Microservices
You can containerize the microservices using Docker.
We need a Dockerfile
in both user-service
and order-service
directories.
For example, in user-service/Dockerfile
:
1
2
3
4
5
6
7
8
9
10
| FROM golang:1.18-alpine
WORKDIR /app
COPY . .
RUN go mod tidy
RUN go build -o user-service .
EXPOSE 8080
CMD ["./user-service"]
|
1
2
3
4
5
6
7
8
9
10
| FROM golang:1.18-alpine
WORKDIR /app
COPY . .
RUN go mod tidy
RUN go build -o order-service .
EXPOSE 8081
CMD ["./order-service"]
|
Build and run the containers with Docker:
1
2
3
4
| docker build -t user-service ./user-service
docker build -t order-service ./order-service
docker run -p 8080:8080 user-service
docker run -p 8081:8081 order-service
|
NOW GO WRITE COME CODE!!!!!!!!!!!!!!!!!!!!
:)