炜平 發表於 2025-10-31 23:57:00

【RabbitMQ】RPC模式(请求/回复)

<h2>本章目标</h2>
<ul>
<li>
<p class="ds-markdown-paragraph">理解RabbitMQ RPC模式的工作原理和适用场景。</p>
</li>
<li>
<p class="ds-markdown-paragraph">掌握回调队列(Callback Queue)和关联ID(Correlation Id)的使用。</p>
</li>
<li>
<p class="ds-markdown-paragraph">实现基于RabbitMQ的异步RPC调用。</p>
</li>
<li>
<p class="ds-markdown-paragraph">学习RPC模式下的错误处理和超时机制。</p>
</li>
<li>
<p class="ds-markdown-paragraph">构建完整的微服务间同步通信解决方案。</p>
</li>
</ul>
<hr>
<h2>一、理论部分</h2>
<h3>1. RPC模式简介</h3>
<p class="ds-markdown-paragraph">RPC(Remote Procedure Call)模式允许客户端应用程序调用远程服务器上的方法,就像调用本地方法一样。在RabbitMQ中,RPC是通过消息队列实现的异步RPC。</p>
<p class="ds-markdown-paragraph">与传统HTTP RPC的区别:</p>
<ul>
<li>
<p class="ds-markdown-paragraph">HTTP RPC:同步,直接连接,需要服务端在线</p>
</li>
<li>
<p class="ds-markdown-paragraph">消息队列RPC:异步,通过消息代理,支持解耦和负载均衡</p>
</li>
</ul>
<h3>2. RabbitMQ RPC核心组件</h3>
<ol start="1">
<li>
<p class="ds-markdown-paragraph">请求队列(Request Queue):客户端发送请求的队列</p>
</li>
<li>
<p class="ds-markdown-paragraph">回复队列(Reply Queue):服务器返回响应的队列</p>
</li>
<li>
<p class="ds-markdown-paragraph">关联ID(Correlation Id):匹配请求和响应的唯一标识</p>
</li>
<li>
<p class="ds-markdown-paragraph">消息属性:使用<code>IBasicProperties.ReplyTo</code>和<code>IBasicProperties.CorrelationId</code></p>
</li>
</ol>
<h3>3. RPC工作流程</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Client端:
</span>1.<span style="color: rgba(0, 0, 0, 1)"> 生成唯一CorrelationId
</span>2.<span style="color: rgba(0, 0, 0, 1)"> 创建临时回复队列
</span>3.<span style="color: rgba(0, 0, 0, 1)"> 发送请求到请求队列,设置ReplyTo和CorrelationId
</span>4.<span style="color: rgba(0, 0, 0, 1)"> 监听回复队列,等待匹配的CorrelationId

Server端:
</span>1.<span style="color: rgba(0, 0, 0, 1)"> 监听请求队列
</span>2.<span style="color: rgba(0, 0, 0, 1)"> 处理请求
</span>3.<span style="color: rgba(0, 0, 0, 1)"> 将响应发送到请求中的ReplyTo队列
</span>4.<span style="color: rgba(0, 0, 0, 1)"> 设置相同的CorrelationId

Client端:
</span>5.<span style="color: rgba(0, 0, 0, 1)"> 收到响应,根据CorrelationId匹配请求
</span>6. 处理响应</pre>
</div>
<h3>4. 适用场景</h3>
<ul>
<li>
<p class="ds-markdown-paragraph">需要同步响应的异步操作</p>
</li>
<li>
<p class="ds-markdown-paragraph">微服务间的同步通信</p>
</li>
<li>
<p class="ds-markdown-paragraph">计算密集型任务的分布式处理</p>
</li>
<li>
<p class="ds-markdown-paragraph">需要负载均衡的同步调用</p>
</li>
</ul>
<hr>
<h2>二、实操部分:构建分布式计算服务</h2>
<p class="ds-markdown-paragraph">我们将创建一个分布式斐波那契数列计算服务,演示完整的RPC模式实现。</p>
<h3>第1步:创建项目结构</h3>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 创建解决方案
dotnet new sln </span>-<span style="color: rgba(0, 0, 0, 1)">n RpcSystem

# 创建项目
dotnet new webapi </span>-<span style="color: rgba(0, 0, 0, 1)">n RpcClient.API
dotnet new classlib </span>-<span style="color: rgba(0, 0, 0, 1)">n RpcClient.Core
dotnet new classlib </span>-<span style="color: rgba(0, 0, 0, 1)">n RpcServer.Service
dotnet new classlib </span>-<span style="color: rgba(0, 0, 0, 1)">n RpcShared

# 添加到解决方案
dotnet sln add RpcClient.API</span>/<span style="color: rgba(0, 0, 0, 1)">RpcClient.API.csproj
dotnet sln add RpcClient.Core</span>/<span style="color: rgba(0, 0, 0, 1)">RpcClient.Core.csproj
dotnet sln add RpcServer.Service</span>/<span style="color: rgba(0, 0, 0, 1)">RpcServer.Service.csproj
dotnet sln add RpcShared</span>/<span style="color: rgba(0, 0, 0, 1)">RpcShared.csproj

# 添加项目引用
dotnet add RpcClient.API reference RpcClient.Core
dotnet add RpcClient.API reference RpcShared
dotnet add RpcClient.Core reference RpcShared
dotnet add RpcServer.Service reference RpcShared

# 添加NuGet包
cd RpcClient.API
dotnet add package RabbitMQ.Client

cd ..</span>/<span style="color: rgba(0, 0, 0, 1)">RpcClient.Core
dotnet add package RabbitMQ.Client

