1. 程式人生 > 實用技巧 >.NET Core 微服務架構 Steeltoe 使用(基於 Spring Cloud)

.NET Core 微服務架構 Steeltoe 使用(基於 Spring Cloud)

.NET Core 微服務架構 Steeltoe 使用(基於 Spring Cloud)

閱讀目錄:

  • 1. Spring Cloud Eureka 註冊服務及呼叫
  • 2. Spring Cloud Hystrix 斷路器
  • 3. Spring Cloud Hystrix 指標監控
  • 4. Spring Cloud Config 配置中心

現在主流的開發平臺是微服務架構,在眾多的微服務開源專案中,Spring Cloud 非常具有代表性,但實現平臺是基於 Java,那在 .NET Core 平臺下,如何相容實現 Spring Cloud 呢?答案就是Steeltoe,或者你也可以自行實現,因為 Spring Cloud 各元件基本都是基於 REST HTTP 介面實現,你可以使用任何語言實現相容。

關於 Steeltoe 的官方介紹:

Steeltoe is an open source project that enables .NET developers to implement industry standard best practices when building resilient microservices for the cloud. The Steeltoe client libraries enable .NET Core and .NET Framework apps to easily leverage Netflix Eureka, Hystrix, Spring Cloud Config Server, and Cloud Foundry services.

這邊就不翻譯了,需要注意的幾點:

  • Netflix Eureka:服務註冊中心,實現服務註冊,以及服務發現呼叫。
  • Hystrix:斷路器,實現熔斷處理。
  • Spring Cloud Config Server:分散式配置中心,主要是讀取配置中心的資訊。
  • Cloud Foundry:開源 PaaS 雲平臺,Steeltoe 基本都執行在此平臺上,執行在其他平臺相容不好。

另外,Steeltoe 不僅支援 .NET Core,還支援 .NET Framework(具體 ASP.NET 4.x 版本)。

1. Spring Cloud Eureka 註冊服務及呼叫

專案程式碼:https://github.com/yuezhongxin/Steeltoe.Samples/tree/master/Discovery-CircuitBreaker/AspDotNetCore

首先,需要部署一個或多個 Spring Cloud Eureka 服務註冊中心,可以使用 Spring Boot 很方便進行實現,這邊就不說了。

建立一個 APS.NET Core 應用程式(2.0 版本),然後 Nuget 安裝程式包:

> install-package Pivotal.Discovery.ClientCore

appsettings.json配置檔案中,增加下面配置:

{
  "spring": {
    "application": {
      "name": "fortune-service"
    }
  },
  "eureka": {
    "client": {
      "serviceUrl": "http://192.168.1.32:8100/eureka/",
      "shouldFetchRegistry": true, //Enable or disable registering as a service
      "shouldRegisterWithEureka": true, //Enable or disable discovering services
      "validate_certificates": false
    },
    "instance": {
      //"hostName": "localhost",
      "port": 5000
    }
  }
}

這樣我們啟動 APS.NET Core 應用程式,就會將fortune-service服務註冊到 Eureka 中了。

EUREKA-CLIENT是用 Spring Boot 實現的一個服務,下面我們測試FORTUNE-SERVICE如何呼叫EUREKA-CLIENT

建立一個IEurekaClientService介面:

public interface IEurekaClientService
{
    Task<string> GetServices();
}

然後再建立IEurekaClientService介面的實現EurekaClientService

public class EurekaClientService : IEurekaClientService
{
    DiscoveryHttpClientHandler _handler;

    private const string GET_SERVICES_URL = "http://eureka-client/home";
    private ILogger<EurekaClientService> _logger;

    public EurekaClientService(IDiscoveryClient client, ILoggerFactory logFactory = null)
        :base(options)
    {
        _handler = new DiscoveryHttpClientHandler(client, logFactory?.CreateLogger<DiscoveryHttpClientHandler>());
        _logger = logFactory?.CreateLogger<EurekaClientService>();
    }

    public async Task<string> GetServices()
    {
        _logger?.LogInformation("GetServices");
        var client = GetClient();
        return await client.GetStringAsync(GET_SERVICES_URL);

    }

    private HttpClient GetClient()
    {
        var client = new HttpClient(_handler, false);
        return client;
    }
}

然後建立一個FortunesController

[Route("api")]
public class FortunesController : Controller
{
    private IEurekaClientService _eurekaClientService;
    private ILogger<FortunesController> _logger;
    public FortunesController(IEurekaClientService eurekaClientService, ILogger<FortunesController> logger)
    {
        _eurekaClientService = eurekaClientService;
        _logger = logger;
    }

    // GET: api/services
    [HttpGet("services")]
    public async Task<IActionResult> GetServices()
    {
        _logger?.LogInformation("api/services");
        return Ok(await _eurekaClientService.GetServices());
    }
}

最後在Startup.cs中,新增如下配置:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; private set; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // 載入服務註冊配置
        services.AddDiscoveryClient(Configuration);

        // Add framework services.
        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime lifetime)
    {
        app.UseStaticFiles();

        app.UseMvc();

        // 啟動服務註冊
        app.UseDiscoveryClient();
    }
}

