[从源码学设计]蚂蚁金服SOFARegistry 之 ChangeNotifier

0x00 摘要

SOFARegistry 是蚂蚁金服开源的一个生产级、高时效、高可用的服务注册中心。

本系列文章重点在于分析设计和架构,即利用多篇文章,从多个角度反推总结 DataServer 或者 SOFARegistry 的实现机制和架构思路,让大家借以学习阿里如何设计。

本文为第十五篇,分析如何执行ChangeNotifier 来通知相关模块:hi,这里有新数据变化来到了,兄弟们走起来。

0x01 业务范畴

1.1 概述

当有数据发布者 publisher 上下线时,会分别触发 publishDataProcessor 或 unPublishDataHandler,Handler 会往 dataChangeEventCenter 中添加一个数据变更事件,用于异步地通知事件变更中心数据的变更。事件变更中心收到该事件之后,会往队列中加入事件。

  1. 此时 dataChangeEventCenter 会根据不同的事件类型异步地对上下线数据进行相应的处理,即把这个事件变更信息变成ChangeNotifier ,进而变成Operator,放到AbstractAcceptorStore;

  2. 与此同时 DataChangeHandler 会把这个事件变更信息通过 ChangeNotifier 对外发布,通知其他节点进行数据同步。

因为篇幅限制,前文对 ChangeNotifier 这部分只是略过,本文就详细讲解下事件变更通知ChangeNotifier。 这里会再把整理流程串起来,会涉及到前面某些文章内容。

先给出图示以便大家了解 ChangeNotifier 的作用。

        +--------------------+
| PublishDataHandler |
+--------+-----------+
|
|
| publisher
|
v
+---------+------------+
|DataChangeEventCenter |
+---------+------------+
|
|
| ChangeData
v
+---------+------------+
| DataChangeEventQueue |
+---------+------------+
|
|
| ChangeData
v
+-------+----------+
| DataChangeHandler|
+-------+----------+
|
|
| ChangeData
v
+------+--------+ +------------+
| ChangeNotifier| +--------> | datumCache |
+------+--------+ +------------+
|
|
v
+---+------+
| notifier |
+---+------+
|
v
+-----------+---------------+
| |
v v
+----+----------------+ +------+----------+
|SessionServerNotifier| | BackUpNotifier |
+----+----------------+ +------+----------+
| |
| |
| |
| v
+--v------------+ +------+----------------+
| sessionServer | | AbstractAcceptorStore |
+---------------+ +-----------------------+

1.2 数据变化

数据变化有两个方向

  • 数据服务器节点变化;

  • 数据的变化,即Publisher和Scriber的变化;

ChangeNotifier就是负责把 Publisher和Scriber的变化 通知给相关模块。变更通知就是一种解耦

0x02 数据结构

我们首先需要看看通知的数据结构。

2.1 接口定义

IDataChangeNotifier是通知的接口定义:

public interface IDataChangeNotifier {
Set<DataSourceTypeEnum> getSuitableSource(); /**
*
* @param datum
* @param lastVersion
*/
void notify(Datum datum, Long lastVersion);
}

2.2 派生类

IDataChangeNotifier 有四个派生类,分别对应了具体数据变化的四种可能,从名字大约可以判断出用途。

public class BackUpNotifier implements IDataChangeNotifier

public class SessionServerNotifier implements IDataChangeNotifier

public class SnapshotBackUpNotifier implements IDataChangeNotifier

public class TempPublisherNotifier implements IDataChangeNotifier

2.3 Bean

对应的Bean如下:

@Bean(name = "dataChangeNotifiers")
public List<IDataChangeNotifier> dataChangeNotifiers() {
List<IDataChangeNotifier> list = new ArrayList<>();
list.add(sessionServerNotifier());
list.add(tempPublisherNotifier());
list.add(backUpNotifier());
return list;
}

0x03 流程

我们从头理一下流程。

3.1 放入消息

