.net core 熔断降级测试例子

.net core版本2.2

aspectcore.core 版本1.2.0

AspectCore.Extensions.DependencyInjection版本1.2.0

Volo.Abp.Core版本0.18.1

Polly 版本6.0.1

版本必须对应

使用abp

public class ApiCoreModule : AbpModule
{
/// <summary>
/// 配置mvc
/// </summary>
/// <param name="context"></param>
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;

services.AddMvc(opt =>
{
//opt.UseCentralRoutePrefix(new RouteAttribute($"api/test/[controller]"));
//opt.Filters.Add(typeof(WebApiResultMiddleware));
}).AddControllersAsServices();


//跨域和MVC
context.Services.AddTransient<IStartupFilter, ApiCoreStartupFilter>();
}
}

internal static class AppBuilderExtensions
{
/// <summary>
/// mvc中间件
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static IApplicationBuilder UseBaseMiddleWare(this IApplicationBuilder app)
{
//设置跨域
app.UseCors(builder =>
{
builder.AllowAnyHeader();
builder.AllowAnyMethod();
builder.AllowAnyOrigin();
builder.SetPreflightMaxAge(TimeSpan.FromSeconds(2592000));
});

return app.UseMvc();
}
}

sealed class ApiCoreStartupFilter : IStartupFilter
{
/// <summary>
/// 配置mvc中间件
/// </summary>
/// <param name="next"></param>
/// <returns></returns>
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseBaseMiddleWare();

next(app);
};
}
}

Startup配置

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

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddApplication<ApiCoreModule>();//mvc方面的处理
return services.BuildDynamicProxyServiceProvider();
}

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

}

控制器

[Route("home")]
public class HomeController : Controller
{

/// <summary>
/// 降级处理
/// </summary>
/// <returns></returns>
[HttpGet("AddOrder")]
[Protector(nameof(AddOrderk), MaxRetryTimes = 3, CacheTtlMilliseconds = 1, EnableCircuitBreaker = true, ExceptionsAllowedBeforeBreaking = 100)]
public virtual async Task AddOrder()
{
throw new System.Exception("ee");
}

/// <summary>
/// 重试处理
/// </summary>
/// <returns></returns>
[HttpGet("hahaha")]
[Protector(MaxRetryTimes = 3, CacheTtlMilliseconds = 1, EnableCircuitBreaker = true, ExceptionsAllowedBeforeBreaking = 100)]
public virtual async Task AddOrderbbb()
{
throw new System.Exception("ee");
}

/// <summary>
/// 降级方法
/// </summary>
/// <returns></returns>
[NonAction]
public virtual async Task<string> AddOrderk()
{

return "hello word";
}
}

 使用AspectCore做拦截器,polly熔断

[AttributeUsage(AttributeTargets.Method)]
public class ProtectorAttribute : AbstractInterceptorAttribute
{
/// <summary>
/// 最多重试几次,如果为0则不重试
/// </summary>
public int MaxRetryTimes { get; set; } = 0;

/// <summary>
/// 重试间隔的毫秒数
/// </summary>
public int RetryIntervalMilliseconds { get; set; } = 100;

/// <summary>
/// 是否启用熔断
/// </summary>
public bool EnableCircuitBreaker { get; set; } = false;

/// <summary>
/// 熔断前出现允许错误几次
/// </summary>
public int ExceptionsAllowedBeforeBreaking { get; set; } = 10;

/// <summary>
/// 熔断多长时间(毫秒)
/// </summary>
public int MillisecondsOfBreak { get; set; } = 1000;

/// <summary>
/// 执行超过多少毫秒则认为超时(0表示不检测超时)
/// </summary>
public int TimeOutMilliseconds { get; set; } = 0;

/// <summary>
/// 缓存多少毫秒(0表示不缓存),用“类名+方法名+所有参数ToString拼接”做缓存Key
/// </summary>

public int CacheTtlMilliseconds { get; set; } = 0;

private readonly static ConcurrentDictionary<MethodInfo, Policy> Policies = new ConcurrentDictionary<MethodInfo, Policy>();

/// <summary>
///
/// </summary>
/// <param name="fallBackMethod">降级的方法名</param>
public ProtectorAttribute(string fallBackMethod = null)
{
this.FallBackMethod = fallBackMethod;
}

public string FallBackMethod { get; set; }

public override async Task Invoke(AspectContext context, AspectDelegate next)
{
var policy = WrapPolly(context);
//把本地调用的AspectContext传递给Polly,主要给FallbackAsync中使用,避免闭包的坑
Context pollyCtx = new Context
{
["aspectContext"] = context
};

await policy.ExecuteAsync(ctx => next(context), pollyCtx);
}

private Policy WrapPolly(AspectContext context)
{
//一个Protector中保持一个policy对象即可
//其实主要是CircuitBreaker要求对于同一段代码要共享一个policy对象
//根据反射原理,同一个方法的MethodInfo是同一个对象,但是对象上取出来的HystrixAttribute
//每次获取的都是不同的对象,因此以MethodInfo为Key保存到policies中,确保一个方法对应一个policy实例

Policies.TryGetValue(context.ServiceMethod, out Policy policy);

lock (Policies)//因为Invoke可能是并发调用,因此要确保policies赋值的线程安全
{
if (policy == null)
{
policy = Policy.NoOpAsync();//创建一个空的Policy
if (EnableCircuitBreaker)
{
policy = policy.WrapAsync(Policy.Handle<Exception>().CircuitBreakerAsync(ExceptionsAllowedBeforeBreaking, TimeSpan.FromMilliseconds(MillisecondsOfBreak)));
}
if (TimeOutMilliseconds > 0)
{
policy = policy.WrapAsync(Policy.TimeoutAsync(() => TimeSpan.FromMilliseconds(TimeOutMilliseconds), Polly.Timeout.TimeoutStrategy.Pessimistic));
}
if (MaxRetryTimes > 0)
{
policy = policy.WrapAsync(Policy.Handle<Exception>().WaitAndRetryAsync(MaxRetryTimes, i => TimeSpan.FromMilliseconds(RetryIntervalMilliseconds)));
}
Policy policyFallBack = Policy
.Handle<Exception>()
.FallbackAsync(async (ctx, t) =>
{
if (this.FallBackMethod != null)
{
AspectContext aspectContext = (AspectContext)ctx["aspectContext"];
var fallBackMethod = context.ServiceMethod.DeclaringType.GetMethod(this.FallBackMethod);
Object fallBackResult = fallBackMethod.Invoke(context.Implementation, context.Parameters);
//不能如下这样,因为这是闭包相关,如果这样写第二次调用Invoke的时候context指向的还是第一次的对象,所以要通过Polly的上下文来传递AspectContext
//context.ReturnValue = fallBackResult;
aspectContext.ReturnValue = fallBackResult;
}
}, async (ex, t) =>
{
//这里加日志,最终所有方法都失败
});

policy = policyFallBack.WrapAsync(policy);
//放入
Policies.TryAdd(context.ServiceMethod, policy);
}
}

return policy;
}

}

猜你喜欢

转载自www.cnblogs.com/caihuaxing/p/12534570.html