Будування мікросервісів за допомогою Go

pic

Архітектура мікросервісів стала популярним вибором для розробки масштабованих і підтримуваних додатків. Розділивши монолітний додаток на менші, незалежні сервіси, команди можуть розробляти, розгортати і масштабувати кожен сервіс окремо. Go, або Golang, є відмінним вибором для побудови мікросервісів завдяки своїй простоті, продуктивності та потужній стандартній бібліотеці. У цьому блозі ми розглянемо, як створити архітектуру мікросервісів за допомогою Go з практичними прикладами.

Чому варто вибрати Go для мікросервісів?

1. Простота

  • Go спроектований таким чином, щоб бути простим і зрозумілим, що знижує когнітивне навантаження на розробників, які працюють з кількома сервісами.

2. Конкурентність

  • Горутини Go дозволяють легко обробляти паралельні операції, що є загальною вимогою в мікросервісах для таких завдань, як обробка запитів або управління фоновими задачами.

3. Продуктивність

  • Компільована природа Go та ефективне управління пам'яттю гарантують високу продуктивність, що критично важливо для сервісів з високим трафіком.

4. Стандартна бібліотека

  • Стандартна бібліотека Go включає потужні пакети для мережевих з'єднань, HTTP-серверів, обробки JSON і багато іншого, що дозволяє створювати надійні мікросервіси без зовнішніх залежностей.

Основні компоненти мікросервісу

  1. API шар: Обробляє вхідні HTTP запити.
  2. Бізнес-логіка: Основна функціональність сервісу.
  3. Шар даних: Керує взаємодією з базами даних або зовнішніми джерелами даних.
  4. Комунікація: Забезпечує взаємодію з іншими сервісами через REST, gRPC або черги повідомлень.
  5. Проміжне програмне забезпечення (Middleware): Обробляє загальні задачі, як автентифікація, логування та обмеження швидкості.

Як почати з Go

Налаштування проекту

Давайте створимо простий мікросервіс для керування продуктами.
Ось як можна структурувати ваш проект на Go:

product-service/  
|-- cmd/  
| |-- main.go  
|-- internal/  
| |-- handler/  
| | |-- product_handler.go  
| |-- service/  
| | |-- product_service.go  
| |-- model/  
| | |-- product.go  
| |-- repository/  
| |-- product_repository.go  
|-- middleware/  
| |-- auth_middleware.go  
| |-- logging_middleware.go  
|-- go.mod

Приклад: Мікросервіс для продуктів

Крок 1: Опис моделі продукту

package model  

type Product struct {  
 ID string `json:"id"`  
 Name string `json:"name"`  
 Description string `json:"description"`  
 Price float64 `json:"price"`  
}

Крок 2: Реалізація шару репозиторію

package repository  

import (  
 "errors"  
 "product-service/internal/model"  
)  

type ProductRepository struct {  
 products map[string]*model.Product  
}  

func NewProductRepository() *ProductRepository {  
 return &ProductRepository{  
 products: make(map[string]*model.Product),  
 }  
}  

func (r *ProductRepository) Create(product *model.Product) error {  
 if _, exists := r.products[product.ID]; exists {  
 return errors.New("продукт вже існує")  
 }  
 r.products[product.ID] = product  
 return nil  
}  

func (r *ProductRepository) GetAll() []*model.Product {  
 var productList []*model.Product  
 for _, product := range r.products {  
 productList = append(productList, product)  
 }  
 return productList  
}

Крок 3: Створення шару сервісу

package service  

import (  
 "product-service/internal/model"  
 "product-service/internal/repository"  
)  

type ProductService struct {  
 repo *repository.ProductRepository  
}  

func NewProductService(repo *repository.ProductRepository) *ProductService {  
 return &ProductService{repo: repo}  
}  

func (s *ProductService) AddProduct(product *model.Product) error {  
 return s.repo.Create(product)  
}  

func (s *ProductService) ListProducts() []*model.Product {  
 return s.repo.GetAll()  
}

Крок 4: Побудова шару обробника

package handler  

import (  
 "encoding/json"  
 "net/http"  
 "product-service/internal/model"  
 "product-service/internal/service"  
)  

type ProductHandler struct {  
 service *service.ProductService  
}  

func NewProductHandler(service *service.ProductService) *ProductHandler {  
 return &ProductHandler{service: service}  
}  