当有数据发布者 publisher 上下线时,会分别触发 publishDataProcessor 或 unPublishDataHandler ,Handler 会往 dataChangeEventCenter 中添加一个数据变更事件,用于异步地通知事件变更中心数据的变更。事件变更中心收到该事件之后,会往队列中加入事件。

在DataServer这里,具体流程如下:

3.1.1 PublishDataHandler

PublishDataHandler 响应 PublishDataRequest。当有Publisher时候,就往DataChangeEventCenter放入消息。即调用下面来放入消息

dataChangeEventCenter.onChange(publisher, dataServerConfig.getLocalDataCenter());

具体代码如下:

public class PublishDataHandler extends AbstractServerHandler<PublishDataRequest> {

    @Autowired
private ForwardService forwardService; @Autowired
private SessionServerConnectionFactory sessionServerConnectionFactory; @Autowired
private DataChangeEventCenter dataChangeEventCenter; @Autowired
private DataServerConfig dataServerConfig; @Autowired
private DatumLeaseManager datumLeaseManager; @Autowired
private ThreadPoolExecutor publishProcessorExecutor; @Override
public Object doHandle(Channel channel, PublishDataRequest request) {
Publisher publisher = Publisher.internPublisher(request.getPublisher());
if (forwardService.needForward()) {
CommonResponse response = new CommonResponse();
response.setSuccess(false);
response.setMessage("Request refused, Server status is not working");
return response;
} dataChangeEventCenter.onChange(publisher, dataServerConfig.getLocalDataCenter()); if (publisher.getPublishType() != PublishType.TEMPORARY) {
String connectId = WordCache.getInstance().getWordCache(
publisher.getSourceAddress().getAddressString());
sessionServerConnectionFactory.registerConnectId(request.getSessionServerProcessId(),
connectId);
// record the renew timestamp
datumLeaseManager.renew(connectId);
} return CommonResponse.buildSuccessResponse();
}
}

此时具体逻辑如下:

        +--------------------+
| PublishDataHandler |
+--------+-----------+
|
|
| publisher
|
v
+---------+------------+
|DataChangeEventCenter |
+---------+------------+

3.1.2 DataChangeEventCenter

DataChangeEventCenter 的核心是一个DataChangeEventQueue数组,

DataChangeEventCenter . onChange函数会首先根据Publisher的DataInfoId获取hash,根据这个hash数值来决定把 DataChangeEvent 消息放入哪个queue来处理,就是调用这个 queue的 onChange 函数。

public class DataChangeEventCenter {
/**
* queues of DataChangeEvent
*/
private DataChangeEventQueue[] dataChangeEventQueues; @Autowired
private DatumCache datumCache; @PostConstruct
public void init() {
if (isInited.compareAndSet(false, true)) {
queueCount = dataServerConfig.getQueueCount();
dataChangeEventQueues = new DataChangeEventQueue[queueCount];
for (int idx = 0; idx < queueCount; idx++) {
dataChangeEventQueues[idx] = new DataChangeEventQueue(idx, dataServerConfig, this,
datumCache);
dataChangeEventQueues[idx].start();
}
}
} /**
* receive changed publisher, then wrap it into the DataChangeEvent and put it into dataChangeEventQueue
*
* @param publisher
* @param dataCenter
*/
public void onChange(Publisher publisher, String dataCenter) {
int idx = hash(publisher.getDataInfoId());
Datum datum = new Datum(publisher, dataCenter);
if (publisher instanceof UnPublisher) {
datum.setContainsUnPub(true);
}
if (publisher.getPublishType() != PublishType.TEMPORARY) {
dataChangeEventQueues[idx].onChange(new DataChangeEvent(DataChangeTypeEnum.MERGE,
DataSourceTypeEnum.PUB, datum));
} else {
dataChangeEventQueues[idx].onChange(new DataChangeEvent(DataChangeTypeEnum.MERGE,
DataSourceTypeEnum.PUB_TEMP, datum));
}
}
}