cd ..</span>/<span style="color: rgba(0, 0, 0, 1)">RpcServer.Service
dotnet add package RabbitMQ.Client</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<h3>第2步:定义共享模型(RpcShared)</h3>
<p class="ds-markdown-paragraph">Models/RpcRequest.cs</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code"><img id="code_img_closed_77709edd-5bf9-4570-9206-07a0dddd9b5c" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_77709edd-5bf9-4570-9206-07a0dddd9b5c" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_77709edd-5bf9-4570-9206-07a0dddd9b5c" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Text.Json.Serialization;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Models
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> RpcRequest
    {
      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> RequestId { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } =<span style="color: rgba(0, 0, 0, 1)"> Guid.NewGuid().ToString();

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Method { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.Empty;

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> Dictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>&gt; Parameters { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">();

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> DateTime Timestamp { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } =<span style="color: rgba(0, 0, 0, 1)"> DateTime.UtcNow;

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> RpcRequest WithParameter(<span style="color: rgba(0, 0, 255, 1)">string</span> key, <span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> value)
      {
            Parameters </span>=<span style="color: rgba(0, 0, 0, 1)"> value;
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> T? GetParameter&lt;T&gt;(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> key)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (Parameters.TryGetValue(key, <span style="color: rgba(0, 0, 255, 1)">out</span> <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> value))
            {
                </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
                {
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span> (T)Convert.ChangeType(value, <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(T));
                }
                </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)">
                {
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">;
                }
            }
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">;
      }
    }
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</div>
</div>
</div>
</div>
</div>
<p class="ds-markdown-paragraph">Models/RpcResponse.cs</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code"><img id="code_img_closed_19c44d6f-3a19-4851-b55a-04994a79eb1b" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_19c44d6f-3a19-4851-b55a-04994a79eb1b" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_19c44d6f-3a19-4851-b55a-04994a79eb1b" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Text.Json.Serialization;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Models
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> RpcResponse
    {
      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> RequestId { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.Empty;

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">bool</span> Success { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">object</span>? Data { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span>? Error { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> DateTime Timestamp { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } =<span style="color: rgba(0, 0, 0, 1)"> DateTime.UtcNow;

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">long</span> ProcessingTimeMs { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> RpcResponse SuccessResponse(<span style="color: rgba(0, 0, 255, 1)">string</span> requestId, <span style="color: rgba(0, 0, 255, 1)">object</span> data, <span style="color: rgba(0, 0, 255, 1)">long</span> processingTimeMs = <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RpcResponse
            {
                RequestId </span>=<span style="color: rgba(0, 0, 0, 1)"> requestId,
                Success </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
                Data </span>=<span style="color: rgba(0, 0, 0, 1)"> data,
                ProcessingTimeMs </span>=<span style="color: rgba(0, 0, 0, 1)"> processingTimeMs
            };
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> RpcResponse ErrorResponse(<span style="color: rgba(0, 0, 255, 1)">string</span> requestId, <span style="color: rgba(0, 0, 255, 1)">string</span> error, <span style="color: rgba(0, 0, 255, 1)">long</span> processingTimeMs = <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RpcResponse
            {
                RequestId </span>=<span style="color: rgba(0, 0, 0, 1)"> requestId,
                Success </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
                Error </span>=<span style="color: rgba(0, 0, 0, 1)"> error,
                ProcessingTimeMs </span>=<span style="color: rgba(0, 0, 0, 1)"> processingTimeMs
            };
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> T? GetData&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)">()
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (Data <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> JsonElement jsonElement)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> jsonElement.Deserialize&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)">();
            }
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> Data <span style="color: rgba(0, 0, 255, 1)">is</span> T typedData ? typedData : <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">;
      }
    }
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</div>
</div>
</div>
</div>
</div>
<p class="ds-markdown-paragraph">Messages/FibonacciRequest.cs</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code"><img id="code_img_closed_71fd4a21-0c1a-4670-b7ee-7a6c52775d76" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_71fd4a21-0c1a-4670-b7ee-7a6c52775d76" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_71fd4a21-0c1a-4670-b7ee-7a6c52775d76" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Messages
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FibonacciRequest
    {
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Number { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">bool</span> UseOptimizedAlgorithm { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FibonacciResponse
    {
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">long</span> Result { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">long</span> CalculationTimeMs { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> InputNumber { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
    }
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</div>
</div>
</div>
</div>
</div>
<h3>第3步:RPC客户端核心库(RpcClient.Core)</h3>
<p class="ds-markdown-paragraph">Services/IRpcClient.cs</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code"><img id="code_img_closed_3d4563a6-460d-4125-8248-07ff2013cc7e" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_3d4563a6-460d-4125-8248-07ff2013cc7e" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_3d4563a6-460d-4125-8248-07ff2013cc7e" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Models;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.Core.Services
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> IRpcClient : IDisposable
    {
      Task</span>&lt;RpcResponse&gt;<span style="color: rgba(0, 0, 0, 1)"> CallAsync(RpcRequest request, TimeSpan timeout);
      Task</span>&lt;TResponse?&gt; CallAsync&lt;TResponse&gt;(RpcRequest request, TimeSpan timeout) <span style="color: rgba(0, 0, 255, 1)">where</span> TResponse : <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</div>
</div>
</div>
</div>
</div>
<p class="ds-markdown-paragraph">Services/RpcClient.cs</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Collections.Concurrent;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Text;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Text.Json;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Microsoft.Extensions.Logging;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RabbitMQ.Client;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RabbitMQ.Client.Events;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Models;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.Core.Services
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient : IRpcClient
    {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IConnection _connection;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IModel _channel;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger&lt;RpcClient&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> _replyQueueName;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ConcurrentDictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, TaskCompletionSource&lt;RpcResponse&gt;&gt;<span style="color: rgba(0, 0, 0, 1)"> _pendingRequests;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> AsyncEventingBasicConsumer _consumer;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">bool</span> _disposed = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;

      </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient(
            IConnectionFactory connectionFactory,
            ILogger</span>&lt;RpcClient&gt;<span style="color: rgba(0, 0, 0, 1)"> logger)
      {
            _logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;
            _pendingRequests </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> ConcurrentDictionary&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>, TaskCompletionSource&lt;RpcResponse&gt;&gt;<span style="color: rgba(0, 0, 0, 1)">();

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 建立连接和通道</span>
            _connection =<span style="color: rgba(0, 0, 0, 1)"> connectionFactory.CreateConnection();
            _channel </span>=<span style="color: rgba(0, 0, 0, 1)"> _connection.CreateModel();

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 声明临时回复队列(排他性,连接关闭时自动删除)</span>
            _replyQueueName =<span style="color: rgba(0, 0, 0, 1)"> _channel.QueueDeclare(
                queue: </span><span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">,
                durable: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
                exclusive: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
                autoDelete: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
                arguments: </span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">).QueueName;

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建消费者监听回复队列</span>
            _consumer = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> AsyncEventingBasicConsumer(_channel);
            _consumer.Received </span>+=<span style="color: rgba(0, 0, 0, 1)"> OnResponseReceived;

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 开始消费回复队列</span>
<span style="color: rgba(0, 0, 0, 1)">            _channel.BasicConsume(
                queue: _replyQueueName,
                autoAck: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
                consumer: _consumer);

            _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC Client initialized with reply queue: {ReplyQueue}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, _replyQueueName);
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;RpcResponse&gt;<span style="color: rgba(0, 0, 0, 1)"> CallAsync(RpcRequest request, TimeSpan timeout)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (_disposed)
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ObjectDisposedException(nameof(RpcClient));

            </span><span style="color: rgba(0, 0, 255, 1)">var</span> tcs = <span style="color: rgba(0, 0, 255, 1)">new</span> TaskCompletionSource&lt;RpcResponse&gt;<span style="color: rgba(0, 0, 0, 1)">();
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> cancellationTokenSource = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> CancellationTokenSource(timeout);

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注册超时取消</span>
            cancellationTokenSource.Token.Register(() =&gt;<span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (_pendingRequests.TryRemove(request.RequestId, <span style="color: rgba(0, 0, 255, 1)">out</span> <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> removedTcs))
                {
                  removedTcs.TrySetException(</span><span style="color: rgba(0, 0, 255, 1)">new</span> TimeoutException($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC call timed out after {timeout.TotalSeconds} seconds</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">));
                  _logger.LogWarning(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC request {RequestId} timed out</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, request.RequestId);
                }
            });

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将请求添加到待处理字典</span>
            <span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">_pendingRequests.TryAdd(request.RequestId, tcs))
            {
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> InvalidOperationException($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Request with ID {request.RequestId} is already pending</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            }

            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 序列化请求</span>
                <span style="color: rgba(0, 0, 255, 1)">var</span> requestJson =<span style="color: rgba(0, 0, 0, 1)"> JsonSerializer.Serialize(request);
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> requestBody =<span style="color: rgba(0, 0, 0, 1)"> Encoding.UTF8.GetBytes(requestJson);

                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置消息属性</span>
                <span style="color: rgba(0, 0, 255, 1)">var</span> properties =<span style="color: rgba(0, 0, 0, 1)"> _channel.CreateBasicProperties();
                properties.ReplyTo </span>=<span style="color: rgba(0, 0, 0, 1)"> _replyQueueName;
                properties.CorrelationId </span>=<span style="color: rgba(0, 0, 0, 1)"> request.RequestId;
                properties.Persistent </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;

                _logger.LogDebug(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Sending RPC request {RequestId} to queue: rpc_queue</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, request.RequestId);

                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发布请求到RPC队列</span>
<span style="color: rgba(0, 0, 0, 1)">                _channel.BasicPublish(
                  exchange: </span><span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">,
                  routingKey: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">rpc_queue</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                  basicProperties: properties,
                  body: requestBody);

                _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC request {RequestId} sent successfully</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, request.RequestId);

                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 等待响应</span>
                <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> tcs.Task;
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
            {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发生异常时移除待处理请求</span>
                _pendingRequests.TryRemove(request.RequestId, <span style="color: rgba(0, 0, 255, 1)">out</span><span style="color: rgba(0, 0, 0, 1)"> _);
                _logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error sending RPC request {RequestId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, request.RequestId);
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
            }
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;TResponse?&gt; CallAsync&lt;TResponse&gt;(RpcRequest request, TimeSpan timeout) <span style="color: rgba(0, 0, 255, 1)">where</span> TResponse : <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> response = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> CallAsync(request, timeout);
            
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">response.Success)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> InvalidOperationException($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC call failed: {response.Error}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            }

            </span><span style="color: rgba(0, 0, 255, 1)">return</span> response.GetData&lt;TResponse&gt;<span style="color: rgba(0, 0, 0, 1)">();
      }

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task OnResponseReceived(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, BasicDeliverEventArgs ea)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> responseBody =<span style="color: rgba(0, 0, 0, 1)"> ea.Body.ToArray();
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> responseJson =<span style="color: rgba(0, 0, 0, 1)"> Encoding.UTF8.GetString(responseBody);
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> correlationId =<span style="color: rgba(0, 0, 0, 1)"> ea.BasicProperties.CorrelationId;

            _logger.LogDebug(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Received RPC response for correlation ID: {CorrelationId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, correlationId);

            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> response = JsonSerializer.Deserialize&lt;RpcResponse&gt;<span style="color: rgba(0, 0, 0, 1)">(responseJson);
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (response == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
                {
                  _logger.LogError(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Failed to deserialize RPC response for correlation ID: {CorrelationId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, correlationId);
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
                }

                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 查找匹配的待处理请求</span>
                <span style="color: rgba(0, 0, 255, 1)">if</span> (_pendingRequests.TryRemove(correlationId, <span style="color: rgba(0, 0, 255, 1)">out</span> <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> tcs))
                {
                  tcs.TrySetResult(response);
                  _logger.LogDebug(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC response for {CorrelationId} delivered to waiting task</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, correlationId);
                }
                </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
                {
                  _logger.LogWarning(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Received response for unknown correlation ID: {CorrelationId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, correlationId);
                }

                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 手动确认消息</span>
                _channel.BasicAck(ea.DeliveryTag, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
            {
                _logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error processing RPC response for correlation ID: {CorrelationId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, correlationId);
               
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理失败时拒绝消息(不重新入队)</span>
                _channel.BasicNack(ea.DeliveryTag, <span style="color: rgba(0, 0, 255, 1)">false</span>, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">);
               
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果反序列化失败,仍然通知等待的任务</span>
                <span style="color: rgba(0, 0, 255, 1)">if</span> (_pendingRequests.TryRemove(correlationId, <span style="color: rgba(0, 0, 255, 1)">out</span> <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> tcs))
                {
                  tcs.TrySetException(</span><span style="color: rgba(0, 0, 255, 1)">new</span> InvalidOperationException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Failed to process RPC response</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">));
                }
            }

            </span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> Task.CompletedTask;
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Dispose()
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">_disposed)
            {
                _disposed </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;

                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 取消所有待处理的请求</span>
                <span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> (requestId, tcs) <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> _pendingRequests)
                {
                  tcs.TrySetCanceled();
                }
                _pendingRequests.Clear();

                _channel</span>?<span style="color: rgba(0, 0, 0, 1)">.Close();
                _channel</span>?<span style="color: rgba(0, 0, 0, 1)">.Dispose();
                _connection</span>?<span style="color: rgba(0, 0, 0, 1)">.Close();
                _connection</span>?<span style="color: rgba(0, 0, 0, 1)">.Dispose();

                _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC Client disposed</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            }
      }
    }
}</span></pre>
</div>
<p class="ds-markdown-paragraph">Services/FibonacciRpcClient.cs</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.Core.Services;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Messages;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Models;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.Core.Services
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FibonacciRpcClient
    {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IRpcClient _rpcClient;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger&lt;FibonacciRpcClient&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> FibonacciRpcClient(IRpcClient rpcClient, ILogger&lt;FibonacciRpcClient&gt;<span style="color: rgba(0, 0, 0, 1)"> logger)
      {
            _rpcClient </span>=<span style="color: rgba(0, 0, 0, 1)"> rpcClient;
            _logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;<span style="color: rgba(0, 0, 255, 1)">long</span>&gt; CalculateFibonacciAsync(<span style="color: rgba(0, 0, 255, 1)">int</span> number, <span style="color: rgba(0, 0, 255, 1)">bool</span> useOptimized = <span style="color: rgba(0, 0, 255, 1)">true</span>, TimeSpan? timeout = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> request = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RpcRequest
            {
                Method </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fibonacci.calculate</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                Timestamp </span>=<span style="color: rgba(0, 0, 0, 1)"> DateTime.UtcNow
            }
            .WithParameter(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">number</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number)
            .WithParameter(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">useOptimized</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, useOptimized);

            timeout </span>??= TimeSpan.FromSeconds(<span style="color: rgba(128, 0, 128, 1)">30</span><span style="color: rgba(0, 0, 0, 1)">);

            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Calculating Fibonacci({Number}) with timeout {Timeout}s</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                  number, timeout.Value.TotalSeconds);

                </span><span style="color: rgba(0, 0, 255, 1)">var</span> response = <span style="color: rgba(0, 0, 255, 1)">await</span> _rpcClient.CallAsync&lt;FibonacciResponse&gt;<span style="color: rgba(0, 0, 0, 1)">(request, timeout.Value);
               
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (response != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
                {
                  _logger.LogInformation(
                        </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Fibonacci({Number}) = {Result} (calculated in {Time}ms)</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                        number, response.Result, response.CalculationTimeMs);
                  
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> response.Result;
                }

                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> InvalidOperationException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Received null response from RPC server</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (TimeoutException ex)
            {
                _logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Fibonacci calculation timed out for number {Number}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number);
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
            {
                _logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error calculating Fibonacci for number {Number}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number);
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
            }
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;FibonacciResponse&gt; CalculateFibonacciDetailedAsync(<span style="color: rgba(0, 0, 255, 1)">int</span> number, <span style="color: rgba(0, 0, 255, 1)">bool</span> useOptimized = <span style="color: rgba(0, 0, 255, 1)">true</span>, TimeSpan? timeout = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> request = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RpcRequest
            {
                Method </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fibonacci.calculate</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                Timestamp </span>=<span style="color: rgba(0, 0, 0, 1)"> DateTime.UtcNow
            }
            .WithParameter(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">number</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number)
            .WithParameter(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">useOptimized</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, useOptimized);

            timeout </span>??= TimeSpan.FromSeconds(<span style="color: rgba(128, 0, 128, 1)">30</span><span style="color: rgba(0, 0, 0, 1)">);

            </span><span style="color: rgba(0, 0, 255, 1)">var</span> response = <span style="color: rgba(0, 0, 255, 1)">await</span> _rpcClient.CallAsync&lt;FibonacciResponse&gt;<span style="color: rgba(0, 0, 0, 1)">(request, timeout.Value);
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> response ?? <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> InvalidOperationException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Received null response from RPC server</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      }
    }
}</span></pre>
</div>
<h3>第4步:RPC客户端API(RpcClient.API)</h3>
<p class="ds-markdown-paragraph">Program.cs</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code"><img id="code_img_closed_410c1ae7-c51d-4563-ad99-06f1777d5911" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_410c1ae7-c51d-4563-ad99-06f1777d5911" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_410c1ae7-c51d-4563-ad99-06f1777d5911" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.API.Services;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.Core.Services;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Models;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RabbitMQ.Client;

</span><span style="color: rgba(0, 0, 255, 1)">var</span> builder =<span style="color: rgba(0, 0, 0, 1)"> WebApplication.CreateBuilder(args);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Add services to the container.</span>
<span style="color: rgba(0, 0, 0, 1)">builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Configure RabbitMQ</span>
builder.Services.AddSingleton&lt;IConnectionFactory&gt;(sp =&gt;<span style="color: rgba(0, 0, 0, 1)">
{
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> configuration = sp.GetRequiredService&lt;IConfiguration&gt;<span style="color: rgba(0, 0, 0, 1)">();
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConnectionFactory
    {
      HostName </span>= configuration[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RabbitMQ:HostName</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
      UserName </span>= configuration[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RabbitMQ:UserName</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
      Password </span>= configuration[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RabbitMQ:Password</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
      Port </span>= <span style="color: rgba(0, 0, 255, 1)">int</span>.Parse(configuration[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RabbitMQ:Port</span><span style="color: rgba(128, 0, 0, 1)">"</span>] ?? <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">5672</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">),
      VirtualHost </span>= configuration[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RabbitMQ:VirtualHost</span><span style="color: rgba(128, 0, 0, 1)">"</span>] ?? <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
      DispatchConsumersAsync </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
    };
});

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Register RPC services</span>
builder.Services.AddSingleton&lt;IRpcClient, RpcClient&gt;<span style="color: rgba(0, 0, 0, 1)">();
builder.Services.AddScoped</span>&lt;FibonacciRpcClient&gt;<span style="color: rgba(0, 0, 0, 1)">();
builder.Services.AddScoped</span>&lt;IMathRpcService, MathRpcService&gt;<span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Add health checks</span>
<span style="color: rgba(0, 0, 0, 1)">builder.Services.AddHealthChecks()
    .AddRabbitMQ(provider </span>=&gt;<span style="color: rgba(0, 0, 0, 1)">
    {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> factory = provider.GetRequiredService&lt;IConfiguration&gt;<span style="color: rgba(0, 0, 0, 1)">();
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> factory.CreateConnection();
    });

</span><span style="color: rgba(0, 0, 255, 1)">var</span> app =<span style="color: rgba(0, 0, 0, 1)"> builder.Build();

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Configure the HTTP request pipeline.</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.MapHealthChecks(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/health</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);

app.Run();</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</div>
</div>
</div>
</div>
</div>
<p class="ds-markdown-paragraph">Services/IMathRpcService.cs</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code"><img id="code_img_closed_732cbec5-d5c4-4b36-94e8-70c72624a747" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_732cbec5-d5c4-4b36-94e8-70c72624a747" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_732cbec5-d5c4-4b36-94e8-70c72624a747" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Messages;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.API.Services
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> IMathRpcService
    {
      Task</span>&lt;<span style="color: rgba(0, 0, 255, 1)">long</span>&gt; CalculateFibonacciAsync(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> number);
      Task</span>&lt;FibonacciResponse&gt; CalculateFibonacciDetailedAsync(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> number);
      Task</span>&lt;<span style="color: rgba(0, 0, 255, 1)">bool</span>&gt;<span style="color: rgba(0, 0, 0, 1)"> HealthCheckAsync();
    }
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</div>
</div>
</div>
</div>
</div>
<p class="ds-markdown-paragraph">Services/MathRpcService.cs</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code"><img id="code_img_closed_8cd15916-b98b-4de2-9ca3-7f9a90540c8a" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_8cd15916-b98b-4de2-9ca3-7f9a90540c8a" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_8cd15916-b98b-4de2-9ca3-7f9a90540c8a" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.Core.Services;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Messages;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.API.Services
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MathRpcService : IMathRpcService
    {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> FibonacciRpcClient _fibonacciClient;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger&lt;MathRpcService&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> MathRpcService(FibonacciRpcClient fibonacciClient, ILogger&lt;MathRpcService&gt;<span style="color: rgba(0, 0, 0, 1)"> logger)
      {
            _fibonacciClient </span>=<span style="color: rgba(0, 0, 0, 1)"> fibonacciClient;
            _logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;<span style="color: rgba(0, 0, 255, 1)">long</span>&gt; CalculateFibonacciAsync(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> number)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (number &lt; <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">)
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Number must be non-negative</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, nameof(number));

            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (number &gt; <span style="color: rgba(128, 0, 128, 1)">50</span><span style="color: rgba(0, 0, 0, 1)">)
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Number too large for demonstration</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, nameof(number));

            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _fibonacciClient.CalculateFibonacciAsync(number);
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;FibonacciResponse&gt; CalculateFibonacciDetailedAsync(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> number)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (number &lt; <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">)
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Number must be non-negative</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, nameof(number));

            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (number &gt; <span style="color: rgba(128, 0, 128, 1)">50</span><span style="color: rgba(0, 0, 0, 1)">)
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Number too large for demonstration</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, nameof(number));

            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _fibonacciClient.CalculateFibonacciDetailedAsync(number);
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;<span style="color: rgba(0, 0, 255, 1)">bool</span>&gt;<span style="color: rgba(0, 0, 0, 1)"> HealthCheckAsync()
      {
            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 简单的健康检查:计算 Fibonacci(1)</span>
                <span style="color: rgba(0, 0, 255, 1)">var</span> result = <span style="color: rgba(0, 0, 255, 1)">await</span> _fibonacciClient.CalculateFibonacciAsync(<span style="color: rgba(128, 0, 128, 1)">1</span>, timeout: TimeSpan.FromSeconds(<span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">));
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> result == <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
            {
                _logger.LogWarning(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC health check failed</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
            }
      }
    }
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</div>
</div>
</div>
</div>
</div>
<p class="ds-markdown-paragraph">Controllers/MathController.cs</p>
<div class="cnblogs_code"><img id="code_img_closed_6c35edc8-1d15-4d95-9ac9-10e982d35e30" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_6c35edc8-1d15-4d95-9ac9-10e982d35e30" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_6c35edc8-1d15-4d95-9ac9-10e982d35e30" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Microsoft.AspNetCore.Mvc;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.API.Services;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Messages;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.API.Controllers
{
   
    </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)]
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MathController : ControllerBase
    {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IMathRpcService _mathService;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger&lt;MathController&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> MathController(IMathRpcService mathService, ILogger&lt;MathController&gt;<span style="color: rgba(0, 0, 0, 1)"> logger)
      {
            _mathService </span>=<span style="color: rgba(0, 0, 0, 1)"> mathService;
            _logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;
      }

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;ActionResult&lt;<span style="color: rgba(0, 0, 255, 1)">long</span>&gt;&gt; CalculateFibonacci(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> number)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Calculating Fibonacci({Number}) via RPC</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number);
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> result = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _mathService.CalculateFibonacciAsync(number);
                </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Ok(result);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (ArgumentException ex)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> BadRequest(ex.Message);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (TimeoutException ex)
            {
                _logger.LogWarning(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Fibonacci calculation timed out for number {Number}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number);
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> StatusCode(<span style="color: rgba(128, 0, 128, 1)">408</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Calculation timed out</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
            {
                _logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error calculating Fibonacci for number {Number}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number);
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> StatusCode(<span style="color: rgba(128, 0, 128, 1)">500</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Internal server error</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            }
      }

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;ActionResult&lt;FibonacciResponse&gt;&gt; CalculateFibonacciDetailed(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> number)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Calculating Fibonacci({Number}) with details via RPC</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number);
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> result = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _mathService.CalculateFibonacciDetailedAsync(number);
                </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Ok(result);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (ArgumentException ex)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> BadRequest(ex.Message);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (TimeoutException ex)
            {
                _logger.LogWarning(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Fibonacci calculation timed out for number {Number}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number);
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> StatusCode(<span style="color: rgba(128, 0, 128, 1)">408</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Calculation timed out</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
            {
                _logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error calculating Fibonacci for number {Number}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number);
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> StatusCode(<span style="color: rgba(128, 0, 128, 1)">500</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Internal server error</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            }
      }

      
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;ActionResult&gt;<span style="color: rgba(0, 0, 0, 1)"> HealthCheck()
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> isHealthy = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _mathService.HealthCheckAsync();
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> isHealthy ? Ok(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC service is healthy</span><span style="color: rgba(128, 0, 0, 1)">"</span>) : StatusCode(<span style="color: rgba(128, 0, 128, 1)">503</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC service is unavailable</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      }
    }
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<h3>第5步:RPC服务器(RpcServer.Service)</h3>
<p class="ds-markdown-paragraph">Program.cs</p>
<div class="cnblogs_code"><img id="code_img_closed_f1aa8e6f-e75b-447a-b842-bf9b42b83c43" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_f1aa8e6f-e75b-447a-b842-bf9b42b83c43" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_f1aa8e6f-e75b-447a-b842-bf9b42b83c43" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcServer.Service.Services;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Models;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RabbitMQ.Client;

</span><span style="color: rgba(0, 0, 255, 1)">var</span> builder =<span style="color: rgba(0, 0, 0, 1)"> Host.CreateApplicationBuilder(args);

builder.Services.AddHostedService</span>&lt;FibonacciRpcServer&gt;<span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Configure RabbitMQ</span>
builder.Services.AddSingleton&lt;IConnectionFactory&gt;(sp =&gt;<span style="color: rgba(0, 0, 0, 1)">
{
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> configuration = sp.GetRequiredService&lt;IConfiguration&gt;<span style="color: rgba(0, 0, 0, 1)">();
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConnectionFactory
    {
      HostName </span>= configuration[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RabbitMQ:HostName</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
      UserName </span>= configuration[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RabbitMQ:UserName</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
      Password </span>= configuration[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RabbitMQ:Password</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">],
      Port </span>= <span style="color: rgba(0, 0, 255, 1)">int</span>.Parse(configuration[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RabbitMQ:Port</span><span style="color: rgba(128, 0, 0, 1)">"</span>] ?? <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">5672</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">),
      VirtualHost </span>= configuration[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RabbitMQ:VirtualHost</span><span style="color: rgba(128, 0, 0, 1)">"</span>] ?? <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
      DispatchConsumersAsync </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
    };
});

builder.Services.AddSingleton</span>&lt;FibonacciCalculator&gt;<span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(0, 0, 255, 1)">var</span> host =<span style="color: rgba(0, 0, 0, 1)"> builder.Build();
host.Run();</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p class="ds-markdown-paragraph">Services/FibonacciCalculator.cs</p>
<div class="cnblogs_code"><img id="code_img_closed_1a18592c-265f-4716-9919-93082de21825" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_1a18592c-265f-4716-9919-93082de21825" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_1a18592c-265f-4716-9919-93082de21825" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Messages;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcServer.Service.Services
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FibonacciCalculator
    {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger&lt;FibonacciCalculator&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> Dictionary&lt;<span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">long</span>&gt; _cache = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">();

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> FibonacciCalculator(ILogger&lt;FibonacciCalculator&gt;<span style="color: rgba(0, 0, 0, 1)"> logger)
      {
            _logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> FibonacciResponse Calculate(<span style="color: rgba(0, 0, 255, 1)">int</span> number, <span style="color: rgba(0, 0, 255, 1)">bool</span> useOptimized = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> startTime =<span style="color: rgba(0, 0, 0, 1)"> DateTime.UtcNow;

            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Calculating Fibonacci({Number}) with optimized: {Optimized}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                  number, useOptimized);

                </span><span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> result;
                </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (useOptimized)
                {
                  result </span>=<span style="color: rgba(0, 0, 0, 1)"> CalculateOptimized(number);
                }
                </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
                {
                  result </span>=<span style="color: rgba(0, 0, 0, 1)"> CalculateNaive(number);
                }

                </span><span style="color: rgba(0, 0, 255, 1)">var</span> calculationTime = (DateTime.UtcNow -<span style="color: rgba(0, 0, 0, 1)"> startTime).TotalMilliseconds;

                _logger.LogInformation(
                  </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Fibonacci({Number}) = {Result} (calculated in {Time}ms)</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                  number, result, calculationTime);

                </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> FibonacciResponse
                {
                  Result </span>=<span style="color: rgba(0, 0, 0, 1)"> result,
                  CalculationTimeMs </span>= (<span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)">)calculationTime,
                  InputNumber </span>=<span style="color: rgba(0, 0, 0, 1)"> number
                };
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
            {
                _logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error calculating Fibonacci({Number})</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, number);
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
            }
      }

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">long</span> CalculateOptimized(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> n)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (n &lt; <span style="color: rgba(128, 0, 128, 1)">0</span>) <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Number must be non-negative</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (n &lt;= <span style="color: rgba(128, 0, 128, 1)">1</span>) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> n;

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 检查缓存</span>
            <span style="color: rgba(0, 0, 255, 1)">if</span> (_cache.TryGetValue(n, <span style="color: rgba(0, 0, 255, 1)">out</span> <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> cachedResult))
            {
                _logger.LogDebug(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Cache hit for Fibonacci({Number})</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, n);
                </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> cachedResult;
            }

            </span><span style="color: rgba(0, 0, 255, 1)">long</span> a = <span style="color: rgba(128, 0, 128, 1)">0</span>, b = <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;

            </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">2</span>; i &lt;= n; i++<span style="color: rgba(0, 0, 0, 1)">)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> temp = a +<span style="color: rgba(0, 0, 0, 1)"> b;
                a </span>=<span style="color: rgba(0, 0, 0, 1)"> b;
                b </span>=<span style="color: rgba(0, 0, 0, 1)"> temp;

                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 缓存中间结果</span>
                <span style="color: rgba(0, 0, 255, 1)">if</span> (i % <span style="color: rgba(128, 0, 128, 1)">10</span> == <span style="color: rgba(128, 0, 128, 1)">0</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 每10个数缓存一次以减少内存使用</span>
<span style="color: rgba(0, 0, 0, 1)">                {
                  _cache </span>=<span style="color: rgba(0, 0, 0, 1)"> b;
                }
            }

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 缓存最终结果</span>
            _cache =<span style="color: rgba(0, 0, 0, 1)"> b;
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> b;
      }

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">long</span> CalculateNaive(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> n)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (n &lt; <span style="color: rgba(128, 0, 128, 1)">0</span>) <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Number must be non-negative</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (n &lt;= <span style="color: rgba(128, 0, 128, 1)">1</span>) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> n;

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模拟计算密集型任务</span>
            Thread.Sleep(<span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">);

            </span><span style="color: rgba(0, 0, 255, 1)">return</span> CalculateNaive(n - <span style="color: rgba(128, 0, 128, 1)">1</span>) + CalculateNaive(n - <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">);
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> ClearCache()
      {
            _cache.Clear();
            _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Fibonacci cache cleared</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      }
    }
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p class="ds-markdown-paragraph">Services/FibonacciRpcServer.cs</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code"><img id="code_img_closed_69ad11e1-0e48-4361-bfa7-207270c9088f" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_69ad11e1-0e48-4361-bfa7-207270c9088f" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_69ad11e1-0e48-4361-bfa7-207270c9088f" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Text;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Text.Json;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Microsoft.Extensions.Options;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RabbitMQ.Client;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RabbitMQ.Client.Events;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Messages;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Models;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcServer.Service.Services
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FibonacciRpcServer : BackgroundService
    {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IConnection _connection;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IModel _channel;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> FibonacciCalculator _calculator;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger&lt;FibonacciRpcServer&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> <span style="color: rgba(0, 0, 255, 1)">string</span> QueueName = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">rpc_queue</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;

      </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> FibonacciRpcServer(
            IConnectionFactory connectionFactory,
            FibonacciCalculator calculator,
            ILogger</span>&lt;FibonacciRpcServer&gt;<span style="color: rgba(0, 0, 0, 1)"> logger)
      {
            _calculator </span>=<span style="color: rgba(0, 0, 0, 1)"> calculator;
            _logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 建立连接和通道</span>
            _connection =<span style="color: rgba(0, 0, 0, 1)"> connectionFactory.CreateConnection();
            _channel </span>=<span style="color: rgba(0, 0, 0, 1)"> _connection.CreateModel();

            InitializeQueue();
      }

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> InitializeQueue()
      {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 声明RPC请求队列(持久化)</span>
<span style="color: rgba(0, 0, 0, 1)">            _channel.QueueDeclare(
                queue: QueueName,
                durable: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
                exclusive: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
                autoDelete: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
                arguments: </span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置公平分发,每次只处理一个请求</span>
            _channel.BasicQos(prefetchSize: <span style="color: rgba(128, 0, 128, 1)">0</span>, prefetchCount: <span style="color: rgba(128, 0, 128, 1)">1</span>, <span style="color: rgba(0, 0, 255, 1)">global</span>: <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">);

            _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Fibonacci RPC Server initialized and listening on queue: {QueueName}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, QueueName);
      }

      </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> Task ExecuteAsync(CancellationToken stoppingToken)
      {
            stoppingToken.ThrowIfCancellationRequested();

            </span><span style="color: rgba(0, 0, 255, 1)">var</span> consumer = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> AsyncEventingBasicConsumer(_channel);
            consumer.Received </span>+= <span style="color: rgba(0, 0, 255, 1)">async</span> (model, ea) =&gt;<span style="color: rgba(0, 0, 0, 1)">
            {
                RpcResponse response </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
                </span><span style="color: rgba(0, 0, 255, 1)">string</span> correlationId =<span style="color: rgba(0, 0, 0, 1)"> ea.BasicProperties.CorrelationId;
                </span><span style="color: rgba(0, 0, 255, 1)">string</span> replyTo =<span style="color: rgba(0, 0, 0, 1)"> ea.BasicProperties.ReplyTo;

                _logger.LogDebug(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Received RPC request with correlation ID: {CorrelationId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, correlationId);

                </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
                {
                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理请求</span>
                  response = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> ProcessRequestAsync(ea.Body.ToArray());
                  response.RequestId </span>=<span style="color: rgba(0, 0, 0, 1)"> correlationId;
                }
                </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
                {
                  _logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error processing RPC request {CorrelationId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, correlationId);
                  response </span>=<span style="color: rgba(0, 0, 0, 1)"> RpcResponse.ErrorResponse(correlationId, ex.Message);
                }
                </span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
                {
                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送响应</span>
                  <span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.IsNullOrEmpty(replyTo))
                  {
                        SendResponse(replyTo, correlationId, response </span>??<span style="color: rgba(0, 0, 0, 1)">
                            RpcResponse.ErrorResponse(correlationId, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Unknown error occurred</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">));
                  }

                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 确认消息处理完成</span>
                  _channel.BasicAck(ea.DeliveryTag, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">);
                }
            };

            _channel.BasicConsume(
                queue: QueueName,
                autoAck: </span><span style="color: rgba(0, 0, 255, 1)">false</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 手动确认</span>
<span style="color: rgba(0, 0, 0, 1)">                consumer: consumer);

            _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Fibonacci RPC Server started successfully</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);

            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Task.CompletedTask;
      }

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;RpcResponse&gt; ProcessRequestAsync(<span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">[] body)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> startTime =<span style="color: rgba(0, 0, 0, 1)"> DateTime.UtcNow;

            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> requestJson =<span style="color: rgba(0, 0, 0, 1)"> Encoding.UTF8.GetString(body);
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> request = JsonSerializer.Deserialize&lt;RpcRequest&gt;<span style="color: rgba(0, 0, 0, 1)">(requestJson);

                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (request == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
                {
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span> RpcResponse.ErrorResponse(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">unknown</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Invalid request format</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
                }

                _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Processing RPC request {RequestId}, Method: {Method}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                  request.RequestId, request.Method);

                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 根据方法名路由到不同的处理逻辑</span>
                <span style="color: rgba(0, 0, 255, 1)">object</span> result = request.Method.ToLowerInvariant() <span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)">
                {
                  </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fibonacci.calculate</span><span style="color: rgba(128, 0, 0, 1)">"</span> =&gt;<span style="color: rgba(0, 0, 0, 1)"> ProcessFibonacciRequest(request),
                  </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ping</span><span style="color: rgba(128, 0, 0, 1)">"</span> =&gt; <span style="color: rgba(0, 0, 255, 1)">new</span> { message = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">pong</span><span style="color: rgba(128, 0, 0, 1)">"</span>, timestamp =<span style="color: rgba(0, 0, 0, 1)"> DateTime.UtcNow },
                  _ </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> NotSupportedException($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Method {request.Method} is not supported</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
                };

                </span><span style="color: rgba(0, 0, 255, 1)">var</span> processingTime = (DateTime.UtcNow -<span style="color: rgba(0, 0, 0, 1)"> startTime).TotalMilliseconds;

                </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> RpcResponse.SuccessResponse(
                  request.RequestId,
                  result,
                  (</span><span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)">)processingTime);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> processingTime = (DateTime.UtcNow -<span style="color: rgba(0, 0, 0, 1)"> startTime).TotalMilliseconds;
                _logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error processing RPC request</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
               
                </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> RpcResponse.ErrorResponse(
                  </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">unknown</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                  ex.Message,
                  (</span><span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)">)processingTime);
            }
      }

      </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> FibonacciResponse ProcessFibonacciRequest(RpcRequest request)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> number = request.GetParameter&lt;<span style="color: rgba(0, 0, 255, 1)">int</span>&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">number</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> useOptimized = request.GetParameter&lt;<span style="color: rgba(0, 0, 255, 1)">bool</span>&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">useOptimized</span><span style="color: rgba(128, 0, 0, 1)">"</span>) ?? <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;

            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (number &lt; <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Fibonacci number must be non-negative</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            }

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 防止过大的计算消耗资源</span>
            <span style="color: rgba(0, 0, 255, 1)">if</span> (number &gt; <span style="color: rgba(128, 0, 128, 1)">50</span><span style="color: rgba(0, 0, 0, 1)">)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Number too large for calculation</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            }

            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> _calculator.Calculate(number, useOptimized);
      }

      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> SendResponse(<span style="color: rgba(0, 0, 255, 1)">string</span> replyTo, <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> correlationId, RpcResponse response)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> responseJson =<span style="color: rgba(0, 0, 0, 1)"> JsonSerializer.Serialize(response);
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> responseBody =<span style="color: rgba(0, 0, 0, 1)"> Encoding.UTF8.GetBytes(responseJson);

                </span><span style="color: rgba(0, 0, 255, 1)">var</span> properties =<span style="color: rgba(0, 0, 0, 1)"> _channel.CreateBasicProperties();
                properties.CorrelationId </span>=<span style="color: rgba(0, 0, 0, 1)"> correlationId;
                properties.Persistent </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;

                _channel.BasicPublish(
                  exchange: </span><span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">,
                  routingKey: replyTo,
                  basicProperties: properties,
                  body: responseBody);

                _logger.LogDebug(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Sent RPC response for correlation ID: {CorrelationId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, correlationId);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
            {
                _logger.LogError(ex, </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error sending RPC response for correlation ID: {CorrelationId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, correlationId);
            }
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Dispose()
      {
            _channel</span>?<span style="color: rgba(0, 0, 0, 1)">.Close();
            _channel</span>?<span style="color: rgba(0, 0, 0, 1)">.Dispose();
            _connection</span>?<span style="color: rgba(0, 0, 0, 1)">.Close();
            _connection</span>?<span style="color: rgba(0, 0, 0, 1)">.Dispose();
            
            _logger.LogInformation(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Fibonacci RPC Server disposed</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            
            </span><span style="color: rgba(0, 0, 255, 1)">base</span><span style="color: rgba(0, 0, 0, 1)">.Dispose();
      }
    }
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</div>
</div>
</div>
</div>
</div>
<h3>第6步:高级特性 - 带重试的RPC客户端</h3>
<p class="ds-markdown-paragraph">Services/ResilientRpcClient.cs</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code"><img id="code_img_closed_eada9a74-74bb-47fe-90af-f1f75478e1e3" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_eada9a74-74bb-47fe-90af-f1f75478e1e3" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_eada9a74-74bb-47fe-90af-f1f75478e1e3" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Microsoft.Extensions.Logging;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Polly;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Polly.Retry;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> RpcShared.Models;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> RpcClient.Core.Services
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ResilientRpcClient : IRpcClient
    {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IRpcClient _innerClient;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger&lt;ResilientRpcClient&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> AsyncRetryPolicy _retryPolicy;

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> ResilientRpcClient(IRpcClient innerClient, ILogger&lt;ResilientRpcClient&gt;<span style="color: rgba(0, 0, 0, 1)"> logger)
      {
            _innerClient </span>=<span style="color: rgba(0, 0, 0, 1)"> innerClient;
            _logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 配置重试策略</span>
            _retryPolicy =<span style="color: rgba(0, 0, 0, 1)"> Policy
                .Handle</span>&lt;TimeoutException&gt;<span style="color: rgba(0, 0, 0, 1)">()
                .Or</span>&lt;InvalidOperationException&gt;<span style="color: rgba(0, 0, 0, 1)">()
                .WaitAndRetryAsync(
                  retryCount: </span><span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">,
                  sleepDurationProvider: retryAttempt </span>=&gt; TimeSpan.FromSeconds(Math.Pow(<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">, retryAttempt)),
                  onRetry: (exception, delay, retryCount, context) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)">
                  {
                        _logger.LogWarning(
                            </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RPC call failed. Retry {RetryCount} after {Delay}ms. Error: {Error}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                            retryCount, delay.TotalMilliseconds, exception.Message);
                  });
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;RpcResponse&gt;<span style="color: rgba(0, 0, 0, 1)"> CallAsync(RpcRequest request, TimeSpan timeout)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> _retryPolicy.ExecuteAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> () =&gt;<span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _innerClient.CallAsync(request, timeout);
            });
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;TResponse?&gt; CallAsync&lt;TResponse&gt;(RpcRequest request, TimeSpan timeout) <span style="color: rgba(0, 0, 255, 1)">where</span> TResponse : <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> _retryPolicy.ExecuteAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> () =&gt;<span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> _innerClient.CallAsync&lt;TResponse&gt;<span style="color: rgba(0, 0, 0, 1)">(request, timeout);
            });
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Dispose()
      {
            _innerClient</span>?<span style="color: rgba(0, 0, 0, 1)">.Dispose();
      }
    }
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
</div>
</div>
</div>
</div>
</div>
<h3>第7步:运行与测试</h3>
<ol start="1">
<li>
<p class="ds-markdown-paragraph">启动服务</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 终端1:启动RPC服务器
cd RpcServer.Service
dotnet run

# 终端2:启动RPC客户端API
cd RpcClient.API
dotnet run</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</li>
<li>
<p class="ds-markdown-paragraph">测试API</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 计算斐波那契数列
curl </span>-X GET <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">https://localhost:7000/api/math/fibonacci/10</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
curl </span>-X GET <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">https://localhost:7000/api/math/fibonacci/20/detailed</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">

# 健康检查
curl </span>-X GET <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">https://localhost:7000/api/math/health</span><span style="color: rgba(128, 0, 0, 1)">"</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</li>
<li>
<p class="ds-markdown-paragraph">测试错误场景</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 测试超时(设置很小的超时时间)
# 测试无效输入
curl </span>-X GET <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">https://localhost:7000/api/math/fibonacci/-5</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
curl </span>-X GET <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">https://localhost:7000/api/math/fibonacci/100</span><span style="color: rgba(128, 0, 0, 1)">"</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</li>
<li>
<p class="ds-markdown-paragraph">观察日志输出</p>
<ul>
<li>
<p class="ds-markdown-paragraph">客户端发送请求,生成CorrelationId</p>
</li>
<li>
<p class="ds-markdown-paragraph">服务器接收请求,处理计算</p>
</li>
<li>
<p class="ds-markdown-paragraph">服务器发送响应,使用相同的CorrelationId</p>
</li>
<li>
<p class="ds-markdown-paragraph">客户端接收响应,匹配CorrelationId</p>
</li>
</ul>
</li>
</ol>
<h3>第8步:性能测试和监控</h3>
<p class="ds-markdown-paragraph">创建性能测试控制器</p>
<div class="md-code-block md-code-block-light">
<div class="md-code-block-banner-wrap">
<div class="md-code-block-banner md-code-block-banner-lite">
<div class="_121d384">
<div class="d2a24f03">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)]
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> BenchmarkController : ControllerBase
{
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IMathRpcService _mathService;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> ILogger&lt;BenchmarkController&gt;<span style="color: rgba(0, 0, 0, 1)"> _logger;

    </span><span style="color: rgba(0, 0, 255, 1)">public</span> BenchmarkController(IMathRpcService mathService, ILogger&lt;BenchmarkController&gt;<span style="color: rgba(0, 0, 0, 1)"> logger)
    {
      _mathService </span>=<span style="color: rgba(0, 0, 0, 1)"> mathService;
      _logger </span>=<span style="color: rgba(0, 0, 0, 1)"> logger;
    }

   
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task&lt;ActionResult&gt; CalculateFibonacciBatch( List&lt;<span style="color: rgba(0, 0, 255, 1)">int</span>&gt;<span style="color: rgba(0, 0, 0, 1)"> numbers)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> results = <span style="color: rgba(0, 0, 255, 1)">new</span> List&lt;<span style="color: rgba(0, 0, 255, 1)">object</span>&gt;<span style="color: rgba(0, 0, 0, 1)">();
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> totalStopwatch =<span style="color: rgba(0, 0, 0, 1)"> System.Diagnostics.Stopwatch.StartNew();

      </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> number <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> numbers)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> stopwatch =<span style="color: rgba(0, 0, 0, 1)"> System.Diagnostics.Stopwatch.StartNew();
            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
            {
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> result = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _mathService.CalculateFibonacciAsync(number);
                results.Add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">
                {
                  number,
                  result,
                  success </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
                  durationMs </span>=<span style="color: rgba(0, 0, 0, 1)"> stopwatch.ElapsedMilliseconds
                });
            }
            </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
            {
                results.Add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">
                {
                  number,
                  success </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
                  error </span>=<span style="color: rgba(0, 0, 0, 1)"> ex.Message,
                  durationMs </span>=<span style="color: rgba(0, 0, 0, 1)"> stopwatch.ElapsedMilliseconds
                });
            }
      }

      </span><span style="color: rgba(0, 0, 255, 1)">return</span> Ok(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">
      {
            totalDurationMs </span>=<span style="color: rgba(0, 0, 0, 1)"> totalStopwatch.ElapsedMilliseconds,
            requests </span>=<span style="color: rgba(0, 0, 0, 1)"> numbers.Count,
            results
      });
    }
}</span></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<hr>
<h2>本章总结</h2>
<p class="ds-markdown-paragraph">在这一章中,我们完整实现了RabbitMQ的RPC模式:</p>
<ol start="1">
<li>
<p class="ds-markdown-paragraph">RPC核心概念:理解了回调队列、关联ID、请求-响应模式。</p>
</li>
<li>
<p class="ds-markdown-paragraph">客户端实现:创建了能够发送请求并异步等待响应的RPC客户端。</p>
</li>
<li>
<p class="ds-markdown-paragraph">服务器实现:构建了处理请求并返回响应的RPC服务器。</p>
</li>
<li>
<p class="ds-markdown-paragraph">错误处理:实现了超时控制、异常处理和重试机制。</p>
</li>
<li>
<p class="ds-markdown-paragraph">性能优化:使用缓存和优化算法提高计算效率。</p>
</li>
<li>
<p class="ds-markdown-paragraph">** resilience**:通过Polly实现了弹性重试策略。</p>
</li>
</ol>
<p class="ds-markdown-paragraph">RPC模式为微服务架构提供了强大的同步通信能力,结合消息队列的异步特性,既保持了系统的解耦性,又提供了同步调用的便利性。这种模式特别适合需要等待计算结果的分布式任务。</p><br><br>
来源:https://www.cnblogs.com/jixingsuiyuan/p/19181480
頁: [1]
查看完整版本: 【RabbitMQ】RPC模式(请求/回复)