Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,14 +1 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
.DS_Store
14 changes: 14 additions & 0 deletions grpc_example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
45 changes: 45 additions & 0 deletions grpc_example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[package]
name = "grpc_test"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tonic = "0.11.0"
tonic-health = "0.11.0"
prost = "0.12.4"
opentelemetry = { version = "0.22", features = ["metrics"] }
opentelemetry-semantic-conventions = "0.14.0"
opentelemetry-otlp = { version = "0.15.0", features = [
"tonic",
"metrics",
"logs",
] }
opentelemetry-appender-log = { version = "0.3.0", default-features = false }
tracing = { version = "0.1.40", features = [
"max_level_debug",
"release_max_level_info",
] }
tracing-opentelemetry = "0.23.0"
tracing-subscriber = "0.3.18"
serde_json = "1.0.116"
config = "0.14.0"
env_logger = "0.11.3"

opentelemetry_sdk = { version = "0.22", features = [
"rt-tokio",
"logs",
"logs_level_enabled",
] }
tokio = { version = "1.37.0", features = ["macros"] }
#opentelemetry-stdout = { version = "0.3.0", features = ["logs"] }
log = "0.4.21"
function_name = "0.3.0"

[build-dependencies]
tonic-build = "0.11.0"

[[bin]] # Bin to run the Tenant gRPC server
name = "main_grpc_server"
path = "src/main_grpc_server.rs"
55 changes: 55 additions & 0 deletions grpc_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Rust Grpc Example

This project is an example of using gRPC in Rust.
It demonstrates how to implement a gRPC server and client in Rust, and provides a simple example of a service and its corresponding client.


## Table of Contents