然後重新啟動服務,執行命令:

$ curl http://192.168.1.3:5000/api/services
Services(get all by DiscoveryClient): [eureka-client, fortune-service]%

可以看到,呼叫是成功的,實際呼叫的是EUREKA-CLIENT服務的介面,獲取的是 Eureka 註冊中心,所有的註冊服務資訊。

ASP.NET 4.x 版本的實現,和上面的類似,這邊就不敘述了,可以檢視專案程式碼:https://github.com/yuezhongxin/Steeltoe.Samples/tree/master/Discovery-CircuitBreaker/AspDotNet4

2. Spring Cloud Hystrix 斷路器

專案程式碼:https://github.com/yuezhongxin/Steeltoe.Samples/tree/master/Configuration/AspDotNetCore

Spring Cloud Hystrix 的實現,需要我們對上面的專案進行改造下。

IEurekaClientService增加一個GetServicesWithHystrix介面:

public interface IEurekaClientService
{
    Task<string> GetServices();

    Task<string> GetServicesWithHystrix();
}

然後對其進行實現:

public class EurekaClientService : HystrixCommand<string>, IEurekaClientService
{
    DiscoveryHttpClientHandler _handler;

    private const string GET_SERVICES_URL = "http://eureka-client/home";
    private ILogger<EurekaClientService> _logger;

    public EurekaClientService(IHystrixCommandOptions options, IDiscoveryClient client, ILoggerFactory logFactory = null)
        :base(options)
    {
        _handler = new DiscoveryHttpClientHandler(client, logFactory?.CreateLogger<DiscoveryHttpClientHandler>());
        IsFallbackUserDefined = true;
        _logger = logFactory?.CreateLogger<EurekaClientService>();
    }

    public async Task<string> GetServices()
    {
        _logger?.LogInformation("GetServices");
        var client = GetClient();
        return await client.GetStringAsync(GET_SERVICES_URL);

    }

    public async Task<string> GetServicesWithHystrix()
    {
        _logger?.LogInformation("GetServices");
        var result = await ExecuteAsync();
        _logger?.LogInformation("GetServices returning: " + result);
        return result;
    }

    protected override async Task<string> RunAsync()
    {
        _logger?.LogInformation("RunAsync");
        var client = GetClient();
        var result = await client.GetStringAsync(GET_SERVICES_URL);
        _logger?.LogInformation("RunAsync returning: " + result);
        return result;
    }

    protected override async Task<string> RunFallbackAsync()
    {
        _logger?.LogInformation("RunFallbackAsync");
        return await Task.FromResult("This is a error(服務斷開,稍後重試)!");
    }

    private HttpClient GetClient()
    {
        var client = new HttpClient(_handler, false);
        return client;
    }
}

然後還需要在Startup.cs中添加註入:

public void ConfigureServices(IServiceCollection services)
{
    // Register FortuneService Hystrix command
    services.AddHystrixCommand<IEurekaClientService, EurekaClientService>("eureka-client", Configuration);
}

然後重啟服務,執行命令:

$ curl http://192.168.1.3:5000/api/services/hystrix
Services(get all by DiscoveryClient): [eureka-client, fortune-service]%

Hystrix 斷路器的作用,體現在呼叫服務出現問題不能訪問,這邊可以進行熔斷處理,我們把eureka-client服務停掉,然後再進行訪問測試:

$ curl http://192.168.1.3:5000/api/services/hystrix
This is a error(服務斷開,稍後重試)!%

可以看到,Hystrix 起到了作用。

ASP.NET 4.x 版本的實現,和上面的類似,這邊就不敘述了,可以檢視專案程式碼:https://github.com/yuezhongxin/Steeltoe.Samples/tree/master/Discovery-CircuitBreaker/AspDotNet4

3. Spring Cloud Hystrix 指標監控

專案程式碼:https://github.com/yuezhongxin/Steeltoe.Samples/tree/master/Discovery-CircuitBreaker/AspDotNetCore

在實際應用中,我們需要對 Hystrix 斷路器進行監控,比如熔斷請求有多少等等,Spring Cloud 中的實現有 Turbine 進行收集,資料展示的話使用 Hystrix Dashboard。

這邊需要我們先建立一個 Hystrix Dashboard 專案,我使用的 Spring Boot 進行實現,這邊就不敘述了。

我們需要再對上面的專案進行改造,在Startup.cs中新增配置,以啟動 Hystrix 指標監控。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Add Hystrix metrics stream to enable monitoring 
        services.AddHystrixMetricsStream(Configuration);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime lifetime)
    {
        // Startup Hystrix metrics stream
        app.UseHystrixMetricsStream();
    }
}

另外,還需要配置下Fortune-Teller-Service.csproj

<ItemGroup Condition="'$(BUILD)' == 'LOCAL'">
    <PackageReference Include="Steeltoe.CircuitBreaker.Hystrix.MetricsStreamCore" Version="2.0.0" />
    <PackageReference Include="RabbitMQ.Client" Version="5.0.1" />
  </ItemGroup>