DataChangeEventQueue 的主要数据成员如下:

public class DataChangeEventQueue {
/**
* a block queue that stores all data change events
*/
private final BlockingQueue<IDataChangeEvent> eventQueue; private final Map<String, Map<String, ChangeData>> CHANGE_DATA_MAP_FOR_MERGE = new ConcurrentHashMap<>(); private final DelayQueue<ChangeData> CHANGE_QUEUE = new DelayQueue(); private DataChangeEventCenter dataChangeEventCenter; private DatumCache datumCache;
}

其执行引擎是一个线程,其block在 BlockingQueue eventQueue 之上,当有消息时候,就取出消息,针对消息类型做不同处理。

public void start() {
Executor executor = ExecutorFactory
.newSingleThreadExecutor(String.format("%s_%s", DataChangeEventQueue.class.getSimpleName(), getName()));
executor.execute(() -> {
while (true) {
try {
IDataChangeEvent event = eventQueue.take();
DataChangeScopeEnum scope = event.getScope();
if (scope == DataChangeScopeEnum.DATUM) {
DataChangeEvent dataChangeEvent = (DataChangeEvent) event;
//Temporary push data will be notify as soon as,and not merge to normal pub data;
if (dataChangeEvent.getSourceType() == DataSourceTypeEnum.PUB_TEMP) {
addTempChangeData(dataChangeEvent.getDatum(), dataChangeEvent.getChangeType(),
dataChangeEvent.getSourceType());
} else {
handleDatum(dataChangeEvent.getChangeType(), dataChangeEvent.getSourceType(),
dataChangeEvent.getDatum());
}
} else if (scope == DataChangeScopeEnum.CLIENT) {
handleClientOff((ClientChangeEvent) event);
} else if (scope == DataChangeScopeEnum.SNAPSHOT) {
handleSnapshot((DatumSnapshotEvent) event);
}
}
}
});
}

对于 Publisher 消息类型,handleDatum 函数会根据changeType是 COVER 还是 MERGE 来做不同处理。

在此步骤中,也会把 ChangeData 放入 CHANGE_QUEUE.put(changeData);

private void handleDatum(DataChangeTypeEnum changeType, DataSourceTypeEnum sourceType,
Datum targetDatum) {
lock.lock();
try {
//get changed datum
ChangeData changeData = getChangeData(targetDatum.getDataCenter(),
targetDatum.getDataInfoId(), sourceType, changeType);
Datum cacheDatum = changeData.getDatum();
if (changeType == DataChangeTypeEnum.COVER || cacheDatum == null) {
changeData.setDatum(targetDatum);
} else {
Map<String, Publisher> targetPubMap = targetDatum.getPubMap();
Map<String, Publisher> cachePubMap = cacheDatum.getPubMap();
for (Publisher pub : targetPubMap.values()) {
String registerId = pub.getRegisterId();
Publisher cachePub = cachePubMap.get(registerId);
if (cachePub != null) {
// if the registerTimestamp of cachePub is greater than the registerTimestamp of pub, it means
// that pub is not the newest data, should be ignored
if (pub.getRegisterTimestamp() < cachePub.getRegisterTimestamp()) {
continue;
}
// if pub and cachePub both are publisher, and sourceAddress of both are equal,
// and version of cachePub is greater than version of pub, should be ignored
if (!(pub instanceof UnPublisher) && !(cachePub instanceof UnPublisher)
&& pub.getSourceAddress().equals(cachePub.getSourceAddress())
&& cachePub.getVersion() > pub.getVersion()) {
continue;
}
}
cachePubMap.put(registerId, pub);
cacheDatum.setVersion(targetDatum.getVersion());
}
}
} finally {
lock.unlock();
}
}

此时具体逻辑如下:

        +--------------------+
| PublishDataHandler |
+--------+-----------+
|
|
| publisher
|
v
+---------+------------+
|DataChangeEventCenter |
+---------+------------+
|
|
| ChangeData
v
+---------+------------+
| DataChangeEventQueue |
+---------+------------+

