携程 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 ...
随机推荐
- 该如何真正进入SEO行业?
今天一个多年的朋友突然问我这个问题,他作为一个seo局外人,感觉SEO挺神秘,我认为要入行就要先了解一个SEO是什么职业,它的工作有那些,然后再考虑怎样进行学习或培训. 一.查看网站状态 seo人员每 ...
- PAT L3-016:二叉搜索树的结构(暴力)
https://www.patest.cn/contests/gplt/L3-016 题意:中文. 思路:暴力构造,暴力查询就好了.只不过操作很多,很麻烦.比赛的时候再给我10分钟就打完了,手速太慢好 ...
- scrapy实战2分布式爬取lagou招聘(加入了免费的User-Agent随机动态获取库 fake-useragent 使用方法查看:https://github.com/hellysmile/fake-useragent)
items.py # -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentati ...
- Node热部署插件
一.supervisor 首先需要使用 npm 安装 supervisor(这里需要注意一点,supervisor必须安装到全局) $ npm install -g supervisor Linux ...
- Spring Boot2(十三):整合定时任务发送邮件
一.前言 主要玩一下SpringBoot的定时任务和发送邮件的功能.定时发送邮件,这在实际生成环境下主要用户系统性能监控时,当超过设定的阙值,就发送邮件通知预警功能.这里只通过简单的写个定时结合邮件通 ...
- HiLoGenerator生成id
using System.Linq; namespace Product.Host { public class HiLoGenerator { ; ; ; private object Sequen ...
- Lucene03--字段属性
Lucene03--字段属性 1.Field 1.1 Field相当于Javabean的属性. 1.2 不同的Field的构造方法参数不一样: 大多数Field的构造函数有三个参数: a)第一个参 ...
- css基础5
今天在这里跟大家分享css基础最核心的部分,浮动和定位.话不多说,直接上干货! 一.浮动 定义:定位元素是相对于其正常位置应该出现的位置.定位元素的位置是相对于自身.父级元素位置.其他元素以及浏览器窗 ...
- 个人永久性免费-Excel催化剂功能第100波-透视多行数据为多列数据结构
在数据处理过程中,大量的非预期格式结构需要作转换,有大家熟知的多维转一维(准确来说应该是交叉表结构的数据转二维表标准数据表结构),也同样有一些需要透视操作的数据源,此篇同样提供更便捷的方法实现此类数据 ...
- 快速掌握mongoDB(五)——通过mongofiles和C#驱动操作GridFS
1 GridFS简介 当前Bson能存储的最大尺寸是16M,我们想把大于16M的文件存入mongoDB中怎么办呢?mongoDB提供的GridFS就是专门做这个的.使用GridFS存储大文件时,文件被 ...