iT邦幫忙

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

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

Day 23: Steeltoe OSS提供的Circuit Breaker - Hystrix Netflix (3)

在昨天文章的尾端部分新增了關於如何觸發HystrixCommand的RunAsync()跟RunFallbackAsync(),有興趣的朋友可以看一下

調用繼承HystrixCommand的FortuneServiceCommand

來看Fortune-Teller-UI專案下,HomeController怎麼調用HystrixCommand,首先在constructor注入FortuneServiceCommand,在controller對應的方法裡面調用FortuneServiceCommand的RandomFortune()方法,藉由RandomFortune()方法來觸發ExecuteAsync(),判斷要執行RunAsync()或是RunFallbackAsync()

    [Route("/")]
    public class HomeController : Controller
    {
        FortuneServiceCommand _fortuneServiceCommand;
        IFakeService1 _service1;

        public HomeController(FortuneServiceCommand fortuneServiceCommand, IFakeService1 service1)
        {
            _fortuneServiceCommand = fortuneServiceCommand;
            _service1 = service1;
        }

        [HttpGet]
        public IActionResult Index()
        {
            return View();
        }

        [HttpGet("random")]
        public async Task<Fortune> Random()
        {
            return await _fortuneServiceCommand.RandomFortune();
        }
    }

將一段時間內多個請求合併:Hystrix Collapser