3.2 消费消息&发送通知

DataChangeHandler 会针对每个DataChangeEventQueue进行消费通知。

public class DataChangeHandler {

    @Autowired
private DataChangeEventCenter dataChangeEventCenter; @Autowired
private DatumCache datumCache; @Resource
private List<IDataChangeNotifier> dataChangeNotifiers; @PostConstruct
public void start() {
DataChangeEventQueue[] queues = dataChangeEventCenter.getQueues();
int queueCount = queues.length;
Executor executor = ExecutorFactory.newFixedThreadPool(queueCount, DataChangeHandler.class.getSimpleName());
Executor notifyExecutor = ExecutorFactory
.newFixedThreadPool(dataServerConfig.getQueueCount() * 5, this.getClass().getSimpleName());
for (int idx = 0; idx < queueCount; idx++) {
final DataChangeEventQueue dataChangeEventQueue = queues[idx];
final String name = dataChangeEventQueue.getName();
executor.execute(() -> {
while (true) {
try {
final ChangeData changeData = dataChangeEventQueue.take();
notifyExecutor.execute(new ChangeNotifier(changeData, name));
}
}
});
}
}
}

3.2.1 DataChangeHandler

DataChangeHandler 会定期提取DataChangeEventCenter中的消息,然后进行处理。

3.2.2 类定义

public class DataChangeHandler {

    @Autowired
private DataServerConfig dataServerConfig; @Autowired
private DataChangeEventCenter dataChangeEventCenter; @Autowired
private DatumCache datumCache; @Resource
private List<IDataChangeNotifier> dataChangeNotifiers;
}

3.2.3 执行引擎

这里是一个双层线程模型。

  • executor = ExecutorFactory.newFixedThreadPool(queueCount)

  • notifyExecutor= ExecutorFactory.newFixedThreadPool(dataServerConfig.getQueueCount() * 5)

可以认为 executor 是控制线程,notifierExecutor是工作线程,工作线程是控制线程的5倍。

  • DataChangeHandler 会遍历 DataChangeEventCenter 中所有 DataChangeEventQueue,
  • 针对每一个dataChangeEventQueue调用executor的一个控制线程,
  • 在这个控制线程里面,可以从 DataChangeEventQueue 之中取出ChangeData,针对每一个ChangeData,调用notifyExecutor的一个工作线程,生成一个ChangeNotifier进行处理。
@PostConstruct
public void start() {
DataChangeEventQueue[] queues = dataChangeEventCenter.getQueues();
int queueCount = queues.length;
Executor executor = ExecutorFactory.newFixedThreadPool(queueCount, DataChangeHandler.class.getSimpleName());
Executor notifyExecutor = ExecutorFactory
.newFixedThreadPool(dataServerConfig.getQueueCount() * 5, this.getClass().getSimpleName()); for (int idx = 0; idx < queueCount; idx++) {
final DataChangeEventQueue dataChangeEventQueue = queues[idx];
final String name = dataChangeEventQueue.getName();
executor.execute(() -> {
while (true) {
final ChangeData changeData = dataChangeEventQueue.take();
notifyExecutor.execute(new ChangeNotifier(changeData, name));
}
});
}
}

3.2.4 业务执行

对于 ChangeData,会生成 ChangeNotifier 进行处理。会把这个事件变更信息通过 ChangeNotifier 对外发布,通知其他节点进行数据同步。

在 ChangeNotifier 之中,会判断changeData的类型做不同处理。

  • 如果是SnapshotData,则:

    • 生成SnapshotData;
    • 调用 datumCache.putSnapshot 做存储;
    • 调用notify做通知;
  • 如果是其他类型,则:
    • 对于pub or unPub merge,需要datum.updateVersion();
    • 如果是 PUB_TEMP,则notifyTempPub(datum, sourceType, changeType);
    • 如果是版本更新,则notify(datum, sourceType, lastVersion);

具体如下:

