add new posts
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "themes/PaperMod"]
|
||||||
|
path = themes/PaperMod
|
||||||
|
url = https://github.com/adityatelange/hugo-PaperMod.git
|
||||||
0
.hugo_build.lock
Normal file
0
.hugo_build.lock
Normal file
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Используем официальный и актуальный образ Hugo
|
||||||
|
FROM hugomods/hugo:latest AS builder
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Сборка статики
|
||||||
|
RUN hugo --minify
|
||||||
|
|
||||||
|
# ЭТАП 2: Раздача через Nginx
|
||||||
|
FROM nginx:alpine
|
||||||
|
COPY --from=builder /src/public /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
6
archetypes/default.md
Normal file
6
archetypes/default.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
title: "{{ replace .Name "-" " " | title }}"
|
||||||
|
date: {{ .Date }}
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
||||||
33
config.toml
Normal file
33
config.toml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
baseURL = 'https://returntozer0.ru'
|
||||||
|
languageCode = 'ru'
|
||||||
|
title = 'returntozer0.ru'
|
||||||
|
theme = 'PaperMod'
|
||||||
|
|
||||||
|
[params]
|
||||||
|
env = 'production'
|
||||||
|
description = 'Software Developer | Founder of GLIPH'
|
||||||
|
author = 'Микаэл Оганесян'
|
||||||
|
ShowCodeCopyButtons = true
|
||||||
|
ShowShareButtons = true
|
||||||
|
ShowReadingTime = true
|
||||||
|
ShowToc = true
|
||||||
|
TocOpen = true
|
||||||
|
[params.profileMode]
|
||||||
|
enabled = true
|
||||||
|
title = 'Микаэл Оганесян'
|
||||||
|
subtitle = 'Software Developer • GLIPH Founder'
|
||||||
|
imageUrl = 'https://github.com/mikaeloganesian.png'
|
||||||
|
imageWidth = 160
|
||||||
|
imageHeight = 160
|
||||||
|
buttons = [
|
||||||
|
{ name = 'Проекты', url = '/projects' },
|
||||||
|
{ name = 'Статьи', url = '/posts' },
|
||||||
|
{ name = 'Gitea', url = 'https://git.returntozer0.ru' }
|
||||||
|
]
|
||||||
|
|
||||||
|
[[params.socialIcons]]
|
||||||
|
name = 'github'
|
||||||
|
url = 'https:/github.com/mikaeloganesian'
|
||||||
|
[[params.socialIcons]]
|
||||||
|
name = 'telegram'
|
||||||
|
url = 'https://t.me/returntozer0'
|
||||||
4
content/posts/_index.md
Normal file
4
content/posts/_index.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Статьи"
|
||||||
|
description: "Заметки об IT-индустрии и то, что лично мне кажется интересным"
|
||||||
|
---
|
||||||
27
content/posts/clean-arch.md
Normal file
27
content/posts/clean-arch.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
title: "Иерархия каталогов в Linux"
|
||||||
|
tags: ["linux", "файловые системы"]
|
||||||
|
categories: ["tech"]
|
||||||
|
---
|
||||||
|
|
||||||
|
В Linux все дерево начинается с корня - `/`. Каждая директория имеет свое строгое назначение:
|
||||||
|
|
||||||
|
### Системные и исполняемые файлы
|
||||||
|
- `/bin` и `/sbin` - содержат основные программы, необходимые для работы системы и ее восстановления. В `/sbin` лежат команды, предназначенные для системного администратора.
|
||||||
|
- `/usr` - может показаться, что является сокращением от user, однако на самом деле является аббривеатурой **Unix System Resources** - вторичная иерархия. Здесь хранятся пользовательские программы, библиотеки и документация. Современные дистрибутивы часто делают `/bin` ссылкой на `/usr/bin`
|
||||||
|
- `/lib`, '/lib64' - системные библиотеки, которые нужны программам из `/bin` и `/sbin` для запуска.
|
||||||
|
|
||||||
|
### Настройки и переменные данные
|
||||||
|
- `/etc` - здесь хранятся **конфигурационные файлы** всей системы.
|
||||||
|
- `/var` - сокращение от **Variable**. Директория для файлов, которые часто меняются. К таким, например, относятся логи (которые, кстати, находятся по пути `/var/log`), базы данных, временные файлы печати и тд.
|
||||||
|
- `/tmp` - временные файлы. Важно помнить, что во многих системах содержимое этой папки очищается при перезагрузке. Это связано с файловой системой, которая предписывает хранение файлов из /tmp в оперативной памяти.
|
||||||
|
|
||||||
|
### Пользовательские данные
|
||||||
|
- `/home` - здесь находятся личные папки пользователей. При вводе `cd ~` система переносит пользователя как раз в директорию `/home/<username>`.
|
||||||
|
- `/root` - домашний каталог суперпользователя (администратора с UID 0). Он вынесен отдельно от остальных пользователей, чтобы администратор мог войти в систему, даже если раздел `/home` не удалось примонтировать.
|
||||||
|
|
||||||
|
### Виртуальные и псевдо-файловые системы
|
||||||
|
Основная идея таких данных - что это не реальные данные на диске, а интерфейсы к ядру:
|
||||||
|
- `/proc` - виртуальная ФС, содержащая информацию о процессах и состоянии ядра. По-хорошему, можно просто прочитать файл в этой папке и узнать, например, модель процессора, который стоит на системе.
|
||||||
|
- `/sys` - информация об устройствах и драйверах.
|
||||||
|
- `/dev` - как мне изначально казалось, сокращение от developer, однако на самом деле является сокращением от **Devices** - файлы устройств. В Linux **все есть файл**, а значит, работа с жестким диском, терминалом, мышкой или любым другим устройством идет так же, как с обычным файлом.
|
||||||
281
content/posts/gRPS.md
Normal file
281
content/posts/gRPS.md
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
---
|
||||||
|
title: "Почему gRPC это лучший выбор для back-to-back взаимодействия"
|
||||||
|
tags: ["backend", "go"]
|
||||||
|
categories: ["tech"]
|
||||||
|
|
||||||
|
---
|
||||||
|
**gRPC** - это высокопроизводительный фреймворк удаленных вызовов процедур от Google, построенный на HTTP/2 и Protocol Buffers. Он быстрее REST, строго типизирован и отлично масштабируется в микросервисных архитектурах.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что такое gRPC?
|
||||||
|
Google Remote Procedure Call - это современный open-source RPC-фреймворк, позволяющий сервисам вызывать методы друг друга так, как если бы они были локальными функциями. Вместо того чтобы думать о HTTP-запросах и JSON-сериализации, разработчик просто вызывает метод на удаленном сервисе.
|
||||||
|
|
||||||
|
gRPC был разработан и открыт компанией **Google** в **2015** году. Он вырос из внутренней системы Stubby, которую Google использовал более десяти лет для связи между своими микросервисами, обрабатывающими миллиарды запросов в секунду.
|
||||||
|
|
||||||
|
Сегодня проект находится под управлением **Cloud Native Computing Foundation (CNCF)** - той же организации, что курирует Kibernetes и Prometheus. Это гарантирует его нейтральное развитие и широкую поддержку индустрии.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ключевые особенности
|
||||||
|
### Protobuf вместо JSON
|
||||||
|
gRPC использует **Protobuf** - бинарный формат сериализации данных. В отличие от текстового JSON:
|
||||||
|
- Данные занимают сильно меньше места
|
||||||
|
- Сериализация и десериализация происходят также быстрее
|
||||||
|
- Схема данных строго типизирована и версионируется через `.proto`-файлы
|
||||||
|
- Автоматическая кодогенерация клиентов и серверов на 10+ языках
|
||||||
|
|
||||||
|
### HTTP/2 под капотом
|
||||||
|
Это дает следующие возможности:
|
||||||
|
- Мультиплексирование
|
||||||
|
- Двунаправленный стриминг
|
||||||
|
- Сжатие заголовков
|
||||||
|
- Server Push
|
||||||
|
|
||||||
|
### Четыре режима взаимодействия
|
||||||
|
```
|
||||||
|
Unary RPC → клиент шлёт запрос, сервер возвращает ответ (как REST)
|
||||||
|
Server Streaming → сервер стримит поток ответов на один запрос
|
||||||
|
Client Streaming → клиент стримит данные, сервер возвращает один ответ
|
||||||
|
Bidirectional → оба шлют потоки данных друг другу одновременно
|
||||||
|
```
|
||||||
|
|
||||||
|
### Строгий контракт через .proto
|
||||||
|
`.proto`-файл — это единственный источник правды для всей коммуникации. Если один сервис меняет API, компилятор Protobuf немедленно сломает несовместимых клиентов ещё на этапе сборки.
|
||||||
|
|
||||||
|
### Встроенные инструменты
|
||||||
|
- **Deadline / Timeout** — встроенный контроль времени ожидания
|
||||||
|
- **Cancellation** — отмена запросов по цепочке вызовов
|
||||||
|
- **Load Balancing** — клиентская балансировка нагрузки из коробки
|
||||||
|
- **Health Checking** — стандартный протокол проверки готовности сервиса
|
||||||
|
- **Interceptors** — аналог middleware для аутентификации, логирования, трассировки
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пример: сервис пользователей на gRPC
|
||||||
|
|
||||||
|
Разберём полный цикл: от `.proto`-контракта до рабочего клиента и сервера на Go.
|
||||||
|
|
||||||
|
### Шаг 1. Определяем контракт (`user.proto`)
|
||||||
|
|
||||||
|
```protobuf
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package user;
|
||||||
|
option go_package = "github.com/example/user-service/proto";
|
||||||
|
|
||||||
|
// Запрос на получение пользователя
|
||||||
|
message GetUserRequest {
|
||||||
|
int64 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ответ с данными пользователя
|
||||||
|
message GetUserResponse {
|
||||||
|
int64 id = 1;
|
||||||
|
string name = 2;
|
||||||
|
string email = 3;
|
||||||
|
string created_at = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запрос на создание пользователя
|
||||||
|
message CreateUserRequest {
|
||||||
|
string name = 1;
|
||||||
|
string email = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сервис с двумя методами
|
||||||
|
service UserService {
|
||||||
|
rpc GetUser (GetUserRequest) returns (GetUserResponse);
|
||||||
|
rpc CreateUser (CreateUserRequest) returns (GetUserResponse);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 2. Генерируем код
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Устанавливаем плагины
|
||||||
|
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||||
|
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||||
|
|
||||||
|
# Генерируем Go-код из .proto
|
||||||
|
protoc --go_out=. --go-grpc_out=. proto/user.proto
|
||||||
|
```
|
||||||
|
|
||||||
|
После этого компилятор создаст два файла:
|
||||||
|
- `user.pb.go` — структуры данных (GetUserRequest, GetUserResponse и т.д.)
|
||||||
|
- `user_grpc.pb.go` — интерфейсы сервера и заглушки клиента
|
||||||
|
|
||||||
|
### Шаг 3. Реализуем сервер (`server/main.go`)
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
pb "github.com/example/user-service/proto"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// userServer реализует интерфейс pb.UserServiceServer
|
||||||
|
type userServer struct {
|
||||||
|
pb.UnimplementedUserServiceServer
|
||||||
|
// В реальности здесь была бы БД
|
||||||
|
users map[int64]*pb.GetUserResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUser — обработчик метода GetUser
|
||||||
|
func (s *userServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
|
||||||
|
user, ok := s.users[req.Id]
|
||||||
|
if !ok {
|
||||||
|
// gRPC-статус коды вместо HTTP-кодов
|
||||||
|
return nil, status.Errorf(codes.NotFound, "пользователь с id=%d не найден", req.Id)
|
||||||
|
}
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUser — обработчик метода CreateUser
|
||||||
|
func (s *userServer) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.GetUserResponse, error) {
|
||||||
|
if req.Email == "" {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "email обязателен")
|
||||||
|
}
|
||||||
|
|
||||||
|
newID := int64(len(s.users) + 1)
|
||||||
|
user := &pb.GetUserResponse{
|
||||||
|
Id: newID,
|
||||||
|
Name: req.Name,
|
||||||
|
Email: req.Email,
|
||||||
|
CreatedAt: time.Now().Format(time.RFC3339),
|
||||||
|
}
|
||||||
|
s.users[newID] = user
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Слушаем порт
|
||||||
|
lis, err := net.Listen("tcp", ":50051")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("не удалось запустить listener: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаём gRPC-сервер
|
||||||
|
grpcServer := grpc.NewServer()
|
||||||
|
|
||||||
|
// Регистрируем наш сервис
|
||||||
|
pb.RegisterUserServiceServer(grpcServer, &userServer{
|
||||||
|
users: make(map[int64]*pb.GetUserResponse),
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println("gRPC-сервер запущен на :50051")
|
||||||
|
if err := grpcServer.Serve(lis); err != nil {
|
||||||
|
log.Fatalf("ошибка сервера: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 4. Пишем клиент (`client/main.go`)
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
pb "github.com/example/user-service/proto"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Подключаемся к серверу
|
||||||
|
conn, err := grpc.Dial(
|
||||||
|
"localhost:50051",
|
||||||
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("не удалось подключиться: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Создаём клиента — это та самая "магия" gRPC:
|
||||||
|
// вызов метода выглядит как локальная функция
|
||||||
|
client := pb.NewUserServiceClient(conn)
|
||||||
|
|
||||||
|
// Контекст с таймаутом — хорошая практика для каждого запроса
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Создаём пользователя
|
||||||
|
created, err := client.CreateUser(ctx, &pb.CreateUserRequest{
|
||||||
|
Name: "Алиса",
|
||||||
|
Email: "alice@example.com",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ошибка CreateUser: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Создан пользователь: ID=%d, Name=%s\n", created.Id, created.Name)
|
||||||
|
|
||||||
|
// Получаем пользователя по ID
|
||||||
|
user, err := client.GetUser(ctx, &pb.GetUserRequest{Id: created.Id})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ошибка GetUser: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Получен пользователь: %s <%s>\n", user.Name, user.Email)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 5. Запускаем
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Терминал 1: запускаем сервер
|
||||||
|
go run server/main.go
|
||||||
|
# gRPC-сервер запущен на :50051
|
||||||
|
|
||||||
|
# Терминал 2: запускаем клиент
|
||||||
|
go run client/main.go
|
||||||
|
# Создан пользователь: ID=1, Name=Алиса
|
||||||
|
# Получен пользователь: Алиса <alice@example.com>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## gRPC vs REST: когда что выбирать?
|
||||||
|
|
||||||
|
| Критерий | REST | gRPC |
|
||||||
|
|---|---|---|
|
||||||
|
| **Скорость** | Хорошая | Отличная (бинарный протокол) |
|
||||||
|
| **Типизация** | Слабая (JSON) | Строгая (Protobuf) |
|
||||||
|
| **Стриминг** | Ограниченный (SSE) | Полноценный (4 режима) |
|
||||||
|
| **Браузерная поддержка** | Нативная | Нужен gRPC-Web |
|
||||||
|
| **Отладка** | Легко (curl, Postman) | Сложнее (нужен grpcurl) |
|
||||||
|
| **Контракт API** | OpenAPI/Swagger | `.proto` (строже) |
|
||||||
|
| **Кодогенерация** | Опционально | Встроена |
|
||||||
|
|
||||||
|
**Выбирайте gRPC**, когда:
|
||||||
|
- Сервисы общаются только между собой (backend-to-backend)
|
||||||
|
- Важна производительность и объём трафика
|
||||||
|
- Нужен стриминг данных в реальном времени
|
||||||
|
- Команда большая и строгий контракт критичен
|
||||||
|
|
||||||
|
**Выбирайте REST**, когда:
|
||||||
|
- API потребляется браузером напрямую
|
||||||
|
- Важна простота и человекочитаемость
|
||||||
|
- Команда небольшая и overhead Protobuf не оправдан
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Итог
|
||||||
|
|
||||||
|
gRPC — это не просто «быстрый REST». Это принципиально другой подход к межсервисному взаимодействию, где строгий контракт, бинарная эффективность и встроенный стриминг делают его идеальным фундаментом для микросервисных архитектур. Google, Netflix, Cloudflare и десятки других компаний используют его в продакшне под нагрузками, которые REST с JSON попросту не выдержал бы.
|
||||||
|
|
||||||
|
Если вы строите систему из нескольких сервисов — gRPC стоит рассмотреть как первый выбор для их взаимодействия.
|
||||||
1
content/projects/GLIPH.md
Normal file
1
content/projects/GLIPH.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
HELLOW THERE!
|
||||||
1
content/projects/HSE.md
Normal file
1
content/projects/HSE.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Кроме всего прочего я работаю frontend-разработчиком в Высшей Школе Экономики и занимаюсь разработкой системы Решебник
|
||||||
7
docker-compose.yml
Normal file
7
docker-compose.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
services:
|
||||||
|
portfolio:
|
||||||
|
build: .
|
||||||
|
container_name: hugo-portfolio
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8081:80"
|
||||||
0
layouts/partials/google_analytics.html
Normal file
0
layouts/partials/google_analytics.html
Normal file
1
themes/PaperMod
Submodule
1
themes/PaperMod
Submodule
Submodule themes/PaperMod added at 10d3dcc0e0
Reference in New Issue
Block a user