- [Technical stack](#technical-stack)
- [Installation](#installation)
- [Usage](#usage)
- [License](#license)

## Technical stack

- [Rust](https://www.rust-lang.org/fr)
- [GRPC](https://grpc.io/)
- [Opentelemetry](https://opentelemetry.io/)
- [Opentelemetry Rust Github](https://github.com/open-telemetry/opentelemetry-rust)

## Installation

You need to have docker installed on your host machine

## Usage

Run telemetry stack with this docker command:

`docker run -p 3000:3000 -p 4317:4317 -p 4318:4318 --rm -ti grafana/otel-lgtm`

Run application with log level info:

`OTEL_SERVICE_NAME=rust_tenant_service cargo run`

After application started, you can call the application GRPC service named `TenantGrpc` with postman with url: [localhost:50501](localhost:50501)
You need to specify [this proto file](proto/tenant.proto)

![Call GRpc](docs/images/postman_call_grpc.png)

You can visualize telemetry with prometheuse stack at [localhost:3000](localhost:3000)

Grafana span:

![Grafana span list example](docs/images/grafana_span_list.png)

![Grafana span trace example](docs/images/grafana_span_trace.png)

Grafana logs:

![Grafana logs example](docs/images/grafana_logs.png)


## License

MIT License.
2 changes: 2 additions & 0 deletions grpc_example/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
otel:
endpoint: "http://localhost:4317"
4 changes: 4 additions & 0 deletions grpc_example/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/tenant.proto")?;
Ok(())
}
57 changes: 57 additions & 0 deletions grpc_example/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
version: '3.8'

services:

prometheus:
image: prom/prometheus
container_name: prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
ports:
- 9090:9090
restart: unless-stopped
volumes:
- ./config/prometheus:/etc/prometheus

grafana:
image: grafana/grafana
container_name: grafana
ports:
- 3000:3000
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=grafana
volumes:
- ./config/grafana:/etc/grafana/provisioning/datasources

# Jaeger
jaeger-all-in-one:
image: jaegertracing/all-in-one:latest
restart: always
ports:
- "16686:16686"

# Zipkin
zipkin:
image: openzipkin/zipkin:latest
environment:
- JAVA_OPTS=-Xms1024m -Xmx1024m -XX:+ExitOnOutOfMemoryError
restart: always
ports:
- "9411:9411"

otel-collector:
image: otel/opentelemetry-collector-contrib
restart: always
command: [ "--config=/etc/otel-collector-config.yaml" ]
volumes:
- ./config/otel-collector/otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "1888:1888" # pprof extension
- "13133:13133" # health_check extension
- "4317:4317" # OTLP gRPC receiver
- "55679:55679" # zpages extension
depends_on:
- jaeger-all-in-one
- zipkin
Binary file added grpc_example/docs/images/grafana_logs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added grpc_example/docs/images/grafana_span_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added grpc_example/docs/images/grafana_span_trace.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added grpc_example/docs/images/postman_call_grpc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions grpc_example/proto/tenant.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
syntax = "proto3";
package tenant_grpc;

// Request
message GetTenantRequest {
string id = 1;
}

message GetTenantListRequest {
}

message CreateTenantRequest {
string name = 1;
}

message UpdateTenantRequest {
string id = 1;
string name = 2;
}

message DeleteTenantRequest {
string id = 1;
}

// Response
message TenantResponse {
string id = 1;
string name = 2;
}

message TenantListResponse {
repeated TenantResponse tenants = 1;
}

message DeleteTenantResponse {
}

service TenantGrpc {
rpc GetTenant (GetTenantRequest) returns (TenantResponse);
rpc GetTenantList (GetTenantListRequest) returns (TenantListResponse);
rpc CreateTenant (CreateTenantRequest) returns (TenantResponse);
rpc UpdateTenant (UpdateTenantRequest) returns (TenantResponse);
rpc DeleteTenant (DeleteTenantRequest) returns (DeleteTenantResponse);
}
14 changes: 14 additions & 0 deletions grpc_example/src/domain/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub mod tenant_service;

pub struct TenantBO {
pub id: String,
pub name: String,
}

pub trait ITenantService: Send + Sync + 'static {
fn get_tenant(&self) -> TenantBO;
fn get_tenant_list(&self) -> Vec<TenantBO>;
fn create_tenant(&self) -> TenantBO;
fn update_tenant(&self) -> TenantBO;
fn delete_tenant(&self);
}
43 changes: 43 additions & 0 deletions grpc_example/src/domain/tenant_service.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::domain::{ITenantService, TenantBO};

pub struct TenantService {}

impl ITenantService for TenantService {
fn get_tenant(&self) -> TenantBO {
let tenant = TenantBO {
id: String::from("1"),
name: String::from("name 1"),
};
tenant
}

fn get_tenant_list(&self) -> Vec<TenantBO> {
let mut tenant_list = Vec::new();
for i in 0..=10 {
let tenant = TenantBO {
id: i.to_string(),
name: format!("name {}", i),
};
tenant_list.push(tenant);
}
tenant_list
}

fn create_tenant(&self) -> TenantBO {
let tenant = TenantBO {
id: String::from("1"),
name: String::from("name 1"),
};
tenant
}

fn update_tenant(&self) -> TenantBO {
let tenant = TenantBO {
id: String::from("1"),
name: String::from("name 1"),
};
tenant
}

fn delete_tenant(&self) {}
}
30 changes: 30 additions & 0 deletions grpc_example/src/grpc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use std::net::SocketAddr;

use self::{
models::tenant_grpc::tenant_grpc_server::TenantGrpcServer,
tenant_grpc_service::TenantGrpcService,
};

mod models;
pub mod tenant_grpc_service;
pub mod tenant_mapper;

#[derive(Debug)]
pub struct OtlpConfig {
pub endpoint: String,
}

#[allow(dead_code)]
pub async fn start_grpc_server(addr: SocketAddr) -> Result<(), Box<dyn std::error::Error>> {
let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
health_reporter
.set_serving::<TenantGrpcServer<TenantGrpcService>>()
.await;

tonic::transport::Server::builder()
.add_service(tenant_grpc_service::create_grpc_service())
.add_service(health_service)
.serve(addr)
.await?;
Ok(())
}
4 changes: 4 additions & 0 deletions grpc_example/src/grpc/models.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Import the generated proto-rust file into a module
pub(crate) mod tenant_grpc {
tonic::include_proto!("tenant_grpc");
}
Loading