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的设计者们到底是怎么样做到这些的呢?我希望能够 ...
随机推荐
- IDEA导入MySQL包
点击[Project Structure] 点击[Modules] 在点击下面的界面 找到自己下载的MySQL包就OK了
- C++程序生成.exe文件,在文件夹中运行时闪现问题
问题描述:在IDE(此为Dev-C++)中编写C++程序,运行时会产生如下文字 但我想取消这三行的显示. 解决方法:1:在IDE中运行时,“请按任意键继续”是消失不掉的,但在该程序的保存路径下可以消灭 ...
- 注解Demo
1.创建注解类 2.在测试实体类中使用注解 3.构建注解解析类 4.执行注解解析方法
- WZ后台管理框架
http://herozhou.coding.me/vue-framework-wz/#/dashboard
- nginx 下载 大文件被截断
如果出现大文件被截断,且ngix的日志大量出现以下类似报错: 则说明是nginx没有fastcgi_temp的读写权限.其中fastcgi_temp是自己的文件夹名称,每个人的不同且路径也会不同,这个 ...
- c++中指针的指针和指针的引用的使用
当指针作为函数的参数进行传递时,实际上本质上是安置传递,即将指针进行了一份拷贝,在函数的内部对这个指针的修改实际上就是对一个在函数内部的那个局部变量的修改.这点事和引用不同的,引用实际上是在参数传递时 ...
- weblogic配置集群(一)
一.程序安装 二.创建域 好的 篇幅太长 我就接下来的操作就写在下一篇博客了 weblogic配置集群(二)
- Oracle 数据库 dbConsole 配置笔记
Oracle安装后,DBConsole有时安装会失败,这时可以通过以下命令恢复: set oracle_sid=orclemca -repos recreateemca -config dbcontr ...
- iOS程序的执行顺序 和 UIViewController的生命周期
iOS程序的执行顺序 1 进入程序的入口 进入main函数, 设置AppDelegate称为函数的代理 2 程序完成加载 -[AppDelegate application:didFinishLau ...
- c语言:简单排序:冒泡排序法、选择排序法、插入排序法(待写)
1.冒泡排序法: 假设有n个数需要按从小到大排序,冒泡排序的原理是,在这一排数字中,将第一个数与第二个数比较大小,如果后面的比前面的小,就将他们交换位置.然后再比较第二个和第三个,再交换,直到第n-1 ...