• 定义一个 Hystrix 豪猪哥,实现net core自定义服务降级和服务熔断。


    定义一个 Hystrix 豪猪哥,实现net core自定义超时和重试。豪猪这种方式因为没办法针对每一个用户做到熔断的实现。所以分开写了,把流控放在下半段里面单独写了一个中间件。

    HystrixCommand.cs  定义

      1 /// <summary>
      2     /// 定义一个 Hystrix 豪猪哥,实现net core自定义服务降级(只有超时和重试两种策略),熔断策略放到中间件里面实现。
      3     /// </summary>
      4     /// <typeparam name="T"></typeparam>
      5     public class HystrixCommand<T> where T : ControllerBase
      6     {
      7         private ILogger<HystrixCommand<T>> _logger;
      8 
      9         private readonly T _controllerBase;
     10 
     11         /// <summary>
     12         /// 
     13         /// </summary>
     14         /// <param name="logger"></param>
     15         /// <param name="controllerBase"></param>
     16         public HystrixCommand(ILogger<HystrixCommand<T>> logger, T controllerBase)
     17         {
     18             this._logger = logger;
     19             this._controllerBase = controllerBase;
     20         }
     21 
     22         const string CURRENT_URL = "CURRENT_URL";
     23         const string RETRY_CURRENT_TIME = "RETRY_CURRENT_TIME";
     24 
     25         /// <summary>
     26         /// 超时策略
     27         /// </summary>
     28         /// <typeparam name="Result"></typeparam>
     29         /// <param name="timeOutMilliseconds">执行超过多少毫秒则认为超时(0表示不检测超时)</param>
     30         /// <returns></returns>
     31         public Policy<Result> TimeoutPolicy<Result>(int timeOutMilliseconds)
     32         {
     33             Policy<Result> policy = null;
     34             if (timeOutMilliseconds > 0)
     35             {//是否启用执行超时的检查
     36                 policy = Policy<Result>
     37                                     .Handle<TimeoutRejectedException>()
     38                                     .Fallback(default(Result), onFallback: (result, context) =>
     39                                     {
     40                                         //超时 TimeOutMilliseconds 毫秒之后的降级处理
     41                                         throw new Exception("time out", result.Exception);
     42                                     })
     43                                     .Wrap(Policy.Timeout(
     44                                             TimeSpan.FromMilliseconds(timeOutMilliseconds)
     45                                             , Polly.Timeout.TimeoutStrategy.Pessimistic
     46                                             , onTimeout: (context, timeSpan, task, exception) =>
     47                                             {
     48                                                 string url = context[CURRENT_URL] as string;
     49                                                 //超时
     50                                                 this._logger.LogError($"{url} => time out, {exception}");
     51                                             }
     52                                         )
     53                                     )
     54                                     ;
     55             }
     56             return policy ?? Policy.NoOp<Result>();
     57         }
     58         /// <summary>
     59         /// 重试策略
     60         /// </summary>
     61         /// <typeparam name="Result"></typeparam>
     62         /// <param name="retryMaxTimes">最多重试几次,如果为0则不重试</param>
     63         /// <param name="retryIntervalMilliseconds">重试间隔的毫秒数</param>
     64         /// <returns></returns>
     65         public Policy<Result> RetryPolicy<Result>(int retryMaxTimes, int retryIntervalMilliseconds)
     66         {
     67             Policy<Result> policy = null;
     68             if (retryMaxTimes > 0)
     69             {
     70                 policy = Policy<Result>
     71                                     .Handle<Exception>()
     72                                     .Fallback(default(Result), onFallback: (result, context) =>
     73                                      {
     74                                          //重试 RetryMaxTimes 次之后的降级处理
     75                                          throw new Exception("retry failed", result.Exception);
     76                                      })
     77                                 ;
     78                 if (retryIntervalMilliseconds > 0)
     79                 {
     80                     policy = policy.Wrap(Policy<Result>
     81                                                     .Handle<Exception>()
     82                                                     .WaitAndRetry(
     83                                                         retryMaxTimes
     84                                                         , retryTime => TimeSpan.FromMilliseconds(retryIntervalMilliseconds)
     85                                                         , onRetry: (result, timeSpan, repeat, context) =>
     86                                                          {
     87                                                              context[RETRY_CURRENT_TIME] = repeat;
     88                                                              string url = context[CURRENT_URL] as string;
     89                                                              //重试
     90                                                              this._logger.LogError($"{url} => retry {repeat}");
     91                                                          }
     92                                                     )
     93                                         )
     94                                     ;
     95                 }
     96                 else
     97                 {
     98                     policy = policy.Wrap(Policy<Result>
     99                                             .Handle<Exception>()
    100                                             .Retry(
    101                                                 retryMaxTimes
    102                                                 , onRetry: (ex, repeat, context) =>
    103                                                 {
    104                                                     context[RETRY_CURRENT_TIME] = repeat;
    105                                                     string url = context[CURRENT_URL] as string;
    106                                                     //重试
    107                                                     this._logger.LogError($"{url} => retry {repeat}");
    108                                                 }
    109                                             )
    110                                         )
    111                                     ;
    112                 }
    113             }
    114             return policy ?? Policy.NoOp<Result>();
    115         }
    116 
    117         /// <summary>
    118         /// 执行带有超时和重试的任务策略
    119         /// </summary>
    120         /// <typeparam name="Result"></typeparam>
    121         /// <param name="func"></param>
    122         /// <param name="timeOutMilliseconds">执行超过多少毫秒则认为超时(0表示不检测超时)</param>
    123         /// <param name="retryMaxTimes">最多重试几次,如果为0则不重试</param>
    124         /// <param name="retryIntervalMilliseconds">重试间隔的毫秒数</param>
    125         /// <returns></returns>
    126         public Result Execute<Result>(Func<Result> func, int timeOutMilliseconds, int retryMaxTimes = 0, int retryIntervalMilliseconds = 0)
    127         {
    128             var httpContext = this._controllerBase.HttpContext;
    129 
    130             Context context = new Context();
    131 
    132             context[CURRENT_URL] = httpContext.Request.GetDisplayUrl();
    133             context[RETRY_CURRENT_TIME] = 0;
    134 
    135             Policy<Result> policy = Policy.NoOp<Result>();//创建一个空的Policy
    136 
    137             #region 超时
    138             policy = policy.Wrap(this.TimeoutPolicy<Result>(timeOutMilliseconds));
    139             #endregion
    140 
    141             #region 重试
    142             policy = policy.Wrap(this.RetryPolicy<Result>(retryMaxTimes, retryIntervalMilliseconds));
    143             #endregion
    144 
    145             return policy.Execute(context =>
    146             {
    147                 //重试 需要按照下面这种方式才能执行多次。
    148                 int retryCurrentTime = (int)context[RETRY_CURRENT_TIME];
    149                 do
    150                 {
    151                     return func();
    152                 } while (retryMaxTimes > retryCurrentTime);
    153             }, context);
    154         }
    155 
    156     }
    HystrixCommand.cs

    Startup.cs 注入

    services.AddTransient(typeof(HystrixCommand<>));

    Controller.cs -> action 使用

     1         /// <summary>
     2         /// 例子只能实现超时和重试的策略
     3         /// </summary>
     4         /// <param name="hystrixCommand"></param>
     5         /// <returns></returns>
     6         public string Test([FromServices] HystrixCommand<CheckController> hystrixCommand)
     7         {
     8             return hystrixCommand.Execute<string>(() =>
     9             {
    10                 return "ok";
    11             }, 500, 3, 1000);//超时 ok //重试 ok //重试间隔 ok
    12         }
    Controller.cs

     下面才是针对每一个用户做到服务器端熔断的实现代码。

    通过middleware可以实现针对用户级别的流控。感谢张浩帮忙。

    CircuitBreakerMiddleware.cs

     1 /// <summary>
     2     /// net core 下面自定义熔断器中间件
     3     /// </summary>
     4     public class CircuitBreakerMiddleware : IDisposable
     5     {
     6         private readonly RequestDelegate _next;
     7         private readonly ConcurrentDictionary<string, AsyncPolicy> _asyncPolicyDict = null;
     8         private readonly ILogger<CircuitBreakerMiddleware> _logger;
     9         private readonly IConfiguration _configuration;
    10 
    11         public CircuitBreakerMiddleware(RequestDelegate next, ILogger<CircuitBreakerMiddleware> logger, IConfiguration configuration)
    12         {
    13             this._next = next;
    14             this._logger = logger;
    15             this._configuration = configuration;//未来url的熔断规则可以从config文件里读取,增加灵活性
    16             logger.LogInformation($"{nameof(CircuitBreakerMiddleware)}.ctor()");
    17             this._asyncPolicyDict = new ConcurrentDictionary<string, AsyncPolicy>(Environment.ProcessorCount, 31);
    18         }
    19 
    20         public async Task InvokeAsync(HttpContext context)
    21         {
    22             var request = context.Request;
    23             string httpMethod = request.Method;
    24             string pathAndQuery = request.GetEncodedPathAndQuery();
    25             var asyncPolicy = this._asyncPolicyDict.GetOrAdd(string.Concat(httpMethod, pathAndQuery), key =>
    26             {
    27                 return Policy.Handle<Exception>().CircuitBreakerAsync(3, TimeSpan.FromSeconds(10));
    28             });
    29             try
    30             {
    31                 await asyncPolicy.ExecuteAsync(async () => await this._next(context));
    32             }
    33             catch (BrokenCircuitException ex)
    34             {
    35                 this._logger.LogError($"{nameof(BrokenCircuitException)}.InnerException.Message:{ex.InnerException.Message}");
    36                 var response = context.Response;
    37                 response.StatusCode = (int)HttpStatusCode.BadRequest;
    38                 response.ContentType = "text/plain; charset=utf-8";
    39                 await response.WriteAsync("Circuit Broken o(╥﹏╥)o");
    40             }
    41 
    42             //var endpoint = context.GetEndpoint();
    43             //if (endpoint != null)
    44             //{
    45             //    var controllerActionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
    46             //    var controllerName = controllerActionDescriptor.ControllerName;
    47             //    var actionName = controllerActionDescriptor.ActionName;
    48             //    if (string.Equals(controllerName, "WeatherForecast", StringComparison.OrdinalIgnoreCase)
    49             //        && string.Equals(actionName, "Test", StringComparison.OrdinalIgnoreCase))
    50             //    {//针对某一个控制器的某一个action,单独设置熔断
    51             //        await Policy.Handle<Exception>().CircuitBreakerAsync(3, TimeSpan.FromSeconds(10)).ExecuteAsync(async () => await this._next(context));
    52             //    }
    53             //    else
    54             //    {
    55             //        await this._next(context);
    56             //    }
    57             //}
    58         }
    59 
    60         public void Dispose()
    61         {
    62             this._asyncPolicyDict.Clear();
    63             this._logger.LogInformation($"{nameof(CircuitBreakerMiddleware)}.Dispose()");
    64         }
    65     }
    CircuitBreakerMiddleware.cs

    Startup.cs

     1 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
     2         {
     3             if (env.IsDevelopment())
     4             {
     5                 app.UseDeveloperExceptionPage();
     6             }
     7 
     8             app.UseRouting();
     9 
    10             //注意位置在Routing下面,UseEndpoints上面
    11             app.UseMiddleware<CircuitBreakerMiddleware>();
    12 
    13             app.UseEndpoints(endpoints =>
    14             {
    15                 endpoints.MapControllers();
    16             });
    17         }
    Startup.cs

    WeatherForecastController.cs

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Threading.Tasks;
     5 using Microsoft.AspNetCore.Mvc;
     6 using Microsoft.Extensions.Logging;
     7 
     8 namespace CircuitBreakerDemo.Controllers
     9 {
    10     [ApiController]
    11     [Route("[controller]")]
    12     public class WeatherForecastController : ControllerBase
    13     {
    14         private static readonly string[] Summaries = new[]
    15         {
    16             "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    17         };
    18 
    19         private static Random _Random = new Random();
    20 
    21         private readonly ILogger<WeatherForecastController> _logger;
    22 
    23         public WeatherForecastController(ILogger<WeatherForecastController> logger)
    24         {
    25             _logger = logger;
    26         }
    27 
    28         [HttpGet]
    29         public IEnumerable<WeatherForecast> Get()
    30         {
    31             if (_Random.Next(Summaries.Length) % 3 == 0)
    32             {
    33                 throw new Exception("程序运行错误");
    34             }
    35 
    36             return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    37             {
    38                 Date = DateTime.Now.AddDays(index),
    39                 TemperatureC = _Random.Next(-20, 55),
    40                 Summary = Summaries[_Random.Next(Summaries.Length)]
    41             })
    42             .ToArray();
    43         }
    44 
    45         [HttpGet("test")]
    46         public string Test()
    47         {
    48             if (_Random.Next(Summaries.Length) % 3 == 0)
    49             {
    50                 throw new Exception("程序运行错误");
    51             }
    52             return Summaries[_Random.Next(Summaries.Length)];
    53         }
    54     }
    55 }
    WeatherForecastController.cs

  • 相关阅读:
    常用软件的安装(windows/linux)
    常用软件的安装(windows/linux)
    图像块的访问(填充 padding,步长 stride,窗 Window/kernel/filter)
    hdu4620 Fruit Ninja Extreme
    java中的object类
    [置顶] android 自定义TextView
    [置顶] think in java interview-高级开发人员面试宝典(八)
    POJ 2112 Optimal Milking (二分+最短路径+网络流)
    BitHacks--位操作技巧
    hdu 4614 Vases and Flowers (二分 线段树)
  • 原文地址:https://www.cnblogs.com/qiyebao/p/13731500.html
Copyright © 2020-2023  润新知