一, 简介
基于 ZooKeeper 服务端, ZooKeeper Java 客户端以及 Spring 框架设计的用于系统内部进行参数维护的系统.
二, 设计背景
在我们日常开发的系统内部, 开发过程中最常见的一项工作便是常用参数的维护, 从我学习 Java 以来, 参数的配置多样化, 最常见的方式是 properties 配置文件或者是 xml 配置文件, 高深点的用法是 JMX MBean 进行参数管理以及数据库参数配置. 我们对现有的参数配置方式进行分析, 详见下表:
配置方式 | 常见问题 | 备注 |
---|---|---|
代码内字符窜字面量配置 | 每次参数的修改都需要重新编译 | |
properties 配置文件 / xml 配置文件 | 普通用法虽然将参数从代码内抽离出来,但是无法随时更新生效 | Spring 提供的 ReloadableResourceBundleMessageSource 工具类可以实现热加载 Properties 文件,将参数配置文件从代码分离可以做到不停机不重启做参数维护,并被程序加载,但是仍需系统重新从文件资源内获取新的参数值 |
JMX 参数配置 | 标准 MBEAN 是有侵入性的,他要管理的对象是符合 JAVA BEAN 规范的对象。但是要作为标准 MBEAN 而被管理,就需要实现一个接口。这个接口的名称必须是类名加上 MBean。 | Spring 支持将普通 Bean 通过配置 MBeanExporter 生成 MBean |
数据库参数配置 | 需要从数据库内加载参数,每次参数的使用需要连接数据库进行数据库查询。后来出现了代码缓存数据库参数,在一次使用后将参数信息放置缓存内,但是这种做法无法感知数据库参数的变化。 | 代码缓存数据库参数方式,可以新增代码设计用于刷新缓存参数,从库内重新读取放置缓存内,也不失为一种方便的参数管理。 |
基于上述各类参数配置分析, 一番思考设想, 设计出如下结构的[参数中心系统](详细设计链接), 设计说明查看下一节:
三, 系统设计说明
参数中心系统, 顾名思义, 主要是将参数集中化, 在实际开发中, 一个业务的实现需要几个甚至数十个模块联合完成, 每个模块都需要进行参数的更新维护, 一个模块的参数更新设计缺陷, 在进行参数维护时, 就可能导致某个业务的中断, 故需要将多参数管理统一化管理; 统一化的参数管理方式, 便可能涉及到了参数数据的统一存储, 统一之后便出现了性能瓶颈需求, 不然所有鸡蛋装一个篮子里, 一出问题全部碎掉; 在集中化后, 各个模块有自己的参数, 有些参数可能仅限单个系统访问, 便需要安全的参数访问方式; 在参数使用过程中, 常见的功能之一便是参数的实时维护; 在项目投产过程中, 经常因为参数配置问题比如配置错误等情况导致业务中断, 故需要一个参数检查表来确认参数的正确性; 参数管理整合后, 需要方便的操作来实现管理功能. 概括一下, 参数中心系统需要满足以下技术需求:
多系统, 多模式, 安全, 动态维护的参数配置
个性化话参数配置(普通字符窜, JSON 字符窜, 数组窜)
低侵入
快捷的参数导入导出功能
便捷的管理方式
上线参数检查表, 用于上线时各类参数的检查, 防止出错
高可用
安全控制
根据上述分析, 设计之初, 思考如何实现多系统多模式的参数存储, 虽然一直知道 ZooKeeper 这个东西, 但从未详细了解过, 偶然机会大致学习了一下 ZooKeeper, 发现 ZooKeeper 的各类机制与参数中心系统设计相吻合, 比如: 多系统多模式的参数存储与 ZooKeeper 的目录型存储方式相似, 查看下面图 3-1 展示; 参数的安全方式认证与 ZooKeeper 的 ACL 机制吻合; 参数实时维护可以借鉴 ZooKeeper 的 Watcher 机制; 参数中心系统的高性能高可用设计与 ZooKeeper 的集群方式相吻合. 这样下来, 参数中心系统最大的问题参数存储模块服务端得到了完美的解决. 接下来的便是基于 ZooKeeper 设计出对应的客户端, 管理端.
图 3-1 基于 ZooKeeper 的参数存储
Java 应用端常用的技术之一便是 Spring 框架, 也符合低侵入的设计原则, 在使用 Spring 开发过程中, 常用的功能之一便是使用 ${}引用 properties 配置文件内的参数, 如此方便的参数配置方式, 我决定使用类似的方式, 配置方式为 zk{}(zk 表示 ZooKeeper 参数), 故客户端的设计是基于 Spring 的设计.
四, 系统技术组合
ZooKeeper 集群 + ZooKeeper Java 客户端 + Spring BeanFactoryPostProcessor 扩展点 + JSON 字符窜解析 + Spring SpEL 表达式
五, 设计实现(重点)
根据上述设计说明等信息, 最后得出这样一个系统, 基于 ZooKeeper 参数存储, Spring 客户端使用 zk{}进行参数配置的参数中心系统.
服务端
服务端设计如 3-1 图所示(在实际开发过程中可能稍有变动)
客户端(重点)
在进行参数中心系统客户端实现之前, 我们先了解一点 Spring 框架的基础知识.
在 Spring 框架开发过程中, 最常用的配置是 < bean/>标签的使用, 在工作中, 最常听的一种说法是, 在 xml 里配置上就可以使用 bean 了, 就有对象了, 这种理解潜在一层含义 xml 直接配置成了 java bean, 而实际上, Spring 中 bean 的定义最终表现为 BeanDefinition 对象, 个人理解为 xml 实际配置的是 bean 的说明信息, Spring 将这些说明信息转换为了 BeanDefinition 对象, 再由 BeanDefinition 生成了我们最终看到的 bean, 实际流程为 [xml 配置 ->BeanDefinition->Bean] 而一般开发者不知道 BeanDefinition, 故理解含义成了[xml 配置 ->Bean], 中间缺少了重要的环节. 在 BeanDefinition 到 Bean 这个过程中, Spring 由 BeanFactory 生成实际的 bean, 在实际 bean 产生前, Spring 提供了 BeanFactoryPostProcessor 扩展点(类似还有 BeanPostProcessor), 通过该扩展点可以获取到配置的 BeanDefinition 信息, 用于自定义扩展对 bean 定义变更修改, 实现自由控制.
Spring 允许开发者实现自定义的扩展点, 实现特定的接口, 使用通用的配置即可注册一个扩展点到 Spring 容器内. 详细学习参考 https://docs.spring.io/spring/docs/4.3.19.RELEASE/spring-framework-reference/htmlsingle/#beans-factory-nature.
参数中心系统参数的配置实现参考了 Spring 的 ${}参数配置, 我们对 ${}的实现做简单学习.${}的实现便使用了 Spring 扩展点 BeanFactoryPostProcessor, 开发中常见配置如下:
上图中采用了 context:property-placeholder 标签配置, 根据 Spring context 的 xsd 说明文件, 我们知道了 property-placeholder 对应的实际类为 org.springframework.context.support.PropertySourcesPlaceholderConfigurer,context:property-placeholder 配置实际为在 spring 容器内注册一个扩展点, 实现 ${}表达式的解析. 实现类图以及调用流程大致如下, 再详细过程查看源码,(有句话叫做师傅领进门, 修行在个人):
参数中心系统客户端的实现代码与上述实现类似, 不同的是 properties 配置文件变成了 ZooKeeper 参数存储,${}变成了 zk{}.
客户端项目名称: itwatertop-pczk-client
管理端
基于 H5 的管理页面设计, 详细情况还未设想(先实现了主要的服务端客户端, 管理端暂时可以使用 ZooKeeper 的客户端).
六, 客户端设计源码
客户端程序结构如下:
文件说明:
BaseLoader.java 数据加载基类
ZookeeperDataLoader.java Java ZooKeeper 客户端实现数据加载, 参数更新回调.
PlaceholderMsg.java zk{zkexp} zk 配置表达式解析结果, 以及使用该表达式的 bean 属性获取 SpEL 表达式.
ParamCenterStore.java 对 ZooKeeper 参数服务端获取的参数信息做缓存, 并且将对应的使用该参数的 Bean 属性表达式统计, 方便 ZooKeeper 参数变更时回调使用.
PczkConstants.java 系统内常量字符窜整合.
PczkStringValueResolver.java 表达式解析统一接口
PczkPropertyPlaceholderConfiguer.java 实现 Spring 扩展点 BeanFactoryPostProcessor, 通过该扩展点对 BeanDefinition 配置元信息做解析以及变更.
PropertyPlaceholderHelper.java 具体实现 zk{}表达式解析规则.
PczkBeanDefinitionVisitor.java 对 Spring IoC 容器内的 BeanDefinition 属性配置信息做解析, 主要结合 PczkPropertyPlaceholderConfiguer.java 使用.
test 目录下包含一部分测试代码, 可以自行查看
客户端代码实现简介:
根据上述需求, 客户端代码需要具备的能力包括:
与 ZooKeeper 服务器的连通, 并获取参数信息
Spring xml 中 zk{}表达式的解析
多样化的参数配置, 支持字符窜, 对象, 数组
ZooKeeper 参数变更回调, 维护参数信息
针对上面 4 种要求, 在开发时使用以下解决办法:
使用 ZooKeeper Java 客户端;
结合 Spring 的扩展点 BeanFactoryPostProcessor;
在 ZooKeeper 服务端节点内设置数据时设置字符窜 / JSON 对象字符窜或者是 JSON 数组字符窜, 客户端使用数据时分别配置为 zk{param},zk{param.key},zk{param[i]}, 通过对 zk{}表达式解析判断 ZooKeeper 参数格式, 若为 JSON 字符窜使用 FastJSON 对字符窜做解析, 访问对应属性值;
针对参数变更回调, 在解析 zk{}表达式时拼接出了可以访问到对应 bean 属性的 SpEL 表达式, 通过 SpEL 表达式访问 bean 属性调用 setter 方法, 因此属性操作也受到 SpEL 表达式的限制. 在个别情况下, 由于参数的变更可能需要别的一下操作处理, 比如重新建立连接, 这个可以自行扩展代码, 比如比较上一次的值和当前值是否一致, 不一致做出新的操作(现在也在设想怎么可以自动识别进行额外操作).
客户端详细实现源码下载: 访问 GitHub 项目(注释还是比较清晰的)
来源: https://www.cnblogs.com/superstudy/p/9696631.html