iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 22
0
Software Development

.Net微服務輕旅行30天系列 第 22

Day 22: Steeltoe OSS提供的Circuit Breaker - Hystrix Netflix (2)

解決昨天的Dashboard無法顯示的問題

昨天問題是因為在執行Fortune-Teller-UI環境變數的SET BUILD=LOCAL 沒有設定到
設定好之後執行Fortune-Teller-UI,打開http://localhost:5555 在打開Hystrix dashboard就可以看到圖表了
https://ithelp.ithome.com.tw/upload/images/20180110/20107867hawyG803jf.png

Fallback

注意看可以看到上面圖表中一個黃色Timeout,這是因為我把後端的Fortune-Teller-Service關掉了,
這時候Hystrix command會自動幫我們執行服務出現錯誤時候的fallback method,就是下圖紅框的部分
https://ithelp.ithome.com.tw/upload/images/20180110/20107867i9FjOuzguN.png

來看Sample code

先到appsettings.json看hystrix的相關參數

  "hystrix": {
    "collapser": {
      "FortuneServiceCollapser": {
        "timerDelayInMilliseconds": 250
      }
    },
    "stream": {
      "validate_certificates": false
    },
    "command": {
      "FortuneService": {
        "threadPoolKeyOverride": "FortuneServiceTPool"
      }
    }
  }

collapser:HystrixCollapser的設定
command: HystrixCommand的設定

Startup.cs的ConfigureServices方法

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddLogging();

            services.AddDiscoveryClient(Configuration);

            // The Fortune service itself, calls the REST APIs to get random fortunes
            services.AddSingleton<IFortuneService, FortuneService>();

            // A Hystrix command that makes use of the FortuneService
            services.AddHystrixCommand<FortuneServiceCommand>("FortuneService", Configuration);

            services.AddTransient<IFakeService1, FakeService1>();
            services.AddTransient<IFakeService2, FakeService2>();
            services.AddTransient<IFakeServices3, FakeService3>();

            // A Hystrix collapser that makes use of the FortuneService above to get a Fortune.
            services.AddHystrixCollapser<IFortuneServiceCollapser, FortuneServiceCollapser>("FortuneServiceCollapser", Configuration);

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

            services.AddHystrixMetricsStream(Configuration);
        }

需要注意的有

  • services.AddHystrixCommand("FortuneService", Configuration);
  • services.AddHystrixCollapser<IFortuneServiceCollapser, FortuneServiceCollapser>("FortuneServiceCollapser", Configuration);
  • services.AddHystrixMetricsStream(Configuration);
    而三個類似的services.AddTransient<IFakeService1, FakeService1>(); 主要是用在Demo HystrixCollaper用的, 在官方文件裡的HystrixCollapser是直接跟後端拿資料,但是Sample code裡面用繼承HystrixCollapser的Fake取得資料,閱讀的時候可能小小搞混

HystrixCommand

