Skip to main content

C#配置Eureka Client的教训

· 4 min read
Ferdinand Su

因为需要将毕设算法模块接入到一个现有的微服务系统中,我需要将我的项目包装成一个Eureka Client,并通过Eureka提供的Service Discovery来完成对其它微服务的调用。又由于我的毕设采用C#完成,需要借助开源项目SteelToe来完成。但是在开发过程中发生了一个折磨了我四天的问题,原来是配置错误导致的

为了便于测试,我们可以直接利用配置好的一个Eureka Server镜像:

docker pull steeltoeoss/eureka-server
docker run --publish 8761:8761 steeltoeoss/eureka-server

Eureka Server最好在Client之前上线。

客户端1:被调用者FakeExternalSystem

在基础的模板上,我们还需要做出如下更改:

项目文件:xxx.csproj

  <ItemGroup>
......
<PackageReference Include="Steeltoe.Discovery.ClientCore" Version="3.1.3" />
<PackageReference Include="Steeltoe.Discovery.Eureka" Version="3.1.3" />
<PackageReference Include="Steeltoe.Extensions.Logging.DynamicSerilogBase" Version="3.1.3" />
<PackageReference Include="Steeltoe.Management.EndpointCore" Version="3.1.3" />
<PackageReference Include="Steeltoe.Management.TracingCore" Version="3.1.3" />
</ItemGroup>

主文件:Program.cs


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services
.AddEndpointsApiExplorer()
.AddSwaggerGen()
.PostConfigure<AspNetCoreInstrumentationOptions>(options =>
{
options.Enrich = (activity, eventName, rawObject) =>
{
if (eventName.Equals("OnStartActivity"))
{
if (rawObject is HttpRequest httpRequest)
{
activity.SetTag("requestProtocol", httpRequest.Protocol);
}
}
};
})
.AddControllers();

builder.WebHost
.ConfigureLogging((builderContext, loggingBuilder) =>
{
// Add Serilog Dynamic Logger
loggingBuilder.AddDynamicSerilog();
})
.AddHealthActuator()
.AddInfoActuator()
.AddLoggersActuator()
.AddServiceDiscovery(options => options.UseEureka())
;

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();

}

app.MapControllers();
app.Run();

应用配置:appsettings.json

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"spring": {
"application": {
"name": "WeatherForecast"
}
},
"eureka": {
"client": {
"serviceUrl": "http://localhost:8761/eureka/",
"shouldFetchRegistry": "false",//WeatherForecast不会调用其它服务,可以设为false
"shouldRegisterWithEureka": true,
"validateCertificates": false
},
"instance": {
"port": "8080",//这里需要和launchsettings里一致
"ipAddress": "localhost",//如果用docker的话,记得改成172那个docker内网地址
"preferIpAddress": true
}
},
"AllowedHosts": "*"
}

客户端2:调用者Dsc.Server

项目文件和Program.cs的变动和上一节相同,但是要注意把自己写的服务Inject进去(这里的服务是InternalServiceProvider),并注入DiscoveryHttpMessageHandler和HttpClientFactory。

Program.cs

......
//Transient
builder.Services.AddTransient<DiscoveryHttpMessageHandler>();
//注入用于InternalServiceProvider的HttpClientFactory
builder.Services.AddHttpClient<InternalServiceProvider>()
//为Factory生成的Client自定义Handler
.AddHttpMessageHandler<DiscoveryHttpMessageHandler>();
//自己写的服务
builder.Services.AddSingleton<InternalServiceProvider>();
......

InternalServiceProvider.cs

public class InternalServiceProvider
{
private readonly ILogger<InternalServiceProvider> _logger;
private readonly IHttpClientFactory _http;

public InternalServiceProvider(
ILogger<InternalServiceProvider> logger,
IHttpClientFactory http)
{
_logger = logger;
_http = http;
}
public async ValueTask<string> GetStringAsync(
string serviceName,string ifName)
{
return await _http.CreateClient(nameof(InternalServiceProvider)).GetStringAsync($"http://{serviceName}/{ifName}");
}
}

然后我们在控制器里给一个这样的Api:

  [HttpGet("")]
public async Task<ActionResult<string>> Get()
{
return await _internalServiceProvider.GetStringAsync("WeatherForecast", "WeatherForecast");
}

最关键的还是应用配置:appsettings.json

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"spring": {
"application": {
"name": "Dsc"
}
},
"eureka": {
"client": {
"serviceUrl": "http://localhost:8761/eureka/",
"shouldFetchRegistry": "true",//非常关键,因为我们需要调用其他服务,所以一定一定要设为true!!!
"shouldRegisterWithEureka": true,
"validateCertificates": false
},
"instance": {
"port": "8081",
"ipAddress": "localhost",
"preferIpAddress": true
}
},
"AllowedHosts": "*"
}

使用docker

你可以这样获取每个容器的ip地址:

docker inspect --format='{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq)

然后把appsettings.json的eureka:instance:ipAddress改为容器地址,端口号改为80即可。