В первой статье этой серии из трех статей пошагово создадим масштабируемое серверное приложение с Golang, gRPC и PostgreSQL, подключим модульные тесты, контейнеризируем приложение и задействуем Envoy как прокси-службу взаимодействия. В итоге спроектируем, выполним оценку и запустим современное серверное приложение.
Настройка Golang с gRPC и PostgreSQL
1. Структура проекта
Организуем проект, обеспечив ему модульность и удобство восприятия:
grpc-app/
├── app/
├── dbconnections/ # Подключение к базе данных
├── models/ # Структура данных моделей
├── proto/ # proto-файлы gRPC
├── generated/ # Генерируемый proto-код
├── service/ # Бизнес-логика
├── controller/ # Реализации запросов gRPC
├── types/ # Основные методы для доступных служб
├── utils/ # Вспомогательные функции
├── Dockerfile # Концигурация Docker
├── Docker-compose.yaml # Концигурация Docker
├── envoy.yaml # Концигурация Docker Envoy
├── main.go # Точка входа приложения
├── app_test.go # Модульные тесты
2. Добавление зависимостей
Сначала инициализируем проект с помощью go mod init <название проекта>, затем добавляем зависимости: grpc, gorm и postgres.
go mod init grpc-app
go get google.golang.org/grpc
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
go get -u gorm.io/gorm
go get -u gorm.io/driver/postgres
3. Определение proto-файлов
Файл буферов протокола является неотъемлемой частью protobuf, независимого от языков программирования и операционных систем фреймворка сериализации Google. В нем описываются структура данных (то есть сообщения) и службы (то есть методы RPC) для взаимодействия в системах на основе gRPC.
syntax = "proto3";
option go_package = "grpc-app/app/generated/buku";
message BukuResponse {
int32 id = 1;
string judul = 2;
string penulis = 3;
int32 kuantitas = 4;
string tempat_penyimpanan = 5;
}
message AddBukuRequest {
string judul = 1;
string penulis = 2;
int32 kuantitas = 3;
string tempat_penyimpanan = 4;
}
message EmptyRequest{
}
message EmptyResponse {
bool success = 1;
}
service BukuService {
rpc AddBuku(AddBukuRequest) returns (BukuResponse);
// Добавляется оставшаяся операция CRUD..
}
4. Генерирование proto-файлов
Командой make gen создаем makefile, в котором перечислены команды для компилирования и генерирования proto-файла:
gen:
@protoc \
--proto_path=protobuf \
--go_out=app/generated/buku --go_opt=paths=source_relative \
--go-grpc_out=app/generated/buku --go-grpc_opt=paths=source_relative \
app/proto/buku.proto
Реализация gRPC и PostgreSQL
1. Настройка подключения к PostgreSQL
package dbconnection
import (
"log"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func StartDB() (*gorm.DB, error) {
dsn := "host=localhost user=postgres password=postgres dbname=database port=8080 sslmode=disable TimeZone=Asia/Jakarta"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("failed to connect database", err)
} else {
log.Println("Connected to database")
}
return db, nil
}
2. Определение модели базы данных
package models
type Buku struct {
ID int32 `gorm:"primaryKey"` // Первичный ключ
Judul string `gorm:"not null"` // Название книги
Penulis string `gorm:"not null"` // Автор
Kuantitas int32 `gorm:"not null"` // Количество
TempatPenyimpanan string `gorm:"not null"` // Место хранения
}
func (Buku) TableName() string {
return "buku"
}
3. Интерфейс gRPC
Создаём интерфейс Golang, которым описываются методы, реализуемые службой для управления операциями.
package types
import (
"context"
"grpc-app/app/generated/buku"
)
type BukuService interface {
AddBuku(context.Context, models.Buku) error
GetAllBuku(context.Context) []*buku.BukuResponse
DeleteBuku(context.Context, *buku.BukuIdRequest) error
UpdateBuku(context.Context, *buku.UpdateBukuRequest) error
}
4. Services
В этом пакете пишем методы для выполнения CRUD-операций с GORM.
package services
import (
"context"
"grpc-app/app/dbconnection"
"grpc-app/app/models"
"grpc-app/app/generated/buku"
"log"
"gorm.io/gorm"
)
type BukuService struct {
db *gorm.DB
}
func NewBukuService() *BukuService {
db, err := dbconnection.StartDB()
if err != nil {
log.Fatalf("Failed to connect to the database: %v", err)
}
return &BukuService{db: db}
}
func (s *BukuService) AddBuku(ctx context.Context, buku models.Buku) error {
newBuku := models.Buku{
Judul: buku.Judul,
Penulis: buku.Penulis,
Kuantitas: buku.Kuantitas,
TempatPenyimpanan: buku.TempatPenyimpanan,
}
if err := s.db.Create(&newBuku).Error; err != nil {
log.Printf("Failed to add new book: %v", err)
return err
}
return nil
}
// Добавляются остальные методы CRUD..
5. Controller
В этом пакете пишем методы для обработки запроса, затем вызываем их в службе для выполнения CRUD-операций. Методом также создается и выводится ответное сообщение в соответствии с интерфейсом gRPC.
package controller
import (
"context"
"grpc-app/app/models"
"grpc-app/app/types"
"grpc-app/app/generated/buku"
"google.golang.org/grpc"
)
type BukuGrpcHandler struct {
bukuService types.BukuService
buku.UnimplementedBukuServiceServer
}
func NewGrpcBukuService(grpc *grpc.Server, bukuService types.BukuService) {
gRPCHandler := &BukuGrpcHandler{
bukuService: bukuService,
}
buku.RegisterBukuServiceServer(grpc, gRPCHandler)
}
func (h *BukuGrpcHandler) AddBuku(ctx context.Context, req *buku.AddBukuRequest) (*buku.BukuResponse, error) {
var idCounter int
add_buku := models.Buku{
ID: int32(idCounter),
Judul: req.Judul,
Penulis: req.Penulis,
Kuantitas: req.Kuantitas,
TempatPenyimpanan: req.TempatPenyimpanan,
}
idCounter++
err := h.bukuService.AddBuku(ctx, add_buku)
if err != nil {
return nil, err
}
res := &buku.BukuResponse{
Id: add_buku.ID,
Judul: add_buku.Judul,
Penulis: add_buku.Penulis,
Kuantitas: add_buku.Kuantitas,
TempatPenyimpanan: add_buku.TempatPenyimpanan,
}
return res, nil
}
// Добавляются остальные методы CRUD..
6. Создание сервера gRPC
Написав и определив все доступные службы gRPC, создаем и запускаем сервер gRPC в пакете main.
package main
import (
"grpc-app/app/dbconnection"
"grpc-app/app/controller"
"grpc-app/app/services"
"log"
"net"
"google.golang.org/grpc"
)
func main() {
db, err := dbconnection.StartDB()
if err != nil {
log.Fatal("failed to connect database", err)
} else {
log.Println("Connected to database")
}
StartGRPCServer()
StartGinServer()
}
func StartGRPCServer() {
portNumber := 9000
lis, err := net.Listen("tcp", ":"+portNumber)
if err != nil {
log.Fatalf("gRPC fail to listen on port "+portNumber+" : %v", err)
} else {
log.Println("gRPC Starting...")
grpcServer := grpc.NewServer()
bukuService := services.NewBukuService()
controller.NewGrpcBukuService(grpcServer, bukuService)
go func() {
log.Println("gRPC listening to port " + portNumber)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("could not start grpc server: %v", err)
}
}()
}
}
func StartGinServer() {
hostName := localhost
portNumber := 9090
server := gin.Default()
server.Run(hostName + ":" + portNumber)
}
Запуск и тестирование приложения
Запускаем приложение командой go run main.go и тестируем службы при помощи Postman, grpcurl, BloomRPC и т. д. Воспользуемся BloomRPC:

