ERP的配置管理实践

源宝导读:随着ERP系统的日益复杂,应用部署的方式越来越复杂,应用的配置也变得越来越庞杂,难以维护和管理。本文将介绍配置中心服务通过集中化、可离线的架构设计,解决ERP配置问题的实践经验。

一、背景

    随着ERP业务的日益复杂,程序配置越来越多,应用部署的方式越来越复杂,对程序配置管理的要求越来越高:

  1. 配置集中管理,一处修改全局生效,避免应用集群中配置不一致的问题。

  2. 不同的应用环境(生产、测试)需要不同的配置。

  3. 部分配置修改后需要立即生效。

  4. 复杂的配置难以理解,容易出错,需要有友好的交互界面来提供辅助。

    在这样的复杂环境下,传统的通过配置文件的方式已无法满足要求,我们开始设计配置管理中心,统一接管ERP的应用配置。

二、基础模型

配置中心被设计为一个独立的Windows服务,以HTTP方式对外提供服务。

如上图所示:

  • 在ERP安装部署时,向配置中心写入安装过程产生的初始配置。

  • 安装部署完成之后,如果环境还有调整,就需要进入配置管理界面,修订配置。

  • 配置修订完成后,会产生配置变更通知。订阅了配置变更事件的客户端就会收到配置变更通知,重新读取就能获取到最新配置。

三、配置项的设计

    不同ERP版本的配置项存在差异,而配置中心不仅需要适配最新版本的ERP,还需要兼容历史版本。为了解决这个问题,配置中心为每个大版本ERP提供独立的配置定义文件,文件采用XML格式。

示例:(系统设置-多时区的配置)

<options><group title="系统设置"><option title="启用多时区" name="Timezone-enable" type="string" ui="radio" description="启用多时区后数据中日期字段将会以“标准时区”存储" defaultvalue="0"><item text="启用" value="1" /><item text="禁用" value="0" /></option><option title="存储时区" name="Timezone-store" type="string" ui="radio" description="系统标准时区,默认为当前站点服务器对应时区,如选择此配置需要保证海外、国内服务器时区设置一致。" defaultvalue="0"><item text="服务器时区" value="0" /><item text="协调世界时(UTC)" value="1" /></option></group>
</options>
  • group:配置分组,便于从逻辑视角对配置项进行分组管理;

  • option:代表一个具体的配置定义,根据其中的ui字段,产生相应的UI。

四、配置客户端设计

上图简要描述了配置中心客户端的实现原理:

  1. 客户端和服务端保持了一个轮询(15s一次),从而能较快的获得配置更新的推送;

  2. 客户端从配置中心服务端获取到应用的最新配置后,会保存在内存中;

  3. 客户端会把从服务端获取到的配置在本地文件系统缓存一份。在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置;

  4. 应用程序可以从客户端获取最新的配置、订阅配置更新通知。

可用性考虑

    配置中心作为基础服务,可用性要求非常高,下面的表格描述了不同场景下配置中心的可用性:

五、兼容性设计

    兼容性设计的目标:

  • 任意配置中心服务端兼容所有版本的配置中心客户端。

  • 任意配置中心客户端兼容所有版本的配置中心服务端。

    服务端兼容主要是服务API的兼容,只要API接口不发生变化,旧版本的客户端就能正常调用。

    我们制定了相关的规范:

  • 接口一旦确定,就不能调整接口签名。唯一例外情况是增加参数,由于WebApi的性质,不存在的参数会以默认值填充。

  • 在增加新特性时,不能修改原有接口的行为,如果原有接口无法满足,就增加新的接口;如果数据结构发生变化,原有接口无法完全支持时,需要在原接口的实现中做兼容处理(牺牲掉一部分新特性)。

例如:

  • 在2.0版本支持应用分库特性后,允许为不同的应用配置不同的数据库,于是数据库的配置变成了多值。

  • 原有的接口需要调整为:如果数据库配置是多值,就返回第一个值。

  • 这样处理后,历史版本的应用也可以使用到最新版本的配置中心,但是不能用到新特性。

    为了保证新版本的客户端可以正常使用旧版本的服务端,还需要做客户端的兼容处理。

    这里定义一个配置读取接口IConfigReader

internal interface IConfigReader { // 接口方法略 }

    特性上有较大变化时,就重新写一个接口实现版本。到目前为止,一共有3个版本的接口实现。

