現在軟體就業環境不景氣,各行各業都忙著裁員優化。作為一個小開發,咱也不能光等著別人來優化咱,也得想辦法優化下自己。就拿手頭上的工作來說吧,我發現我的微服務應用里,既有AgileConfig這個配置中心組件,又有一個Consul 服務發現組件。本來吧他倆也沒啥事,各幹個的。但是,我在操作AgileCo ...
現在軟體就業環境不景氣,各行各業都忙著裁員優化。作為一個小開發,咱也不能光等著別人來優化咱,也得想辦法優化下自己。就拿手頭上的工作來說吧,我發現我的微服務應用里,既有AgileConfig這個配置中心組件,又有一個Consul 服務發現組件。本來吧他倆也沒啥事,各幹個的。但是,我在操作AgileConfig的時候發現了一個事
然後我又一百度發現了這個AgileConfig 1.6.0 發佈 - 支持服務註冊與發現 - Agile.Zhou - 博客園 (cnblogs.com),有點意思。稍微一思索,我們現在的微服務解決方案里網關用的ocelot+consul 作為HTTP api網關,同時 還是用了 yarp做 grpc的網關,明顯可以看出來有一套多餘的網關在這裡。基於目前的情況,我是一直想優化掉 ocelot+consul這個組合。改用 agileConfig+yarp,奈何前期對微服務機制不是很熟悉,有堆坑要填。現在看到agileconfig的服務列表,又勾起了我這個優化的想法。說乾就乾,我理想的目標是可以直接從agileconfig上獲取到所有註冊的服務,然後用代碼來動態給yarp添加代理配置。這樣既可以優化掉一個consul服務,又可以免去每次服務部署時繁瑣的網關配置。
首先第一個任務就是解決yarp如何用代碼實現動態配置的問題。bing 里搜索 yarp 動態配置 ,優先看博客園的博主發的文章,事實上我也就只看了這一篇Welcome to YARP - 2.2 配置功能 - 配置提供者(Configuration Providers) - coding-y - 博客園 (cnblogs.com) 。完美,問題解決。下麵就是代碼時間。
通過上面兩篇文章我們知道,agileconfig會提供一個IDiscoveryService 介面來供程式獲取註冊的服務信息。同時 yarp也提供了從記憶體中提供配置的 InMemoryConfigProvider
,那我們只需要在agileconfig 註冊之後 通過 IDisconverService介面 獲取所有已註冊服務,然後再讓yarp應用上記憶體中的配置即可實現服務註冊後自動配置代理的需求。
下麵我們動動手指頭 按下 ctrl c ,ctrl v實現如下代碼:代碼不具備通用性 需要進一步優化 建議已給出。
using AgileConfig.Client;
using AgileConfig.Client.RegisterCenter;
using Newtonsoft.Json.Linq;
using Yarp.ReverseProxy.Configuration;
using Yarp.ReverseProxy.Transforms;
namespace Microsoft.Extensions.DependencyInjection
{
public static class AgileConfigProxyConfigProviderExtend
{
const string NotProxyStr = "notProxy";
const string TransformsStr = "Transforms";
static readonly ILogger _Logger = LoggerFactory.Create(b => { }).CreateLogger("AgileConfigProxyConfigProviderExtend");
public static RouteConfig[] GetRoutes(this IDiscoveryService discoveryService)
{
var routes = new List<RouteConfig>();
foreach (var item in discoveryService.Services)
{
if (item.MetaData.Any(r=>r.Equals(NotProxyStr, StringComparison.OrdinalIgnoreCase)))
{
continue;
}
var route = new RouteConfig
{
RouteId = item.ServiceId,
ClusterId = item.ServiceName,
Match = new RouteMatch
{
Path = $"/{item.ServiceName}/{{**all}}"
}
};
//.WithTransformPathRouteValues(pattern: new PathString("/{**all}"))
try
{
var transformStr = item.MetaData.FirstOrDefault(r => r.StartsWith(TransformsStr));
if (transformStr is not null)
{
var jobj = JObject.Parse(transformStr.Split(':')[1]);
foreach (var k in jobj)
{
route.WithTransform(d => d.Add(k.Key, k.Value?.ToString() ?? ""));
}
}
}
catch (Exception e)
{
_Logger.LogError(e,"生成路由【轉換】配置時出錯");
}
routes.Add(route);
_Logger.LogTrace("添加路由{RouteId}", route.RouteId);
}
return routes.ToArray();
}
public static ClusterConfig[] GetClusters(this IDiscoveryService discoveryService)
{
var clusters = new List<ClusterConfig>();
var proxyServices = discoveryService.Services
.Where(r => !r.MetaData.Any(r => r.Equals(NotProxyStr, StringComparison.OrdinalIgnoreCase)))
.GroupBy(p => p.ServiceName);
foreach (var item in proxyServices)
{
var destinations = new Dictionary<string, DestinationConfig>(StringComparer.OrdinalIgnoreCase);
foreach (var service in item)
{
destinations.Add(service.ServiceId, new DestinationConfig() {
Address=service.AsHttpHost()
});
}
clusters.Add(new ClusterConfig
{
ClusterId = item.Key,
Destinations = destinations
});
}
return clusters.ToArray();
}
//可以再加一個重載 支持傳入一個委托 來自定義構造配置。
public static IReverseProxyBuilder LoadFromAgileConfigByInMemoryConfigProvider(this IReverseProxyBuilder builder, ConfigClient client)
{
var discoveryService = client.DiscoveryService();
discoveryService ??= new DiscoveryService(client, LoggerFactory.Create(b => b.SetMinimumLevel(LogLevel.Information)));
var configProvider = new InMemoryConfigProvider(discoveryService.GetRoutes(), discoveryService.GetClusters());
builder.Services.AddSingleton(configProvider);
builder.Services.AddSingleton<IProxyConfigProvider>(configProvider);
discoveryService.ReLoaded += () =>
{
configProvider.Update(discoveryService.GetRoutes(), discoveryService.GetClusters());
};
return builder;
}
}
}
現在只需要調用如下代碼,即可給原有的yarp服務加上自動生成代理配置的功能了。
builder.Services.AddReverseProxy()//添加ReverseProxy相關服務到DI
.LoadFromAgileConfigByInMemoryConfigProvider((ConfigClient)client);
註意:註冊AgileConfig時候請使用 UseAgileConfig()方法註冊。Addxxx方法會導致無法獲取到agileConfig上的已註冊服務對信息。
代碼已傳gitee:https://gitee.com/dotnetfans/yarp-auto-proxy.-agile-config
同時也個給agileConfig 提交了合併請求。