func (h *ProductHandler) CreateProduct(w http.ResponseWriter, r *http.Request) {  
 var product model.Product  
 if err := json.NewDecoder(r.Body).Decode(&product); err != nil {  
 http.Error(w, "Невірний вхід", http.StatusBadRequest)  
 return  
 }  

 if err := h.service.AddProduct(&product); err != nil {  
 http.Error(w, err.Error(), http.StatusConflict)  
 return  
 }  

 w.WriteHeader(http.StatusCreated)  
}  

func (h *ProductHandler) GetProducts(w http.ResponseWriter, r *http.Request) {  
 products := h.service.ListProducts()  
 w.Header().Set("Content-Type", "application/json")  
 json.NewEncoder(w).Encode(products)  
}

Крок 5: Реалізація проміжного програмного забезпечення

Проміжне програмне забезпечення для автентифікації

package middleware  

import (  
 "net/http"  
)  

func AuthMiddleware(next http.Handler) http.Handler {  
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {  
 token := r.Header.Get("Authorization")  
 if token == "" {  
 http.Error(w, "Неавторизовано", http.StatusUnauthorized)  
 return  
 }  
 // Додаткові перевірки токену можна додати тут.

next.ServeHTTP(w, r)  
 })  
}

Проміжне програмне забезпечення для логування

package middleware  

import (  
 "log"  
 "net/http"  
 "time"  
)  

func LoggingMiddleware(next http.Handler) http.Handler {  
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {  
 start := time.Now()  
 log.Printf("Почато %s %s", r.Method, r.URL.Path)  

 next.ServeHTTP(w, r)  

 log.Printf("Завершено за %s", time.Since(start))  
 })  
}

Крок 6: Підключення всього в main.go

package main  

import (  
 "log"  
 "net/http"  
 "product-service/internal/handler"  
 "product-service/internal/repository"  
 "product-service/internal/service"  
 "product-service/middleware"  
)  

func main() {  
 repo := repository.NewProductRepository()  
 productService := service.NewProductService(repo)  
 productHandler := handler.NewProductHandler(productService)  

 mux := http.NewServeMux()  
 mux.HandleFunc("/products", productHandler.GetProducts)  
 mux.HandleFunc("/products/create", productHandler.CreateProduct)  

 wrappedMux := middleware.AuthMiddleware(middleware.LoggingMiddleware(mux))  

 log.Println("Запуск сервера на порту :8080")  
 log.Fatal(http.ListenAndServe(":8080", wrappedMux))  
}

Запуск мікросервісу

  1. Ініціалізуйте модуль Go:
go mod init product-service
  1. Запустіть сервіс:
go run cmd/main.go
  1. Протестуйте кінцеві точки за допомогою інструментів, таких як curl або Postman:
  • Створення продукту:
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer token" -d '{"id": "1", "name": "Laptop", "description": "MAC", "price": 1000}' http://localhost:8080/products/create
  • Список всіх продуктів:
curl -H "Authorization: Bearer token" http://localhost:8080/products

Покращення мікросервісу

  • Інтеграція з базою даних: Замінити зберігання в пам'яті на базу даних, наприклад PostgreSQL або MongoDB.
  • Автентифікація: Використовувати JWT або OAuth2 для надійної безпеки.
  • Масштабування: Розгорнути сервіс за допомогою Docker і Kubernetes.
  • Комунікація між сервісами: Використовувати gRPC або чергу повідомлень, наприклад RabbitMQ, для комунікації між мікросервісами.
  • Моніторинг: Інтегрувати інструменти, такі як Prometheus і Grafana для реального моніторингу та сповіщень.

Висновок

Go надає інструменти та продуктивність, необхідні для створення надійних і масштабованих мікросервісів. Структуруючи свій код правильно і використовуючи стандартну бібліотеку Go, ви можете створювати сервіси, які легко підтримувати і розширювати. Проміжне програмне забезпечення ще більше покращує функціональність, вирішуючи загальні задачі, такі як автентифікація та логування. Як тільки ви розвиваєте свою екосистему мікросервісів, подумайте про додавання більш просунутих функцій, таких як моніторинг, трасування та автоматичне тестування для забезпечення надійності вашої системи.

Перекладено з: Building Microservices with Go

Leave a Reply

Your email address will not be published. Required fields are marked *