private class ChangeNotifier implements Runnable {

    private ChangeData changeData;
private String name; @Override
public void run() {
if (changeData instanceof SnapshotData) {
......
} else {
Datum datum = changeData.getDatum(); String dataCenter = datum.getDataCenter();
String dataInfoId = datum.getDataInfoId();
DataSourceTypeEnum sourceType = changeData.getSourceType();
DataChangeTypeEnum changeType = changeData.getChangeType(); if (changeType == DataChangeTypeEnum.MERGE
&& sourceType != DataSourceTypeEnum.BACKUP
&& sourceType != DataSourceTypeEnum.SYNC) {
//update version for pub or unPub merge to cache
//if the version product before merge to cache,it may be cause small version override big one
datum.updateVersion();
} long version = datum.getVersion(); try {
if (sourceType == DataSourceTypeEnum.CLEAN) {
if (datumCache.cleanDatum(dataCenter, dataInfoId)) {
......
} } else if (sourceType == DataSourceTypeEnum.PUB_TEMP) {
notifyTempPub(datum, sourceType, changeType);
} else {
MergeResult mergeResult = datumCache.putDatum(changeType, datum);
Long lastVersion = mergeResult.getLastVersion(); if (lastVersion != null
&& lastVersion.longValue() == LocalDatumStorage.ERROR_DATUM_VERSION) {
return;
} //lastVersion null means first add datum
if (lastVersion == null || version != lastVersion) {
if (mergeResult.isChangeFlag()) {
notify(datum, sourceType, lastVersion);
}
}
}
}
}
}
}

此时具体逻辑如下:

        +--------------------+
| PublishDataHandler |
+--------+-----------+
|
|
| publisher
|
v
+---------+------------+
|DataChangeEventCenter |
+---------+------------+
|
|
| ChangeData
v
+---------+------------+
| DataChangeEventQueue |
+---------+------------+
|
|
| ChangeData
v
+-------+----------+
| DataChangeHandler|
+-------+----------+
|
|
| ChangeData
v
+------+--------+ +------------+
| ChangeNotifier| +--------> | datumCache |
+------+--------+ +------------+

3.2.5 通知

notify函数会遍历dataChangeNotifiers,找出可以支持本Datum对应SourceType的Notifier来执行。

具体如何支持哪些函数,是由getSuitableSource设置的。

private void notify(Datum datum, DataSourceTypeEnum sourceType, Long lastVersion) {
for (IDataChangeNotifier notifier : dataChangeNotifiers) {
if (notifier.getSuitableSource().contains(sourceType)) {
notifier.notify(datum, lastVersion);
}
}
}

对应的Bean是:

@Bean(name = "dataChangeNotifiers")
public List<IDataChangeNotifier> dataChangeNotifiers() {
List<IDataChangeNotifier> list = new ArrayList<>();
list.add(sessionServerNotifier());
list.add(tempPublisherNotifier());
list.add(backUpNotifier());
return list;
}

3.2.6 BackUpNotifier同步

就是调用 syncDataService.appendOperator 进行通知,其实就是把 Datum 变成 Operator,存到AbstractAcceptorStore。

public class BackUpNotifier implements IDataChangeNotifier {

    @Autowired
private SyncDataService syncDataService; @Override
public Set<DataSourceTypeEnum> getSuitableSource() {
Set<DataSourceTypeEnum> set = new HashSet<>();
set.add(DataSourceTypeEnum.PUB);
return set;
} @Override
public void notify(Datum datum, Long lastVersion) {
syncDataService.appendOperator(new Operator(datum.getVersion(), lastVersion, datum,
DataSourceTypeEnum.BACKUP));
}
}

3.2.7 SessionServerNotifier通知数据变化

SessionServerNotifier 则要复杂很多。

public class SessionServerNotifier implements IDataChangeNotifier {

    private AsyncHashedWheelTimer          asyncHashedWheelTimer;