先來看一下HystrxCommand的interface

    public class HystrixCommand<TResult> : AbstractCommand<TResult>, IHystrixExecutable<TResult>, IHystrixObservable<TResult>, IHystrixInvokable
    {
        protected readonly Func<TResult> _run;
        protected readonly Func<TResult> _fallback;

        public HystrixCommand(IHystrixCommandGroupKey group, Func<TResult> run = null, Func<TResult> fallback = null, ILogger logger = null);
        public HystrixCommand(IHystrixCommandOptions commandOptions, Func<TResult> run = null, Func<TResult> fallback = null, ILogger logger = null);
        public HystrixCommand(IHystrixCommandGroupKey group, IHystrixThreadPoolKey threadPool, Func<TResult> run = null, Func<TResult> fallback = null, ILogger logger = null);
        public HystrixCommand(IHystrixCommandGroupKey group, int executionIsolationThreadTimeoutInMilliseconds, Func<TResult> run = null, Func<TResult> fallback = null, ILogger logger = null);
        public HystrixCommand(IHystrixCommandGroupKey group, IHystrixThreadPoolKey threadPool, int executionIsolationThreadTimeoutInMilliseconds, Func<TResult> run = null, Func<TResult> fallback = null, ILogger logger = null);
        public HystrixCommand(IHystrixCommandGroupKey group, IHystrixCommandKey key, IHystrixThreadPoolKey threadPoolKey, IHystrixCircuitBreaker circuitBreaker, IHystrixThreadPool threadPool, IHystrixCommandOptions commandOptionsDefaults, IHystrixThreadPoolOptions threadPoolOptionsDefaults, HystrixCommandMetrics metrics, SemaphoreSlim fallbackSemaphore, SemaphoreSlim executionSemaphore, HystrixOptionsStrategy optionsStrategy, HystrixCommandExecutionHook executionHook, Func<TResult> run, Func<TResult> fallback, ILogger logger = null);

        public TResult Execute();
        public Task<TResult> ExecuteAsync();
        public Task<TResult> ExecuteAsync(CancellationToken token);
        public IObservable<TResult> Observe();
        public IObservable<TResult> Observe(CancellationToken token);
        public IObservable<TResult> ToObservable();
        protected override TResult DoFallback();
        protected override TResult DoRun();
        protected virtual TResult Run();
        [AsyncStateMachine(typeof(HystrixCommand<>.<RunAsync>d__17))]
        protected virtual Task<TResult> RunAsync();
        protected virtual TResult RunFallback();
        [AsyncStateMachine(typeof(HystrixCommand<>.<RunFallbackAsync>d__18))]
        protected virtual Task<TResult> RunFallbackAsync();

注意可以複寫的方法有

  • 同步的Run()/RunFallBack()
  • 非同步的Run()/RunFallbackAsync()
    看需求可以任意選用

接著看實作HystrixCommand的FortuneServiceCommand.cs

using Microsoft.Extensions.Logging;
using Steeltoe.CircuitBreaker.Hystrix;
using System.Threading.Tasks;

namespace Fortune_Teller_UI.Services
{
    public class FortuneServiceCommand : HystrixCommand<Fortune>
    {
        IFortuneService _fortuneService;
        ILogger<FortuneServiceCommand> _logger;

        public FortuneServiceCommand(IHystrixCommandOptions options, IFortuneService fortuneService, ILogger<FortuneServiceCommand> logger) : base(options)
        {
            _fortuneService = fortuneService;
            _logger = logger;
            IsFallbackUserDefined = true;
        }
        public async Task<Fortune> RandomFortune()
        {
            return await ExecuteAsync();
        }
        protected override async Task<Fortune> RunAsync()
        {
            var result = await _fortuneService.RandomFortuneAsync();
            _logger.LogInformation("Run: {0}", result);
            return result;
        }

        protected override async Task<Fortune> RunFallbackAsync()
        {
            _logger.LogInformation("RunFallback");
            return await Task.FromResult<Fortune>(new Fortune() { Id = 9999, Text = "You will have a happy day!" });
        }        
    }
}

Constructor有傳入IHystrixCommandOptions, 可以藉由更改options來更改Command的設定
https://ithelp.ithome.com.tw/upload/images/20180110/20107867BS66sYNcfy.png

實際上使用的FortuneService也會在這邊注入,而在這裡複寫了HystrixCommand的兩個非同步方法,
RunAsync()跟RunFallbackAsync(),可以看到一般FortuneService放在RunAsunc()裡面使用,而RunFallbackAsync則是塞了一個假資料作為服務狀態為open(斷路不正常)時候的替代方案,那怎麼調用這兩個方法,
這邊可以看到有一個新增的方法RandomFortune()作為外部調用FortuneServiceCommand的方法,然後由ExecuteAsync()來決定觸發RunAsync或是RunFallbackAsync

https://ithelp.ithome.com.tw/upload/images/20180111/20107867R7nAcCPjpo.png


上一篇
Day 21: Steeltoe OSS提供的Circuit Breaker - Hystrix Netflix (1)
下一篇
Day 23: Steeltoe OSS提供的Circuit Breaker - Hystrix Netflix (3)
系列文
.Net微服務輕旅行30天30

尚未有邦友留言

立即登入留言