<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Linh Truong Cong Hong</title>
    <description>The latest articles on DEV Community by Linh Truong Cong Hong (@linhtch90).</description>
    <link>https://dev.to/linhtch90</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F446735%2F226c0655-6148-4a57-8d1b-91b1179ba28a.jpeg</url>
      <title>DEV Community: Linh Truong Cong Hong</title>
      <link>https://dev.to/linhtch90</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/linhtch90"/>
    <language>en</language>
    <item>
      <title>Battle-Testing an IoT Telemetry Pipeline: Load Testing 60x Storm Spikes &amp; Chaos Engineering with Grafana k6, KEDA, and Opossum</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Mon, 26 Oct 2026 15:34:55 +0000</pubDate>
      <link>https://dev.to/linhtch90/battle-testing-an-iot-telemetry-pipeline-load-testing-60x-storm-spikes-chaos-engineering-with-39hh</link>
      <guid>https://dev.to/linhtch90/battle-testing-an-iot-telemetry-pipeline-load-testing-60x-storm-spikes-chaos-engineering-with-39hh</guid>
      <description>&lt;p&gt;In our &lt;a href="https://dev.to/linhtch90/designing-a-cloud-native-weather-telemetry-system-handling-60x-storm-traffic-spikes-with-nestjs-jic"&gt;previous article&lt;/a&gt;, we walked through the architecture of the &lt;strong&gt;&lt;a href="https://github.com/linhtch90/weather-monitoring-system" rel="noopener noreferrer"&gt;Cloud-Native Weather Telemetry Monitoring System&lt;/a&gt;&lt;/strong&gt;—a distributed microservices pipeline built with &lt;strong&gt;NestJS, gRPC, RabbitMQ, TimescaleDB, and Redis&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Designing an architecture on paper that claims to handle &lt;strong&gt;60x storm traffic surges&lt;/strong&gt; and &lt;strong&gt;downstream database outages&lt;/strong&gt; is easy. &lt;em&gt;Proving&lt;/em&gt; that it survives those conditions without dropping sensor data or paging engineers at 3:00 AM requires a rigorous verification strategy.&lt;/p&gt;