    @Autowired
private DataServerConfig dataServerConfig; @Autowired
private Exchange boltExchange; @Autowired
private SessionServerConnectionFactory sessionServerConnectionFactory; @Autowired
private DatumCache datumCache; @Override
public Set<DataSourceTypeEnum> getSuitableSource() {
Set<DataSourceTypeEnum> set = new HashSet<>();
set.add(DataSourceTypeEnum.PUB);
set.add(DataSourceTypeEnum.SYNC);
set.add(DataSourceTypeEnum.SNAPSHOT);
return set;
}
}
3.2.7.1 时间轮

建立了一个500毫秒的时间轮。

@PostConstruct
public void init() {
ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder();
threadFactoryBuilder.setDaemon(true);
asyncHashedWheelTimer = new AsyncHashedWheelTimer(threadFactoryBuilder.setNameFormat(
"Registry-SessionServerNotifier-WheelTimer").build(), 500, TimeUnit.MILLISECONDS, 1024,
dataServerConfig.getSessionServerNotifierRetryExecutorThreadSize(),
dataServerConfig.getSessionServerNotifierRetryExecutorQueueSize(), threadFactoryBuilder
.setNameFormat("Registry-SessionServerNotifier-WheelExecutor-%d").build(),
new TaskFailedCallback() {
@Override
public void executionRejected(Throwable e) {
LOGGER.error("executionRejected: " + e.getMessage(), e);
} @Override
public void executionFailed(Throwable e) {
LOGGER.error("executionFailed: " + e.getMessage(), e);
}
});
}

从业务角度看,当有publisher相关消息来临时候,

DataChangeHandler的notify函数会遍历dataChangeNotifiers,找出可以支持本Datum对应SourceType的Notifier来执行。

private void notify(Datum datum, DataSourceTypeEnum sourceType, Long lastVersion) {
for (IDataChangeNotifier notifier : dataChangeNotifiers) {
if (notifier.getSuitableSource().contains(sourceType)) {
notifier.notify(datum, lastVersion);
}
}
}

到了SessionServerNotifier这里的notify函数,会遍历目前缓存的所有Connection,逐一通知。

@Override
public void notify(Datum datum, Long lastVersion) {
DataChangeRequest request = new DataChangeRequest(datum.getDataInfoId(),
datum.getDataCenter(), datum.getVersion());
List<Connection> connections = sessionServerConnectionFactory.getSessionConnections();
for (Connection connection : connections) {
doNotify(new NotifyCallback(connection, request));
}
}

具体通知函数:

private void doNotify(NotifyCallback notifyCallback) {
Connection connection = notifyCallback.connection;
DataChangeRequest request = notifyCallback.request;
try {
//check connection active
if (!connection.isFine()) {
return;
}
Server sessionServer = boltExchange.getServer(dataServerConfig.getPort());
sessionServer.sendCallback(sessionServer.getChannel(connection.getRemoteAddress()),
request, notifyCallback, dataServerConfig.getRpcTimeout());
} catch (Exception e) { onFailed(notifyCallback);
}
}

而时间轮是在调用失败的重试中使用。

就是当没有达到失败重试最大次数时,进行定时重试。

private void onFailed(NotifyCallback notifyCallback) {

    DataChangeRequest request = notifyCallback.request;
Connection connection = notifyCallback.connection;
notifyCallback.retryTimes++; //check version, if it's fall behind, stop retry
long _currentVersion = datumCache.get(request.getDataCenter(), request.getDataInfoId()).getVersion();
if (request.getVersion() != _currentVersion) {
return;
} if (notifyCallback.retryTimes <= dataServerConfig.getNotifySessionRetryTimes()) {
this.asyncHashedWheelTimer.newTimeout(timeout -> {
//check version, if it's fall behind, stop retry
long currentVersion = datumCache.get(request.getDataCenter(), request.getDataInfoId()).getVersion();
if (request.getVersion() == currentVersion) {
doNotify(notifyCallback);
}
}, getDelayTimeForRetry(notifyCallback.retryTimes), TimeUnit.MILLISECONDS);
}
}

