官方文档存在的问题

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

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

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

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

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

传统 .NET 项目快速接入

快速进入正题。

安装依赖包

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

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

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

配置应用标识 & 服务地址

在您的启动项目中,打开App.configWeb.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 项目集成实践的更多相关文章

  1. Spring Boot 2.0 整合携程Apollo配置中心

    原文:https://www.jianshu.com/p/23d695af7e80 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够 ...

  2. 携程Apollo配置中心架构深度剖析

    转自:http://www.uml.org.cn/wfw/201808153.asp 一.介绍 Apollo(阿波罗)[参考附录]是携程框架部研发并开源的一款生产级的配置中心产品,它能够集中管理应用在 ...

  3. 携程apollo配置中心服务端如何感知配置更新?

    引言 前面有写过一篇<分布式配置中心apollo是如何实时感知配置被修改>,也就是客户端client是如何知道配置被修改了,有不少读者私信我你既然说了client端是如何感知的,那服务端又 ...

  4. 基于winserver的Apollo配置中心分布式&集群部署实践(正确部署姿势)

    基于winserver的Apollo配置中心分布式&集群部署实践(正确部署姿势)   前言 前几天对Apollo配置中心的demo进行一个部署试用,现公司已决定使用,这两天进行分布式部署的时候 ...

  5. SpringBoot 整合携程Apollo配置管理中心

    携程官网对apollo的使用讲解了很多种方式的使用,但是感觉一些细节还是没讲全,特别是eureka配置中心地址的配置 这里对springboot整合apollo说一下 >SpringBoot启动 ...

  6. 携程的配置中心(阿波罗apollo)

    https://github.com/ctripcorp/apollo https://pan.baidu.com/s/1dFEGMIX#list/path=%2Fmeetup%20ppt%2F040 ...

  7. Apollo配置中心的实战

    31.携程 Apollo 配置中心介绍~1.mp4 32.Apollo核心概念~1.mp4 32.Apollo核心概念~1.mp4 每个应用需要有一个唯一的AppID 要在指定的机器上的server. ...

  8. 携程apollo系列-客户端集成

    本文讲解如何在 Java 程序中集成 Apollo 配置, 主要涉及到一些基础用法. 对于一些高级用法, 比如如何加密/解密配置项 (可用于数据库密码配置), 如何动态切换数据源地址,如何动态切换日志 ...

  9. (转)实验文档3:在kubernetes集群里集成Apollo配置中心

    使用ConfigMap管理应用配置 拆分环境 主机名 角色 ip HDSS7-11.host.com zk1.od.com(Test环境) 10.4.7.11 HDSS7-12.host.com zk ...

随机推荐

  1. hive 常用的 join 操作 实例

    test_a 表 id value 1 java 2 python 3 c++ test_b 表 id value 1 java 2 go 3 php 4 c++ 1. join 计算的是笛卡尔积,不 ...

  2. Python编程菜鸟成长记--A1--01--编程语言介绍

    目录 1.重点知识 2.什么是编程?为什么要编程? 3.有哪些编程语言? 3.1.机器语言 3.2.汇编语言 3.3.高级语言 3.3.1.编译型语言 3.3.2.解释型语言 3.4.小结 4.主流编 ...

  3. 005-python-字典操作

    1. 字典 dict 用{}来表示 键值对数据 {key:value} 唯一性 键 都必须是可哈希的 不可变的数据类型就可以当做字典中的键 值 没有任何限制 dic = {'name':'alex', ...

  4. iOS组件化开发一本地环境配置(一)

    首先我们要使用pod支持组件化开发 解决CocoaPods慢的方案(gem和pod repo换源) gem换源 $ gem sources --remove https://rubygems.org/ ...

  5. scrapy基础知识之 Scrapy 和 scrapy-redis的区别:

    Scrapy 和 scrapy-redis的区别 Scrapy 是一个通用的爬虫框架,但是不支持分布式,Scrapy-redis是为了更方便地实现Scrapy分布式爬取,而提供了一些以redis为基础 ...

  6. ZigBee入门第一天

    按键查询控制灯的状态 1.宏定义灯和按键 2.按键和灯初始化 3.用if语句消抖的方法,实现按键控制灯的状态 相关寄存器 PxSEL PxDIR #include"ioCC2530.h&qu ...

  7. 『开发技巧』Keras自定义对象(层、评价函数与损失)

    1.自定义层 对于简单.无状态的自定义操作,你也许可以通过 layers.core.Lambda 层来实现.但是对于那些包含了可训练权重的自定义层,你应该自己实现这种层. 这是一个 Keras2.0  ...

  8. 转: windows系统下mysql出现Error 1045(28000) Access Denied for user 'root'@'localhost'

    windows系统下mysql出现Error 1045(28000) Access Denied for user 'root'@'localhost' 转自 http://zxy5241.space ...

  9. Java第二次作业——数组和String类

    Java第二次作业--数组和String类 学习总结 1.学习使用Eclipse关联jdk源代码,查看String类的equals()方法,截图,并学习其实现方法.举例说明equals方法和==的区别 ...

  10. 让Redis突破内存大小的限制

    Redis虽然可以实现持久化存储,也是基于数据内存模型的基础之上,单机内存大小限制着Redis存储的数据量,有没有一种替代方案呢?本文介绍一款笔者使用的采用New BSD License 许可协议的软 ...