這個東西還蠻有趣的,千言萬語不如一圖!
https://raw.githubusercontent.com/wiki/Netflix/Hystrix/images/collapser-1280.png
(https://raw.githubusercontent.com/wiki/Netflix/Hystrix/images/collapser-1280.png)
由圖可以可以清楚看到Hystrix Collapser可以將來自前端的多個類似的請求合併成一個(在一定的時間間隔之內),有效減輕後端服務的負載(較少的執行緒與連線)

先看一下Hystrix Collapser定義的介面

    public abstract class HystrixCollapser<BatchReturnType, RequestResponseType, RequestArgumentType> : IHystrixExecutable<RequestResponseType>, IHystrixObservable<RequestResponseType>, IHystrixInvokable
    {
        protected internal CancellationToken token;

        protected HystrixCollapser();
        protected HystrixCollapser(IHystrixCollapserKey collapserKey);
        protected HystrixCollapser(IHystrixCollapserOptions options);
        protected HystrixCollapser(IHystrixCollapserKey collapserKey, RequestCollapserScope scope);
        protected HystrixCollapser(IHystrixCollapserKey collapserKey, RequestCollapserScope scope, ICollapserTimer timer, IHystrixCollapserOptions options);
        protected HystrixCollapser(IHystrixCollapserKey collapserKey, RequestCollapserScope scope, ICollapserTimer timer, IHystrixCollapserOptions optionsDefault, HystrixCollapserMetrics metrics);

        public virtual HystrixCollapserMetrics Metrics { get; }
        public virtual RequestCollapserScope Scope { get; }
        public virtual IHystrixCollapserKey CollapserKey { get; }
        public abstract RequestArgumentType RequestArgument { get; }
        protected virtual string CacheKey { get; }

        public RequestResponseType Execute();
        public Task<RequestResponseType> ExecuteAsync(CancellationToken token);
        public Task<RequestResponseType> ExecuteAsync();
        public IObservable<RequestResponseType> Observe();
        public IObservable<RequestResponseType> Observe(CancellationToken token);
        public IObservable<RequestResponseType> ToObservable();
        public Task<RequestResponseType> ToTask();
        protected bool AddCacheEntryIfAbsent(string cacheKey, out HystrixCachedTask<RequestResponseType> entry);
        protected abstract HystrixCommand<BatchReturnType> CreateCommand(ICollection<ICollapsedRequest<RequestResponseType, RequestArgumentType>> requests);
        protected virtual Exception DecomposeException(Exception e);
        protected abstract void MapResponseToRequests(BatchReturnType batchResponse, ICollection<ICollapsedRequest<RequestResponseType, RequestArgumentType>> requests);
        protected virtual ICollection<ICollection<ICollapsedRequest<RequestResponseType, RequestArgumentType>>> ShardRequests(ICollection<ICollapsedRequest<RequestResponseType, RequestArgumentType>> requests);
    }

以Sample來看,需要注意的參數跟方法有

BatchReturnType: 整批回傳的型別,在Sample裡就是List<Fortune>
RequestResponseType:  單個request的回傳型別Fortune  ps: 命名ResponseType好像比較好
RequestArgumentType: request的參數 例如userId productID之類的 這邊用FortuneId
CreateCommand(...): 創建合併後的HystrixCommand
MapResponseToRequests(...): 將回傳的結果對應到請求
Execute()/ExecuteAsync(): 單一請求之下由HystrixCommand負責觸發後續的RunAsync(),在合併請求下由HystrixCollapser負責

實作HystrixCollapser的部分請看FortuneServiceCollapser.cs

當然還是得定義一個合併後用的HystrixCommand, 下面是用於合併請求的MultiFortuneServiceCommand.cs

    public class MultiFortuneServiceCommand : HystrixCommand<List<Fortune>>
    {
        ILogger<MultiFortuneServiceCommand> _logger;
        ICollection<ICollapsedRequest<Fortune, int>> _requests;
        IFortuneService _fortuneService;

        public MultiFortuneServiceCommand(IHystrixCommandGroupKey groupKey, 
            ICollection<ICollapsedRequest<Fortune, int>> requests, 
            IFortuneService fortuneService, 
            ILogger<MultiFortuneServiceCommand> logger) : base(groupKey)
        {
            _fortuneService = fortuneService;
            _logger = logger;
            _requests = requests;
        }

        protected override async Task<List<Fortune>> RunAsync()
        {
            List<int> ids = new List<int>();
            foreach(var req in _requests)
            {
                ids.Add(req.Argument);
            }
            return await _fortuneService.GetFortunesAsync(ids);
        }

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

比較跟單一請求的HystrixCommand不同點是constructor傳入的參數不同,像是IHystrixCommandGroupKey跟
ICollection,此外由於在多重請求之下的ExecuteAsync()是由HystrixCollapser負責,所以HystrixCommand不會 撰寫給外部調用的方法

public FortuneServiceCommand(IHystrixCommandOptions options, IFortuneService fortuneService,
           ILogger<FortuneServiceCommand> logger) : base(options)

public MultiFortuneServiceCommand(IHystrixCommandGroupKey groupKey, 
           ICollection<ICollapsedRequest<Fortune, int>> requests, 
           IFortuneService fortuneService, 
           ILogger<MultiFortuneServiceCommand> logger) : base(groupKey)
       { 

來看一下官方文件裡面的範例HomeController ps: Sample code裡面沒有
合併請求之下使用的是HystrixCollapser而非直接用HystrixCommand,所以在constructor注入FortuneServiceCollapser,然後在GetFortuneById(int id)方法裡,直接調用HystrixCollapser的ExecuteAsync()觸發MultiFortuneServiceCommand

public class HomeController : Controller
{
   FortuneServiceCollapser _fortuneService;

   public HomeController(FortuneServiceCollapser fortuneService)
   {
       _fortuneService = fortuneService;
   }

   [HttpGet("random")]
   public async Task<Fortune> Random()
   {
       // Fortune IDs are 1000-1049
       int id = random.Next(50) + 1000;
       return GetFortuneById(id);
   }

   [HttpGet]
   public IActionResult Index()
   {
       return View();
   }

   protected Task<Fortune> GetFortuneById(int id)
   {
       // Use the FortuneServiceCollapser to obtain a Fortune by its Fortune Id
       _fortuneService.FortuneId = id;
       return _fortuneService.ExecuteAsync();
   }

}

上一篇
Day 22: Steeltoe OSS提供的Circuit Breaker - Hystrix Netflix (2)
下一篇
Day 24: Steeltoe OSS提供的Circuit Breaker - Hystrix Netflix (4)
系列文
.Net微服務輕旅行30天30

尚未有邦友留言

立即登入留言