具体逻辑如下:

        +--------------------+
| PublishDataHandler |
+--------+-----------+
|
|
| publisher
|
v
+---------+------------+
|DataChangeEventCenter |
+---------+------------+
|
|
| ChangeData
v
+---------+------------+
| DataChangeEventQueue |
+---------+------------+
|
|
| ChangeData
v
+-------+----------+
| DataChangeHandler|
+-------+----------+
|
|
| ChangeData
v
+------+--------+ +------------+
| ChangeNotifier| +--------> | datumCache |
+------+--------+ +------------+
|
|
v
+---+------+
| notifier |
+---+------+
|
v
+-----------+---------------+
| |
v v
+----+----------------+ +------+----------+
|SessionServerNotifier| | BackUpNotifier |
+----+----------------+ +------+----------+
| |
| |
| |
| v
+--v------------+ +------+----------------+
| sessionServer | | AbstractAcceptorStore |
+---------------+ +-----------------------+

0x04 总结

本文是把注册中的一个点“事件变更通知ChangeNotifie“进行细化展开,以 SessionServerNotifier 和 BackUpNotifier 为例,为大家进行解释ChangeNotifier的原理和使用。把包括 dataChangeEventCenter 等功能也梳理了一下,希望对大家有所帮助。

在 DataServer,数据变化有两个方向:

  • 数据服务器节点变化;

  • 数据的变化,即 Publisher 和 Scriber 的变化;

ChangeNotifier就是负责把 Publisher 和 Scriber 的变化 通知给相关模块。变更通知就是一种解耦

0xFF 参考

[从源码学设计]蚂蚁金服SOFARegistry之服务上线

[从源码学设计]蚂蚁金服SOFARegistry 之 服务注册和操作日志

