Loading...
Loading...

Go Microservices Tutorial

Microservices architecture in Go enables building scalable, maintainable systems composed of small, independent services. This tutorial covers service design, communication patterns, and deployment strategies.

1. Microservice Fundamentals

1.1 Service Design Principles

Well-designed microservices follow these core principles:

// Good microservice characteristics:
// 1. Single responsibility
type PaymentService struct {
    processor PaymentProcessor
}

// 2. Explicit boundaries
type OrderService struct {
    paymentClient PaymentClient // Clear dependency
}

// 3. Independent deployability
// Each service has own:
// - Source repo
// - Build pipeline
// - Deployment unit

1.2 Project Structure

Typical microservice project layout:

/order-service
  ├── cmd/          # Main application
  ├── internal/     # Private implementation
  ├── pkg/          # Shared library code
  ├── api/          # Protocol definitions
  ├── Dockerfile    # Containerization
  └── go.mod        # Module definition

2. Service Implementation

2.1 HTTP Service

Basic RESTful microservice with Chi router:

package main

import (
    "github.com/go-chi/chi/v5"
    "net/http"
)

func main() {
    r := chi.NewRouter()
    
    // Service endpoints
    r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("OK"))
    })
    
    r.Route("/api/v1", func(r chi.Router) {
        r.Get("/orders", listOrdersHandler)
        r.Post("/orders", createOrderHandler)
    })
    
    // Start server
    http.ListenAndServe(":8080", r)
}

2.2 Configuration

Environment-aware configuration:

type Config struct {
    Port        int    `env:"PORT" envDefault:"8080"`
    DBHost      string `env:"DB_HOST" envDefault:"localhost"`
    Environment string `env:"ENV" envDefault:"development"`
}

func LoadConfig() (*Config, error) {
    var cfg Config
    if err := env.Parse(&cfg); err != nil {
        return nil, err
    }
    return &cfg, nil
}

3. Service Communication

3.1 REST Clients

Making HTTP requests between services:

type ProductClient struct {
    baseURL string
    client  *http.Client
}

func (c *ProductClient) GetProduct(id string) (*Product, error) {
    resp, err := c.client.Get(fmt.Sprintf("%s/products/%s", c.baseURL, id))
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    var product Product
    if err := json.NewDecoder(resp.Body).Decode(&product); err != nil {
        return nil, err
    }
    return &product, nil
}

3.2 gRPC Services

High-performance RPC communication:

// Protobuf definition (product.proto)
service ProductService {
    rpc GetProduct (ProductRequest) returns (ProductResponse);
}

// Go implementation
type productServer struct {
    pb.UnimplementedProductServiceServer
}

func (s *productServer) GetProduct(ctx context.Context, 
    req *pb.ProductRequest) (*pb.ProductResponse, error) {
    // Business logic
    return &pb.ProductResponse{...}, nil
}

4. Data Management

4.1 Database Patterns

Database access with repository pattern:

type OrderRepository interface {
    Create(order *Order) error
    GetByID(id string) (*Order, error)
}

type PostgresOrderRepo struct {
    db *sql.DB
}

func (r *PostgresOrderRepo) Create(order *Order) error {
    _, err := r.db.Exec(
        "INSERT INTO orders (...) VALUES (...)",
        // fields...
    )
    return err
}

4.2 Event Sourcing

Using events for inter-service communication:

type OrderCreatedEvent struct {
    OrderID    string    `json:"order_id"`
    UserID     string    `json:"user_id"`
    OccurredAt time.Time `json:"occurred_at"`
}

func PublishOrderCreated(event OrderCreatedEvent) error {
    eventBytes, err := json.Marshal(event)
    if err != nil {
        return err
    }
    return kafkaProducer.Publish("orders.created", eventBytes)
}

5. Deployment & Observability

5.1 Containerization

Dockerfile for Go microservices:

# Build stage
FROM golang:1.19-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o service ./cmd/service

# Runtime stage
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/service .
EXPOSE 8080
CMD ["./service"]

5.2 Monitoring

Adding Prometheus metrics:

func main() {
    // Create metrics
    requests := prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total HTTP requests",
        },
        []string{"path", "method", "status"},
    )
    prometheus.MustRegister(requests)
    
    // Instrument handler
    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":8080", nil)
}

6. Best Practices

6.1 Service Design

  • Domain-driven boundaries: Align services with business capabilities
  • Loose coupling: Prefer events over direct HTTP calls
  • Circuit breakers: Implement fault tolerance patterns

6.2 Development Workflow

// Recommended tools:
// - go-kit/kit for service scaffolding
// - wire for dependency injection
// - testify for testing
// - docker-compose for local development

// CI/CD Pipeline:
// 1. Run unit tests
// 2. Build container
// 3. Run integration tests
// 4. Deploy to staging
// 5. Canary release
0 Interaction
0 Views
Views
0 Likes
×
×
×
🍪 CookieConsent@Ptutorials:~

Welcome to Ptutorials

$ Allow cookies on this site ? (y/n)

top-home