375 lines
8.7 KiB
Markdown
375 lines
8.7 KiB
Markdown
# Руководство по созданию микросервиса
|
||
|
||
## 🚀 Быстрый старт
|
||
|
||
### Шаг 1: Скопировать шаблон
|
||
|
||
```bash
|
||
cp -r microservices/template_service microservices/my_new_service
|
||
cd microservices/my_new_service
|
||
```
|
||
|
||
### Шаг 2: Выбрать порт
|
||
|
||
Выберите порт из диапазона **13000-14000**. Уже занятые:
|
||
- 13001 - User Service
|
||
|
||
Пусть ваш сервис будет на порту **13002**.
|
||
|
||
### Шаг 3: Создать proto файл
|
||
|
||
Создайте `shared_proto/proto/my_service.proto`:
|
||
|
||
```protobuf
|
||
syntax = "proto3";
|
||
|
||
package my_service;
|
||
|
||
message GetDataRequest {
|
||
string id = 1;
|
||
}
|
||
|
||
message Data {
|
||
string id = 1;
|
||
string content = 2;
|
||
}
|
||
|
||
service MyService {
|
||
rpc GetData(GetDataRequest) returns (Data);
|
||
}
|
||
```
|
||
|
||
### Шаг 4: Обновить shared_proto/src/lib.rs
|
||
|
||
Добавьте:
|
||
|
||
```rust
|
||
pub mod my_service {
|
||
tonic::include_proto!("my_service");
|
||
}
|
||
```
|
||
|
||
### Шаг 5: Обновить shared_proto/build.rs
|
||
|
||
Добавьте:
|
||
|
||
```rust
|
||
tonic_build::compile_protos("proto/my_service.proto")?;
|
||
```
|
||
|
||
### Шаг 6: Обновить microservices/my_new_service/Cargo.toml
|
||
|
||
```toml
|
||
[package]
|
||
name = "my_new_service"
|
||
version.workspace = true
|
||
edition.workspace = true
|
||
authors.workspace = true
|
||
|
||
[dependencies]
|
||
tokio = { version = "1.35", features = ["full"] }
|
||
axum = "0.7"
|
||
serde = { version = "1.0", features = ["derive"] }
|
||
serde_json = "1.0"
|
||
tower = "0.4"
|
||
tower-http = { version = "0.5", features = ["trace"] }
|
||
tracing = "0.1"
|
||
tracing-subscriber = "0.3"
|
||
tonic = "0.11"
|
||
prost = "0.12"
|
||
shared_proto = { path = "../../shared_proto" }
|
||
```
|
||
|
||
### Шаг 7: Реализовать сервис
|
||
|
||
Обновите `microservices/my_new_service/src/main.rs`:
|
||
|
||
```rust
|
||
use tonic::{Request, Response, Status, transport::Server};
|
||
use shared_proto::my_service::{
|
||
my_service_server::{MyService, MyServiceServer},
|
||
GetDataRequest, Data,
|
||
};
|
||
use tracing_subscriber;
|
||
|
||
pub struct MyServiceImpl;
|
||
|
||
#[tonic::async_trait]
|
||
impl MyService for MyServiceImpl {
|
||
async fn get_data(
|
||
&self,
|
||
request: Request<GetDataRequest>,
|
||
) -> Result<Response<Data>, Status> {
|
||
let id = request.into_inner().id;
|
||
tracing::info!("GetData called with id: {}", id);
|
||
|
||
Ok(Response::new(Data {
|
||
id: id.clone(),
|
||
content: format!("Data for {}", id),
|
||
}))
|
||
}
|
||
}
|
||
|
||
#[tokio::main]
|
||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||
tracing_subscriber::fmt::init();
|
||
|
||
let addr = "127.0.0.1:13002".parse()?;
|
||
let my_service = MyServiceImpl;
|
||
|
||
tracing::info!("MyService gRPC server listening on {}", addr);
|
||
|
||
Server::builder()
|
||
.add_service(MyServiceServer::new(my_service))
|
||
.serve(addr)
|
||
.await?;
|
||
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
### Шаг 8: Добавить в workspace
|
||
|
||
Обновите корневой `Cargo.toml`:
|
||
|
||
```toml
|
||
[workspace]
|
||
members = [
|
||
"api_gateway",
|
||
"api",
|
||
"microservices/user_service",
|
||
"microservices/my_new_service",
|
||
"shared_proto"
|
||
]
|
||
```
|
||
|
||
### Шаг 9: Скомпилировать
|
||
|
||
```bash
|
||
cargo check
|
||
# или
|
||
cargo build
|
||
```
|
||
|
||
## 📚 Примеры
|
||
|
||
### Пример 1: Сервис продуктов
|
||
|
||
**shared_proto/proto/product.proto:**
|
||
```protobuf
|
||
syntax = "proto3";
|
||
|
||
package product;
|
||
|
||
message Product {
|
||
int32 id = 1;
|
||
string name = 2;
|
||
float price = 3;
|
||
}
|
||
|
||
message GetProductRequest {
|
||
int32 id = 1;
|
||
}
|
||
|
||
message CreateProductRequest {
|
||
string name = 1;
|
||
float price = 2;
|
||
}
|
||
|
||
service ProductService {
|
||
rpc GetProduct(GetProductRequest) returns (Product);
|
||
rpc CreateProduct(CreateProductRequest) returns (Product);
|
||
}
|
||
```
|
||
|
||
**microservices/product_service/src/main.rs:**
|
||
```rust
|
||
use tonic::{Request, Response, Status, transport::Server};
|
||
use shared_proto::product::{
|
||
product_service_server::{ProductService, ProductServiceServer},
|
||
GetProductRequest, Product, CreateProductRequest,
|
||
};
|
||
use tracing_subscriber;
|
||
|
||
pub struct ProductServiceImpl;
|
||
|
||
#[tonic::async_trait]
|
||
impl ProductService for ProductServiceImpl {
|
||
async fn get_product(
|
||
&self,
|
||
request: Request<GetProductRequest>,
|
||
) -> Result<Response<Product>, Status> {
|
||
let id = request.into_inner().id;
|
||
|
||
Ok(Response::new(Product {
|
||
id,
|
||
name: "Product".to_string(),
|
||
price: 99.99,
|
||
}))
|
||
}
|
||
|
||
async fn create_product(
|
||
&self,
|
||
request: Request<CreateProductRequest>,
|
||
) -> Result<Response<Product>, Status> {
|
||
let req = request.into_inner();
|
||
|
||
Ok(Response::new(Product {
|
||
id: 1,
|
||
name: req.name,
|
||
price: req.price,
|
||
}))
|
||
}
|
||
}
|
||
|
||
#[tokio::main]
|
||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||
tracing_subscriber::fmt::init();
|
||
|
||
let addr = "127.0.0.1:13002".parse()?;
|
||
let service = ProductServiceImpl;
|
||
|
||
tracing::info!("Product Service listening on {}", addr);
|
||
|
||
Server::builder()
|
||
.add_service(ProductServiceServer::new(service))
|
||
.serve(addr)
|
||
.await?;
|
||
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
### Пример 2: Сервис заказов
|
||
|
||
Использует User Service и Product Service:
|
||
|
||
**microservices/order_service/src/main.rs:**
|
||
```rust
|
||
use tonic::transport::Channel;
|
||
use shared_proto::order::{
|
||
order_service_server::{OrderService, OrderServiceServer},
|
||
CreateOrderRequest, Order,
|
||
};
|
||
use shared_proto::user::user_service_client::UserServiceClient;
|
||
use shared_proto::product::product_service_client::ProductServiceClient;
|
||
|
||
pub struct OrderServiceImpl {
|
||
user_client: UserServiceClient<Channel>,
|
||
product_client: ProductServiceClient<Channel>,
|
||
}
|
||
|
||
#[tonic::async_trait]
|
||
impl OrderService for OrderServiceImpl {
|
||
async fn create_order(
|
||
&self,
|
||
request: tonic::Request<CreateOrderRequest>,
|
||
) -> Result<tonic::Response<Order>, tonic::Status> {
|
||
let req = request.into_inner();
|
||
|
||
// Вызвать User Service
|
||
// Вызвать Product Service
|
||
// Создать заказ
|
||
|
||
Ok(tonic::Response::new(Order {
|
||
id: 1,
|
||
user_id: req.user_id,
|
||
product_id: req.product_id,
|
||
}))
|
||
}
|
||
}
|
||
|
||
#[tokio::main]
|
||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||
tracing_subscriber::fmt::init();
|
||
|
||
let user_channel = Channel::from_static("http://127.0.0.1:13001")
|
||
.connect()
|
||
.await?;
|
||
let product_channel = Channel::from_static("http://127.0.0.1:13002")
|
||
.connect()
|
||
.await?;
|
||
|
||
let user_client = UserServiceClient::new(user_channel);
|
||
let product_client = ProductServiceClient::new(product_channel);
|
||
|
||
let order_service = OrderServiceImpl {
|
||
user_client,
|
||
product_client,
|
||
};
|
||
|
||
let addr = "127.0.0.1:13003".parse()?;
|
||
tracing::info!("Order Service listening on {}", addr);
|
||
|
||
tonic::transport::Server::builder()
|
||
.add_service(OrderServiceServer::new(order_service))
|
||
.serve(addr)
|
||
.await?;
|
||
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
## 🔄 Интеграция с API Gateway
|
||
|
||
После создания сервиса, обновите `api_gateway/src/main.rs` для добавления REST endpoints:
|
||
|
||
```rust
|
||
// Создать gRPC клиента для вашего сервиса
|
||
let my_service_channel = Channel::from_static("http://127.0.0.1:13002")
|
||
.connect()
|
||
.await?;
|
||
|
||
// Добавить REST endpoints
|
||
.route("/api/data/:id", get(get_data_handler))
|
||
```
|
||
|
||
## ✅ Чек-лист
|
||
|
||
- [ ] Скопирован шаблон сервиса
|
||
- [ ] Выбран порт (13000-14000)
|
||
- [ ] Создан proto файл
|
||
- [ ] Обновлен `shared_proto/src/lib.rs`
|
||
- [ ] Обновлен `shared_proto/build.rs`
|
||
- [ ] Обновлен `Cargo.toml` микросервиса
|
||
- [ ] Реализована логика сервиса
|
||
- [ ] Добавлен в workspace `Cargo.toml`
|
||
- [ ] Успешно компилируется (`cargo check`)
|
||
- [ ] Добавлены тесты (опционально)
|
||
|
||
## 🐛 Отладка
|
||
|
||
### Проверить что сервис запускается
|
||
|
||
```bash
|
||
cargo run -p my_new_service
|
||
```
|
||
|
||
Должно вывести:
|
||
```
|
||
MyService gRPC server listening on 127.0.0.1:13002
|
||
```
|
||
|
||
### Проверить gRPC endpoint
|
||
|
||
```bash
|
||
# Установить grpcurl если не установлен
|
||
# https://github.com/fullstorydev/grpcurl
|
||
|
||
grpcurl -plaintext localhost:13002 list
|
||
```
|
||
|
||
### Логирование
|
||
|
||
```bash
|
||
RUST_LOG=debug cargo run -p my_new_service
|
||
```
|
||
|
||
## 📞 Помощь
|
||
|
||
Если возникли проблемы:
|
||
1. Проверьте что все proto файлы скомпилировались
|
||
2. Убедитесь что порт не занят другим процессом
|
||
3. Проверьте логи сервиса
|
||
4. Обратитесь к `ARCHITECTURE.md` для общей информации
|