携程 Apollo 配置中心传统 .NET 项目集成实践

官方文档存在的问题#

可能由于 Apollo 配置中心的客户端源码一直处于更新中,导致其相关文档有些跟不上节奏,部分文档写的不规范,很容易给做对接的新手朋友造成误导。

比如,我在参考如下两个文档使用传统 .NET 客户端做接入的时候就发现了些问题。

1.两个文档关于标识应用身份的AppId的配置节点不一致。

2.第二个文档关于应用配置发布环境的Environment配置节点的描述出现明显错误。

当然,这些问题随时都有可能被修复。若您看到文档内容与本文描述不符,请以官方文档为准。

传统 .NET 项目快速接入#

快速进入正题。

安装依赖包#

在您项目的基础设施层,通过 NuGet 包管理器或使用如下命令添加传统 .NET 项目使用的客户端:

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

从上面的包名能看出什么?我这里选装的是2.0.3的版本。还有,这应该是一个 Javaer 起的名字。

配置应用标识 & 服务地址#

在您的启动项目中,打开App.configWeb.config配置文件,在<appSettings>节点中增加如下节点:

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

若您部署了多套 Config Service,支持多环境,请参考如下配置:

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

出处:https://www.cnblogs.com/esofar/p/11310921.html

版权:本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。

转载于:https://www.cnblogs.com/xichji/p/11324893.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值