136 lines
4.9 KiB
C#
136 lines
4.9 KiB
C#
using Microsoft.Extensions.Logging;
|
||
using Nacos.V2;
|
||
using Nacos.V2.Common;
|
||
using Polly;
|
||
using Polly.Retry;
|
||
using Refit;
|
||
using System;
|
||
using System.Text.Json;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace Microservice.Common
|
||
{
|
||
public class ServiceClient : IServiceClient
|
||
{
|
||
private readonly INacosNamingService _nacosNamingService;
|
||
private readonly ILogger<ServiceClient> _logger;
|
||
private readonly AsyncRetryPolicy _retryPolicy;
|
||
|
||
public ServiceClient(INacosNamingService nacosNamingService, ILogger<ServiceClient> logger)
|
||
{
|
||
_nacosNamingService = nacosNamingService;
|
||
_logger = logger;
|
||
|
||
// 配置重试策略
|
||
_retryPolicy = Policy
|
||
.Handle<Exception>()
|
||
.WaitAndRetryAsync(
|
||
retryCount: 3,
|
||
sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt))
|
||
);
|
||
}
|
||
|
||
public async Task<TResponse> GetAsync<TResponse>(string serviceName, string endpoint, object queryParams = null)
|
||
{
|
||
try
|
||
{
|
||
var result = await SendRequestAsync<TResponse>(serviceName, endpoint, "GET", null, queryParams);
|
||
return result;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
|
||
public async Task<TResponse> PostAsync<TResponse>(string serviceName, string endpoint, object data)
|
||
{
|
||
return await SendRequestAsync<TResponse>(serviceName, endpoint, "POST", data);
|
||
}
|
||
|
||
public async Task<TResponse> PutAsync<TResponse>(string serviceName, string endpoint, object data)
|
||
{
|
||
return await SendRequestAsync<TResponse>(serviceName, endpoint, "PUT", data);
|
||
}
|
||
|
||
public async Task<TResponse> DeleteAsync<TResponse>(string serviceName, string endpoint)
|
||
{
|
||
return await SendRequestAsync<TResponse>(serviceName, endpoint, "DELETE");
|
||
}
|
||
|
||
private async Task<TResponse> SendRequestAsync<TResponse>(string serviceName, string endpoint, string method, object data = null, object queryParams = null)
|
||
{
|
||
try
|
||
{
|
||
// 从Nacos获取服务实例
|
||
var instance = await _nacosNamingService.SelectOneHealthyInstance(serviceName);
|
||
if (instance == null)
|
||
{
|
||
throw new Exception($"未找到服务 {serviceName} 的可用实例");
|
||
}
|
||
|
||
// 构建服务基础URL
|
||
var baseUrl = $"http://{instance.Ip}:{instance.Port}";
|
||
|
||
// 创建Refit服务实例
|
||
var serviceApi = RestService.For<IServiceApi>(baseUrl);
|
||
|
||
// 确保endpoint不包含前导的斜杠,因为IServiceApi中的路径模板已经包含了一个斜杠
|
||
if (!string.IsNullOrEmpty(endpoint))
|
||
{
|
||
// 移除所有前导的'/'
|
||
endpoint = endpoint.TrimStart('/');
|
||
}
|
||
|
||
// 使用重试策略
|
||
TResponse result = default;
|
||
await _retryPolicy.ExecuteAsync(async () =>
|
||
{
|
||
ApiResponse<TResponse> apiResponse;
|
||
|
||
// 根据HTTP方法调用不同的API
|
||
switch (method.ToUpper())
|
||
{
|
||
case "GET":
|
||
apiResponse = await serviceApi.GetAsync<TResponse>(endpoint);
|
||
break;
|
||
case "POST":
|
||
apiResponse = await serviceApi.PostAsync<TResponse>(endpoint, data);
|
||
break;
|
||
case "PUT":
|
||
apiResponse = await serviceApi.PutAsync<TResponse>(endpoint, data);
|
||
break;
|
||
case "DELETE":
|
||
apiResponse = await serviceApi.DeleteAsync<TResponse>(endpoint);
|
||
break;
|
||
default:
|
||
throw new NotSupportedException($"不支持的HTTP方法: {method}");
|
||
}
|
||
|
||
// 检查响应状态码
|
||
if (apiResponse.StatusCode >= 200 && apiResponse.StatusCode < 300)
|
||
{
|
||
// 成功响应,返回数据
|
||
result = apiResponse.Data;
|
||
}
|
||
else
|
||
{
|
||
// 失败响应,抛出异常
|
||
throw new ApiException(apiResponse.StatusCode, apiResponse.Message);
|
||
}
|
||
});
|
||
|
||
return result;
|
||
}
|
||
catch (ApiException)
|
||
{
|
||
// 重新抛出 ApiException
|
||
throw;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
}
|
||
} |