    当应用需要读取配置时,首先确认服务端的版本,然后创建一个相应接口的实现版本。

public static IConfigReader Get(string serviceUri, string env, string siteGroupKey, string appId,
Version serviceVersion)
{if (serviceVersion >= new Version(2, 1, 0, 17)){// 支持合并接口读取和变更时间戳return new ConfigClientV3(serviceUri, env, siteGroupKey, appId);}else if (serviceVersion >= new Version(2, 1)){// 支持分离部署return new ConfigClientV2(serviceUri, env, siteGroupKey, appId);}else{// 兼容版本:1.xreturn new ConfigClientV1(serviceUri, env, siteGroupKey, appId);}
}

我们来看看不同场景下的兼容性是如何保证的:

5、总结

    目前的配置中心服务架构有两个特点:

  • 灵活可扩展的配置项设计:很好的满足了多环境多版本系统不同的配置要求;

  • 可离线的配置同步架构:既满足了配置集中统一管理的需求,又保证不会因为配置中心单点故障导致ERP整体宕机。

    随着ERP的应用越来越多,应用间的关系越来越复杂,ERP部署的模式越来越丰富,配置中心的功能也演变的更加复杂,只有通过架构的不断优化演进,配置中心才能更稳定高效的支撑ERP系统的运行。

------ END ------

作者简介

王同学: 研发工程师,目前负责ERP运行平台的设计与开发工作。

也许您还想看

链路追踪在ERP系统中的应用实践

从案例角度解析建模平台动态规则引擎

如何解决大批量数据保存的性能问题

研发协同平台持续交付2.0架构演进

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309039.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《LIO-SAM阅读笔记》1.IMU预积分模块

前言&#xff1a; LIO-SAM是一个多传感器融合的紧耦合SLAM框架&#xff0c;融合的传感器类型有雷达、IMU和GPS&#xff0c;其中雷达和IMU在LIO-SAM框架中必须使用的。LIO-SAM的优化策略采用了GTSAM库&#xff0c;GTSAM库采用了因子图的优化方法&#xff0c;其提供了一些列C的外…

EntityFramework Core 迁移忽略主外键关系

【导读】本文来源于一位公众号童鞋私信我的问题&#xff0c;在我稍加思索后给出了如下一种方案&#xff0c;在此之前我也思考过这个问题&#xff0c;借此机会我稍微看了下&#xff0c;目前能够想到的也只是本文所述方案。为何要忽略主外键关系我们不仅疑惑为何要忽略主外键关系…

Deque(双向队列 c++模版实现 算法导论第三版第十章10.1-5题)

Deque&#xff08;双向队列 c模版实现 &#xff09; 算法导论第三版第十章10.1-5题 #ifndef C11LEARN_DEQUE_H #define C11LEARN_DEQUE_H template<typename T> class Deque { private:int capacity;T*array;int head;int tail; public:Deque(int capacity 20);Deque(…

Java实现线性表(顺序表,链表)

顺序表: package seqTable;import java.util.ArrayList; import java.util.Scanner;public class SeqList {private int data[];private int len;private int maxSize;public SeqList(){data new int[100];maxSize 100;len 0;}public SeqList(int n){data new int[n];maxS…

你很可能需要知道这个调试小技巧

缘起 最近在调试的时候&#xff0c;需要观察第三方容器中每一个元素的值。默认情况下&#xff0c;vs 并不知道如何显示第三方容器的内容&#xff0c;只能手动观察容器中的每一个值&#xff0c;超级不方便。我找到一个非常给力的好办法&#xff0c;你还知道其它好办法吗&#xf…

List(C++模版实现的带哨兵的双向链表)

List(C模版实现的带哨兵的双向链表&#xff09; // // Created by 许加权 on 2021/7/10. //#ifndef C11LEARN_LIST_H #define C11LEARN_LIST_H template<typename T> class List { protected:class Node{public:Node *pre;Node *next;T key;Node(){}Node(const T key):k…

Java实现栈(顺序栈,链栈)

顺序栈&#xff1a; package SeqStack;public class Stack {private int top;private int base[];private int stackSize;public Stack(){stackSize 100;base new int[stackSize];top -1;}public Stack(int n){stackSize n;base new int[stackSize];top -1;}public bool…

全宇宙首本 VS Code 中文书,来了!

大家好&#xff01;我是韩骏&#xff0c;VS Code 中文社区创始人&#xff0c;VS Code 的代码贡献者。2013 年&#xff0c;毕业于上海交通大学软件学院&#xff0c;现在是微软开发平台事业部的软件工程师。写过 20 多款 VS Code 插件&#xff0c;其中最热门的 Code Runner 插件有…

用一个单链表L实现一个栈(算法导论第十章10.2-2题)

用一个单链表L实现一个栈&#xff08;算法导论第十章10.2-2题&#xff09; template<typename T> class HalfNode { public:T key;HalfNode* next; public:HalfNode(){next nullptr;};HalfNode(const T key):key(key){next nullptr;}; }; template<typename T> …

Java实现队列(循环队列,链队列)

循环队列: package SeqQueue;public class Queue {private int data[];private int queueSize;private int front,rear;public Queue(){data new int[100];queueSize 100;front rear 0;}public Queue(int n){queueSize n;data new int[queueSize];front rear 0;}publi…

C# 从1到Core--委托与事件

委托与事件在C#1.0的时候就有了&#xff0c;随着C#版本的不断更新&#xff0c;有些写法和功能也在不断改变。本文温故一下这些改变&#xff0c;以及在NET Core中关于事件的一点改变。一、C#1.0 从委托开始1. 基本方式什么是委托&#xff0c;就不说概念了&#xff0c;用例子说话…

用一个单链表L实现一个队列(算法导论第十章10.2-3)

用一个单链表L实现一个队列&#xff08;算法导论第十章10.2-3) template<typename T> class HalfNode { public:T key;HalfNode* next; public:HalfNode(){next nullptr;};HalfNode(const T key):key(key){next nullptr;}; }; template<typename T> class Singl…

开源导入导出库Magicodes.IE 多sheet导入教程

多Sheet导入教程说明本教程主要说明如何使用Magicodes.IE.Excel完成多个Sheet数据的Excel导入。要点多个相同格式的Sheet数据导入多个不同格式的Sheet数据导入主要步骤1. 多个相同格式的Sheet数据导入1.1 创建导入Sheet的Dto主要代码如下所示&#xff1a;学生数据Dto/// <su…

Java实现二叉树

二叉树: package Tree;import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; import java.util.Stack;public class BinTree {private class TreeNode{int data;TreeNode left;TreeNode right;public TreeNode(){data 0;left null;right nul…

解决 Azure AD 在 Azure Front Door 下登录失败的问题

点击上方关注“汪宇杰博客” ^_^导语最近我给博客系统加上了 Azure Front Door&#xff0c;集齐了12项 Azure 服务打算召唤神龙。没想到刚上线&#xff0c;Azure AD 的单点登录就爆了。OIDC 跳转错误当我尝试登录博客后台的时候&#xff0c;OIDC的跳转URL突然变成了 https://ed…

二叉搜索树的插入与删除(C语言)

代码如下: BinTree Insert( BinTree BST, ElementType X ) {if( !BST ){ /* 若原树为空&#xff0c;生成并返回一个结点的二叉搜索树 */BST (BinTree)malloc(sizeof(struct TNode));BST->Data X;BST->Left BST->Right NULL;}else { /* 开始找要插入元素的位置 */…

算法导论10-2.4题

算法导论10-2.4题 template<typename T> typename List<T>::Node* List<T>::search01(const T &key) {Nil->key key;Node * current Nil->next;while (current->key!key){current current->next;}return current; }其他全部代码链接

Linux中作业控制命令

开门见山&#xff0c;最近.NET劝退师要在linux上写些长时间运行的脚本&#xff0c;获取Azure BlobStorage存储的数据。记录一下Linux中后台执行作业的命令。Linux作业作业(Job)是shell管理的进程(每个job都有一个关联的PID)&#xff0c;每个作业会被分配一个线性job ID。有两种…

使用单项循环链表实现字典操作(算法导论第十章10.2-5题)

使用单项循环链表实现字典操作(算法导论第十章10.2-5题) template<typename T> void insert(SingleCycleL<T> & l,T key) {HalfNode<T> * t new HalfNode<T>(key);t->next l.Nil->next;l.Nil->next t; } template<typename T> …

《Unit Testing》2.1 经典学派如何做测试隔离

经典学派如何解决隔离问题首先&#xff0c;再回顾一下单元测试的三个重要特性&#xff1a;验证一小段代码&#xff08;或者叫一个单元&#xff09;执行速度快使用隔离的方式进行针对第一个特性就会引出一个问题&#xff1a;多小的一段代码才足够小&#xff1f;如果你采用针对每…