Заключение
Созданием программы Go с gRPC и PostgreSQL обеспечивается высокоэффективная, масштабируемая архитектура для построения распределенных систем. Комбинируя высокопроизводительную, всеязычную RPC-платформу gRPC с надежной реляционной базой данных PostgreSQL, разработчики конструируют системы для обработки сложных моделей данных и эффективного функционирования при большой нагрузке.
С gRPC обеспечивается быстрое взаимодействие служб при поддержке двунаправленной потоковой передачи, аутентификации и автоматического генерирования кода для клиент-серверных взаимодействий. С PostgreSQL же обеспечиваются соответствие требованиям ACID, мощная поддержка запросов и возможность при необходимости масштабироваться.
Таким сочетанием гарантируется не только респонсивность и масштабируемость приложений, но и их сопровождаемость и безопасность. Комбинируя модель параллелизма Go с надежностью PostgreSQL, разработчики создают высокореспонсивные бэкенды, которые легко масштабируются и оптимизируются для повышения производительности.
В целом этой архитектурой обеспечивается прочная основа для создания современных микросервисных приложений, которые эффективно справляются со сложными задачами.
Читайте также:
- Как писать безопасный код на Go
- Изучаем gRPC и Flutter для разработки современных приложений
- Переход на PgCat — прокси-сервер Postgres следующего поколения
Читайте нас в Telegram, VK и Дзен
Перевод статьи Izzan Alfadhil: Implementing gRPC and PostgreSQL in GO





