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

官方文档存在的问题
可能由于 Apollo 配置中心的客户端源码一直处于更新中,导致其相关文档有些跟不上节奏,部分文档写的不规范,很容易给做对接的新手朋友造成误导。
比如,我在参考如下两个文档使用传统 .NET 客户端做接入的时候就发现了些问题。
- ctripcorp/apollo - .Net客户端使用指南
- ctripcorp/apollo.net - .Net客户端 System.Configuration.ConfigurationManager 集成
问题一:两个文档关于标识应用身份的AppId的配置节点不一致。


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

当然,这些问题随时都有可能被修复。若您看到文档内容与本文描述不符,请以官方文档为准。
传统 .NET 项目快速接入
快速进入正题。
安装依赖包
在您项目的基础设施层,通过 NuGet 包管理器或使用如下命令添加传统 .NET 项目使用的客户端:
Install-Package Com.Ctrip.Framework.Apollo.ConfigurationManager -Version 2.0.3
从上面的包名能看出什么?我这里选装的是2.0.3的版本。还有,这应该是一个 Javaer 起的名字。
配置应用标识 & 服务地址
在您的启动项目中,打开App.config或Web.config配置文件,在<appSettings>节点中增加如下节点:
<!-- Change to the actual app id -->
<add key="Apollo.AppID" value="R01001" />
<add key="Apollo.MetaServer" value="http://localhost:8080" />
若您部署了多套 Config Service,支持多环境,请参考如下配置:
<!-- 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 客户端了。
二次封装代码
我们习惯在项目中使用第三方库的时候封装一层,这种封装是浅层的,一般都是在项目的基础设施层来做,这样其他层使用就不需要再次引入依赖包。
不说了,直接上代码吧。
代码结构大致如下:
├─MyCompany.MyProject.Infrastructure # 项目基础设施层
│ │
│ └─Configuration
│ ApolloConfiguration.cs # Apollo 分布式配置项读取实现
│ ConfigurationChangeEventArgs.cs # 配置更改回调事件参数
│ IConfiguration.cs # 配置抽象接口,可基于此接口实现本地配置读取
IConfiguration
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
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
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注册到应用容器中,请参考如下代码:
public class DependencyRegistrar : IDependencyRegistrar
{
public void Register(ContainerBuilder builder, ITypeFinder typeFinder)
{
// 我们项目使用的 DI 框架是 Autofac,注册这个地方按需修改吧,注意将实例注册成单例。
builder.RegisterType<ApolloConfiguration>()
.As<IConfiguration>()
.Named<IConfiguration>("configuration")
.SingleInstance();
...
}
public int Order
{
get { return 1; }
}
}
接下来就可以在项目中使用了,请参考如下代码:
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");
...
}
...
}
}
携程 Apollo 配置中心传统 .NET 项目集成实践的更多相关文章
- Spring Boot 2.0 整合携程Apollo配置中心
原文:https://www.jianshu.com/p/23d695af7e80 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够 ...
- 携程Apollo配置中心架构深度剖析
转自:http://www.uml.org.cn/wfw/201808153.asp 一.介绍 Apollo(阿波罗)[参考附录]是携程框架部研发并开源的一款生产级的配置中心产品,它能够集中管理应用在 ...
- 携程apollo配置中心服务端如何感知配置更新?
引言 前面有写过一篇<分布式配置中心apollo是如何实时感知配置被修改>,也就是客户端client是如何知道配置被修改了,有不少读者私信我你既然说了client端是如何感知的,那服务端又 ...
- 基于winserver的Apollo配置中心分布式&集群部署实践(正确部署姿势)
基于winserver的Apollo配置中心分布式&集群部署实践(正确部署姿势) 前言 前几天对Apollo配置中心的demo进行一个部署试用,现公司已决定使用,这两天进行分布式部署的时候 ...
- SpringBoot 整合携程Apollo配置管理中心
携程官网对apollo的使用讲解了很多种方式的使用,但是感觉一些细节还是没讲全,特别是eureka配置中心地址的配置 这里对springboot整合apollo说一下 >SpringBoot启动 ...
- 携程的配置中心(阿波罗apollo)
https://github.com/ctripcorp/apollo https://pan.baidu.com/s/1dFEGMIX#list/path=%2Fmeetup%20ppt%2F040 ...
- Apollo配置中心的实战
31.携程 Apollo 配置中心介绍~1.mp4 32.Apollo核心概念~1.mp4 32.Apollo核心概念~1.mp4 每个应用需要有一个唯一的AppID 要在指定的机器上的server. ...
- 携程apollo系列-客户端集成
本文讲解如何在 Java 程序中集成 Apollo 配置, 主要涉及到一些基础用法. 对于一些高级用法, 比如如何加密/解密配置项 (可用于数据库密码配置), 如何动态切换数据源地址,如何动态切换日志 ...
- (转)实验文档3:在kubernetes集群里集成Apollo配置中心
使用ConfigMap管理应用配置 拆分环境 主机名 角色 ip HDSS7-11.host.com zk1.od.com(Test环境) 10.4.7.11 HDSS7-12.host.com zk ...
随机推荐
- BFS(二):数的变换
[例1]整数变换(POJ 3278 “Catch That Cow”) 给定两个整数a和b(0 ≤a,b≤100,000),要求把a变换到b.变换规则为:(1)当前数加1:(2)当前数减1:(3)当前 ...
- white box白盒测试
逻辑覆盖法:语句覆盖,判定覆盖,条件覆盖,判定/条件覆盖,组合覆盖,路径覆盖 基本路径测试法:Control Flow Graphs, CFG.带箭头的边 条件覆盖:使每个判定中每个条件的可能值至少满 ...
- Linux嵌入式kgdb调试环境搭建
======================= 我的环境 ==========================PC 端: win7 + vmware-15 ubuntu16.04开发板:Freesca ...
- leetcode笔记 动态规划在字符串匹配中的应用
目录 leetcode笔记 动态规划在字符串匹配中的应用 0 参考文献 1. [10. Regular Expression Matching] 1.1 题目 1.2 思路 && 解题 ...
- c++ 组合
组合 题目描述 已知一个一维数组a1..n,又已知一整数m. 如能使数组a中任意几个元素之和等于m,则输出YES,反之则为NO. 输入 输入包括两行,第一行包含两个整数n m(1<=n<2 ...
- 6.秋招复习简单整理之请你谈谈JDBC的反射,以及它的作用?
通过反射com.mysql.jdbc.Driver类,实例化该类时会调用该类的静态代码块,该代码块会去java的DriverManager类中注册自己,DriverManager管理所有已注册的驱动类 ...
- Spring Cloud Alibaba | Nacos配置管理
目录 Spring Cloud Alibaba | Nacos配置管理 1. pom.xml 项目依赖 2. 在 bootstrap.properties 中配置 Nacos server 的地址和应 ...
- MYSQL语句强化练习
之前发现自己写sql不怎么得心应手,总是百度零零散散的学习一下,所以在空闲的时候自己就专门找一下mysql的强化题敲一下练习一下,简要记录一下,sql写着写着就会越来越熟练,总之要自己加油! 表结构 ...
- [NOI2014]魔法森林题解
这道题正解其实是LCT,然而貌似SPFA也可以成功水过,所以根本不知道LCT的我只能说SPFA了. 这道题最大的限制是两种精灵就意味着一条道可能有两个权值,因此我们需要去将其中一个固定,然后再推另一个 ...
- Java编程思想:一个小小的控制框架
这个实验我很喜欢,学到了非常多的东西: import java.util.ArrayList; import java.util.List; public class Test { public st ...