zoukankan      html  css  js  c++  java
  • 携程 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」创作共享协议,转载请在文章明显位置注明作者及出处。

  • 相关阅读:
    Java8学习笔记(五)--Stream API详解[转]
    Java8学习笔记(四)--接口增强
    Java8学习笔记(三)--方法引入
    JAVA8学习笔记(二)----三个预定义接口
    JAVA8学习笔记(一)----Lambda表达式
    Java基础加强总结(三)——代理(Proxy)
    Java基础加强总结(二)——泛型
    mysql统计表的大小
    jquery异步上传图片
    瀑布流
  • 原文地址:https://www.cnblogs.com/xichji/p/11324893.html
Copyright © 2011-2022 走看看