RPC 远程调用

目前并不推荐大家使用 grpc 作为远程调用的框架首选。因为,很多的中间件目前还不支持 grpc 协议。可能会造成和中间件集成问题。
目前,微软官方推荐大家使用 IHttpClientFactory 来发送 http 请求。并在官方文档提到了开源组件 Refit
Refit 是在 IHttpClientFactory 的基础上进行了包装。把远程的 Http 调用抽象成 c# 中的接口,并通过依赖注入到运行环境进行调用。

备注

使用细节,请参见 Refit 在 github 的使用说明。 另外, IHttpClientFactory 官方集成了 Polly 。可以对调用失败的请求执行重试、熔断等操作。详细也请参考 Polly 自身的文档

集成及使用

  1. 添加项目引用

    dotnet add package Refit.HttpClientFactory
    dotnet add package Refit.Newtonsoft.Json
    dotnet add package Microsoft.Extensions.Http.Polly
    
  2. 定义调用的下游系统接口

    这里我们以调用飞书发送消息为例。

    public interface IFeiShuApi
    {
        /// <summary>
        /// 获取token
        /// </summary>
        [Post("/open-apis/auth/v3/tenant_access_token/internal")]
        Task<ApiResponse<LoginModel>> GetToken([Body(buffered: true)] TokenInput input);
        /// <summary>
        /// 发送消息
        /// </summary>
        [Post("/open-apis/im/v1/messages")]
        Task<ApiResponse<BasicResponse<SendMessageResponse>>> SendMessage(string receive_id_type, [Body(buffered: true)] FeiShuSendMessageInput input, [Authorize("Bearer")] string token);
    }
    
  3. Startup.cs 中添加依赖注入

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRefitClient<IFeiShuApi>(new RefitSettings())
                    .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://open.feishu.cn"))
                    // .AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600))) // 增加 Polly 的失败策略。
                    ;
        }
    }
    
  4. 在 Services 中调用远程 api

    public class FeiShuMessageService : IFeiShuMessageService
    {
        // 注入 IFeiShuApi
        private readonly IFeiShuApi _feiShuApi;
        public FeiShuMessageService(IFeiShuApi feiShuApi)
        {
            _feiShuApi = feiShuApi;
        }
    
        public async Task SendMessage(FeiShuSendingMsgBody msgBody, FeiShuSendMessageInput input)
        {
            var tokenRes = await _feiShuApi.GetToken(new TokenInput());
            var res = await _feiShuApi.SendMessage("user_id", input, tokenRes.Content.tenant_access_token);
            if(res.IsSuccessStatusCode)
            {
                _logger.LogInformation();
            }
            else
            {
                _logger.LogError(res.Error, res.Error.Content);
            }
        }
    }
    

错误处理

  1. 方法定义的返回值是: Task<IApiResponse>, Task<IApiResponse<T>>, Task<ApiResponse<T>> 则调用请求会被组件捕获。程序仅需要判断 response 响应状态

    var response = await _myRefitClient.GetSomeStuff();
    if(response.IsSuccessStatusCode)
    {
        //do your thing
    }
    else
    {
        _logger.LogError(response.Error, response.Error.Content);
    }
    
  2. 方法定义的返回值是:Task<T> 则调用异常需要自己处理

    try
    {
        var result = await awesomeApi.GetFooAsync("bar");
    }
    catch (ApiException exception)
    {
        //exception handling
    }