<ItemGroup Condition="'$(BUILD)' == ''">
  <PackageReference Include="Steeltoe.CircuitBreaker.Hystrix.MetricsEventsCore" Version="2.0.0" />
  <PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
</ItemGroup>

然後重啟專案,然後瀏覽器開啟:http://192.168.1.3:5000/hystrix/hystrix.stream

會看到不斷實時重新整理的 Hystrix 指標監控資料了,但顯示並不友好,我們還需要在儀表盤中顯示。

瀏覽器開啟 Hystrix Dashboard(地址:http://192.168.1.31:8170/hystrix),然後在輸入框中輸入:http://192.168.1.3:5000/hystrix/hystrix.stream

然後點選 Monitor Stream 按鈕,就可以看到 Hystrix 圖形化監控了(多次請求http://192.168.1.3:5000/api/services/hystrix,以便測試):

另外,ASP.NET 4.x 版本配置的話,訪問http://192.168.1.3:5000/hystrix/hystrix.stream會報 404 錯誤,原因是 ASP.NET 4.x 版本暫不支援 Cloud Foundry 以外的平臺,詳情參見

4. Spring Cloud Config 配置中心

專案程式碼:https://github.com/yuezhongxin/Steeltoe.Samples/tree/master/Configuration/AspDotNetCore

需要注意的是,這邊只測試 Steeltoe 讀取配置中心資料,需要先開發一個 Spring Cloud Config Server 配置中心服務,這邊就不敘述了。

我使用的 GitHub 作為配置中心倉庫,xishuai-config-dev.yml配置詳情:

info:
  profile: dev
  name: xishuai7
  password: '{cipher}AQAc+v42S+FW7H5DiATfeeHY887KLwmeBq+cbXYslcQTtEBNL9a5FKbeF1qDpwrscWtGThPsbb0QFUMb03FN6yZBP2ujF29J8Fvm89igasxA7F67ohJgUku5ni9qOsMNqm5juexCTGJvzPkyinymGFYz55MUqrySZQPbRxoQU9tcfbOv9AH4xR/3DPe5krqjo3kk5pK6QWpH37rBgQZLmM7TWooyPiRkuc5Wn/1z6rQIzH5rCLqv4C8J16MAwgU1W+KTrHd4t8hIDAQG9vwkL9SYAvlz38HMKL9utu2g4c9jhAJE/H0mePlp+LDrWSgnC+R+nyH91niaUlwv3wsehP0maYCgEsTJn/3vsNouk5VCy4IGGZbkPubuJM6hE8RP0r4='

注:對password進行了加密處理。

建立一個 APS.NET Core 應用程式(2.0 版本),然後 Nuget 安裝程式包:

> install-package Steeltoe.Extensions.Configuration.ConfigServerCore

appsettings.json配置檔案中,增加下面配置:

{
  "spring": {
    "application": {
      "name": "xishuai-config" //配置檔名稱
    },
    "cloud": {
      "config": {
        "uri": "http://manager1:8180", //指向配置中心地址
        "env": "dev" //配置中心profile
      }
    }
  }
}

然後建立一個ConfigServerData模型:

public class ConfigServerData
{
    public Info Info { get; set; }
}

public class Info
{
    public string Profile { get; set; }
    public string Name { get; set; }
    public string Password { get; set; }
}

增加HomeController訪問:

public class HomeController : Controller
{
    private IOptionsSnapshot<ConfigServerData> IConfigServerData { get; set; }

    private IConfigurationRoot Config { get; set; }

    public HomeController(IConfigurationRoot config, IOptionsSnapshot<ConfigServerData> configServerData)
    {
        if (configServerData != null)
            IConfigServerData = configServerData;

        Config = config;
    }

    public IActionResult Error()
    {
        return View();
    }

    public ConfigServerData ConfigServer()
    {
        var data = IConfigServerData.Value;
        return data;
    }

    public IActionResult Reload()
    {
        if (Config != null)
        {
            Config.Reload();
        }

        return View();
    }
}

Startup.cs中增加配置:

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables()
            .AddConfigServer(env);
        Configuration = builder.Build();
    }

    public IConfiguration Configuration { get; set; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOptions();

        // Optional:  Adds IConfiguration and IConfigurationRoot to service container
        services.AddConfiguration(Configuration);

        // Adds the configuration data POCO configured with data returned from the Spring Cloud Config Server
        services.Configure<ConfigServerData>(Configuration);
    }
}

啟動專案,然後執行命令:

$ curl http://192.168.1.3:5000/home/ConfigServer
{"info":{"profile":"dev","name":"xishuai7","password":"xishuai123"}}

當配置中心資料更新了,可以訪問http://192.168.1.3:5000/home/Reload進行重新整理配置。

ASP.NET 4.x 版本的實現,和上面的類似,這邊就不敘述了,可以檢視專案程式碼:https://github.com/yuezhongxin/Steeltoe.Samples/tree/master/Configuration/AspDotNet4

參考資料: