攜程 Apollo 配置中心傳統 .NET 項目集成實踐

官方文檔存在的問題#

可能因爲 Apollo 配置中心的客戶端源碼一直處於更新中,致使其相關文檔有些跟不上節奏,部分文檔寫的不規範,很容易給作對接的新手朋友形成誤導。css

好比,我在參考以下兩個文檔使用傳統 .NET 客戶端作接入的時候就發現了些問題。html

1.兩個文檔關於標識應用身份的AppId的配置節點不一致。

git

2.第二個文檔關於應用配置發佈環境的Environment配置節點的描述出現明顯錯誤。
github

固然,這些問題隨時都有可能被修復。若您看到文檔內容與本文描述不符,請以官方文檔爲準。app

傳統 .NET 項目快速接入#

快速進入正題。框架

安裝依賴包#

在您項目的基礎設施層,經過 NuGet 包管理器或使用以下命令添加傳統 .NET 項目使用的客戶端:分佈式

Copy
Install-Package Com.Ctrip.Framework.Apollo.ConfigurationManager -Version 2.0.3

從上面的包名能看出什麼?我這裏選裝的是2.0.3的版本。還有,這應該是一個 Javaer 起的名字。post

配置應用標識 & 服務地址#

在您的啓動項目中,打開App.configWeb.config配置文件,在<appSettings>節點中增長以下節點:ui

Copy
<!-- Change to the actual app id --> <add key="Apollo.AppID" value="R01001" /> <add key="Apollo.MetaServer" value="http://localhost:8080" />

若您部署了多套 Config Service,支持多環境,請參考以下配置:url

Copy
<!-- Change to the actual app id --> <add key="Apollo.AppID" value="R01001" /> <!-- Should change the apollo config service url for each environment --> <add key="Apollo.Env" value="DEV" /> <add key="Apollo.DEV.Meta" value="http://localhost:8080"/> <add key="Apollo.FAT.Meta" value="http://localhost:8081"/> <add key="Apollo.UAT.Meta" value="http://localhost:8082"/> <add key="Apollo.PRO.Meta" value="http://localhost:8083"/>

配置完成後,就能夠準備在咱們項目中使用 Apollo 客戶端了。

二次封裝代碼#

咱們習慣在項目中使用第三方庫的時候封裝一層,這種封裝是淺層的,通常都是在項目的基礎設施層來作,這樣其餘層使用就不須要再次引入依賴包。

不說了,直接上代碼吧。

代碼結構大體以下:

Copy
├─MyCompany.MyProject.Infrastructure # 項目基礎設施層 │ │ │ └─Configuration │ ApolloConfiguration.cs # Apollo 分佈式配置項讀取實現 │ ConfigurationChangeEventArgs.cs # 配置更改回調事件參數 │ IConfiguration.cs # 配置抽象接口,可基於此接口實現本地配置讀取

IConfiguration#