[从源码学设计]蚂蚁金服SOFARegistry 之 ChangeNotifier的更多相关文章

  1. [从源码学设计]蚂蚁金服SOFARegistry之程序基本架构

    [从源码学设计]蚂蚁金服SOFARegistry之程序基本架构 0x00 摘要 之前我们通过三篇文章初步分析了 MetaServer 的基本架构,MetaServer 这三篇文章为我们接下来的工作做了 ...

  2. [从源码学设计]蚂蚁金服SOFARegistry之网络封装和操作

    [从源码学设计]蚂蚁金服SOFARegistry之网络封装和操作 目录 [从源码学设计]蚂蚁金服SOFARegistry之网络封装和操作 0x00 摘要 0x01 业务领域 1.1 SOFARegis ...

  3. [从源码学设计]蚂蚁金服SOFARegistry网络操作之连接管理

    [从源码学设计]蚂蚁金服SOFARegistry网络操作之连接管理 目录 [从源码学设计]蚂蚁金服SOFARegistry网络操作之连接管理 0x00 摘要 0x01 业务领域 1.1 应用场景 0x ...

  4. [从源码学设计]蚂蚁金服SOFARegistry之消息总线

    [从源码学设计]蚂蚁金服SOFARegistry之消息总线 目录 [从源码学设计]蚂蚁金服SOFARegistry之消息总线 0x00 摘要 0x01 相关概念 1.1 事件驱动模型 1.1.1 概念 ...

  5. [从源码学设计]蚂蚁金服SOFARegistry之消息总线异步处理

    [从源码学设计]蚂蚁金服SOFARegistry之消息总线异步处理 目录 [从源码学设计]蚂蚁金服SOFARegistry之消息总线异步处理 0x00 摘要 0x01 为何分离 0x02 业务领域 2 ...

  6. [从源码学设计]蚂蚁金服SOFARegistry之存储结构

    [从源码学设计]蚂蚁金服SOFARegistry之存储结构 目录 [从源码学设计]蚂蚁金服SOFARegistry之存储结构 0x00 摘要 0x01 业务范畴 1.1 缓存 1.2 DataServ ...

  7. [从源码学设计]蚂蚁金服SOFARegistry之推拉模型

    [从源码学设计]蚂蚁金服SOFARegistry之推拉模型 目录 [从源码学设计]蚂蚁金服SOFARegistry之推拉模型 0x00 摘要 0x01 相关概念 1.1 推模型和拉模型 1.1.1 推 ...

  8. [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用

    [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用 目录 [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用 0x00 摘要 0x01 业务领域 1.1 应用场景 0x02 定 ...

  9. [从源码学设计]蚂蚁金服SOFARegistry 之 自动调节间隔周期性任务

    [从源码学设计]蚂蚁金服SOFARegistry 之 自动调节间隔周期性任务 目录 [从源码学设计]蚂蚁金服SOFARegistry 之 自动调节间隔周期性任务 0x00 摘要 0x01 业务领域 0 ...

随机推荐

  1. Alpha冲刺——序言篇(任务与计划)

    Alpha冲刺--序言篇(任务与计划) 1.整个项目预期的任务量 需求规格说明书 架构设计,原型设计,原型改进(给目标用户展现原型,并进一步理解需求) 编码规范完成.平台环境搭建完成.初步架构搭建 队 ...

  2. clion2020最新安装破解版教程 内含激活码、破解补丁、无限重置时间插件

    clion是一款开发C.C++等相关程序的利器,到目前为止已经更新到2020.3版本啦!还没有升级的小伙伴们赶紧升级啦,本文教大家如何安装clion2020.3版本并且破解,此方法亲测100%可以永久 ...

  3. jxl导出excel小demo

    1.首先在pom文件加入jar包 <dependency> <groupId>net.sourceforge.jexcelapi</groupId> <art ...

  4. 重磅!Panda Global获悉立陶宛下周将发行区块链数字货币!

    近日,Panda Global从路透社获悉,立陶宛将在下周开始预售2.4万枚由央行发行的数字货币.该名为LBCoin的数字货币基于区块链技术生产,也是该国试点具有国家支持背景的数字货币和区块链技术的项 ...

  5. 【题解】「UVA1149」装箱 Bin Packing

    做法显然:贪心,但是怎么贪? 首先从大到小或从小到大排序,窝这次是从大到小排,这样更容易理解(从小到大更方变) 然后设置两个变量 front 和 after 作为前指针和后指针. 循环判断: 当前后两 ...

  6. Java并发编程的艺术(六)——中断、安全停止线程

    什么是中断 Java的一种机制,用于一个线程去暂停另一个线程的运行.就是一个正在运行的线程被其他线程给打断,停止运行挂起了. 我觉得,在Java中,这种中断机制只是一种方便程序员编写进程间的通信罢了. ...

  7. GaussDB(DWS)应用实践丨负载管理与作业排队处理方法

    摘要:本文用来总结一些GaussDB(DWS)在实际应用过程中,可能出现的各种作业排队的情况,以及出现排队时,我们应该怎么去判断是否正常,调整一些参数,让资源分配与负载管理更符合当前的业务:或者在作业 ...

  8. Spark 源码浅读-SparkSubmit

    Spark 源码浅读-任务提交SparkSubmit main方法 main方法主要用于初始化日志,然后接着调用doSubmit方法. override def main(args: Array[St ...

  9. day112:MoFang:种植园使用websocket代替http&服务端基于flask-socketio提供服务&服务端响应信息&种植园页面显示初始化

    目录 1.种植园使用websocket代替http 2.服务端基于socket提供服务 3.服务端响应信息 4.种植园页面展示 1.种植园使用websocket代替http 我们需要完成的种植园,是一 ...

  10. Python高级语法-私有属性-名字重整(4.7.1)

    @ 目录 1.说明 2.代码 关于作者 1.说明 使用__dict__魔法方法 可以看到所有的属性,包括公有的,私有的,保护的等等 不能调用的原因就是,解释器把名字属性给重组了 其实是可以访问到的 2 ...