&lt;p&gt;In this post, we’ll dive deep into how we test the three pillars of production readiness across our pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scalability &amp;amp; Performance Testing&lt;/strong&gt; (simulating 60x dynamic frequency bursts and 5,000-station fleet growth with &lt;strong&gt;Grafana k6&lt;/strong&gt;, &lt;strong&gt;KEDA&lt;/strong&gt;, and &lt;strong&gt;HPA&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Availability Under Dependency Outages&lt;/strong&gt; (verifying &lt;strong&gt;Opossum&lt;/strong&gt; circuit breakers and &lt;strong&gt;Redis&lt;/strong&gt; cache-aside fallbacks when downstream services die).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilience &amp;amp; Fault Isolation&lt;/strong&gt; (injecting physical anomalies and malformed "poison pill" payloads to verify &lt;strong&gt;RabbitMQ Dead Letter Queues&lt;/strong&gt; and consumer backpressure).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Why Standard Load Tests Fail for Weather Telemetry
&lt;/h2&gt;

&lt;p&gt;Most web applications experience gradual, predictable traffic curves that follow human activity (morning ramp-up, afternoon peak, evening cool-down). Weather monitoring networks behave completely differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Calm Weather Baseline&lt;/strong&gt;: Thousands of stations report once every &lt;strong&gt;60 seconds&lt;/strong&gt; (&lt;code&gt;1 req/min&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Storm Surge (60x Instantaneous Spike)&lt;/strong&gt;: When &lt;code&gt;processing-service&lt;/code&gt; detects a severe anomaly—such as a rapid barometric pressure drop ($\Delta P &amp;gt; 3\text{ hPa}$ over 3 hours) or wind gusts exceeding $28.0\text{ m/s}$—&lt;code&gt;alerting-service&lt;/code&gt; issues a gRPC &lt;code&gt;UpdateReportingFrequency&lt;/code&gt; command that shifts stations into &lt;strong&gt;1-second emergency sampling mode&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Double Jeopardy&lt;/strong&gt;: Traffic spikes by &lt;strong&gt;6000%&lt;/strong&gt; at the exact moment when meteorological data is most critical and when storm conditions may simultaneously degrade network links or infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To validate our system against this reality, we defined strict &lt;strong&gt;Service Level Objectives (SLOs)&lt;/strong&gt; before writing our test suites:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Target SLO&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ingestion Latency (&lt;code&gt;p95&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt; 50 ms&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Edge stations on unstable satellite/cellular links hold open sockets briefly; slow ACKs drain battery and cause retry storms.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ingestion Latency (&lt;code&gt;p99&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt; 150 ms&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tail latency must stay bounded even during GC pauses or Redis cache misses.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ingestion Error Rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt; 0.1%&lt;/code&gt; (&lt;code&gt;rate &amp;lt; 0.001&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;High-resolution storm telemetry cannot be lost at the gateway.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Outage Availability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;100%&lt;/code&gt; degraded ingestion&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;station-service&lt;/code&gt; crashes, &lt;code&gt;ingestion-service&lt;/code&gt; must trip its circuit breaker within &lt;code&gt;2000ms&lt;/code&gt; and continue accepting valid telemetry.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Poison-Pill Recovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; consumer restarts&lt;/td&gt;
&lt;td&gt;Corrupted packets must be isolated to &lt;code&gt;weather.telemetry.dlq&lt;/code&gt; without blocking healthy messages in &lt;code&gt;weather.raw.telemetry&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  2. Scalability Testing with Grafana k6
&lt;/h2&gt;

&lt;p&gt;We use &lt;strong&gt;&lt;a href="https://k6.io/" rel="noopener noreferrer"&gt;Grafana k6&lt;/a&gt;&lt;/strong&gt; for performance and load testing because its Go-based runtime generates massive concurrent load from a single machine while allowing us to write expressive test scenarios in JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario A: The 60x Dynamic Frequency Spike (&lt;code&gt;ramping-arrival-rate&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;A common mistake in load testing is using &lt;strong&gt;closed-model Virtual Users (VUs)&lt;/strong&gt; where each VU waits for the server to respond before sending the next request. If your server slows down under load, closed-model VUs naturally throttle themselves—masking real-world saturation!&lt;/p&gt;

&lt;p&gt;Real IoT weather stations follow an &lt;strong&gt;open workload model&lt;/strong&gt;: when the clock ticks 1 second, 1,200 stations transmit their packets whether your server finished the previous batch or not. To model this accurately in &lt;a href="https://github.com/linhtch90/weather-monitoring-system/blob/main/test/k6/scenarios/dynamic-frequency-spike.js" rel="noopener noreferrer"&gt;&lt;code&gt;test/k6/scenarios/dynamic-frequency-spike.js&lt;/code&gt;&lt;/a&gt;, we use k6’s &lt;code&gt;ramping-arrival-rate&lt;/code&gt; executor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test/k6/scenarios/dynamic-frequency-spike.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;baseline_and_spike&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ramping-arrival-rate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;startRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Baseline: ~20 req/s across fleet (60s reporting mode)&lt;/span&gt;
      &lt;span class="na"&gt;timeUnit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;preAllocatedVUs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxVUs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;30s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;// Stage 1: Warmup / Calm Weather Baseline&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;15s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// Stage 2: 30x Surge as storm front approaches&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// Stage 3: 60x Burst — 1,200 req/sec sustained&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;30s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;// Stage 4: Storm passes, return to 60s interval&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;thresholds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;http_req_duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p(95)&amp;lt;50&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p(99)&amp;lt;150&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Sub-50ms p95 SLO&lt;/span&gt;
    &lt;span class="na"&gt;http_req_failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rate&amp;lt;0.001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;               &lt;span class="c1"&gt;// &amp;lt;0.1% failure tolerance&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the test function, &lt;strong&gt;20% of payloads simulate active storm readings&lt;/strong&gt; (&lt;code&gt;pressure: 998.0 hPa&lt;/code&gt;, &lt;code&gt;wind_speed: 32.5 m/s&lt;/code&gt;, &lt;code&gt;precipitation: 15.0 mm&lt;/code&gt;). This ensures our load test doesn't just exercise the HTTP gateway—it simultaneously triggers the sliding-window &lt;code&gt;AnomalyDetector&lt;/code&gt; in &lt;code&gt;processing-service&lt;/code&gt; and fires real anomaly events through &lt;code&gt;alerting-service&lt;/code&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stationId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;STATIONS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;STATIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isStorm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 20% probability of storm readings&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;station_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;18.5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;65.0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;pressure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isStorm&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;998.0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1013.2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Sudden drop when storm&lt;/span&gt;
    &lt;span class="na"&gt;wind_speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isStorm&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;32.5&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// High gusts when storm&lt;/span&gt;
    &lt;span class="na"&gt;wind_direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;precipitation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isStorm&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;15.0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;battery_voltage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;12.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec_token_alps_001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/telemetry`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status is 200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ingestion code SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scenario B: Horizontal Fleet Growth (&lt;code&gt;500&lt;/code&gt; to &lt;code&gt;5,000&lt;/code&gt; Concurrent Stations)
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;dynamic-frequency-spike.js&lt;/code&gt; tests temporal burstiness across existing stations, &lt;a href="https://github.com/linhtch90/weather-monitoring-system/blob/main/test/k6/scenarios/fleet-growth.js" rel="noopener noreferrer"&gt;&lt;code&gt;test/k6/scenarios/fleet-growth.js&lt;/code&gt;&lt;/a&gt; tests &lt;strong&gt;high-cardinality station onboarding&lt;/strong&gt;—ramping from &lt;code&gt;500&lt;/code&gt; to &lt;code&gt;5,000&lt;/code&gt; concurrent stations (&lt;code&gt;ST-REGIONAL-0001&lt;/code&gt; through &lt;code&gt;ST-REGIONAL-1000&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test/k6/scenarios/fleet-growth.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;30s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// 500 concurrent stations&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// Ramp to 2,000 stations&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// Peak at 5,000 stations&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;30s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// Ramp down&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;thresholds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;http_req_duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p(95)&amp;lt;100&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;http_req_failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rate&amp;lt;0.01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is high station cardinality important to test separately?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Redis Key Cardinality&lt;/strong&gt;: Every new &lt;code&gt;station_id&lt;/code&gt; triggers an initial cache miss on &lt;code&gt;station:&amp;lt;id&amp;gt;:valid&lt;/code&gt;, testing our gRPC connection pool between &lt;code&gt;ingestion-service&lt;/code&gt; and &lt;code&gt;station-service&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-Memory Sliding Windows&lt;/strong&gt;: &lt;code&gt;AnomalyDetector&lt;/code&gt; in &lt;code&gt;processing-service&lt;/code&gt; maintains a per-station sliding window of historical readings. Testing 1,000+ distinct station IDs verifies that our sliding-window pruning prevents memory leaks.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  3. How the Architecture Absorbs the Load: Autoscaling &amp;amp; Micro-Batching
&lt;/h2&gt;

&lt;p&gt;When you run &lt;code&gt;dynamic-frequency-spike.js&lt;/code&gt; and traffic jumps from &lt;code&gt;20 req/s&lt;/code&gt; to &lt;code&gt;1,200 req/s&lt;/code&gt; in 15 seconds, four architectural mechanisms work together to keep &lt;code&gt;p95&lt;/code&gt; latency under &lt;code&gt;50ms&lt;/code&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Stateless Ingress Scaling via Kubernetes HPA
&lt;/h3&gt;

&lt;p&gt;At the edge, &lt;code&gt;ingestion-service&lt;/code&gt; does zero disk I/O. It validates the payload, checks Redis (&lt;code&gt;~0.5ms&lt;/code&gt;), and publishes to RabbitMQ. In Kubernetes (&lt;a href="https://github.com/linhtch90/weather-monitoring-system/blob/main/k8s/autoscaling/hpa-ingestion.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;k8s/autoscaling/hpa-ingestion.yaml&lt;/code&gt;&lt;/a&gt;), a standard Horizontal Pod Autoscaler scales &lt;code&gt;ingestion-service&lt;/code&gt; from &lt;strong&gt;3 to 25 replicas&lt;/strong&gt; whenever CPU crosses &lt;code&gt;70%&lt;/code&gt; or memory crosses &lt;code&gt;80%&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Event-Driven Consumer Scaling via KEDA
&lt;/h3&gt;

&lt;p&gt;CPU utilization is a terrible metric for scaling message queue consumers—an I/O-bound worker waiting on database flushes might sit at 25% CPU while the RabbitMQ queue builds a backlog of 100,000 messages.&lt;/p&gt;

&lt;p&gt;Instead, we use &lt;strong&gt;KEDA (Kubernetes Event-driven Autoscaling)&lt;/strong&gt; in &lt;a href="https://github.com/linhtch90/weather-monitoring-system/blob/main/k8s/autoscaling/keda-processing-scaledobject.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;k8s/autoscaling/keda-processing-scaledobject.yaml&lt;/code&gt;&lt;/a&gt; to monitor the exact depth of the &lt;code&gt;weather.raw.telemetry&lt;/code&gt; queue every 5 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# k8s/autoscaling/keda-processing-scaledobject.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keda.sh/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ScaledObject&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;processing-service-scaler&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;weather-system&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scaleTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;processing-service&lt;/span&gt;
  &lt;span class="na"&gt;minReplicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
  &lt;span class="na"&gt;cooldownPeriod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
  &lt;span class="na"&gt;pollingInterval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="na"&gt;triggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rabbitmq&lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amqp&lt;/span&gt;
        &lt;span class="na"&gt;queueName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;weather.raw.telemetry&lt;/span&gt;
        &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;QueueLength&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;500"&lt;/span&gt; &lt;span class="c1"&gt;# Scale up 1 replica for every 500 queued messages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Bounded Consumer Backpressure (&lt;code&gt;prefetch: 200&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;To prevent a sudden surge of messages in RabbitMQ from flooding a single &lt;code&gt;processing-service&lt;/code&gt; pod's V8 heap and causing an &lt;code&gt;OutOfMemory&lt;/code&gt; crash, every consumer channel enforces &lt;code&gt;channel.prefetch(200)&lt;/code&gt; with manual acknowledgments (&lt;code&gt;noAck: false&lt;/code&gt;). RabbitMQ will never push more than 200 unacknowledged messages to a worker at once.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Multi-Row Micro-Batching into TimescaleDB
&lt;/h3&gt;

&lt;p&gt;Even if KEDA spins up 20 &lt;code&gt;processing-service&lt;/code&gt; pods, executing 1,200 individual &lt;code&gt;INSERT&lt;/code&gt; statements per second would exhaust PostgreSQL connection pools and lock indexes.&lt;/p&gt;

&lt;p&gt;Instead, &lt;code&gt;ProcessingService&lt;/code&gt; buffers incoming messages in memory until either &lt;strong&gt;500 records accumulate&lt;/strong&gt; OR a &lt;strong&gt;250ms timer expires&lt;/strong&gt;, then flushes them in a single parameterized multi-row &lt;code&gt;INSERT&lt;/code&gt; via &lt;code&gt;pg.Pool&lt;/code&gt; (&lt;code&gt;TimescaleRepository.insertBatch&lt;/code&gt;). At &lt;code&gt;1,200 req/s&lt;/code&gt;, 1,200 network round-trips to TimescaleDB collapse into &lt;strong&gt;just 2.4 bulk writes per second&lt;/strong&gt;!&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Availability &amp;amp; Chaos Engineering: Breaking Things on Purpose
&lt;/h2&gt;

&lt;p&gt;High throughput in ideal conditions is only half the story. In distributed systems, dependencies &lt;em&gt;will&lt;/em&gt; fail. We built an automated chaos engineering suite under &lt;a href="https://github.com/linhtch90/weather-monitoring-system/tree/main/test/chaos" rel="noopener noreferrer"&gt;&lt;code&gt;test/chaos/&lt;/code&gt;&lt;/a&gt; (runnable via &lt;code&gt;npm run test:chaos&lt;/code&gt;) to prove our system degrades gracefully under partial outages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Experiment 1: Station Service Outage &amp;amp; Circuit Breaker Verification
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Hypothesis&lt;/strong&gt;: When a station sends telemetry and its auth token isn't cached in Redis, &lt;code&gt;ingestion-service&lt;/code&gt; makes a synchronous gRPC call (&lt;code&gt;ValidateStation&lt;/code&gt;) to &lt;code&gt;station-service&lt;/code&gt;. What happens if &lt;code&gt;station-service&lt;/code&gt; (or its PostgreSQL database) crashes or hangs during a storm?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Without protection&lt;/strong&gt;: Every incoming HTTP/gRPC request hangs waiting for &lt;code&gt;station-service&lt;/code&gt;, exhausting socket pools and causing a &lt;strong&gt;total ingestion outage&lt;/strong&gt; (cascading failure).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With our Opossum Circuit Breaker&lt;/strong&gt;: After 50% of requests fail or exceed the &lt;code&gt;2000ms&lt;/code&gt; timeout, the circuit breaker trips to &lt;strong&gt;&lt;code&gt;OPEN&lt;/code&gt;&lt;/strong&gt;, fails fast in &lt;code&gt;&amp;lt; 1ms&lt;/code&gt;, and invokes a &lt;strong&gt;degraded fallback handler&lt;/strong&gt; that validates the station ID against a strict format regex (&lt;code&gt;/ST-[A-Z0-9-]+$/&lt;/code&gt;). Valid stations continue having their telemetry queued to RabbitMQ!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at the chaos test in &lt;a href="https://github.com/linhtch90/weather-monitoring-system/blob/main/test/chaos/test-circuit-breaker.js" rel="noopener noreferrer"&gt;&lt;code&gt;test/chaos/test-circuit-breaker.js&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test/chaos/test-circuit-breaker.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;station_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ST-ALPS-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;21.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;55.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pressure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1013.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;wind_speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;3.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;wind_direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;precipitation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;battery_voltage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;12.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec_token_alps_001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ingestionUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/telemetry`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;latency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[Req &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] ✅ SUCCESS in &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fallback&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[Req &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] 🛡️ DEGRADED FALLBACK in &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  What Happens During the Chaos Run:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;We stop &lt;code&gt;station-service&lt;/code&gt; (and flush Redis) while keeping &lt;code&gt;ingestion-service&lt;/code&gt; and &lt;code&gt;rabbitmq&lt;/code&gt; running.&lt;/li&gt;
&lt;li&gt;We fire 20 sequential telemetry requests via &lt;code&gt;node test/chaos/test-circuit-breaker.js&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requests 1–2&lt;/strong&gt;: Experience the gRPC connection refusal/timeout, trigger the circuit breaker fallback, and still return &lt;code&gt;HTTP 200&lt;/code&gt; (&lt;code&gt;code: 1&lt;/code&gt;) because the fallback permits format-valid station &lt;code&gt;ST-ALPS-001&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Circuit Breaker Trips (&lt;code&gt;CLOSED -&amp;gt; OPEN&lt;/code&gt;)&lt;/strong&gt;: &lt;code&gt;AppLogger&lt;/code&gt; logs &lt;code&gt;CircuitBreaker[StationService.ValidateStation] OPENED — failing fast and using fallback&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requests 3–20&lt;/strong&gt;: Bypass the dead gRPC channel completely! Latency drops to &lt;strong&gt;&lt;code&gt;2ms–4ms&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;100% of the 20 telemetry packets are safely persisted into RabbitMQ&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Once &lt;code&gt;station-service&lt;/code&gt; recovers, after &lt;code&gt;resetTimeout: 5000ms&lt;/code&gt;, the breaker enters &lt;code&gt;HALF_OPEN&lt;/code&gt;, probes a single gRPC call, and snaps back to &lt;code&gt;CLOSED&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Experiment 2: Poison-Pill Injection &amp;amp; Dead Letter Queue (DLQ) Isolation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Hypothesis&lt;/strong&gt;: Edge IoT sensors exposed to lightning, freezing rain, or failing batteries occasionally transmit corrupted JSON or physically impossible readings (e.g., &lt;code&gt;-500°C&lt;/code&gt; temperature or &lt;code&gt;150%&lt;/code&gt; relative humidity).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Danger (Head-of-Line Blocking)&lt;/strong&gt;: If a consumer fails to process a corrupted message and requeues it (&lt;code&gt;channel.nack(msg, false, true)&lt;/code&gt;), RabbitMQ will redeliver that same poison pill in an infinite loop—blocking all valid telemetry behind it and pinning CPU at 100%.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We defend against poison pills at &lt;strong&gt;two layers&lt;/strong&gt;, verified by &lt;a href="https://github.com/linhtch90/weather-monitoring-system/blob/main/test/chaos/test-dlq-poison-pill.js" rel="noopener noreferrer"&gt;&lt;code&gt;test/chaos/test-dlq-poison-pill.js&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test/chaos/test-dlq-poison-pill.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;poisonPills&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Physical impossibility: Temperature -500°C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;station_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ST-ALPS-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pressure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1013&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;wind_speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;wind_direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;precipitation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Physical impossibility: Humidity 150%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;station_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ST-ALPS-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pressure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1013&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;wind_speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;wind_direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;precipitation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing required station_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pressure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1013&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;wind_speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;wind_direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;precipitation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Layer 1 — Perimeter Sanity Check (&lt;code&gt;ingestion-service&lt;/code&gt;)&lt;/strong&gt;: Before touching Redis or RabbitMQ, &lt;code&gt;IngestionService.validateSensorRanges()&lt;/code&gt; enforces physical meteorological boundaries:

&lt;ul&gt;
&lt;li&gt;Temperature: &lt;code&gt;-90°C&lt;/code&gt; to &lt;code&gt;+65°C&lt;/code&gt; (Absolute zero is &lt;code&gt;-273.15°C&lt;/code&gt;; &lt;code&gt;-500°C&lt;/code&gt; is rejected immediately with &lt;code&gt;400 Bad Request&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Humidity: &lt;code&gt;0%&lt;/code&gt; to &lt;code&gt;100%&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Barometric Pressure: &lt;code&gt;850 hPa&lt;/code&gt; to &lt;code&gt;1100 hPa&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wind Speed: &lt;code&gt;0 m/s&lt;/code&gt; to &lt;code&gt;150 m/s&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer 2 — Dead Letter Exchange Isolation (&lt;code&gt;processing-service&lt;/code&gt;)&lt;/strong&gt;: If a malformed or unparseable binary packet gets published directly onto &lt;code&gt;weather.raw.telemetry&lt;/code&gt; by a legacy producer, &lt;code&gt;ProcessingService&lt;/code&gt; catches the parse error and executes:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// requeue = false!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;weather.raw.telemetry&lt;/code&gt; is configured with &lt;code&gt;x-dead-letter-exchange: weather.telemetry.dlx&lt;/code&gt;, RabbitMQ automatically routes the poison pill into &lt;code&gt;weather.telemetry.dlq&lt;/code&gt; for forensic inspection while the worker immediately moves on to the next batch.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Run the Verification Suite Yourself!
&lt;/h2&gt;

&lt;p&gt;Everything described in this post is 100% open-source (MIT Licensed) and reproducible on your local machine in minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Clone &amp;amp; Start the Stack
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/linhtch90/weather-monitoring-system.git
&lt;span class="nb"&gt;cd &lt;/span&gt;weather-monitoring-system
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Start RabbitMQ, TimescaleDB, PostgreSQL, Redis, and Prometheus&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Run Unit &amp;amp; Contract Verification
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Run the Chaos &amp;amp; Resilience Suite
&lt;/h3&gt;

&lt;p&gt;With the microservices running, execute our automated chaos runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nb"&gt;test&lt;/span&gt;:chaos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;==================================================
⚡ RUNNING WEATHER SYSTEM CHAOS &amp;amp; RESILIENCE SUITE
==================================================

1. Testing Ingestion Circuit Breaker...
🧪 Starting Chaos Experiment: Station Service Outage &amp;amp; Circuit Breaker Verification
[Req 1] ✅ SUCCESS in 8ms
...
[Req 20] ✅ SUCCESS in 3ms

--- Experiment Summary ---
Total Requests: 20
Successful: 20
Graceful Fallbacks: 0
Zero unhandled crash exceptions observed.

2. Testing Poison Pill Rejection &amp;amp; DLQ...
🧪 Starting Chaos Experiment: Poison Pill Injection &amp;amp; DLQ Isolation
[Test: Physical impossiblity: Temperature -500°C] -&amp;gt; Status: 400 (Caught and rejected without crashing)
[Test: Physical impossibility: Humidity 150%] -&amp;gt; Status: 400 (Caught and rejected without crashing)
[Test: Missing required station_id] -&amp;gt; Status: 400 (Caught and rejected without crashing)

✅ Poison pills rejected at ingress; corrupted queue packets routed to weather.telemetry.dlq.
✅ All resilience experiments completed successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Trigger the 60x Storm Load Test with Grafana k6
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run the 20 req/s -&amp;gt; 1,200 req/s storm burst test&lt;/span&gt;
k6 run &lt;span class="nb"&gt;test&lt;/span&gt;/k6/scenarios/dynamic-frequency-spike.js

&lt;span class="c"&gt;# Or run the 500 -&amp;gt; 5,000 station fleet growth test&lt;/span&gt;
k6 run &lt;span class="nb"&gt;test&lt;/span&gt;/k6/scenarios/fleet-growth.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test Open Workloads with Arrival Rates&lt;/strong&gt;: When testing IoT or telemetry pipelines, use &lt;code&gt;ramping-arrival-rate&lt;/code&gt; in Grafana k6 rather than closed-loop VUs so your load generator doesn't politely slow down when your server struggles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale Consumers on Queue Depth, Not CPU&lt;/strong&gt;: Pairing RabbitMQ with &lt;strong&gt;KEDA&lt;/strong&gt; (&lt;code&gt;QueueLength: 500&lt;/code&gt;) and consumer &lt;code&gt;prefetch: 200&lt;/code&gt; gives you elastic horizontal scaling while protecting worker memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never Trust a Synchronous Dependency on a Hot Path&lt;/strong&gt;: Wrapping gRPC calls in &lt;strong&gt;Opossum circuit breakers&lt;/strong&gt; with Redis caching and a deterministic fallback turns a catastrophic downstream outage into a non-event.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate Chaos Checks&lt;/strong&gt;: Treat circuit-breaker trips and poison-pill DLQ isolation like unit tests—script them (&lt;code&gt;npm run test:chaos&lt;/code&gt;) and run them continuously.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Check out the full repository, load test scripts, and Kubernetes autoscaling manifests on GitHub:&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://github.com/linhtch90/weather-monitoring-system" rel="noopener noreferrer"&gt;github.com/linhtch90/weather-monitoring-system&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>performance</category>
      <category>testing</category>
      <category>devops</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Designing a Cloud-Native Weather Telemetry System: Handling 60x Storm Traffic Spikes with NestJS, gRPC, RabbitMQ &amp; TimescaleDB</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Sat, 24 Oct 2026 13:22:11 +0000</pubDate>
      <link>https://dev.to/linhtch90/designing-a-cloud-native-weather-telemetry-system-handling-60x-storm-traffic-spikes-with-nestjs-jic</link>
      <guid>https://dev.to/linhtch90/designing-a-cloud-native-weather-telemetry-system-handling-60x-storm-traffic-spikes-with-nestjs-jic</guid>
      <description>&lt;p&gt;Most IoT tutorials assume a flat, predictable stream of sensor data: &lt;em&gt;1,000 devices sending 1 payload per minute&lt;/em&gt;. In the real world, weather telemetry behaves nothing like that.&lt;/p&gt;

&lt;p&gt;During calm weather, thousands of remote monitoring stations report once every &lt;strong&gt;60 seconds&lt;/strong&gt;. The moment a storm front or rapid barometric pressure drop hits (

&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;Δ&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;&amp;nbsp;hPa&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;/3&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;h&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
), scientists and automated control loops command those stations to switch to &lt;strong&gt;1-second high-resolution sampling&lt;/strong&gt;. Overnight, your ingestion pipeline faces an immediate &lt;strong&gt;60x traffic explosion&lt;/strong&gt;—right when losing data is least acceptable.&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk through how we designed and built the &lt;strong&gt;&lt;a href="https://github.com/linhtch90/weather-monitoring-system" rel="noopener noreferrer"&gt;Cloud-Native Weather Telemetry Monitoring System&lt;/a&gt;&lt;/strong&gt;—a 100% open-source microservices platform built with &lt;strong&gt;Node.js, TypeScript, NestJS, gRPC, RabbitMQ, TimescaleDB, and Redis&lt;/strong&gt;—and the engineering trade-offs we made to guarantee scalability, availability, and chaos resilience.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. The Core Requirements vs. Architectural Decisions
&lt;/h2&gt;

&lt;p&gt;Before writing a single line of code, we mapped out five non-negotiable domain requirements and paired each with a specific architectural pattern:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain Requirement&lt;/th&gt;
&lt;th&gt;The Engineering Challenge&lt;/th&gt;
&lt;th&gt;Our Architectural Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynamic 60x Traffic Surges&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stations shift from &lt;code&gt;1 req/60s&lt;/code&gt; to &lt;code&gt;1 req/1s&lt;/code&gt; during severe weather, overwhelming synchronous DB writes.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Queue-Decoupled Ingestion + Micro-Batching&lt;/strong&gt;: Gateway offloads to RabbitMQ in &lt;code&gt;&amp;lt; 15ms&lt;/code&gt;; workers flush 500-row batches every &lt;code&gt;250ms&lt;/code&gt; to TimescaleDB.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dual Communication Needs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Some calls need instant validation (&lt;code&gt;ValidateStation&lt;/code&gt;), while telemetry streams need buffered throughput.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;gRPC + RabbitMQ Hybrid&lt;/strong&gt;: Direct HTTP/2 binary Protobuf (&lt;strong&gt;gRPC&lt;/strong&gt;) for synchronous RPCs; &lt;strong&gt;RabbitMQ&lt;/strong&gt; topic exchanges for async telemetry streams.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Diverse Edge Hardware&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Modern stations support HTTP/2 gRPC streams; constrained cellular/satellite IoT devices only speak HTTP/1.1 JSON.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Multi-Protocol Hybrid Gateway&lt;/strong&gt;: A single NestJS &lt;code&gt;IngestionController&lt;/code&gt; exposing both &lt;code&gt;:50052&lt;/code&gt; (gRPC) and &lt;code&gt;:3000&lt;/code&gt; (REST) backed by the same domain service.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zero Cascading Failures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;station-service&lt;/code&gt; or Postgres slows down during a storm, the ingestion gateway must not drop incoming station packets.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Opossum Circuit Breakers + Redis Cache-Aside&lt;/strong&gt;: 5-minute TTL Redis token cache + circuit breaker fallback that accepts format-valid station payloads in degraded mode.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;100% Open-Source Stack&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zero vendor lock-in; runnable on any laptop via Docker Compose or scaled on Kubernetes.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;NestJS, RabbitMQ, TimescaleDB (PostgreSQL 16), Redis, Prometheus, KEDA, and Grafana k6&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  2. High-Level System Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffzduccpzr2x6ts0uuq73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffzduccpzr2x6ts0uuq73.png" alt="System Architecture Diagram" width="800" height="691"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip — Diagram as Code&lt;/strong&gt;: Rather than drawing static diagrams in external tools that quickly go stale, we wrote a zero-dependency Python generator (&lt;code&gt;prompt/generate_diagram.py&lt;/code&gt;) that renders our architecture SVG directly from code. Whenever a queue or service changes, we update the script and regenerate the vector diagram in Git.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The system is structured as a &lt;strong&gt;NestJS Monorepo&lt;/strong&gt; divided into four strictly isolated microservices (&lt;code&gt;apps/*&lt;/code&gt;) and three shared libraries (&lt;code&gt;libs/*&lt;/code&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ingestion-service&lt;/code&gt; (&lt;code&gt;:3000&lt;/code&gt; HTTP / &lt;code&gt;:50052&lt;/code&gt; gRPC)&lt;/strong&gt;: The ultra-fast front door. Validates physical sensor ranges, checks station credentials via Redis/gRPC, and publishes to RabbitMQ with Publisher Confirms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;station-service&lt;/code&gt; (&lt;code&gt;:50051&lt;/code&gt; gRPC)&lt;/strong&gt;: The source of truth for station metadata, coordinates, and active sampling intervals stored in PostgreSQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;processing-service&lt;/code&gt; (RabbitMQ Worker)&lt;/strong&gt;: Consumes raw telemetry with &lt;code&gt;prefetch: 200&lt;/code&gt;, evaluates sliding-window meteorological anomalies, and bulk-inserts micro-batches into TimescaleDB hypertables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;alerting-service&lt;/code&gt; (RabbitMQ Worker + gRPC Client)&lt;/strong&gt;: Listens for &lt;code&gt;weather.anomaly.detected&lt;/code&gt; events and dispatches direct gRPC &lt;code&gt;UpdateReportingFrequency(1s)&lt;/code&gt; commands back to &lt;code&gt;station-service&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  3. Deep Dive: Key Engineering Best Practices
&lt;/h2&gt;
&lt;h3&gt;
  
  
  3.1 Dual Communication: Why Both gRPC and RabbitMQ?
&lt;/h3&gt;

&lt;p&gt;A common microservice anti-pattern is forcing &lt;em&gt;all&lt;/em&gt; inter-service communication through a single hammer—either making everything an HTTP/gRPC call (creating brittle synchronous chains) or making everything an async event (turning simple queries into complex correlation-ID state machines).&lt;/p&gt;

&lt;p&gt;We split communication by intent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Direct Synchronous (gRPC with Proto3)&lt;/strong&gt;: When &lt;code&gt;ingestion-service&lt;/code&gt; experiences a Redis cache miss for &lt;code&gt;station:ST-ALPS-001:valid&lt;/code&gt;, it needs an immediate answer from &lt;code&gt;station-service&lt;/code&gt;. Using gRPC over HTTP/2 gives us strict contract enforcement (&lt;code&gt;station.proto&lt;/code&gt;) and low-latency binary serialization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indirect Asynchronous (RabbitMQ AMQP 0-9-1)&lt;/strong&gt;: Once a telemetry payload is validated, the station shouldn't wait for analytical processing or disk indexing. &lt;code&gt;ingestion-service&lt;/code&gt; publishes to &lt;code&gt;weather.telemetry.topic&lt;/code&gt; with routing key &lt;code&gt;telemetry.raw.&amp;lt;region&amp;gt;.&amp;lt;stationId&amp;gt;&lt;/code&gt; and immediately returns an &lt;code&gt;IngestionAck&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Transport-Agnostic Controller: Exposing both gRPC and REST on the same logic&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;UseInterceptors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LoggingInterceptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IngestionController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;ingestionService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IngestionService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. gRPC Unary Endpoint (:50052)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;GrpcMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WeatherIngestionService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SubmitTelemetry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;submitTelemetryGrpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TelemetryPayload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IngestionAck&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ingestionService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processTelemetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. HTTP REST Gateway Fallback (:3000)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api/v1/telemetry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HttpCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;submitTelemetryHttp&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TelemetryPayload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IngestionAck&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ingestionService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processTelemetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 The Hybrid Persistence Strategy: TypeORM vs. Raw &lt;code&gt;pg.Pool&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;One of the most impactful technical choices we made was &lt;strong&gt;refusing to use a single database access pattern everywhere&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Relational Metadata (&lt;code&gt;station-service&lt;/code&gt;) $\rightarrow$ TypeORM&lt;/strong&gt;:
Station registry queries (&lt;code&gt;findById&lt;/code&gt;, &lt;code&gt;updateInterval&lt;/code&gt;) are low-frequency relational operations. Here, &lt;strong&gt;TypeORM&lt;/strong&gt; (&lt;code&gt;DataSource&lt;/code&gt; and &lt;code&gt;Repository&amp;lt;StationRecord&amp;gt;&lt;/code&gt;) gives us clean &lt;code&gt;@Entity('stations')&lt;/code&gt; decorators, type safety, and maintainable schema evolution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-Series Telemetry (&lt;code&gt;processing-service&lt;/code&gt;) $\rightarrow$ Raw &lt;code&gt;pg.Pool&lt;/code&gt; Micro-Batches&lt;/strong&gt;:
During a 60x storm surge, &lt;code&gt;processing-service&lt;/code&gt; commits &lt;strong&gt;500 rows every 250ms&lt;/strong&gt; into our TimescaleDB hypertable (&lt;code&gt;weather_metrics&lt;/code&gt;). Using an ORM on this hot path would instantiate 500 class instances per batch, run reflection metadata, and trigger heavy V8 garbage collection pauses. Instead, we build a single parameterized multi-row SQL &lt;code&gt;INSERT&lt;/code&gt; and stream it directly over the PostgreSQL wire protocol:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// High-throughput parameterized multi-row INSERT in TimescaleRepository&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;valueClauses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;paramIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;valueClauses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`($&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paramIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paramIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paramIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paramIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paramIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paramIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paramIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paramIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paramIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;station_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pressure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wind_speed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wind_direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;precipitation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;battery_voltage&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mf"&gt;12.0&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  INSERT INTO weather_metrics 
    (time, station_id, temperature, humidity, pressure, wind_speed, wind_direction, precipitation, battery_voltage)
  VALUES &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;valueClauses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Combined with &lt;strong&gt;TimescaleDB 1-day hypertable chunks&lt;/strong&gt; and &lt;strong&gt;Continuous Aggregates&lt;/strong&gt; (&lt;code&gt;weather_hourly_summary&lt;/code&gt;), historical queries over millions of rows return in milliseconds.&lt;/p&gt;


&lt;h3&gt;
  
  
  3.3 The Closed-Loop Dynamic Frequency Controller
&lt;/h3&gt;

&lt;p&gt;How does the system react automatically when extreme weather hits?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Station: 1005 hPa] ──► [ProcessingConsumer: AnomalyDetector (ΔP &amp;gt;= 3.0 hPa)]
                                │
                                ▼ (Publishes `weather.anomaly.detected`)
                        [AlertingConsumer]
                                │
                                ▼ (Direct gRPC: UpdateReportingFrequency)
                        [StationService: Sets interval = 1s]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside &lt;code&gt;processing-service&lt;/code&gt;, the &lt;code&gt;AnomalyDetector&lt;/code&gt; tracks a sliding window of barometric pressure and wind gusts per station. If pressure drops by $\ge 3.0\text{ hPa}$ or wind speed exceeds $30\text{ m/s}$, it emits a critical event to RabbitMQ. &lt;code&gt;alerting-service&lt;/code&gt; consumes that event and invokes &lt;code&gt;station-service&lt;/code&gt; via gRPC to shift the station's reporting interval from &lt;code&gt;60s&lt;/code&gt; down to &lt;code&gt;1s&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As the 60x wave of 1-second packets hits RabbitMQ, &lt;strong&gt;KEDA (Kubernetes Event-driven Autoscaling)&lt;/strong&gt; monitors the &lt;code&gt;weather.raw.telemetry&lt;/code&gt; queue depth (&lt;code&gt;queueLength &amp;gt; 500&lt;/code&gt;) and horizontally scales &lt;code&gt;processing-service&lt;/code&gt; pods from 2 to 20 replicas.&lt;/p&gt;




&lt;h3&gt;
  
  
  3.4 Designing for Failure: Circuit Breakers, Backpressure &amp;amp; Poison Pills
&lt;/h3&gt;

&lt;p&gt;A cloud-native system is only as good as its worst failure mode. We baked in four layers of defense:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Opossum Circuit Breakers on gRPC&lt;/strong&gt;:
Every gRPC call from &lt;code&gt;ingestion-service&lt;/code&gt; to &lt;code&gt;station-service&lt;/code&gt; is wrapped in an &lt;strong&gt;Opossum circuit breaker&lt;/strong&gt; (&lt;code&gt;timeout: 2000ms&lt;/code&gt;, &lt;code&gt;errorThresholdPercentage: 50%&lt;/code&gt;, &lt;code&gt;resetTimeout: 5000ms&lt;/code&gt;). If &lt;code&gt;station-service&lt;/code&gt; crashes, the circuit trips to &lt;code&gt;OPEN&lt;/code&gt; and immediately executes a fallback handler that validates the station ID format (&lt;code&gt;/^ST-[A-Z0-9-]+$/&lt;/code&gt;) and accepts the telemetry in degraded mode—resulting in &lt;strong&gt;zero client timeouts or HTTP 500 errors&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RabbitMQ Publisher Confirms &amp;amp; Consumer Prefetch&lt;/strong&gt;:
&lt;code&gt;ingestion-service&lt;/code&gt; uses AMQP &lt;code&gt;ConfirmChannel&lt;/code&gt; so a station only receives an &lt;code&gt;ACK&lt;/code&gt; once the broker confirms persistence. Downstream, &lt;code&gt;processing-service&lt;/code&gt; sets &lt;code&gt;channel.prefetch(200)&lt;/code&gt; and &lt;code&gt;noAck: false&lt;/code&gt;, guaranteeing that a slow database never causes out-of-memory crashes in Node.js workers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead Letter Exchanges (DLX / DLQ)&lt;/strong&gt;:
If a malfunctioning sensor sends corrupted JSON or unprocessable frames ("poison pills"), &lt;code&gt;processing-service&lt;/code&gt; rejects the message with &lt;code&gt;channel.nack(msg, false, false)&lt;/code&gt;, routing it straight to &lt;code&gt;weather.telemetry.dlq&lt;/code&gt; so it never blocks healthy telemetry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Power-Aware Telemetry (&lt;code&gt;battery_voltage&lt;/code&gt;)&lt;/strong&gt;:
Remote alpine and buoy stations run on solar-charged batteries. Because 1-second burst mode drastically increases radio transmission power draw, every telemetry frame includes &lt;code&gt;battery_voltage&lt;/code&gt; so operators can monitor discharge curves and prevent stations from going dark mid-storm.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. Proving It Works: Load Testing (k6) &amp;amp; Chaos Engineering
&lt;/h2&gt;

&lt;p&gt;Claims about scalability and resilience mean nothing without automated verification. We built two dedicated test suites into the repository:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. 60x Dynamic Spike Simulation (&lt;code&gt;test/k6/scenarios/dynamic-frequency-spike.js&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Using &lt;strong&gt;Grafana k6&lt;/strong&gt;'s &lt;code&gt;ramping-arrival-rate&lt;/code&gt; executor, we simulate calm baseline traffic (&lt;code&gt;20 req/s&lt;/code&gt;), ramp to a &lt;code&gt;600 req/s&lt;/code&gt; storm front, sustain a &lt;strong&gt;60x burst (&lt;code&gt;1,200 req/s&lt;/code&gt;)&lt;/strong&gt;, and recover—enforcing strict SLO thresholds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;p(95) &amp;lt; 50ms&lt;/code&gt; and &lt;code&gt;p(99) &amp;lt; 150ms&lt;/code&gt; ingestion latency&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt; 0.1%&lt;/code&gt; error rate&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Automated Chaos Suite (&lt;code&gt;test/chaos/run-chaos-suite.sh&lt;/code&gt;)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Circuit Breaker Chaos Test&lt;/strong&gt;: Floods the gateway while &lt;code&gt;station-service&lt;/code&gt; is offline, verifying seamless fallback responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poison Pill DLQ Test&lt;/strong&gt;: Injects impossible physical readings (e.g., &lt;code&gt;-500°C&lt;/code&gt; temperature, &lt;code&gt;150%&lt;/code&gt; humidity, missing &lt;code&gt;station_id&lt;/code&gt;) and verifies ingress rejection and DLQ isolation.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Bonus: AI-Native Repository Ergonomics (&lt;code&gt;AGENTS.md&lt;/code&gt; &amp;amp; &lt;code&gt;.agents/skills&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Because we built and evolved this system alongside AI coding agents, we structured the repository with first-class &lt;strong&gt;AI Agent Customizations&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/linhtch90/weather-monitoring-system/blob/main/AGENTS.md" rel="noopener noreferrer"&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;: Always-on architectural guardrails (enforcing monorepo isolation between &lt;code&gt;apps/*&lt;/code&gt;, mandatory circuit breakers on gRPC, and hybrid persistence rules).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/linhtch90/weather-monitoring-system/tree/main/.agents/skills" rel="noopener noreferrer"&gt;&lt;code&gt;.agents/skills/&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;: On-demand runbooks (&lt;code&gt;update-grpc-contract&lt;/code&gt;, &lt;code&gt;scaffold-microservice&lt;/code&gt;, &lt;code&gt;run-verification-suite&lt;/code&gt;) so any developer or AI assistant can extend &lt;code&gt;.proto&lt;/code&gt; contracts or run the chaos suite with zero guesswork.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Yourself!
&lt;/h2&gt;

&lt;p&gt;The entire project is open-source and boots locally in minutes with Docker Compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@github.com:linhtch90/weather-monitoring-system.git
&lt;span class="nb"&gt;cd &lt;/span&gt;weather-monitoring-system
npm &lt;span class="nb"&gt;install
&lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Start the 4 microservices in watch mode:&lt;/span&gt;
npm run start:station:dev
npm run start:ingestion:dev
npm run start:processing:dev
npm run start:alerting:dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔗 &lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/linhtch90/weather-monitoring-system" rel="noopener noreferrer"&gt;linhtch90/weather-monitoring-system&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What patterns do you use to handle sudden IoT traffic spikes or hybrid SQL/Time-Series workloads? Let’s discuss in the comments!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>node</category>
      <category>microservices</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Build a microservices system with Java Spring Boot</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Sun, 06 Feb 2022 14:36:19 +0000</pubDate>
      <link>https://dev.to/linhtch90/build-a-microservices-system-with-java-spring-boot-2hn6</link>
      <guid>https://dev.to/linhtch90/build-a-microservices-system-with-java-spring-boot-2hn6</guid>
      <description>&lt;p&gt;&lt;strong&gt;Github repo for this project: (Please give me a Github star ⭐ for this effort! Thank you!)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/linhtch90/microservices-java-demo-zoo-devto" rel="noopener noreferrer"&gt;https://github.com/linhtch90/microservices-java-demo-zoo-devto&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Microservice is the concept of decomposing a large monolith application into small, independent, distributed services. This could improve the flexibility, scalability and resiliency of the whole system.&lt;/p&gt;

&lt;p&gt;The introduction of Spring Cloud framework makes it easier for developers to build and deploy microservices to private and public clouds. This framework has wrapped a wide varieties of popular cloud-management microservices frameworks under a well-organized framework.&lt;/p&gt;

&lt;p&gt;In this blog post, I want to help you have an overview about Spring Cloud framework by building a simple microservices system with Spring Cloud step by step. This system includes the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuration server&lt;/li&gt;
&lt;li&gt;Service discovery with Eureka&lt;/li&gt;
&lt;li&gt;2 demo microservices called Tiger and Panther&lt;/li&gt;
&lt;li&gt;Gateway server&lt;/li&gt;
&lt;li&gt;RabbitMQ for messaging between microservices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The relationships between those services are described as the following figure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4181ec1w31ansqdukz6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4181ec1w31ansqdukz6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's begin building this system!&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration server
&lt;/h2&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.zooconfsvr&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-zoo-config-server&lt;/li&gt;
&lt;li&gt;Name: microservice-zoo-config-server&lt;/li&gt;
&lt;li&gt;Description: zoo microservices config server&lt;/li&gt;
&lt;li&gt;JDK: I am using JDK 17 for this tutorial. I have not tested this project with older JDKs yet!&lt;/li&gt;
&lt;li&gt;Dependencies: Config Server (So other services can get their configuration information from this service)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-config-server&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a folder called &lt;code&gt;config/&lt;/code&gt; and create 4 configuration files &lt;code&gt;eureka-server.properties&lt;/code&gt;, &lt;code&gt;gateway-server.properties&lt;/code&gt;, &lt;code&gt;panther-service.properties&lt;/code&gt;, &lt;code&gt;tiger-service.properties&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableConfigServer&lt;/code&gt; annotation to the main entrance class &lt;code&gt;MicroserviceZooConfigServerApplication.java&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service discovery (with Eureka)
&lt;/h2&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.zooeurekasvr&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-zoo-services-discovery&lt;/li&gt;
&lt;li&gt;Name: microservice-zoo-services-discovery&lt;/li&gt;
&lt;li&gt;Description: zoo microservices services discovery&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka server, config client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;java.version&amp;gt;&lt;/span&gt;17&lt;span class="nt"&gt;&amp;lt;/java.version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;spring-cloud.version&amp;gt;&lt;/span&gt;2021.0.0&lt;span class="nt"&gt;&amp;lt;/spring-cloud.version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-loadbalancer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-server&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableEurekaServer&lt;/code&gt; annotation to &lt;code&gt;MicroserviceZooServicesDiscoveryApplication.java&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Gateway server
&lt;/h3&gt;

&lt;p&gt;Gateway server is the service that handles all request from client. Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.zoogateway&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-zoo-gateway&lt;/li&gt;
&lt;li&gt;Name: microservice-zoo-gateway&lt;/li&gt;
&lt;li&gt;Description: microservice gateway for the zoo demo&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka client, config client, gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;java.version&amp;gt;&lt;/span&gt;17&lt;span class="nt"&gt;&amp;lt;/java.version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;spring-cloud.version&amp;gt;&lt;/span&gt;2021.0.0&lt;span class="nt"&gt;&amp;lt;/spring-cloud.version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-gateway&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-client&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableEurekaServer&lt;/code&gt; annotation to &lt;code&gt;MicroserviceZooGatewayApplication.java&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tiger service
&lt;/h3&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.tigerservice&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-tiger&lt;/li&gt;
&lt;li&gt;Name: microservice-tiger&lt;/li&gt;
&lt;li&gt;Description: microservice tiger&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka client, config client, web, client load balancer, open feign (also for load balancer), RabbitMQ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-loadbalancer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-client&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-openfeign&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.1.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-amqp&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableFeignClients&lt;/code&gt; annotation to &lt;code&gt;MicroserviceTigerApplication.java&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.tigerservice.service&lt;/code&gt; package and add 2 files &lt;code&gt;ClientToPantherService.java&lt;/code&gt; for directly call Panther service and &lt;code&gt;RabitMQSender.java&lt;/code&gt; for sending a string message to Panther via RabbitMQ. &lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.tigerservice.controller&lt;/code&gt; package and add &lt;code&gt;TigerController.java&lt;/code&gt;, specifying controllers for calling Panther service.&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.tigerservice.component&lt;/code&gt; package and add &lt;code&gt;RabitMQSenderComponent.java&lt;/code&gt; for creating Beans related to messaging service.&lt;/li&gt;
&lt;li&gt;Please refer to my Github repo for the content of these files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Panther service
&lt;/h2&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.pantherservice&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-panther&lt;/li&gt;
&lt;li&gt;Name: microservice-panther&lt;/li&gt;
&lt;li&gt;Description: microservice panther&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka client, config client, web, client load balancer, RabbitMQ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-loadbalancer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-client&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-amqp&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create controller and other components for the services as follow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.pantherservice.controller&lt;/code&gt; package and add &lt;code&gt;TigerCallPanther.java&lt;/code&gt; for directly respond to calls from Tiger service in the form of a string.&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.pantherservice.component&lt;/code&gt; package and add 2 files &lt;code&gt;RabbitMQComponent.java&lt;/code&gt; and &lt;code&gt;RabbitMQReceiver.java&lt;/code&gt; for settings related to listening and responding to messages sent from Tiger service. Right after receiving the message from Tiger service, Panther service print the message into the Log.&lt;/li&gt;
&lt;li&gt;Please refer to my Github repo for the content of these files. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  RabbitMQ
&lt;/h2&gt;

&lt;p&gt;Please refer to &lt;a href="https://medium.com/m/global-identity?redirectUrl=https%3A%2F%2Fcodeburst.io%2Fget-started-with-rabbitmq-on-docker-4428d7f6e46b" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt; for using RabbitMQ with Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the whole system
&lt;/h2&gt;

&lt;p&gt;Please follow this order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start RabbitMQ with Docker (see above section)&lt;/li&gt;
&lt;li&gt;Start Configuration server &lt;/li&gt;
&lt;li&gt;Start Discovery service &lt;/li&gt;
&lt;li&gt;Start Tiger service &lt;/li&gt;
&lt;li&gt;Start Panther service &lt;/li&gt;
&lt;li&gt;Start Gateway server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing microservices with Postman
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Direct call from Tiger service to Panther service: Open Postman and send a request to
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8074/tiger-service/tiger/callpantherservice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response from Panther service is a string with content.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PANTHER RECEIVED CALL FROM TIGER&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send message from Tiger service to Panther service: Open Postman and send a request to
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8074/tiger-service/tiger/sendmessagetopanther
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is not any response in Postman. It is okay. Please go to the terminal screen where you are running Panther service. You will see a log message saying:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Received message is: Message from Tiger to Panther via RabbitMQ&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And that is all everybody!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you feel this project is useful for you, please give my project a Github star ⭐ as an encouragement!&lt;/p&gt;

&lt;p&gt;Thank you so much!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Microservices with Java Spring Boot</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Sat, 05 Feb 2022 15:04:06 +0000</pubDate>
      <link>https://dev.to/linhtch90/microservices-with-java-spring-boot-5bpj</link>
      <guid>https://dev.to/linhtch90/microservices-with-java-spring-boot-5bpj</guid>
      <description>&lt;p&gt;&lt;strong&gt;Github repo for this project:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/linhtch90/microservices-java-demo-zoo-devto"&gt;https://github.com/linhtch90/microservices-java-demo-zoo-devto&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Microservice is the concept of decomposing a large monolith application into small, independent, distributed services. This could improve the flexibility, scalability and resiliency of the whole system.&lt;/p&gt;

&lt;p&gt;The introduction of Spring Cloud framework makes it easier for developers to build and deploy microservices to private and public clouds. This framework has wrapped a wide varieties of popular cloud-management microservices frameworks under a well-organized framework.&lt;/p&gt;

&lt;p&gt;In this blog post, I want to help you have an overview about Spring Cloud framework by building a simple microservices system with Spring Cloud step by step. This system includes the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuration server&lt;/li&gt;
&lt;li&gt;Service discovery with Eureka&lt;/li&gt;
&lt;li&gt;2 demo microservices called Tiger and Panther&lt;/li&gt;
&lt;li&gt;Gateway server&lt;/li&gt;
&lt;li&gt;RabbitMQ for messaging between microservices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The relationships between those services are described as the following figure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bQim7yHE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6gd1rwndwlcetzxs1nsz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bQim7yHE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6gd1rwndwlcetzxs1nsz.png" alt="Image description" width="561" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's begin building this system!&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration server
&lt;/h2&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.zooconfsvr&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-zoo-config-server&lt;/li&gt;
&lt;li&gt;Name: microservice-zoo-config-server&lt;/li&gt;
&lt;li&gt;Description: zoo microservices config server&lt;/li&gt;
&lt;li&gt;JDK: I am using JDK 17 for this tutorial. I have not tested this project with older JDKs yet!&lt;/li&gt;
&lt;li&gt;Dependencies: Config Server (So other services can get their configuration information from this service)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-config-server&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a folder called &lt;code&gt;config/&lt;/code&gt; and create 4 configuration files &lt;code&gt;eureka-server.properties&lt;/code&gt;, &lt;code&gt;gateway-server.properties&lt;/code&gt;, &lt;code&gt;panther-service.properties&lt;/code&gt;, &lt;code&gt;tiger-service.properties&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableConfigServer&lt;/code&gt; annotation to the main entrance class &lt;code&gt;MicroserviceZooConfigServerApplication.java&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service discovery (with Eureka)
&lt;/h2&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.zooeurekasvr&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-zoo-services-discovery&lt;/li&gt;
&lt;li&gt;Name: microservice-zoo-services-discovery&lt;/li&gt;
&lt;li&gt;Description: zoo microservices services discovery&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka server, config client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;java.version&amp;gt;&lt;/span&gt;17&lt;span class="nt"&gt;&amp;lt;/java.version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;spring-cloud.version&amp;gt;&lt;/span&gt;2021.0.0&lt;span class="nt"&gt;&amp;lt;/spring-cloud.version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-loadbalancer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-server&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableEurekaServer&lt;/code&gt; annotation to &lt;code&gt;MicroserviceZooServicesDiscoveryApplication.java&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Gateway server
&lt;/h3&gt;

&lt;p&gt;Gateway server is the service that handles all request from client. Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.zoogateway&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-zoo-gateway&lt;/li&gt;
&lt;li&gt;Name: microservice-zoo-gateway&lt;/li&gt;
&lt;li&gt;Description: microservice gateway for the zoo demo&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka client, config client, gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;java.version&amp;gt;&lt;/span&gt;17&lt;span class="nt"&gt;&amp;lt;/java.version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;spring-cloud.version&amp;gt;&lt;/span&gt;2021.0.0&lt;span class="nt"&gt;&amp;lt;/spring-cloud.version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-gateway&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-client&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableEurekaServer&lt;/code&gt; annotation to &lt;code&gt;MicroserviceZooGatewayApplication.java&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tiger service
&lt;/h3&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.tigerservice&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-tiger&lt;/li&gt;
&lt;li&gt;Name: microservice-tiger&lt;/li&gt;
&lt;li&gt;Description: microservice tiger&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka client, config client, web, client load balancer, open feign (also for load balancer), RabbitMQ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-loadbalancer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-client&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-openfeign&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.1.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-amqp&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableFeignClients&lt;/code&gt; annotation to &lt;code&gt;MicroserviceTigerApplication.java&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.tigerservice.service&lt;/code&gt; package and add 2 files &lt;code&gt;ClientToPantherService.java&lt;/code&gt; for directly call Panther service and &lt;code&gt;RabitMQSender.java&lt;/code&gt; for sending a string message to Panther via RabbitMQ. &lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.tigerservice.controller&lt;/code&gt; package and add &lt;code&gt;TigerController.java&lt;/code&gt;, specifying controllers for calling Panther service.&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.tigerservice.component&lt;/code&gt; package and add &lt;code&gt;RabitMQSenderComponent.java&lt;/code&gt; for creating Beans related to messaging service.&lt;/li&gt;
&lt;li&gt;Please refer to my Github repo for the content of these files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Panther service
&lt;/h2&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.pantherservice&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-panther&lt;/li&gt;
&lt;li&gt;Name: microservice-panther&lt;/li&gt;
&lt;li&gt;Description: microservice panther&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka client, config client, web, client load balancer, RabbitMQ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-loadbalancer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-client&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-amqp&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create controller and other components for the services as follow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.pantherservice.controller&lt;/code&gt; package and add &lt;code&gt;TigerCallPanther.java&lt;/code&gt; for directly respond to calls from Tiger service in the form of a string.&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.pantherservice.component&lt;/code&gt; package and add 2 files &lt;code&gt;RabbitMQComponent.java&lt;/code&gt; and &lt;code&gt;RabbitMQReceiver.java&lt;/code&gt; for settings related to listening and responding to messages sent from Tiger service. Right after receiving the message from Tiger service, Panther service print the message into the Log.&lt;/li&gt;
&lt;li&gt;Please refer to my Github repo for the content of these files. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  RabbitMQ
&lt;/h2&gt;

&lt;p&gt;Please refer to &lt;a href="https://medium.com/m/global-identity?redirectUrl=https%3A%2F%2Fcodeburst.io%2Fget-started-with-rabbitmq-on-docker-4428d7f6e46b"&gt;this tutorial&lt;/a&gt; for using RabbitMQ with Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the whole system
&lt;/h2&gt;

&lt;p&gt;Please follow this order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start RabbitMQ with Docker (see above section)&lt;/li&gt;
&lt;li&gt;Start Configuration server &lt;/li&gt;
&lt;li&gt;Start Discovery service &lt;/li&gt;
&lt;li&gt;Start Tiger service &lt;/li&gt;
&lt;li&gt;Start Panther service &lt;/li&gt;
&lt;li&gt;Start Gateway server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing microservices with Postman
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Direct call from Tiger service to Panther service: Open Postman and send a request to
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8074/tiger-service/tiger/callpantherservice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result should look like the following figure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3IFE0-V1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l8ptdl71bwqbyatcfrem.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3IFE0-V1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l8ptdl71bwqbyatcfrem.png" alt="Image description" width="880" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send message from Tiger service to Panther service: Open Postman and send a request to
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8074/tiger-service/tiger/sendmessagetopanther
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is not any response in Postman. It is okay. Please go to the terminal screen where you are running Panther service. You will see a log message saying:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Received message is: Message from Tiger to Panther via RabbitMQ&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And that is all everybody!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you feel this project is useful for you, please give my project a Github star ⭐ as an encouragement!&lt;/p&gt;

&lt;p&gt;Thank you so much!&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Build a microservices system with Java</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Sat, 05 Feb 2022 11:47:12 +0000</pubDate>
      <link>https://dev.to/linhtch90/build-a-microservices-system-with-java-2noh</link>
      <guid>https://dev.to/linhtch90/build-a-microservices-system-with-java-2noh</guid>
      <description>&lt;p&gt;&lt;strong&gt;Github repo for this project: (Please give me a Github star ⭐ for this effort! Thank you!)&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/linhtch90/microservices-java-demo-zoo-devto"&gt;https://github.com/linhtch90/microservices-java-demo-zoo-devto&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Microservice is the concept of decomposing a large monolith application into small, independent, distributed services. This could improve the flexibility, scalability and resiliency of the whole system.&lt;/p&gt;

&lt;p&gt;The introduction of Spring Cloud framework makes it easier for developers to build and deploy microservices to private and public clouds. This framework has wrapped a wide varieties of popular cloud-management microservices frameworks under a well-organized framework.&lt;/p&gt;

&lt;p&gt;In this blog post, I want to help you have an overview about Spring Cloud framework by building a simple microservices system with Spring Cloud step by step. This system includes the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuration server&lt;/li&gt;
&lt;li&gt;Service discovery with Eureka&lt;/li&gt;
&lt;li&gt;2 demo microservices called Tiger and Panther&lt;/li&gt;
&lt;li&gt;Gateway server&lt;/li&gt;
&lt;li&gt;RabbitMQ for messaging between microservices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The relationships between those services are described as the following figure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---N5S-wCT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rtj39d2b1x7gnhuvr6op.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---N5S-wCT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rtj39d2b1x7gnhuvr6op.png" alt="Image description" width="561" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's begin building this system!&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuration server
&lt;/h2&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.zooconfsvr&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-zoo-config-server&lt;/li&gt;
&lt;li&gt;Name: microservice-zoo-config-server&lt;/li&gt;
&lt;li&gt;Description: zoo microservices config server&lt;/li&gt;
&lt;li&gt;JDK: I am using JDK 17 for this tutorial. I have not tested this project with older JDKs yet!&lt;/li&gt;
&lt;li&gt;Dependencies: Config Server (So other services can get their configuration information from this service)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-config-server&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a folder called &lt;code&gt;config/&lt;/code&gt; and create 4 configuration files &lt;code&gt;eureka-server.properties&lt;/code&gt;, &lt;code&gt;gateway-server.properties&lt;/code&gt;, &lt;code&gt;panther-service.properties&lt;/code&gt;, &lt;code&gt;tiger-service.properties&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableConfigServer&lt;/code&gt; annotation to the main entrance class &lt;code&gt;MicroserviceZooConfigServerApplication.java&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service discovery (with Eureka)
&lt;/h2&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.zooeurekasvr&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-zoo-services-discovery&lt;/li&gt;
&lt;li&gt;Name: microservice-zoo-services-discovery&lt;/li&gt;
&lt;li&gt;Description: zoo microservices services discovery&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka server, config client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;java.version&amp;gt;&lt;/span&gt;17&lt;span class="nt"&gt;&amp;lt;/java.version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;spring-cloud.version&amp;gt;&lt;/span&gt;2021.0.0&lt;span class="nt"&gt;&amp;lt;/spring-cloud.version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-loadbalancer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-server&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableEurekaServer&lt;/code&gt; annotation to &lt;code&gt;MicroserviceZooServicesDiscoveryApplication.java&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Gateway server
&lt;/h3&gt;

&lt;p&gt;Gateway server is the service that handles all request from client. Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.zoogateway&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-zoo-gateway&lt;/li&gt;
&lt;li&gt;Name: microservice-zoo-gateway&lt;/li&gt;
&lt;li&gt;Description: microservice gateway for the zoo demo&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka client, config client, gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;java.version&amp;gt;&lt;/span&gt;17&lt;span class="nt"&gt;&amp;lt;/java.version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;spring-cloud.version&amp;gt;&lt;/span&gt;2021.0.0&lt;span class="nt"&gt;&amp;lt;/spring-cloud.version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-gateway&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-client&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableEurekaServer&lt;/code&gt; annotation to &lt;code&gt;MicroserviceZooGatewayApplication.java&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tiger service
&lt;/h3&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.tigerservice&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-tiger&lt;/li&gt;
&lt;li&gt;Name: microservice-tiger&lt;/li&gt;
&lt;li&gt;Description: microservice tiger&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka client, config client, web, client load balancer, open feign (also for load balancer), RabbitMQ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-loadbalancer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-client&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-openfeign&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.1.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-amqp&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add &lt;code&gt;@EnableFeignClients&lt;/code&gt; annotation to &lt;code&gt;MicroserviceTigerApplication.java&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.tigerservice.service&lt;/code&gt; package and add 2 files &lt;code&gt;ClientToPantherService.java&lt;/code&gt; for directly call Panther service and &lt;code&gt;RabitMQSender.java&lt;/code&gt; for sending a string message to Panther via RabbitMQ. &lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.tigerservice.controller&lt;/code&gt; package and add &lt;code&gt;TigerController.java&lt;/code&gt;, specifying controllers for calling Panther service.&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.tigerservice.component&lt;/code&gt; package and add &lt;code&gt;RabitMQSenderComponent.java&lt;/code&gt; for creating Beans related to messaging service.&lt;/li&gt;
&lt;li&gt;Please refer to my Github repo for the content of these files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Panther service
&lt;/h2&gt;

&lt;p&gt;Go to &lt;code&gt;start.spring.io&lt;/code&gt; and generate a new project with the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group ID: com.microservices.pantherservice&lt;/li&gt;
&lt;li&gt;Artifact ID: microservice-panther&lt;/li&gt;
&lt;li&gt;Name: microservice-panther&lt;/li&gt;
&lt;li&gt;Description: microservice panther&lt;/li&gt;
&lt;li&gt;Dependencies: Eureka client, config client, web, client load balancer, RabbitMQ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;pom.xml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-bootstrap&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-loadbalancer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-netflix-eureka-client&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-config&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-amqp&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-dependencies&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring-cloud.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In folder &lt;code&gt;src/main/resources/&lt;/code&gt;, add a file called &lt;code&gt;bootstrap.properties&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;The content of those &lt;code&gt;.properties&lt;/code&gt; files could be found in my Github repo (I put the link on top of this blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create controller and other components for the services as follow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.pantherservice.controller&lt;/code&gt; package and add &lt;code&gt;TigerCallPanther.java&lt;/code&gt; for directly respond to calls from Tiger service in the form of a string.&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;com.microservices.pantherservice.component&lt;/code&gt; package and add 2 files &lt;code&gt;RabbitMQComponent.java&lt;/code&gt; and &lt;code&gt;RabbitMQReceiver.java&lt;/code&gt; for settings related to listening and responding to messages sent from Tiger service. Right after receiving the message from Tiger service, Panther service print the message into the Log.&lt;/li&gt;
&lt;li&gt;Please refer to my Github repo for the content of these files. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  RabbitMQ
&lt;/h2&gt;

&lt;p&gt;Please refer to &lt;a href="https://medium.com/m/global-identity?redirectUrl=https%3A%2F%2Fcodeburst.io%2Fget-started-with-rabbitmq-on-docker-4428d7f6e46b"&gt;this tutorial&lt;/a&gt; for using RabbitMQ with Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the whole system
&lt;/h2&gt;

&lt;p&gt;Please follow this order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start RabbitMQ with Docker (see above section)&lt;/li&gt;
&lt;li&gt;Start Configuration server &lt;/li&gt;
&lt;li&gt;Start Discovery service &lt;/li&gt;
&lt;li&gt;Start Tiger service &lt;/li&gt;
&lt;li&gt;Start Panther service &lt;/li&gt;
&lt;li&gt;Start Gateway server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing microservices with Postman
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Direct call from Tiger service to Panther service: Open Postman and send a request to
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8074/tiger-service/tiger/callpantherservice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result should look like the following figure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SHslcMQE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/daoh9nwggg4x9420aisd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SHslcMQE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/daoh9nwggg4x9420aisd.png" alt="Image description" width="880" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send message from Tiger service to Panther service: Open Postman and send a request to
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8074/tiger-service/tiger/sendmessagetopanther
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is not any response in Postman. It is okay. Please go to the terminal screen where you are running Panther service. You will see a log message saying:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Received message is: Message from Tiger to Panther via RabbitMQ&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And that is all everybody!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you feel this project is useful for you, please give my project a Github star ⭐ as an encouragement!&lt;/p&gt;

&lt;p&gt;Thank you so much!&lt;/p&gt;

</description>
      <category>java</category>
      <category>microservices</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to get start understanding source code of open source projects?</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Sat, 29 Jan 2022 09:33:57 +0000</pubDate>
      <link>https://dev.to/linhtch90/how-to-get-start-understanding-source-code-of-open-source-projects-8om</link>
      <guid>https://dev.to/linhtch90/how-to-get-start-understanding-source-code-of-open-source-projects-8om</guid>
      <description>&lt;p&gt;Hello,&lt;/p&gt;

&lt;p&gt;Open source software is awesome and important for the industry. However, whenever I try to read some code for an open source project with the intent to contribute, I get scared. I get this weird anxiety and for some reason my brain freezes. I can't understand anything what the code is doing and how it's working, it looks too complicated even if I kinda understand what things are supposed to do.&lt;/p&gt;

&lt;p&gt;I can't describe the feeling. Even if I know the language and I've learned a lot.&lt;/p&gt;

&lt;p&gt;How do I start?&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>contributorswanted</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Clone Twitter functions with ReactJS</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Wed, 26 Jan 2022 08:38:04 +0000</pubDate>
      <link>https://dev.to/linhtch90/clone-twitter-functions-with-reactjs-h64</link>
      <guid>https://dev.to/linhtch90/clone-twitter-functions-with-reactjs-h64</guid>
      <description>&lt;p&gt;I have developed a small social network web app for educational purpose and want to share this with the community. This app is built with ReactJS for frontend, Java Spring Boot for backend and MongoDB as the database.&lt;/p&gt;

&lt;p&gt;The app tries to mimic a few functions of Twitter, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create post with text and image&lt;/li&gt;
&lt;li&gt;The number of characters in each post is limited to 200 characters&lt;/li&gt;
&lt;li&gt;Like, comment and re-post a post&lt;/li&gt;
&lt;li&gt;View the number of likes, comments and shares of a post&lt;/li&gt;
&lt;li&gt;Comments are toggle&lt;/li&gt;
&lt;li&gt;The number of character in each comment is limited to 100 characters&lt;/li&gt;
&lt;li&gt;Follow, un-follow someone&lt;/li&gt;
&lt;li&gt;See who are following us and follow them back&lt;/li&gt;
&lt;li&gt;Check who we are following&lt;/li&gt;
&lt;li&gt;View all posts of an individual&lt;/li&gt;
&lt;li&gt;View our own posts&lt;/li&gt;
&lt;li&gt;List all user accounts in the entire social network (due to the number of users are still small, I did not develop search user function)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Due to limited storage space, client app compresses the uploaded image to the maximum of 250px (width or height) User avatar is created by hashing user id and convert that hash code into a hexagonal image&lt;/li&gt;
&lt;li&gt;Authentication and authorization use JWT&lt;/li&gt;
&lt;li&gt;Responsive frontend for desktop and mobile devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Technical stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: ReactJS, Redux Toolkit, React Router, React Bootstrap&lt;/li&gt;
&lt;li&gt;Backend: Java Spring Boot&lt;/li&gt;
&lt;li&gt;Database: MongoDB &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For source code and demo, please visit my Github repo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/linhtch90/psn-social-network-public-source"&gt;https://github.com/linhtch90/psn-social-network-public-source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please give me a ⭐ Github star ⭐ for my effort if you think this project is useful for you!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Develop a Twitter clone with ReactJS</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Fri, 21 Jan 2022 17:17:25 +0000</pubDate>
      <link>https://dev.to/linhtch90/develop-a-twitter-clone-with-reactjs-50dh</link>
      <guid>https://dev.to/linhtch90/develop-a-twitter-clone-with-reactjs-50dh</guid>
      <description>&lt;p&gt;I have developed a small social network web app for educational purpose and want to share this with the community. This app is built with ReactJS for frontend, Java Spring Boot for backend and MongoDB as the database.&lt;/p&gt;

&lt;p&gt;The app tries to mimic a few functions of Twitter, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create post with text and image&lt;/li&gt;
&lt;li&gt;The number of characters in each post is limited to 200 characters&lt;/li&gt;
&lt;li&gt;Like, comment and re-post a post&lt;/li&gt;
&lt;li&gt;View the number of likes, comments and shares of a post&lt;/li&gt;
&lt;li&gt;Comments are toggle&lt;/li&gt;
&lt;li&gt;The number of character in each comment is limited to 100 characters&lt;/li&gt;
&lt;li&gt;Follow, un-follow someone&lt;/li&gt;
&lt;li&gt;See who are following us and follow them back&lt;/li&gt;
&lt;li&gt;Check who we are following&lt;/li&gt;
&lt;li&gt;View all posts of an individual&lt;/li&gt;
&lt;li&gt;View our own posts&lt;/li&gt;
&lt;li&gt;List all user accounts in the entire social network (due to the number of users are still small, I did not develop search user function)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Due to limited storage space, client app compresses the uploaded image to the maximum of 250px (width or height) User avatar is created by hashing user id and convert that hash code into a hexagonal image&lt;/li&gt;
&lt;li&gt;Authentication and authorization use JWT&lt;/li&gt;
&lt;li&gt;Responsive frontend for desktop and mobile devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Technical stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: ReactJS, Redux Toolkit, React Router, React Bootstrap&lt;/li&gt;
&lt;li&gt;Backend: Java Spring Boot&lt;/li&gt;
&lt;li&gt;Database: MongoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please give me a ⭐ Github star ⭐ for my effort if you think this project is useful for you!&lt;/p&gt;

&lt;p&gt;Live Demo and Source Code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/linhtch90/psn-social-network-public-source"&gt;https://github.com/linhtch90/psn-social-network-public-source&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>100daysofcode</category>
    </item>
    <item>
      <title>Develope a twitter clone with ReactJS</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Tue, 18 Jan 2022 05:03:14 +0000</pubDate>
      <link>https://dev.to/linhtch90/develope-a-twitter-clone-with-reactjs-2gh5</link>
      <guid>https://dev.to/linhtch90/develope-a-twitter-clone-with-reactjs-2gh5</guid>
      <description>&lt;p&gt;I have developed a small social network web app for educational purpose and want to share this with the community. This app is built with ReactJS for frontend, Java Spring Boot for backend and MongoDB as the database.&lt;/p&gt;

&lt;p&gt;The app tries to mimic a few functions of Twitter, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create post with text and image&lt;/li&gt;
&lt;li&gt;The number of characters in each post is limited to 200 characters&lt;/li&gt;
&lt;li&gt;Like, comment and re-post a post&lt;/li&gt;
&lt;li&gt;View the number of likes, comments and shares of a post&lt;/li&gt;
&lt;li&gt;Comments are toggle&lt;/li&gt;
&lt;li&gt;The number of character in each comment is limited to 100 characters&lt;/li&gt;
&lt;li&gt;Follow, un-follow someone&lt;/li&gt;
&lt;li&gt;See who are following us and follow them back&lt;/li&gt;
&lt;li&gt;Check who we are following&lt;/li&gt;
&lt;li&gt;View all posts of an individual&lt;/li&gt;
&lt;li&gt;View our own posts&lt;/li&gt;
&lt;li&gt;List all user accounts in the entire social network (due to the number of users are still small, I did not develop search user function)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Due to limited storage space, client app compresses the uploaded image to the maximum of 250px (width or height) User avatar is created by hashing user id and convert that hash code into a hexagonal image&lt;/li&gt;
&lt;li&gt;Authentication and authorization use JWT&lt;/li&gt;
&lt;li&gt;Responsive frontend for desktop and mobile devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Technical stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: ReactJS, Redux Toolkit, React Router, React Bootstrap&lt;/li&gt;
&lt;li&gt;Backend: Java Spring Boot&lt;/li&gt;
&lt;li&gt;Database: MongoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope you enjoy it and give me a ⭐ Github star ⭐ for my effort!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live Demo and Source Code:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/linhtch90/psn-social-network-public-source"&gt;https://github.com/linhtch90/psn-social-network-public-source&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>node</category>
    </item>
    <item>
      <title>Code a social network with ReactJS</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Mon, 17 Jan 2022 08:55:12 +0000</pubDate>
      <link>https://dev.to/linhtch90/code-a-social-network-with-reactjs-2936</link>
      <guid>https://dev.to/linhtch90/code-a-social-network-with-reactjs-2936</guid>
      <description>&lt;p&gt;I have developed a small social network web app for educational purpose and want to share this with the community. This app is built with ReactJS for frontend, Java Spring Boot for backend and MongoDB as the database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--emH6jDZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fhtck1mho63dkhp3a54u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--emH6jDZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fhtck1mho63dkhp3a54u.png" alt="Image description" width="880" height="1265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app tries to mimic a few functions of Twitter, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create post with text and image&lt;/li&gt;
&lt;li&gt;The number of characters in each post is limited to 200 characters&lt;/li&gt;
&lt;li&gt;Like, comment and re-post a post&lt;/li&gt;
&lt;li&gt;View the number of likes, comments and shares of a post&lt;/li&gt;
&lt;li&gt;Comments are toggle&lt;/li&gt;
&lt;li&gt;The number of character in each comment is limited to 100 characters&lt;/li&gt;
&lt;li&gt;Follow, un-follow someone&lt;/li&gt;
&lt;li&gt;See who are following us and follow them back&lt;/li&gt;
&lt;li&gt;Check who we are following&lt;/li&gt;
&lt;li&gt;View all posts of an individual&lt;/li&gt;
&lt;li&gt;View our own posts&lt;/li&gt;
&lt;li&gt;List all user accounts in the entire social network (due to the number of users are still small, I did not develop search user function)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Due to limited storage space, client app compresses the uploaded image to the maximum of 250px (width or height) User avatar is created by hashing user id and convert that hash code into a hexagonal image&lt;/li&gt;
&lt;li&gt;Authentication and authorization use JWT&lt;/li&gt;
&lt;li&gt;Responsive frontend for desktop and mobile devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Technical stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: ReactJS, Redux Toolkit, React Router, React Bootstrap&lt;/li&gt;
&lt;li&gt;Backend: Java Spring Boot&lt;/li&gt;
&lt;li&gt;Database: MongoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope you enjoy it and give me a ⭐ Github star ⭐ for my effort!&lt;/p&gt;

&lt;p&gt;Live Demo:&lt;br&gt;
&lt;a href="https://psn-social-network.herokuapp.com/#/"&gt;https://psn-social-network.herokuapp.com/#/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source Code:&lt;br&gt;
&lt;a href="https://github.com/linhtch90/psn-social-network-public-source"&gt;https://github.com/linhtch90/psn-social-network-public-source&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Build a social network app with ReactJS, Spring Boot and MongoDB</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Fri, 14 Jan 2022 02:07:30 +0000</pubDate>
      <link>https://dev.to/linhtch90/build-a-social-network-app-with-reactjs-spring-boot-and-mongodb-31p9</link>
      <guid>https://dev.to/linhtch90/build-a-social-network-app-with-reactjs-spring-boot-and-mongodb-31p9</guid>
      <description>&lt;p&gt;I have developed a small social network web app for educational purpose and want to share this with the community. This app is built with ReactJS for frontend, Java Spring Boot for backend and MongoDB as the database.&lt;/p&gt;

&lt;p&gt;The app tries to mimic a few functions of Twitter, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create post with text and image&lt;/li&gt;
&lt;li&gt;The number of characters in each post is limited to 200 characters&lt;/li&gt;
&lt;li&gt;Like, comment and re-post a post&lt;/li&gt;
&lt;li&gt;View the number of likes, comments and shares of a post&lt;/li&gt;
&lt;li&gt;Comments are toggle&lt;/li&gt;
&lt;li&gt;The number of character in each comment is limited to 100 characters&lt;/li&gt;
&lt;li&gt;Follow, un-follow someone&lt;/li&gt;
&lt;li&gt;See who are following us and follow them back&lt;/li&gt;
&lt;li&gt;Check who we are following&lt;/li&gt;
&lt;li&gt;View all posts of an individual&lt;/li&gt;
&lt;li&gt;View our own posts&lt;/li&gt;
&lt;li&gt;List all user accounts in the entire social network (due to the number of users are still small, I did not develop search user function)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Due to limited storage space, client app compresses the uploaded image to the maximum of 250px (width or height)&lt;/li&gt;
&lt;li&gt;User avatar is created by hashing user id and convert that hash code into a hexagonal image&lt;/li&gt;
&lt;li&gt;Authentication and authorization use JWT&lt;/li&gt;
&lt;li&gt;Responsive frontend for desktop and mobile devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Technical stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: ReactJS, Redux Toolkit, React Router, React Bootstrap&lt;/li&gt;
&lt;li&gt;Backend: Java Spring Boot&lt;/li&gt;
&lt;li&gt;Database: MongoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this project is useful for you , please give me a Github star ⭐ for my effort!&lt;/p&gt;

&lt;p&gt;Source Code:&lt;br&gt;
&lt;a href="https://github.com/linhtch90/psn-social-network-public-source"&gt;https://github.com/linhtch90/psn-social-network-public-source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Live Demo:&lt;br&gt;
&lt;a href="https://psn-social-network.herokuapp.com/#/"&gt;https://psn-social-network.herokuapp.com/#/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>java</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to build a social network app with ReactJS</title>
      <dc:creator>Linh Truong Cong Hong</dc:creator>
      <pubDate>Thu, 13 Jan 2022 07:51:38 +0000</pubDate>
      <link>https://dev.to/linhtch90/how-to-build-a-social-network-app-with-reactjs-34d3</link>
      <guid>https://dev.to/linhtch90/how-to-build-a-social-network-app-with-reactjs-34d3</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fiEBHOZI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e3cisbw1uoop1p1dd2j3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fiEBHOZI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e3cisbw1uoop1p1dd2j3.png" alt="Image description" width="880" height="1265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have developed a small social network web app for educational purpose and want to share this with the community. This app is built with ReactJS for frontend, Java Spring Boot for backend and MongoDB as the database.&lt;/p&gt;

&lt;p&gt;The app tries to mimic a few functions of Twitter, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create post with text and image&lt;/li&gt;
&lt;li&gt;The number of characters in each post is limited to 200 characters&lt;/li&gt;
&lt;li&gt;Like, comment and re-post a post&lt;/li&gt;
&lt;li&gt;View the number of likes, comments and shares of a post&lt;/li&gt;
&lt;li&gt;Comments are toggle&lt;/li&gt;
&lt;li&gt;The number of character in each comment is limited to 100 characters&lt;/li&gt;
&lt;li&gt;Follow, un-follow someone&lt;/li&gt;
&lt;li&gt;See who are following us and follow them back&lt;/li&gt;
&lt;li&gt;Check who we are following&lt;/li&gt;
&lt;li&gt;View all posts of an individual&lt;/li&gt;
&lt;li&gt;View our own posts&lt;/li&gt;
&lt;li&gt;List all user accounts in the entire social network (due to the number of users are still small, I did not develop search user function)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Due to limited storage space, client app compresses the uploaded image to the maximum of 250px (width or height)&lt;/li&gt;
&lt;li&gt;User avatar is created by hashing user id and convert that hash code into a hexagonal image&lt;/li&gt;
&lt;li&gt;Authentication and authorization use JWT&lt;/li&gt;
&lt;li&gt;Responsive frontend for desktop and mobile devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Technical stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: ReactJS, Redux Toolkit, React Router, React Bootstrap&lt;/li&gt;
&lt;li&gt;Backend: Java Spring Boot&lt;/li&gt;
&lt;li&gt;Database: MongoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this project is useful for you , please give me a ⭐ Github star ⭐ for my effort!&lt;/p&gt;

&lt;p&gt;Source Code:&lt;br&gt;
&lt;a href="https://github.com/linhtch90/psn-social-network-public-source"&gt;https://github.com/linhtch90/psn-social-network-public-source&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
