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