diamond的设计思路
diamond主要包含四个包:diamond-client、diamond-sdk、diamond-server和diamond-util
client就非常简单的进行http的调用server拿数据
server查数据库返回给client
其中server是集群
当一台server服务接收到写请求的时候:
1、先写数据库;
2、更新缓存(hashmap)
3、再写磁盘;
4、通知其他节点的服务器http
diamond的消息传输是通过http长链接来传输的。
diamond-client读取配置的context的时候是通过 DataId和GroupId来查询,本地Context是存在本地服务器文件的。文件路径根据一定规则来配置。
private Map<String, DiamondManager> dmMap = new HashMap<>();
private DiamondManager getDiamondManager(String dataId, String group) {
String key = dataId + "#_#" + group;
DiamondManager dm = dmMap.get(key);
if (dm == null) {
dm = new DefaultDiamondManager(group, dataId, new ArrayList<ManagerListener>());
dmMap.put(key, dm);
}
return dm;
}
获取远程服务器列表并保持到本地
protected void synAcquireServerAddress() {
if (!isRun) {
throw new RuntimeException("ServerAddressProcessor不在运行状态,无法同步获取服务器地址列表");
}
if (MockServer.isTestMode()) {
diamondConfigure.addDomainName("测试模式,没有使用的真实服务器");
return;
}
int acquireCount = 0;
if (diamondConfigure.getDomainNameList().size() == 0) {
if (!acquireServerAddressOnce(acquireCount)) {
acquireCount++;
if (acquireServerAddressOnce(acquireCount)) {
// 存入本地文件
storeServerAddressesToLocal();
log.info("在同步获取服务器列表时,向日常ConfigServer服务器获取到了服务器列表");
}
else {
log.info("从本地获取Diamond地址列表");
reloadServerAddresses();
if (diamondConfigure.getDomainNameList().size() == 0)
throw new RuntimeException("当前没有可用的服务器列表,请检查~/diamond/ServerAddress文件");
}
}
else {
log.info("在同步获取服务器列表时,向线上ConfigServer服务器获取到了服务器列表");
// 存入本地文件
storeServerAddressesToLocal();
}
}
}
如果在本地缓存map找不到就执行远程查找,这个代码是有一定问题的,应该是先从本地服务器找,新版已经解决了这个问题
/**
* 使用指定的集群类型clusterType
*
* @param group
* @param dataId
* @param managerListenerList
* @param clusterType
*/
public DefaultDiamondManager(String group, String dataId, List<ManagerListener> managerListenerList) {
this.dataId = dataId;
this.group = group;
diamondSubscriber = DiamondClientFactory.getSingletonDiamondSubscriber();
this.managerListeners.addAll(managerListenerList);
((DefaultSubscriberListener) diamondSubscriber.getSubscriberListener()).addManagerListeners(this.dataId,
this.group, this.managerListeners);
diamondSubscriber.addDataId(this.dataId, this.group);
diamondSubscriber.start();
}
接下来看这个订阅者start是如何执行的
/**
* 启动DiamondSubscriber:<br>
* 1.阻塞主动获取所有的DataId配置信息<br>
* 2.启动定时线程定时获取所有的DataId配置信息<br>
*/
public synchronized void start() {
if (isRun) {
return;
}
if (null == scheduledExecutor || scheduledExecutor.isTerminated()) {
scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
}
localConfigInfoProcessor.start(this.diamondConfigure.getFilePath() + "/" + DATA_DIR);
serverAddressProcessor = new ServerAddressProcessor(this.diamondConfigure, this.scheduledExecutor);
serverAddressProcessor.start();
this.snapshotConfigInfoProcessor =
new SnapshotConfigInfoProcessor(this.diamondConfigure.getFilePath() + "/" + SNAPSHOT_DIR);
// 设置domainNamePos值
randomDomainNamePos();
initHttpClient();
// 初始化完毕
isRun = true;
if (log.isInfoEnabled()) {
log.info("当前使用的域名有:" + this.diamondConfigure.getDomainNameList());
}
if (MockServer.isTestMode()) {
bFirstCheck = false;
}
else {
// 设置轮询间隔时间
this.diamondConfigure.setPollingIntervalTime(Constants.POLLING_INTERVAL_TIME);
}
// 轮询
rotateCheckConfigInfo();
addShutdownHook();
}
我们重点看下 serverAddressProcessor.start();这个有点像继承runable,然后执行run,我们看下具体逻辑
public synchronized void start() {
if (isRun) {
return;
}
isRun = true;
initHttpClient();
if (this.diamondConfigure.isLocalFirst()) {
acquireServerAddressFromLocal();
}
else {
synAcquireServerAddress();
asynAcquireServerAddress();
}
}
我们可以看到判断是否先从本地服务器获取,这个我上面说过了,新版的逻辑已经改掉了,信保就不适用hashMap作为缓存,的确hashMap容易被jvm回收,也占用内存空间。那么我们看下synACquireServerAddress方法
protected void synAcquireServerAddress() {
if (!isRun) {
throw new RuntimeException("ServerAddressProcessor不在运行状态,无法同步获取服务器地址列表");
}
if (MockServer.isTestMode()) {
diamondConfigure.addDomainName("测试模式,没有使用的真实服务器");
return;
}
int acquireCount = 0;
if (diamondConfigure.getDomainNameList().size() == 0) {
if (!acquireServerAddressOnce(acquireCount)) {
acquireCount++;
if (acquireServerAddressOnce(acquireCount)) {
// 存入本地文件
storeServerAddressesToLocal();
log.info("在同步获取服务器列表时,向日常ConfigServer服务器获取到了服务器列表");
}
else {
log.info("从本地获取Diamond地址列表");
reloadServerAddresses();
if (diamondConfigure.getDomainNameList().size() == 0)
throw new RuntimeException("当前没有可用的服务器列表,请检查~/diamond/ServerAddress文件");
}
}
else {
log.info("在同步获取服务器列表时,向线上ConfigServer服务器获取到了服务器列表");
// 存入本地文件
storeServerAddressesToLocal();
}
}
}
我们看diamand是怎么获取服务器列表的
/**
* 获取diamond服务器地址列表
*
* @param acquireCount
* 根据0或1决定从日常或线上获取
* @return
*/
private boolean acquireServerAddressOnce(int acquireCount) {
HostConfiguration hostConfiguration = configHttpClient.getHostConfiguration();
String configServerAddress;
int port;
if (null != diamondConfigure.getConfigServerAddress()) {
configServerAddress = diamondConfigure.getConfigServerAddress();
port = diamondConfigure.getConfigServerPort();
}
else {
if (acquireCount == 0) {
configServerAddress = Constants.DEFAULT_DOMAINNAME;
port = Constants.DEFAULT_PORT;
}
else {
configServerAddress = Constants.DAILY_DOMAINNAME;
port = Constants.DEFAULT_PORT;
}
}
hostConfiguration.setHost(configServerAddress, port);
String serverAddressUrl = Constants.CONFIG_HTTP_URI_FILE;
HttpMethod httpMethod = new GetMethod(serverAddressUrl);
// 设置HttpMethod的参数
HttpMethodParams params = new HttpMethodParams();
params.setSoTimeout(diamondConfigure.getOnceTimeout());
// ///////////////////////
httpMethod.setParams(params);
try {
if (SC_OK == configHttpClient.executeMethod(httpMethod)) {
InputStreamReader reader = new InputStreamReader(httpMethod.getResponseBodyAsStream());
BufferedReader bufferedReader = new BufferedReader(reader);
String address = null;
List<String> newDomainNameList = new LinkedList<String>();
while ((address = bufferedReader.readLine()) != null) {
address = address.trim();
if (StringUtils.isNotBlank(address)) {
newDomainNameList.add(address);
}
}
if (newDomainNameList.size() > 0) {
log.debug("更新使用的服务器列表");
this.diamondConfigure.setDomainNameList(newDomainNameList);
return true;
}
}
else {
log.warn("没有可用的新服务器列表");
}
}
catch (HttpException e) {
log.error(getErrorMessage(configServerAddress) + ", " + e);
}
catch (IOException e) {
log.error(getErrorMessage(configServerAddress) + ", " + e);
}
catch (Exception e) {
log.error(getErrorMessage(configServerAddress) + ", " + e);
}
finally {
httpMethod.releaseConnection();
}
return false;
}
思路简单清晰,但是这边有一个设计思想就是订阅者DiamondSubscriber和监听SubscriberListener的思想是值得研究下,我们研究下他们是怎么关联起来的。
/**
* Diamond订阅者的配置信息监听器
*
* @author aoqiong
*
*/
public interface SubscriberListener {
public Executor getExecutor();
/**
* 接收到一次配置信息
*
* @param configureInfomation
*/
public void receiveConfigInfo(final ConfigureInfomation configureInfomation);
}
这个监听器只有一个默认的实现DefaultSubscriberListener
public class DefaultSubscriberListener implements SubscriberListener {
// 回调日志单独记录
private static final Log dataLog = LogFactory.getLog(LoggerInit.LOG_NAME_CONFIG_DATA);
private final ConcurrentMap<String/* dataId + group */, CopyOnWriteArrayList<ManagerListener>/* listeners */> allListeners =
new ConcurrentHashMap<String, CopyOnWriteArrayList<ManagerListener>>();
public Executor getExecutor() {
return null;
}
那么这个DefaultSubscriberListener和DefaultDiamondSubscriber有什么关系呢?如我们上面的代码,为了方便我这边重写贴下:
/**
* 使用指定的集群类型clusterType
*
* @param group
* @param dataId
* @param managerListenerList
* @param clusterType
*/
public DefaultDiamondManager(String group, String dataId, List<ManagerListener> managerListenerList) {
this.dataId = dataId;
this.group = group;
diamondSubscriber = DiamondClientFactory.getSingletonDiamondSubscriber();
this.managerListeners.addAll(managerListenerList);
((DefaultSubscriberListener) diamondSubscriber.getSubscriberListener()).addManagerListeners(this.dataId,
this.group, this.managerListeners);
diamondSubscriber.addDataId(this.dataId, this.group);
diamondSubscriber.start();
}
我们是通过diamondSubscriber的start去获取服务器列表的数据的,但是在start之前的addManagerListeners是干嘛的呢?原因很简单,那就是跟这个diamondSubscriber增加一个listeners的监听。
this.managerListeners.addAll(managerListenerList);
((DefaultSubscriberListener) diamondSubscriber.getSubscriberListener()).addManagerListeners(this.dataId,
this.group, this.managerListeners);
diamondSubscriber 是通过DiamondClientFactory.getSingletonDiamondSubscriber()获取的,其实他就是一个DefaultDiamondSubscriber类,那么就是这个监听就是加到这个DefaultDiamondSubscriber类上的,这个监听其实就是DefaultSubscriberListener。
public class DiamondClientFactory {
private static DiamondSubscriber diamondSubscriber = new DefaultDiamondSubscriber(new DefaultSubscriberListener());
public static DiamondSubscriber getSingletonDiamondSubscriber() {
return diamondSubscriber;
}
}
以上我们是从一个client的角度解析diamand的设计思想和逻辑处理,思想其实不是很复杂,但是订阅监听的设计思想是亮点,本文也重点讲了订阅和监听,已经如何获取服务器列表的方法。
diamond的设计思路的更多相关文章
- TYPESDK手游聚合SDK服务端设计思路与架构之一:应用场景分析
TYPESDK 服务端设计思路与架构之一:应用场景分析 作为一个渠道SDK统一接入框架,TYPESDK从一开始,所面对的需求场景就是多款游戏,通过一个统一的SDK服务端,能够同时接入几十个甚至几百个各 ...
- 分享一个CQRS/ES架构中基于写文件的EventStore的设计思路
最近打算用C#实现一个基于文件的EventStore. 什么是EventStore 关于什么是EventStore,如果还不清楚的朋友可以去了解下CQRS/Event Sourcing这种架构,我博客 ...
- ENode框架单台机器在处理Command时的设计思路
设计目标 尽量快的处理命令和事件,保证吞吐量: 处理完一个命令后不需要等待命令产生的事件持久化完成就能处理下一个命令,从而保证领域内的业务逻辑处理不依赖于持久化IO,实现真正的in-memory: 保 ...
- WebGIS中快速整合管理多源矢量服务以及服务权限控制的一种设计思路
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 在真实项目中,往往GIS服务数据源被其他多个信息中心或者第三方 ...
- OpenStack 通用设计思路 - 每天5分钟玩转 OpenStack(25)
API 前端服务 每个 OpenStack 组件可能包含若干子服务,其中必定有一个 API 服务负责接收客户请求. 以 Nova 为例,nova-api 作为 Nova 组件对外的唯一窗口,向客户暴露 ...
- Redis入门指南(第2版) Redis设计思路学习与总结
https://www.qcloud.com/community/article/222 宋增宽,腾讯工程师,16年毕业加入腾讯,从事海量服务后台设计与研发工作,现在负责QQ群后台等项目,喜欢研究技术 ...
- MVC3 数据验证用法之密码验证设计思路
描述:MVC数据验证使用小结 内容:display,Required,stringLength,Remote,compare,RegularExpression 本人最近在公司用mvc做了一个修改密码 ...
- YYCache设计思路及源码学习
设计思路 利用YYCache来进行操作,实质操作分为了内存缓存操作(YYMemoryCache)和硬盘缓存操作(YYDiskCache).内存缓存设计一般是在内存中开辟一个空间用以保存请求的数据(一般 ...
- 第三篇:Retrofit SDK的设计思路
2016-05-08 15:24:03 Retreofit毫无疑问是一个优美的开源框架,有轻量级.耦合性低.扩展性好.灵活性高的特点,那么Retrofit的设计者们到底是怎么样做到这些的呢?我希望能够 ...
随机推荐
- 2G内存编译android4.0
http://blog.csdn.net/leerobin83/article/details/7873229 1.Error occurred during initialization of VM ...
- 百度地图经纬度和地址互转(Java代码)
这是基于springmvc+mybatis 的一个controller.如果不是这个框架,可以把方法实体抽到自己写的一个类中,然后再测试 package com.uwitec.controller.s ...
- windows下Oracle数据库完全删除
1.1 停止所有oracle的服务 1.2 删除安装路径 app及其下所有文件 1.3 删除注册表 regedit 进入 在下列列表中找到与oracle相关的注册表项删除 1.HKEY_L ...
- HDU 5656 CA Loves GCD (容斥)
题意:给定一个数组,每次他会从中选出若干个(至少一个数),求出所有数的GCD然后放回去,为了使自己不会无聊,会把每种不同的选法都选一遍,想知道他得到的所有GCD的和是多少. 析:枚举gcd,然后求每个 ...
- vmware虚拟机centOs安装教程
1安装vmware 虚拟机软件 1.解压vmware安装 汉化vmware虚拟机 复制注册码,并填写进vmware 2安装linux(centos)虚拟机 1. 点击文件----->新建虚拟机 ...
- js判断软键盘是否开启弹出
移动端关于页面布局,如果底部有position:fixed的盒子,又有input,当软键盘弹出收起都会影响页面布局.这时候Android可以监听resize事件,代码如下,而ios没有相关事件. va ...
- 鼠标经过,显示悬浮DIV
属牛人的本命佛是虚空藏菩萨.属牛人可佩戴属牛黄玉本命佛来提升财运,黄玉的金黄色代表蒸蒸日上,而“金黄”也就是“黄金”的到(倒)来,象征着富贵与财气,佩戴可以招财招富贵,同时黄玉亦是希望之石,可带来智慧 ...
- REST WebService与SOAP WebService的比较
在SOA的基础技术实现方式中WebService占据了很重要的地位,通常我们提到WebService第一想法就是SOAP消息在各种传输协议上交互.近几年REST的思想伴随着SOA逐渐被大家接受,同时各 ...
- css3动画特效集合
<!DOCTYPE html> <html> <head> <title></title> <meta charset="u ...
- x86_64汇编调试程序初步
寄存器说明: rdi 存第1个参数(值或地址) rsi 存第2个参数 rdx 存第3个参数 rcx 存第4个参数 r8 存第5个参数 r9 存第6个参数 rax 第1个返回值 rdx 第2个返回值 r ...