From 5df3db3962950b74d40c371fcf2358fc02cf6785 Mon Sep 17 00:00:00 2001 From: Andrea Cavallo <67713825+Andrea-Cavallo@users.noreply.github.com> Date: Sat, 17 Aug 2024 10:55:02 +0200 Subject: [PATCH] fixed readme, migliorata struttura --- README.md | 288 +++++++++++++++----- changelog.md | 27 ++ docker/docker-compose.yml | 9 - docker/scripts/api/protos/temperature.proto | 29 -- docker/scripts/grpc-test.js | 30 -- docker/qodana.yaml => qodana.yaml | 0 temperature_grpc_client/client.md | 213 +++++++++++++++ temperature_grpc_client/service/client.md | 97 ------- temperature_grpc_server/server.md | 217 +++++++++++++++ temperature_grpc_server/service/server.md | 101 ------- 10 files changed, 675 insertions(+), 336 deletions(-) create mode 100644 changelog.md delete mode 100644 docker/scripts/api/protos/temperature.proto delete mode 100644 docker/scripts/grpc-test.js rename docker/qodana.yaml => qodana.yaml (100%) create mode 100644 temperature_grpc_client/client.md delete mode 100644 temperature_grpc_client/service/client.md create mode 100644 temperature_grpc_server/server.md delete mode 100644 temperature_grpc_server/service/server.md diff --git a/README.md b/README.md index d37e198..e0192a8 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,79 @@ -# Super cool Go con GRPC - - - +# Real-Time Temperature Monitoring with Go and gRPC + +## Introduzione + +Questo progetto dimostra l'integrazione di servizi gRPC con il linguaggio Go, +fornendo un esempio completo di un server gRPC che serve dati di temperatura e un client gRPC che li consuma, +il tutto con una gestione avanzata delle metriche, connessione a MongoDB per persistere i dati, funzionalità di allerta tramite Twilio. +Il progetto utilizza Docker per facilitare l'impostazione dell'ambiente, inclusi la configurazione di MongoDB e altre dipendenze tipo jaeger. + + + +[Click here for the English version](#english-version) + +# Struttura del progetto + + +```text +├── api --> qua dentro il file .proto +│ ├── protos +│ │ ├── temperature.proto +│ ├── api.md +├── docker ---> tutto cio' relativo a docker +│ ├── docker-compose.yml +│ ├── init-mongo.js +│ ├── loki-config.yml +│ ├── otel-collector-config.yaml +│ ├── prometheus.yml +├── extra --> immagini del progetto +├── pkg --> pkg tutto quello che deve essere visibile e condiviso +│ ├── telemetry +│ ├── temperature +├── temperature_grpc_client -> il Client GRPC +│ ├── cmd +│ ├── plot +│ ├── service +│ ├── client.md +├── temperature_grpc_server --> il Server GRPC +│ ├── alert_twilio +│ │ ├── alert.go +│ ├── cmd +│ │ ├── server +│ │ │ ├── main.go +│ ├── mongodb --> connessione a MONGODB +│ │ ├── config +│ │ ├── temperature_repo +│ ├── service +│ │ ├── server.go +│ ├── server.md +├── .editorconfig +├── .gitignore +├── changelog.md +├── go.mod +├── go.sum +``` -## Esecuzione del Progetto +# Esecuzione del Progetto -### 1. Prerequisiti +## 1. Prerequisiti -Prima di eseguire il progetto, è necessario ottenere una chiave API per utilizzare il servizio WeatherAPI. Questo servizio è completamente gratuito, e puoi ottenere la tua chiave API seguendo questi passi: +Prima di eseguire il progetto, è necessario ottenere una chiave API per utilizzare il servizio WeatherAPI. Questo servizio è completamente gratuito e puoi ottenere la tua chiave API seguendo questi passi: 1. Visita il sito [WeatherAPI](https://www.weatherapi.com/). 2. Registrati e segui le istruzioni per ottenere la tua chiave API. 3. Una volta ottenuta, salva la chiave API in un luogo sicuro. -### 2. Configurare l'API Key come Variabile d'Ambiente +## 2. Configurare l'API Key come Variabile d'Ambiente Per mantenere la tua chiave API sicura e fuori dal codice sorgente, configurala come una variabile d'ambiente. Segui questi passi: -#### Su Linux/MacOS: +### Su Linux/MacOS: ```bash export WEATHER_API_KEY=la-tua-api-key ``` -#### Su Windows (nel terminale PowerShell): +### Su Windows (nel terminale PowerShell): ```powershell $env:WEATHER_API_KEY="la-tua-api-key" @@ -31,61 +81,75 @@ $env:WEATHER_API_KEY="la-tua-api-key" Assicurati che la variabile d'ambiente `WEATHER_API_KEY` sia impostata correttamente prima di procedere. -### 3. Build del docker-compose.yml +## 3. Build del Docker Compose + +Assicurati di avere Docker installato e di avere accesso al file `docker-compose.yml`: -- Assicurarsi di ditar su docker -- docker/docker-compose.yml +- Posizionati nella directory che contiene `docker-compose.yml` +## 4. Opzionale - Configurazione di Twilio -### 4. Opzionale +Se desideri inviare avvisi tramite Twilio, segui i seguenti passi per creare un account su Twilio e configurare le variabili d'ambiente: +Consulta: [Twilio WhatsApp Quickstart (Go)](https://www.twilio.com/docs/whatsapp/quickstart/go) + +Configurare le seguenti variabili d'ambiente: + +```bash +export TWILIO_ACCOUNT_SID=il-tuo-account-sid +export TWILIO_AUTH_TOKEN=il-tuo-auth-token +export TWILIO_PHONE_NUMBER=il-tuo-numero-di-telefono +export ALERT_PHONE_NUMBER=il-numero-di-telefono-per-gli-avvisi +``` -### 3. Eseguire il Server +## 5. Eseguire il Server Una volta configurata la chiave API, puoi avviare il server: 1. Naviga alla directory `temperature_grpc_server`: - ```bash - cd temperature_grpc_server/cmd/server - ``` + ```bash + cd temperature_grpc_server/cmd/server + ``` 2. Esegui il server: - ```bash - go run main.go - ``` + ```bash + go run main.go + ``` - Il server inizierà ad ascoltare le richieste gRPC. +Il server inizierà ad ascoltare le richieste gRPC. -### 4. Eseguire il Client +## 6. Eseguire il Client Con il server in esecuzione, ora puoi avviare il client per inviare richieste al server: 1. Apri un nuovo terminale e naviga alla directory `temperature_grpc_client`: - ```bash - cd temperature_grpc_client/cmd/client - ``` + ```bash + cd temperature_grpc_client/cmd/client + ``` 2. Esegui il client: - ```bash - go run main.go - ``` + ```bash + go run main.go + ``` - Il client invierà una richiesta gRPC al server e riceverà una risposta con i dati di temperatura. +Il client invierà una richiesta gRPC al server e riceverà una risposta con i dati di temperatura. +## Grafico della Temperatura + +Ogni volta che arresti l'applicazione, in particolare il client, vedrai disegnato un grafico che mostrerà la variazione della temperatura. + +![Grafico della Temperatura](./temp_graph.png) ## Per il dettaglio del client e del server - temperature_grpc_client/client.md - temperature_grpc_server/server.md - -## Approfondimenti sulla tecnologia scelta: - - +## Approfondimenti sulla tecnologia scelta gRPC può essere considerato un successore di RPC, ed è leggero in termini di peso. Google lo ha sviluppato per la comunicazione tra microservizi e altri sistemi che necessitano di interagire. Ci sono diversi vantaggi nell'usare gRPC. @@ -104,55 +168,139 @@ Oltre ai vantaggi chiave menzionati sopra, gRPC promuove un design migliore per gRPC è circa sette volte più veloce di REST nel ricevere dati e circa dieci volte più veloce di REST nel trasmettere dati per un payload specifico. Questo è principalmente dovuto al pacchettamento compatto dei Protocol Buffers e all'uso di HTTP/2 da parte di gRPC. -## Tipi di RPC -gRPC supporta quattro tipi di metodi di servizio: +## Autore -1. **RPC Unario**: il client invia una singola richiesta e ottiene una singola risposta, come una chiamata a funzione normale. -```protobuf -rpc SayHello(HelloRequest) returns (HelloResponse); -``` +Progetto sviluppato da [Andrea Cavallo]. -2. **RPC con Streaming Server**: il client invia una richiesta e riceve un flusso di risposte. -```protobuf -rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse); -``` +## Licenza + +Questo progetto è distribuito sotto la licenza Apache 2.0. Vedi il file LICENSE per ulteriori dettagli. -3. **RPC con Streaming Client**: il client invia un flusso di richieste e riceve una singola risposta. -```protobuf -rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse); +# English Version + +# Super cool Go with GRPC + + + +# Project Execution + +## 1. Prerequisites + +Before running the project, you need to obtain an API key to use the WeatherAPI service. This service is completely free, and you can get your API key by following these steps: + +1. Visit the [WeatherAPI](https://www.weatherapi.com/) website. +2. Sign up and follow the instructions to get your API key. +3. Once obtained, save the API key in a safe place. + +## 2. Configure the API Key as an Environment Variable + +To keep your API key secure and out of the source code, configure it as an environment variable. Follow these steps: + +### On Linux/MacOS: + +```bash +export WEATHER_API_KEY=your-api-key ``` -4. **RPC con Streaming Bidirezionale**: entrambe le parti inviano flussi di messaggi. -```protobuf -rpc BidiHello(stream HelloRequest) returns (stream HelloResponse); +### On Windows (in PowerShell): + +```powershell +$env:WEATHER_API_KEY="your-api-key" ``` -## Utilizzo dell'API -### Lato Server -Il server implementa i metodi dichiarati dal servizio e gestisce le chiamate client utilizzando un server gRPC. La struttura gRPC decodifica le richieste in arrivo, esegue i metodi di servizio e codifica le risposte. +Ensure the `WEATHER_API_KEY` environment variable is set correctly before proceeding. + +## 3. Docker Compose Build -### Lato Client -Il client utilizza un oggetto locale noto come "stub" che implementa gli stessi metodi del servizio. Chiamando questi metodi, i parametri vengono invocati nel tipo di messaggio specificato dal protocol buffer e inviati al server, che a sua volta risponde con i messaggi corrispondenti. +Ensure you have Docker installed and access to the `docker-compose.yml` file: -## Sincrono vs Asincrono -Le chiamate RPC sincrone bloccano fino all'arrivo della risposta. Tuttavia, molte situazioni richiedono l'avvio di RPC senza bloccare il thread corrente. gRPC fornisce API sia sincrone che asincrone. +- Navigate to the directory that contains `docker-compose.yml`. -## Deadline/Timeout -gRPC consente ai client di specificare quanto tempo sono disposti ad attendere una risposta, dopo di che l'RPC viene terminato con un errore DEADLINE_EXCEEDED. Il server può verificare se l'RPC è scaduto. +## 4. Optional - Twilio Configuration -## Terminazione e Cancellazione di un RPC -Client e server determinano localmente il successo di una chiamata, che potrebbe non corrispondere. Entrambe le parti possono annullare una chiamata RPC in qualsiasi momento. +If you wish to send alerts via Twilio, follow these steps to create a Twilio account and set up the environment variables: -## Metadata -La metadata sono informazioni su una chiamata RPC sotto forma di coppie chiave-valore. gRPC consente la gestione di metadata definiti dall'utente senza influenzare il funzionamento di base. +Consult: [Twilio WhatsApp Quickstart (Go)](https://www.twilio.com/docs/whatsapp/quickstart/go) -## Canali -Un canale gRPC fornisce una connessione a un server gRPC su un host e una porta specificati. Viene utilizzato per creare un client stub. I client possono specificare argomenti del canale per modificare il comportamento predefinito. +Set the following environment variables: -Ad esempio: -```go -conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure()) +```bash +export TWILIO_ACCOUNT_SID=your-account-sid +export TWILIO_AUTH_TOKEN=your-auth-token +export TWILIO_PHONE_NUMBER=your-phone-number +export ALERT_PHONE_NUMBER=alert-phone-number ``` -https://signoz.io/blog/opentelemetry-grpc-golang/ + +## 5. Running the Server + +Once the API key is configured, you can start the server: + +1. Navigate to the `temperature_grpc_server` directory: + + ```bash + cd temperature_grpc_server/cmd/server + ``` + +2. Start the server: + + ```bash + go run main.go + ``` + +The server will start listening for gRPC requests. + +## 6. Running the Client + +With the server running, you can now start the client to send requests to the server: + +1. Open a new terminal and navigate to the `temperature_grpc_client` directory: + + ```bash + cd temperature_grpc_client/cmd/client + ``` + +2. Start the client: + + ```bash + go run main.go + ``` + +The client will send a gRPC request to the server and receive a response with the temperature data. + +## Temperature Graph + +Whenever you shut down the application, especially the client, you will see a graph showing the temperature variation. + +![Temperature Graph](./temp_graph.png) + +## Detailed Client and Server Documentation + +- temperature_grpc_client/client.md +- temperature_grpc_server/server.md + +## Insights into the Chosen Technology + +gRPC can be considered a successor to RPC and is lightweight. Google developed it for communication between microservices and other systems that need to interact. There are several advantages to using gRPC. + +## Advantages of gRPC + +- **Uses Protocol Buffers (Protobuf)** instead of JSON. +- **Built on HTTP/2** instead of HTTP 1.1. +- **Built-in Code Generation**. +- **High Performance**. +- **SSL Security**. + +In addition to the key advantages mentioned above, gRPC promotes better design for your application. gRPC is API-oriented, unlike REST, which is resource-oriented. It is also asynchronous by default, meaning it does not block the thread on request and can serve millions of requests in parallel, ensuring high scalability. + +## Advantages of gRPC over REST + +gRPC is about seven times faster than REST in receiving data and about ten times faster than REST in transmitting data for a specific payload. This is primarily due to the compact packing of Protocol Buffers and the use of HTTP/2 by gRPC. + + ## Author -Andrea Cavallo + +Project developed by [Andrea Cavallo]. + +## License + +This project is licensed under the Apache 2.0 License. See the LICENSE file for more details. diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..ff7c399 --- /dev/null +++ b/changelog.md @@ -0,0 +1,27 @@ +# Changelog + +Tutte le modifiche rilevanti apportate a questo progetto saranno documentate in questo file. + +## [1.0.0] - 2024-08-17 + +### Aggiunto +- Integrazione di gRPC per la comunicazione tra client e server con supporto per i quattro tipi di RPC: Unario, Streaming Server, Streaming Client, e Streaming Bidirezionale. +- Implementazione del servizio `TemperatureService` per ottenere i dati della temperatura attuale di una località specificata. +- Configurazione del server gRPC e del client gRPC per inviare e ricevere richieste gRPC. +- Supporto per la creazione di grafici della temperatura alla chiusura del client. +- Documentazione dettagliata per l'installazione e l'uso del progetto, inclusa la configurazione di chiavi API e variabili d'ambiente. +- Supporto per l'invio di avvisi tramite Twilio con configurazione opzionale. +- Docker Compose configurato con servizi per MongoDB, Jaeger, OpenTelemetry Collector, Prometheus, Grafana, Loki, Node Exporter, cAdvisor, e K6 per il test delle prestazioni. +- Integrazione con Prometheus per la raccolta delle metriche e Grafana per la visualizzazione delle stesse. +- Supporto per tracing distribuito con Jaeger tramite OpenTelemetry. + +### Modificato +- Aggiornamento della documentazione per includere dettagli sull'uso di Grafana con credenziali predefinite (`admin/admin`). +- Miglioramento della configurazione Docker Compose per una maggiore stabilità e facilità di utilizzo. + +### Risolto +- Problemi di connessione tra client e server gRPC ora risolti, garantendo un flusso di dati stabile e continuo. +- Risolto un problema con la mancata individuazione del file `.proto` da parte di K6 durante l'esecuzione dei test delle prestazioni. + +### Rilascio Iniziale +- Rilascio iniziale del progetto con tutte le funzionalità di base per il monitoraggio delle temperature utilizzando gRPC e l'integrazione di strumenti di osservabilità. diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 0c1dc86..e7c3a47 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -79,15 +79,6 @@ services: - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro - k6: - image: loadimpact/k6:latest - container_name: k6 - volumes: - - ./scripts:/scripts - - ./api/protos:/api/protos # Monta la directory corretta - entrypoint: ["k6", "run", "/scripts/grpc-test.js"] - depends_on: - - jaeger volumes: mongo_data: diff --git a/docker/scripts/api/protos/temperature.proto b/docker/scripts/api/protos/temperature.proto deleted file mode 100644 index 1203963..0000000 --- a/docker/scripts/api/protos/temperature.proto +++ /dev/null @@ -1,29 +0,0 @@ -syntax = "proto3"; - -package temperature; - -option go_package = "go_with_grpc/pkg/temperature"; - - -// Messaggio per rappresentare una lettura della temperatura -message TemperatureReading { - string location = 1; // Nome della città o woeid - double temperature = 2; // Temperatura in gradi Celsius - int64 timestamp = 3; // Timestamp Unix della lettura -} - -// Richiesta per ottenere la temperatura attuale in questo caso prende in ingresso solo la citta -message GetCurrentTemperatureRequest { - string location = 1; -} - -// Risposta per ottenere la temperatura attuale -message GetCurrentTemperatureResponse { - TemperatureReading reading = 1; // Lettura della temperatura attuale -} - - -// Definizione del servizio gRPC -service TemperatureService { - rpc GetCurrentTemperature(GetCurrentTemperatureRequest) returns (GetCurrentTemperatureResponse); -} diff --git a/docker/scripts/grpc-test.js b/docker/scripts/grpc-test.js deleted file mode 100644 index 33aebeb..0000000 --- a/docker/scripts/grpc-test.js +++ /dev/null @@ -1,30 +0,0 @@ -import grpc from 'k6/net/grpc'; -import { check, sleep } from 'k6'; - -const client = new grpc.Client(); -client.load(['api/protos'], 'temperature.proto'); // Percorso relativo dentro il container - -export let options = { - stages: [ - { duration: '30s', target: 10 }, - { duration: '1m', target: 10 }, - { duration: '10s', target: 0 }, - ], -}; - -export default function () { - client.connect('localhost:50051', { - plaintext: true, - }); - - const response = client.invoke('temperature.TemperatureService/GetCurrentTemperature', { - location: 'Rome', - }); - - check(response, { - 'status is OK': (r) => r.status === grpc.StatusOK, - }); - - client.close(); - sleep(1); -} diff --git a/docker/qodana.yaml b/qodana.yaml similarity index 100% rename from docker/qodana.yaml rename to qodana.yaml diff --git a/temperature_grpc_client/client.md b/temperature_grpc_client/client.md new file mode 100644 index 0000000..2534054 --- /dev/null +++ b/temperature_grpc_client/client.md @@ -0,0 +1,213 @@ +# gRPC Temperature Client + +Il client gRPC per il polling delle temperature utilizza il servizio `TemperatureService` per ottenere le temperature correnti di una località specificata. Questo client è configurato per eseguire il polling a intervalli regolari e può essere interrotto in modo sicuro tramite segnali di sistema. + +[Click here for the English version](#english-version) + +## Requisiti + +- Go 1.22 o superiore +- Chiave API di WeatherAPI configurata come variabile d'ambiente (`WEATHER_API_KEY`) + +## Configurazione + +Prima di eseguire il client, assicurati di avere configurato correttamente la chiave API di WeatherAPI come descritto nella guida di configurazione. + +## Esecuzione del Client + +Per eseguire il client, segui questi passaggi: + +1. Prima di lanciare il client, assicurati di aver avviato il server: + + ```bash + cd temperature_grpc_server/cmd/server + go run main.go + ``` + +2. Apri un terminale e naviga alla directory `temperature_grpc_client`: + + ```bash + cd temperature_grpc_client/cmd/client + ``` + +3. Esegui il client: + + ```bash + go run main.go + ``` + +La chiusura del client (premendo `Ctrl+C`) farà sì che il programma disegni un grafico con la variazione delle temperature registrate durante l'esecuzione, tramite il file `plot.go`. + +## Funzionamento del Codice + +Il codice del client esegue le seguenti operazioni: + +1. **Configurazione**: + - Impostazione dell'indirizzo del server gRPC (`grpcServerAddress`). + - Definizione dell'intervallo di polling (`pollingInterval`). + - Specificazione della località per il polling delle temperature (`location`). + + ```go + const ( + grpcServerAddress = "localhost:50051" + pollingInterval = 10 * time.Second + location = "Rome" + ) + ``` + +2. **Gestione dei Segnali di Interruzione**: + - Creazione di un contesto che gestisce i segnali di interruzione (es. `SIGTERM` e `os.Interrupt`) per consentire una chiusura sicura del processo. + + ```go + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + ``` + +3. **Avvio del Polling**: + - Invocazione della funzione `startPolling(ctx)` che avvia il polling delle temperature. + + ```go + startPolling(ctx) + ``` + +4. **Funzione di Polling**: + - Creazione del client gRPC utilizzando la funzione `NewClient`. + - Configurazione di un ticker per eseguire il polling a intervalli regolari. + - Esecuzione del polling finché il contesto non viene cancellato, interrompendo il polling in modo sicuro quando viene ricevuto un segnale di interruzione. + + ```go + func startPolling(ctx context.Context) { + client, err := service.NewClient(grpcServerAddress) + if err != nil { + log.Fatalf("Errore nella creazione del client gRPC: %v", err) + } + + ticker := time.NewTicker(pollingInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + log.Println("Interruzione ricevuta, fermo il polling.") + return + case <-ticker.C: + log.Println("Polling temperatures...") + client.GetCurrentTemperature(location) + } + } + } + ``` + +## Interruzione del Client + +Per interrompere il client: + +- Premere `Ctrl+C` nel terminale in cui il client è in esecuzione. + +Il client gestirà l'interruzione in modo sicuro, annullando il polling, terminando il processo e disegnando un grafico con la variazione delle temperature tramite `plot.go`. + +# English Version + +The gRPC client for polling temperatures uses the `TemperatureService` to get current temperatures for a specified location. This client is configured to poll at regular intervals and can be safely interrupted via system signals. + +## Requirements + +- Go 1.22 or higher +- WeatherAPI API key configured as an environment variable (`WEATHER_API_KEY`) + +## Configuration + +Before running the client, ensure that the WeatherAPI API key is configured correctly as described in the setup guide. + +## Running the Client + +To run the client, follow these steps: + +1. Before launching the client, make sure to start the server: + + ```bash + cd temperature_grpc_server/cmd/server + go run main.go + ``` + +2. Open a terminal and navigate to the `temperature_grpc_client` directory: + + ```bash + cd temperature_grpc_client/cmd/client + ``` + +3. Run the client: + + ```bash + go run main.go + ``` + +Stopping the client (by pressing `Ctrl+C`) will cause the program to draw a graph with the temperature variations recorded during the application's runtime, using the `plot.go`. + +## Code Operation + +The client code performs the following operations: + +1. **Configuration**: + - Setting the gRPC server address (`grpcServerAddress`). + - Defining the polling interval (`pollingInterval`). + - Specifying the location for temperature polling (`location`). + + ```go + const ( + grpcServerAddress = "localhost:50051" + pollingInterval = 10 * time.Second + location = "Rome" + ) + ``` + +2. **Handling Interrupt Signals**: + - Creating a context that handles interrupt signals (e.g., `SIGTERM` and `os.Interrupt`) to allow safe process shutdown. + + ```go + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + ``` + +3. **Start Polling**: + - Invoking the `startPolling(ctx)` function that starts temperature polling. + + ```go + startPolling(ctx) + ``` + +4. **Polling Function**: + - Creating the gRPC client using the `NewClient` function. + - Setting up a ticker to perform polling at regular intervals. + - Performing polling until the context is canceled, safely stopping polling when an interrupt signal is received. + + ```go + func startPolling(ctx context.Context) { + client, err := service.NewClient(grpcServerAddress) + if err != nil { + log.Fatalf("Error creating the gRPC client: %v", err) + } + + ticker := time.NewTicker(pollingInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + log.Println("Interrupt signal received, stopping polling.") + return + case <-ticker.C: + log.Println("Polling temperatures...") + client.GetCurrentTemperature(location) + } + } + } + ``` + +## Stopping the Client + +To stop the client: + +- Press `Ctrl+C` in the terminal where the client is running. + +The client will handle the interruption safely, cancel the polling, terminate the process, and draw a graph with temperature variations using `plot.go`. diff --git a/temperature_grpc_client/service/client.md b/temperature_grpc_client/service/client.md deleted file mode 100644 index 97f10ed..0000000 --- a/temperature_grpc_client/service/client.md +++ /dev/null @@ -1,97 +0,0 @@ -# gRPC Temperature Client - -Il client gRPC per il polling delle temperature utilizza il servizio `TemperatureService` per ottenere le temperature correnti di una località specificata. Questo client è configurato per eseguire il polling a intervalli regolari e può essere interrotto in modo sicuro tramite segnali di sistema. - -## Requisiti - -- Go 1.22 o superiore -- Chiave API di WeatherAPI configurata come variabile d'ambiente (`WEATHER_API_KEY`) - -## Configurazione - -Prima di eseguire il client, assicurati di avere configurato correttamente la chiave API di WeatherAPI come descritto nella guida di configurazione. - -## Esecuzione del Client - -Per eseguire il client, segui questi passaggi: - -1. Apri un terminale e naviga alla directory `temperature_grpc_client`: - - ```bash - cd temperature_grpc_client/cmd/client - ``` - -2. Esegui il client: - - ```bash - go run main.go - ``` - -## Funzionamento del Codice - -Il codice del client esegue le seguenti operazioni: - -1. **Configurazione**: - - Impostazione dell'indirizzo del server gRPC (`grpcServerAddress`). - - Definizione dell'intervallo di polling (`pollingInterval`). - - Specificazione della località per il polling delle temperature (`location`). - - ```go - const ( - grpcServerAddress = "localhost:50051" - pollingInterval = 10 * time.Second - location = "Rome" - ) - ``` - -2. **Gestione dei Segnali di Interruzione**: - - Creazione di un contesto che gestisce i segnali di interruzione (es. `SIGTERM` e `os.Interrupt`) per consentire una chiusura sicura del processo. - - ```go - ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) - defer stop() - ``` - -3. **Avvio del Polling**: - - Invocazione della funzione `startPolling(ctx)` che avvia il polling delle temperature. - - ```go - startPolling(ctx) - ``` - -4. **Funzione di Polling**: - - Creazione del client gRPC utilizzando la funzione `NewClient`. - - Configurazione di un ticker per eseguire il polling a intervalli regolari. - - Esecuzione del polling finché il contesto non viene cancellato, interrompendo il polling in modo sicuro quando viene ricevuto un segnale di interruzione. - - ```go - func startPolling(ctx context.Context) { - client, err := service.NewClient(grpcServerAddress) - if (err != nil) { - log.Fatalf("Errore nella creazione del client gRPC: %v", err) - } - - ticker := time.NewTicker(pollingInterval) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - log.Println("Interruzione ricevuta, fermo il polling.") - return - case <-ticker.C: - log.Println("Polling temperatures...") - client.GetCurrentTemperature(location) - } - } - } - ``` - -## Interruzione del Client - -Per interrompere il client: - -- Premere `Ctrl+C` nel terminale in cui il client è in esecuzione. - -Il client gestirà l'interruzione in modo sicuro, annullando il polling e terminando il processo. - diff --git a/temperature_grpc_server/server.md b/temperature_grpc_server/server.md new file mode 100644 index 0000000..f5bf6ab --- /dev/null +++ b/temperature_grpc_server/server.md @@ -0,0 +1,217 @@ +# gRPC Temperature Server + +Il server gRPC per il servizio `TemperatureService` gestisce richieste di temperatura da client gRPC. Questo server è configurato per ascoltare su una porta specificata e può essere interrotto in modo sicuro tramite segnali di sistema. + +[Click here for the English version](#english-version) + +## Requisiti + +- Go 1.22 o superiore +- Chiave API di WeatherAPI configurata come variabile d'ambiente (`WEATHER_API_KEY`) + +## Configurazione + +Prima di eseguire il server, assicurati di avere configurato correttamente la chiave API di WeatherAPI come descritto nella guida di configurazione. + +## Esecuzione del Server + +Prima di lanciare il server, assicurati di avviare `docker-compose` con l'istanza di MongoDB. Segui questi passaggi: + +1. Avvia l'istanza di MongoDB con Docker Compose: + +```bash +docker-compose up -d +``` + +2. Apri un terminale e naviga alla directory `temperature_grpc_server`: + +```bash +cd temperature_grpc_server/cmd/server +``` + +3. Esegui il server: + +```bash +go run main.go +``` + +## Funzionamento del Codice + +Il codice del server esegue le seguenti operazioni: + +1. **Configurazione**: +- Impostazione dell'indirizzo del server gRPC (`grpcAddress`). +- Definizione del protocollo di rete (`tcp`). + +```go +const grpcAddress = ":50051" +const tcp = "tcp" +``` + +2. **Gestione dei Segnali di Interruzione**: +- Creazione di un contesto che gestisce i segnali di interruzione (es. `SIGTERM` e `os.Interrupt`) per consentire una chiusura sicura del processo. + +```go +ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) +defer stop() +``` + +3. **Avvio del Server gRPC**: +- Invocazione della funzione `startGRPCServer(ctx, grpcAddress)` che avvia il server gRPC per gestire le richieste del servizio `TemperatureService`. + +```go +if err := startGRPCServer(ctx, grpcAddress); err != nil { +log.Fatalf("Failed to start gRPC server: %v", err) +} +``` + +4. **Funzione di Avvio del Server gRPC**: +- Creazione di un listener sulla porta specificata. +- Inizializzazione di un nuovo server gRPC. +- Registrazione del server del servizio `TemperatureService`. +- Avvio del server gRPC per gestire le richieste in arrivo. +- Gestione della chiusura sicura del server gRPC quando il contesto viene annullato. + +```go +func startGRPCServer(ctx context.Context, address string) error { +listener, err := net.Listen(tcp, address) +if err != nil { +return fmt.Errorf("failed to listen on %s: %w", address, err) +} +defer listener.Close() + +grpcServer := grpc.NewServer() + +// Stoppa se il context viene terminato +go func() { +<-ctx.Done() +log.Println("Shutting down gRPC server...") +grpcServer.GracefulStop() +}() + +pb.RegisterTemperatureServiceServer(grpcServer, service.NewServer()) +log.Printf("gRPC server listening on %s\n", address) + +if err := grpcServer.Serve(listener); err != nil { +return fmt.Errorf("failed to serve gRPC server: %w", err) +} + +return nil +} +``` + +## Interruzione del Server + +Per interrompere il server: + +- Premere `Ctrl+C` nel terminale in cui il server è in esecuzione. + +Il server gestirà l'interruzione in modo sicuro, annullando il contesto e terminando il processo. + +# English Version + +The gRPC server for the `TemperatureService` handles temperature requests from gRPC clients. This server is configured to listen on a specified port and can be safely interrupted via system signals. + +## Requirements + +- Go 1.22 or higher +- WeatherAPI API key configured as an environment variable (`WEATHER_API_KEY`) + +## Configuration + +Before running the server, ensure that the WeatherAPI API key is configured correctly as described in the setup guide. + +## Running the Server + +Before launching the server, make sure to start `docker-compose` with the MongoDB instance. Follow these steps: + +1. Start the MongoDB instance with Docker Compose: + +```bash +docker-compose up -d +``` + +2. Open a terminal and navigate to the `temperature_grpc_server` directory: + +```bash +cd temperature_grpc_server/cmd/server +``` + +3. Run the server: + +```bash +go run main.go +``` + +## Code Operation + +The server code performs the following operations: + +1. **Configuration**: +- Setting the gRPC server address (`grpcAddress`). +- Defining the network protocol (`tcp`). + +```go +const grpcAddress = ":50051" +const tcp = "tcp" +``` + +2. **Handling Interrupt Signals**: +- Creating a context that handles interrupt signals (e.g., `SIGTERM` and `os.Interrupt`) to allow safe process shutdown. + +```go +ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) +defer stop() +``` + +3. **Starting the gRPC Server**: +- Calling the `startGRPCServer(ctx, grpcAddress)` function that starts the gRPC server to handle `TemperatureService` requests. + +```go +if err := startGRPCServer(ctx, grpcAddress); err != nil { +log.Fatalf("Failed to start gRPC server: %v", err) +} +``` + +4. **gRPC Server Start Function**: +- Creating a listener on the specified port. +- Initializing a new gRPC server. +- Registering the `TemperatureService` server. +- Starting the gRPC server to handle incoming requests. +- Managing safe shutdown of the gRPC server when the context is canceled. + +```go +func startGRPCServer(ctx context.Context, address string) error { +listener, err := net.Listen(tcp, address) +if err != nil { +return fmt.Errorf("failed to listen on %s: %w", address, err) +} +defer listener.Close() + +grpcServer := grpc.NewServer() + +// Stop if context is done +go func() { +<-ctx.Done() +log.Println("Shutting down gRPC server...") +grpcServer.GracefulStop() +}() + +pb.RegisterTemperatureServiceServer(grpcServer, service.NewServer()) +log.Printf("gRPC server listening on %s\n", address) + +if err := grpcServer.Serve(listener); err != nil { +return fmt.Errorf("failed to serve gRPC server: %w", err) +} + +return nil +} +``` + +## Stopping the Server + +To stop the server: + +- Press `Ctrl+C` in the terminal where the server is running. + +The server will handle the interruption safely, canceling the context and terminating the process. diff --git a/temperature_grpc_server/service/server.md b/temperature_grpc_server/service/server.md deleted file mode 100644 index ba5a303..0000000 --- a/temperature_grpc_server/service/server.md +++ /dev/null @@ -1,101 +0,0 @@ -# gRPC Temperature Server - -Il server gRPC per il servizio `TemperatureService` gestisce richieste di temperatura da client gRPC. Questo server è configurato per ascoltare su una porta specificata e può essere interrotto in modo sicuro tramite segnali di sistema. - -## Requisiti - -- Go 1.22 o superiore -- Chiave API di WeatherAPI configurata come variabile d'ambiente (`WEATHER_API_KEY`) - -## Configurazione - -Prima di eseguire il server, assicurati di avere configurato correttamente la chiave API di WeatherAPI come descritto nella guida di configurazione. - -## Esecuzione del Server - -Per eseguire il server, segui questi passaggi: - -1. Apri un terminale e naviga alla directory `temperature_grpc_server`: - -```bash -cd temperature_grpc_server/cmd/server -``` - -2. Esegui il server: - -```bash -go run main.go -``` - -## Funzionamento del Codice - -Il codice del server esegue le seguenti operazioni: - -1. **Configurazione**: -- Impostazione dell'indirizzo del server gRPC (`grpcAddress`). -- Definizione del protocollo di rete (`tcp`). - -```go -const grpcAddress = ":50051" -const tcp = "tcp" -``` - -2. **Gestione dei Segnali di Interruzione**: -- Creazione di un contesto che gestisce i segnali di interruzione (es. `SIGTERM` e `os.Interrupt`) per consentire una chiusura sicura del processo. - -```go -ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) -defer stop() -``` - -3. **Avvio del Server gRPC**: -- Invocazione della funzione `startGRPCServer(ctx, grpcAddress)` che avvia il server gRPC per gestire le richieste del servizio `TemperatureService`. - -```go -if err := startGRPCServer(ctx, grpcAddress); err != nil { -log.Fatalf("Failed to start gRPC server: %v", err) -} -``` - -4. **Funzione di Avvio del Server gRPC**: -- Creazione di un listener sulla porta specificata. -- Inizializzazione di un nuovo server gRPC. -- Registrazione del server del servizio `TemperatureService`. -- Avvio del server gRPC per gestire le richieste in arrivo. -- Gestione della chiusura sicura del server gRPC quando il contesto viene annullato. - -```go -func startGRPCServer(ctx context.Context, address string) error { -listener, err := net.Listen(tcp, address) -if err != nil { -return fmt.Errorf("failed to listen on %s: %w", address, err) -} -defer listener.Close() - -grpcServer := grpc.NewServer() - -// Stoppa se il context viene terminato -go func() { -<-ctx.Done() -log.Println("Shutting down gRPC server...") -grpcServer.GracefulStop() -}() - -pb.RegisterTemperatureServiceServer(grpcServer, service.NewServer()) -log.Printf("gRPC server listening on %s\n", address) - -if err := grpcServer.Serve(listener); err != nil { -return fmt.Errorf("failed to serve gRPC server: %w", err) -} - -return nil -} -``` - -## Interruzione del Server - -Per interrompere il server: - -- Premere `Ctrl+C` nel terminale in cui il server è in esecuzione. - -Il server gestirà l'interruzione in modo sicuro, annullando il contesto e terminando il processo.