/// <summary> /// Puts the specified value in the cache. /// </summary> /// <param name="key">The cache key.</param> /// <param name="value">The value to put into the cache.</param> /// <param name="ttl">The time-to-live for the cache entry.</param> public void Put(string key, object value, Ttl ttl) { #if PORTABLE using (Microsoft.Extensions.Caching.Memory.ICacheEntry entry = _cache.CreateEntry(key)) { entry.Value = value; if (ttl.SlidingExpiration) { entry.SlidingExpiration = ttl.Timespan; } else { entry.AbsoluteExpirationRelativeToNow = ttl.Timespan; } } #else System.Runtime.Caching.CacheItemPolicy cacheItemPolicy = new System.Runtime.Caching.CacheItemPolicy(); if (ttl.SlidingExpiration) { cacheItemPolicy.SlidingExpiration = ttl.Timespan; } else { cacheItemPolicy.AbsoluteExpiration = SystemClock.DateTimeOffsetUtcNow().Add(ttl.Timespan); } _cache.Set(key, value, cacheItemPolicy); #endif }
public void Get_should_return_instance_previously_stored_in_cache() { #if PORTABLE MemoryCacheImplementation memoryCache = new Microsoft.Extensions.Caching.Memory.MemoryCache(new Microsoft.Extensions.Caching.Memory.MemoryCacheOptions()); #else MemoryCacheImplementation memoryCache = System.Runtime.Caching.MemoryCache.Default; #endif string key = "anything"; object value = new object(); #if PORTABLE using (Microsoft.Extensions.Caching.Memory.ICacheEntry entry = memoryCache.CreateEntry(key)) { entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10); entry.Value = value; } #else memoryCache[key] = value; #endif MemoryCacheProvider provider = new MemoryCacheProvider(memoryCache); object got = provider.Get(key); got.Should().BeSameAs(value); }
public override async Task Invoke(AspectContext context, AspectDelegate next) { //一个HystrixCommand中保持一个policy对象即可 //其实主要是CircuitBreaker要求对于同一段代码要共享一个policy对象 //根据反射原理,同一个方法的MethodInfo是同一个对象,但是对象上取出来的HystrixCommandAttribute //每次获取的都是不同的对象,因此以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) => { 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); } } //把本地调用的AspectContext传递给Polly,主要给FallbackAsync中使用,避免闭包的坑 Context pollyCtx = new Context(); pollyCtx["aspectContext"] = context; //Install-Package Microsoft.Extensions.Caching.Memory if (CacheTTLMilliseconds > 0) { //用类名+方法名+参数的下划线连接起来作为缓存key string cacheKey = "HystrixMethodCacheManager_Key_" + context.ServiceMethod.DeclaringType + "." + context.ServiceMethod + string.Join("_", context.Parameters); //尝试去缓存中获取。如果找到了,则直接用缓存中的值做返回值 if (memoryCache.TryGetValue(cacheKey, out var cacheValue)) { context.ReturnValue = cacheValue; } else { //如果缓存中没有,则执行实际被拦截的方法 await policy.ExecuteAsync(ctx => next(context), pollyCtx); //存入缓存中 using (var cacheEntry = memoryCache.CreateEntry(cacheKey)) { cacheEntry.Value = context.ReturnValue; cacheEntry.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMilliseconds(CacheTTLMilliseconds); } } } else//如果没有启用缓存,就直接执行业务方法 { await policy.ExecuteAsync(ctx => next(context), pollyCtx); } }
public override async Task Invoke(AspectContext context, AspectDelegate next) { //一个HystrixCommand中保持一个policy对象即可 //其实主要是CircuitBreaker要求对于同一段代码要共享一个policy对象 //根据反射原理,同一个方法就对应一个HystrixCommandAttribute,无论几次调用, //而不同方法对应不同的HystrixCommandAttribute对象,天然的一个policy对象共享 //因为同一个方法共享一个policy,因此这个CircuitBreaker是针对所有请求的。 //Attribute也不会在运行时再去改变属性的值,共享同一个policy对象也没问题 lock (this) { if (policy == null) { policy = Policy.Handle <Exception>() .FallbackAsync(async(ctx, t) => { 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) => { }); if (MaxRetryTimes > 0)//重试 { policy = policy.WrapAsync(Policy.Handle <Exception>().WaitAndRetryAsync(MaxRetryTimes, i => TimeSpan.FromMilliseconds(RetryIntervalMilliseconds))); } if (EnableCircuitBreater)//熔断 { policy = policy.WrapAsync(Policy.Handle <Exception>().CircuitBreakerAsync(ExceptionAllowedBeforeBreaking, TimeSpan.FromMilliseconds(MillisecondsOfBreak))); } if (TimeOutMilliseconds > 0)//超时 { policy = policy.WrapAsync(Policy.TimeoutAsync(() => TimeSpan.FromMilliseconds(TimeOutMilliseconds), Polly.Timeout.TimeoutStrategy.Pessimistic)); } } } //把本地调用的AspectContext传递给Polly,主要给FallBackMethod中使用,避免闭包的坑 Context pollyCtx = new Context(); pollyCtx["aspectContext"] = context; if (CacheTTLMilliseconds > 0) { //用类名+方法名+参数的下划线连接起来作为缓存key string cacheKey = "HystrixMethodCacheManager_Key_" + context.ServiceMethod.DeclaringType + "." + context.ServiceMethod + string.Join("_", context.Parameters); //尝试去缓存中获取。如果找到了,则直接用缓存中的值做返回值 if (memoryCache.TryGetValue(cacheKey, out var cacheValue)) { context.ReturnValue = cacheValue; } else { //如果缓存中没有,则执行实际被拦截的方法 await policy.ExecuteAsync(ctx => next(context), pollyCtx); //存入缓存中 using (var cacheEntry = memoryCache.CreateEntry(cacheKey)) { cacheEntry.Value = context.ReturnValue; cacheEntry.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMilliseconds(CacheTTLMilliseconds); } } } else//如果没有启用缓存,就直接执行业务方法 { await policy.ExecuteAsync(ctx => next(context), pollyCtx); } }
public override async Task Invoke(AspectContext context, AspectDelegate next) { ISyncPolicy policy = Policy.Handle <Exception>().RetryAsync(); policy.Execute(() => { var fallBackMethod = context.ServiceMethod.DeclaringType.GetMethod(this.FallBackMethod); Object fallBackResult = fallBackMethod.Invoke(context.Implementation, context.Parameters); context.ReturnValue = fallBackResult; }); if (MaxRetryTimes > 0) { policy = policy.Wrap(Policy.Handle <Exception>().WaitAndRetry(MaxRetryTimes, i => TimeSpan.FromMilliseconds(RetryIntervalMilliseconds))); } if (EnableCircuitBreaker) { policy = policy.Wrap(Policy.Handle <Exception>().CircuitBreaker(ExceptionsAllowedBeforeBreaking, TimeSpan.FromMilliseconds(RetryIntervalMilliseconds))); } if (TimeOutMilliseconds > 0) { policy = policy.Wrap(Policy.Timeout(() => TimeSpan.FromMilliseconds(TimeOutMilliseconds), TimeoutStrategy.Pessimistic)); } //Install-Package Microsoft.Extensions.Caching.Memory if (CacheTTLMilliseconds > 0) { //用类名+方法名+参数的下划线连接起来作为缓存key string cacheKey = "HystrixMethodCacheManager_Key_" + context.ServiceMethod.DeclaringType + "." + context.ServiceMethod + string.Join("_", context.Parameters); Object cacheValue; //尝试去缓存中获取 if (memoryCache.TryGetValue(cacheKey, out cacheValue)) { context.ReturnValue = cacheValue; await Task.FromResult(0); } else { object returnValue = null; policy.Execute(() => { //执行实际的方法 // returnValue = context.Invoke(next); returnValue = next(context);//执行被拦截的方法 }); using (var cacheEntry = memoryCache.CreateEntry(cacheKey)) { cacheEntry.Value = context.ReturnValue; cacheEntry.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMilliseconds(CacheTTLMilliseconds); } await Task.FromResult(returnValue); } } else//如果没有启用缓存,就直接执行业务方法 { object returnValue = null; policy.Execute(() => { returnValue = context.Invoke(next); }); await Task.FromResult(returnValue); } }
public override async Task Invoke(AspectContext context, AspectDelegate next) { /* * share one policy in a instance,because CircuitBreaker require so. * * the MethodInfo gotten by reflect is always same one, * however the instance gotten by reflect is different every time. * so we save it in policies with MethodInfo as its key. */ policies.TryGetValue(context.ServiceMethod, out var policy); //current method is possible to be called by more than one thread. lock (policies) { if (policy == null) { policy = Policy.NoOpAsync(); if (EnableCircuitBreaker) { policy = policy.WrapAsync(Policy.Handle <Exception>() .CircuitBreakerAsync(ExceptionsAllowedBeforeBreaking, TimeSpan.FromMilliseconds(DurationOfBreak))); } if (TimeOut > 0) { policy = policy.WrapAsync(Policy.TimeoutAsync( () => TimeSpan.FromMilliseconds(TimeOut), Polly.Timeout.TimeoutStrategy.Pessimistic)); } if (MaxRetryTimes > 0) { policy = policy.WrapAsync(Policy.Handle <Exception>().WaitAndRetryAsync(MaxRetryTimes, i => TimeSpan.FromMilliseconds(RetryIntervalMilliseconds))); } var policyFallBack = Policy .Handle <Exception>() .FallbackAsync(async(ctx, t) => { var aspectContext = (AspectContext)ctx["aspectContext"]; var fallBackMethod = context.ImplementationMethod.DeclaringType?.GetMethod(FallBackMethod); var fallBackResult = fallBackMethod?.Invoke(context.Implementation, context.Parameters); aspectContext.ReturnValue = fallBackResult; await Task.CompletedTask; }, async(ex, t) => { Logger?.LogError(ex, ex.Message); await Task.CompletedTask; }); policy = policyFallBack.WrapAsync(policy); policies.TryAdd(context.ServiceMethod, policy); } } //transfer local AspectContext to Polly,especially FallbackAsync.this way can avoid troubles in closure var pollyCtx = new Context { ["aspectContext"] = context }; if (CacheTtl > 0) { //use assembly.class.method+parameters as cache key var cacheKey = $"HystrixMethodCacheManager_Key_{context.ImplementationMethod.DeclaringType.FullName}.{context.ImplementationMethod}{string.Join("_", context.Parameters)}"; //try to get result from cache firstly.If success return it directly if (MemoryCache.TryGetValue(cacheKey, out var cacheValue)) { context.ReturnValue = cacheValue; } else { //it's not cached currently,just execute the method await policy.ExecuteAsync(ctx => next(context), pollyCtx); //cache it using (var cacheEntry = MemoryCache.CreateEntry(cacheKey)) { cacheEntry.Value = context.ReturnValue; cacheEntry.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMilliseconds(CacheTtl); } } } else //disabled cache,just execute the method directly { await policy.ExecuteAsync(ctx => next(context), pollyCtx); } }