Copy
using System; using System.Configuration; namespace MyCompany.MyProject.Infrastructure { /// <summary> /// 配置抽象接口。 /// </summary> public interface IConfiguration { /// <summary> /// 配置更改回調事件。 /// </summary> event EventHandler<ConfigurationChangeEventArgs> ConfigChanged; /// <summary> /// 獲取配置項。 /// </summary> /// <param name="key">鍵</param> /// <param name="namespaces">命名空間集合</param> /// <returns></returns> string GetValue(string key, params string[] namespaces); /// <summary> /// 獲取配置項。 /// </summary> /// <typeparam name="TValue">值類型</typeparam> /// <param name="key">鍵</param> /// <param name="namespaces">命名空間集合</param> /// <returns></returns> TValue GetValue<TValue>(string key, params string[] namespaces); /// <summary> /// 獲取配置項,若是值爲 <see cref="null"/> 則取參數 <see cref="defaultValue"/> 值。 /// </summary> /// <param name="key">鍵</param> /// <param name="defaultValue">默認值</param> /// <param name="namespaces">命名空間集合</param> /// <returns></returns> string GetDefaultValue(string key, string defaultValue, params string[] namespaces); /// <summary> /// 獲取配置項,若是值爲 <see cref="null"/> 則取參數 <see cref="defaultValue"/> 值。 /// </summary> /// <typeparam name="TValue">值類型</typeparam> /// <param name="key">鍵</param> /// <param name="defaultValue">默認值</param> /// <param name="namespaces">命名空間集合</param> /// <returns></returns> TValue GetDefaultValue<TValue>(string key, TValue defaultValue, params string[] namespaces); } }

ConfigurationChangeEventArgs#

Copy
using Com.Ctrip.Framework.Apollo.Model; using System.Collections.Generic; namespace MyCompany.MyProject.Infrastructure { public class ConfigurationChangeEventArgs { public IEnumerable<string> ChangedKeys => Changes.Keys; public bool IsChanged(string key) => Changes.ContainsKey(key); public string Namespace { get; } public IReadOnlyDictionary<string, ConfigChange> Changes { get; } public ConfigurationChangeEventArgs(string namespaceName, IReadOnlyDictionary<string, ConfigChange> changes) { Namespace = namespaceName; Changes = changes; } public ConfigChange GetChange(string key) { Changes.TryGetValue(key, out var change); return change; } } }

ApolloConfiguration#

Copy
using System; using System.Configuration; using System.Globalization; using Com.Ctrip.Framework.Apollo; using Com.Ctrip.Framework.Apollo.Model; namespace MyCompany.MyProject.Infrastructure { public class ApolloConfiguration : IConfiguration { private readonly string _defaultValue = null; public event EventHandler<ConfigurationChangeEventArgs> ConfigChanged; private IConfig GetConfig(params string[] namespaces) { var config = namespaces == null || namespaces.Length == 0 ? ApolloConfigurationManager.GetAppConfig().GetAwaiter().GetResult() : ApolloConfigurationManager.GetConfig(namespaces).GetAwaiter().GetResult(); config.ConfigChanged += (object sender, ConfigChangeEventArgs args) => { ConfigChanged(sender, new ConfigurationChangeEventArgs(args.Namespace, args.Changes)); }; return config; } public string GetValue(string key, params string[] namespaces) { key = key ?? throw new ArgumentNullException(nameof(key)); var config = GetConfig(namespaces); return config.GetProperty(key, _defaultValue); } public TValue GetValue<TValue>(string key, params string[] namespaces) { var value = GetValue(key, namespaces); return value == null ? default(TValue) : (TValue)Convert.ChangeType(value, typeof(TValue), CultureInfo.InvariantCulture); } public string GetDefaultValue(string key, string defaultValue, params string[] namespaces) { key = key ?? throw new ArgumentNullException(nameof(key)); var config = GetConfig(namespaces); return config.GetProperty(key, defaultValue); } public TValue GetDefaultValue<TValue>(string key, TValue defaultValue, params string[] namespaces) { var value = GetDefaultValue(key, defaultValue, namespaces); return value == null ? default(TValue) : (TValue)Convert.ChangeType(value, typeof(TValue), CultureInfo.InvariantCulture); } } }

正確使用姿式#

在使用以前須要先把ApolloConfiguration註冊到應用容器中,請參考以下代碼:

Copy
// 這裏咱們項目使用的 DI 框架是`Autofac`,按需修改吧,記得將實例註冊成單例模式。 public class DependencyRegistrar : IDependencyRegistrar { public void Register(ContainerBuilder builder, ITypeFinder typeFinder) { builder.RegisterType<ApolloConfiguration>() .As<IConfiguration>() .Named<IConfiguration>("configuration") .SingleInstance(); ... } public int Order { get { return 1; } } }

接下來就能夠在項目中使用了,請參考以下代碼:

Copy
public class UserController : BaseController { private readonly IConfiguration _configuration; public UserController(IConfiguration configuration) { _configuration = configuration; } public ActionResult Add(AddUserInput model) { if (ModelState.IsValid) { // 從 Apollo 分佈式配置中心 項目`R01001` 默認命名空間`application`下 讀取配置項。 model.Password = _configuration.GetValue("DefaultUserPassword"); ... } ... } }

做者: Esofar

出處:http://www.javashuo.com/article/p-vaupvpgk-w.html

版權:本站使用「CC BY 4.0」創做共享協議,轉載請在文章明顯位置註明做者及出處。

相關文章
相關標籤/搜索