首页 > 编程知识 正文

如何区别服务端与客户端(apollo配置中心原理)

时间:2023-05-06 20:24:02 阅读:86765 作者:3700

服务端

1、服务器端的实时推送设计

新配置版本的大致流程

用户在门户网站上编辑和发布配置。 门户通过调用管理员服务提供的接口来执行发布操作。 Admin Service收到请求后,将发布消息发送到各配置服务,通知用户配置服务的配置发生了更改。 config服务接收到发布消息后,会通知对应的客户端,通过Http实现长时间的连接。

2 .发送释放消息的方法

发布消息通过Mysql实现了简单的消息队列。 之所以不采用消息中间件,是因为Apollo在部署时尽量简化,将外部依赖降到最低

发送发布消息的实现方法

Admin Service在发布配置后将消息记录插入到发布消息表中。 Config Service启动线程并定期扫描发布消息表,以查看是否有新的消息记录。 Config Service发现新的消息记录时,会通知所有消息监听器。 消息侦听器在收到配置发布的信息时,会通知相应的客户端。 3 .如何实现配置服务通知客户端

通知通过基于Http的长连接实现,主要分为以下步骤。

客户端在配置服务的通知/v2接口上启动Http请求。 通告/V2接口通过spring延迟结果挂起请求,不会立即返回。 如果在60s内没有公开客户机感兴趣的配置,则将Http状态代码304返回给客户机。 如果检测到配置更改,则调用延迟结果的设置结果方法,传递配置更改的namespace信息,并立即返回请求。 客户端从返回的结果中获取配置已更改的namespace后,将立即请求Config Service获取该namespace的最新配置。

客户端

1、设计原理

Apollo客户端的实现原理

客户端和服务器端保持了较长的连接,并编译了配置的实时更新推送。 定时提取配置是客户端的本地定时任务,缺省情况下每5分钟提取一次。 也可以在运行时指定系统属性: Apollo.refresh interval以复盖它。 单位是分钟,推定时拉=双保险。 从apolloconfigurationcenter服务器端获取APP应用程序的最新配置后,客户端将存储在内存中。 客户端将从服务器端获取的配置缓存在本地文件系统中,如果服务或网络不可用,则可以使用本地配置。 也就是说,本地开发模型env=Local。 2、与Spring整合后通过@Value获取配置原理

Spring从3.1版开始添加了配置环境和属性源。

ConfigurableEnvironment实现了一个环境接口,包含多个属性,运行时结构如下图所示。

配置Spring

需要注意的是,属性源之间存在优先级,如果一个密钥存在于多个属性源中,则位于前面的属性源优先。 的原理是,在APP启动阶段,Apollo从远程获取配置并进行分组

装成 PropertySource 并插入到第一个即可,如下图所示。

3、动态刷新配置的实现原理

Apollo Client 中定义了 SpringValueProcessor 类,其实现了 BeanPostProcessor 用于处理值修改。

public class SpringValueProcessor extends ApolloProcessor implements BeanFactoryPostProcessor, BeanFactoryAware { private PlaceholderHelper placeholderHelper = new PlaceholderHelper(); private BeanFactory beanFactory; public SpringValueRegistry springValueRegistry = new SpringValueRegistry(); @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { Class clazz = bean.getClass(); for (Field field : findAllField(clazz)) { processField(bean, beanName, field); } return bean; } /** * 核心处理 */ private void processField(Object bean, String beanName, Field field) { // register @Value on field Value value = field.getAnnotation(Value.class); if (value == null) { return; } Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value()); if (keys.isEmpty()) { return; } for (String key : keys) { SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false); springValueRegistry.register(beanFactory, key, springValue); logger.debug("Monitoring {}", springValue); } } }

通过实现 BeanPostProcessor 来处理每个 bean 中的值,然后将这个配置信息封装成一个 SpringValue 存储到 springValueRegistry 中

SpringValue 代码如下所示。

public class SpringValue { private MethodParameter methodParameter; private Field field; private Object bean; private String beanName; private String key; private String placeholder; private Class<?> targetType; private Type genericType; private boolean isJson; }

SpringValueRegistry 就是利用 Map 来存储,代码如下所示。

public class SpringValueRegistry { private final Map<BeanFactory, Multimap<String, SpringValue>> registry = Maps.newConcurrentMap(); private final Object LOCK = new Object(); // 注册 public void register(BeanFactory beanFactory, String key, SpringValue springValue) { if (!registry.containsKey(beanFactory)) { synchronized (LOCK) { if (!registry.containsKey(beanFactory)) { registry.put(beanFactory, LinkedListMultimap.<String, SpringValue>create()); } } } registry.get(beanFactory).put(key, springValue); } // 获取 public Collection<SpringValue> get(BeanFactory beanFactory, String key) { Multimap<String, SpringValue> beanFactorySpringValues = registry.get(beanFactory); if (beanFactorySpringValues == null) { return null; } return beanFactorySpringValues.get(key); } }

当 AutoUpdateConfigChangeListener 监听到变化就会调用 onChange 更新值

public class AutoUpdateConfigChangeListener implements ConfigChangeListener{ private final SpringValueRegistry springValueRegistry; @Override public void onChange(ConfigChangeEvent changeEvent) { Set<String> keys = changeEvent.changedKeys(); if (CollectionUtils.isEmpty(keys)) { return; } for (String key : keys) { // 1. check whether the changed key is relevant Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key); if (targetValues == null || targetValues.isEmpty()) { continue; } // 2. update the value 更新值 for (SpringValue val : targetValues) { updateSpringValue(val); } } } }

最后

本文参考

http://c.biancheng.net/view/5480.html

http://c.biancheng.net/view/5482.html

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。