Блог Вечного Джуна

Построение микросервиса комментариев на Go для этого блога

14.07.2024

1. Архитектурное решение

Сервис комментариев общается с фронтендом и основным приложением через HTTP API. Основные компоненты:

  1. Go Comment Service — REST API на Go.
  2. PostgreSQL — хранилище комментариев.
  3. Symfony Blog — отправляет запросы к микросервису.
  4. Docker Compose — оркестрация локальной среды.

2. Проектирование API

Определил следующие конечные точки:

Структура модели Comment

type Comment struct {
    ID        int64     `json:"id"`
    PostID    int64     `json:"postId"`
    Author    string    `json:"author"`
    Body      string    `json:"body"`
    CreatedAt time.Time `json:"createdAt"`
}

3. Реализация сервиса на Go

Использовал фреймворк Gin для простого роутинга и sqlx для работы с базой.

main.go

package main

import (
    "github.com/gin-gonic/gin"
    "log"
)

func main() {
    router := gin.Default()
    db, err := NewDB("postgres://user:pass@db:5432/comments?sslmode=disable")
    if err != nil {
        log.Fatalf("DB connection failed: %v", err)
    }
    service := NewCommentService(db)

    router.GET("/comments", service.GetComments)
    router.POST("/comments", service.CreateComment)
    router.DELETE("/comments/:id", service.DeleteComment)

    log.Println("Comment service is running on :8080")
    router.Run(":8080")
}

handlers.go

package main

import (
    "net/http"
    "strconv"

    "github.com/gin-gonic/gin"
)

func (s *CommentService) GetComments(c *gin.Context) {
    postIdStr := c.Query("postId")
    postId, err := strconv.ParseInt(postIdStr, 10, 64)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid postId"})
        return
    }

    comments, err := s.repo.FindByPostID(postId)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, comments)
}

func (s *CommentService) CreateComment(c *gin.Context) {
    var newComment Comment
    if err := c.ShouldBindJSON(&newComment); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    created, err := s.repo.Create(newComment)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusCreated, created)
}

func (s *CommentService) DeleteComment(c *gin.Context) {
    idStr := c.Param("id")
    id, err := strconv.ParseInt(idStr, 10, 64)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid id"})
        return
    }
    if err := s.repo.Delete(id); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.Status(http.StatusNoContent)
}

4. Репозиторий и миграции

Использовал sqlx и файл миграции schema.sql:

CREATE TABLE comments (
    id SERIAL PRIMARY KEY,
    post_id BIGINT NOT NULL,
    author TEXT NOT NULL,
    body TEXT NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);

5. Интеграция с Symfony

В Symfony заменил старый сервис комментариев HTTP-клиентом на Go-сервис:

// src/Service/CommentClient.php
namespace App\Service;

use Symfony\Contracts\HttpClient\HttpClientInterface;

class CommentClient
{
    private HttpClientInterface $httpClient;
    private string $baseUri;

    public function __construct(HttpClientInterface $httpClient, string $baseUri)
    {
        $this->httpClient = $httpClient;
        $this->baseUri = rtrim($baseUri, '/');
    }

    public function getComments(int $postId): array
    {
        $response = $this->httpClient->request('GET', $this->baseUri . '/comments', [
            'query' => ['postId' => $postId]
        ]);
        return $response->toArray();
    }
}

6. Docker Compose

version: '3.8'
services:
  blog:
    build: ./symfony
    ports:
      - "8000:8000"
    depends_on:
      - comment-service
  comment-service:
    build: ./comment-service
    ports:
      - "8080:8080"
    depends_on:
      - db
  db:
    image: postgres:14
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: comments
    volumes:
      - db-data:/var/lib/postgresql/data
volumes:
  db-data:

8. Тестирование и деплой

Написал простые тесты для Go через httptest, проверил CRUD операции. Деплой организовал через Kubernetes с Helm-чартами: два Deployment (Symfony и Go), один StatefulSet (Postgres) и Service тип LoadBalancer.

Категории: Golang

Комментарии (0)

Добавить комментарий