NetCore Ocelot 之 Cache

发布时间 2023-10-09 15:55:16作者: 云霄宇霁

Ocelot supports some very rudimentary caching at the moment provider by the CacheManager project. This s an amazing project that is solving a lot of caching problems. I would recommend using this package to cache with Ocelot.

The following example shows how to add CacheManager to Ocelot so that you can do output caching.

First of all add the following NuGet package.

Install-Package Ocelot.Cache.CacheManager

This will give you access to the Ocelot cache manager extension methods.

The second thing you need to do something like the following to your ConfigureService.

builder.Services.AddOcelot()
    .AddCacheManager(c => c.WithDictionaryHandle())// cache manager
    .AddSingletonDefinedAggregator<CustomAdvancedAggregator>()
    .AddCustomLoadBalancer((serviceProvider, route, serviceDiscoveryProvider) => new CustomRandomLoadBalancer(serviceDiscoveryProvider.Get))
    .AddConsul()
    .AddPolly().

Finally in order to use caching on a route in your Route configuration add this setting.

 "FileCacheOptions": {
        "TtlSeconds": 10,
        "Region": "gatewaycacheregion"
      }

In this example ttl seconds is set to 10 which means the cache will expire after 15 seconds.

Anyway Ocelot currently supports caching on the URL of the downstream service and setting a TTL in seconds to expire the cache. You can also clear the cache for a region by calling Ocelot's administartion API.

The result is whin 10 seconds the result always same due to the cache e.g.

  If you want to add your own caching method implement the following interface and register them in DI.

IOcelotCache<CacheResponse> this is for output caching.

IOcelotCache<FileConfiguration> this is for caching the file configuration if you are calling something remote to get your config such as Consul.

My custom cache impletemant IOcelotCache<CacheResponse>

    public class CustomCache : IOcelotCache<CachedResponse>
    {
        private static Dictionary<string, CacheObj> _cacheObj = new Dictionary<string, CacheObj>();
        public void Add(string key, CachedResponse value, TimeSpan ttl, string region)
        {
            var cacheKey = $"{region}_{key}";
            if (!_cacheObj.ContainsKey(cacheKey))
                _cacheObj.Add(cacheKey, new CacheObj()
                {
                    ExpireTime = DateTime.Now.Add(ttl),
                    Response = value,
                });
        }

        public void AddAndDelete(string key, CachedResponse value, TimeSpan ttl, string region)
        {
            var cacheKey = $"{region}_{key}";
            if (_cacheObj.ContainsKey(cacheKey))
                _cacheObj.Remove(cacheKey);
            _cacheObj.Add(cacheKey, new CacheObj()
            {
                ExpireTime = DateTime.Now.Add(ttl),
                Response = value,
            });
        }

        public void ClearRegion(string region)
        {
            var cacheKeys = _cacheObj.Where(o => o.Key.StartsWith(region)).Select(o => o.Key);
            foreach (var key in cacheKeys)
                _cacheObj.Remove(key);
        }

        public CachedResponse Get(string key, string region)
        {
            var cacheKey = $"{region}_{key}";
            if (!_cacheObj.ContainsKey(cacheKey)) return null;
            var cacheObj = _cacheObj[cacheKey];
            if (cacheObj != null && cacheObj.ExpireTime >= DateTime.Now)
                return cacheObj.Response;
            _cacheObj.Remove(cacheKey);
            return null;
        }

        internal class CacheObj
        {
            public DateTime ExpireTime { get; set; }
            public CachedResponse Response { get; set; }
        }
    }

Add CustomCache to DI

builder.Services.AddSingleton<IOcelotCache<CachedResponse>, CustomCache>();

The result is same as above.