标签 java 下的文章

实现高可用的两种方案与实战


我之前在一片文章 用Nginx+Redis实现session共享的均衡负载 中做了一个负载均衡的实验,其主要架构如下:

round.png

debian1作为调度服务器承担请求分发的任务,即用户访问的是debian1,然后debain1把请求按照一定的策略发送给应用服务器:debian2或者debain3,甚至更多的debain4、5、6......

状态数据可以放在外部的分布式缓存服务分布式数据库服务中,这样应用服务本身就是无状态的,所以机器增减都是很容易的,应用的高可用是有保证的(对于有状态的高可用不仅要注意机器增减与切换、还要注意备份冗余数据一致性等问题)。但是当时忽略了一个地方,那就是调度服务器debian1本身的高可用性没有考虑到,存在单点问题。

高可用的首要想法就是双机热备,故障时自动切换,所以我们要给debian1加一个备机debain1'。我现在按照自己的知识粗浅的把解决方案分为两类:客户端有感知的高可用对客户端透明的高可用,并分别挑选一个示例做一下实验。

注:下面实现高可用都用的是双机热备,为了方便,把调度服务器debian1简称为主机,把调度服务器debian1的备机debian1'简称为备机


基于一致性哈希的分布式内存键值存储——CHKV


Consistent Hashing based Key-Value Memory Storage

基于一致性哈希的分布式内存键值存储——CHKV
目前的定位就是作为 CacheDataBase 的功能先不考虑。

系统设计

  • NameNode : 维护 DataNode节点 列表,用心跳检测 DataNode(一般被动,被动失效时主动询问三次),节点增减等系统信息变化时调整数据并通知 Client
  • DataNode : 存储具体的数据,向 NameNode 主动发起心跳并采用请求响应的方式来实现上下线,便于 NameNode 发起挪动数据指令,实际挪动操作由 DataNode 自行完成;
  • Client : 负责向 NameNode 请求 DataNode 相关信息并监听其变化,操纵数据时直接向对应 DataNode 发起请求就行,
    目前支持set,get,delete,keys,expire几个操作;

NameNode 失效则整个系统不可用。

若当成内存数据库使用,则要注意持久化,而且只要有一个 DataNode 失效(未经请求与数据转移就下线了)整个系统就不可对外服务;
若当成内存缓存使用,则 DataNode 失效只是失去了一部分缓存,系统仍然可用。

DataNode 失效(未经请求与数据转移就断开了和 NameNode 的连接)则 NameNode 需要及时通知 Client

客户 要使用 CHKV 就必须使用 Client 库或者自己依据协议(兼容redis)实现,可以是多种语言的API。
当然也可以把 Client 当做 Proxy,使得 CHKV 内部结构对 客户 透明,亦即有如下两种方式:


源码分析JDK8之AbstractQueuedSynchronizer


前言

源码分析我认为主要有两个作用:满足好奇心,我想每一个有追求的人都不会满足于仅仅做一个API Caller实现功能就好,我们也想知道它到底是怎么实现的;借鉴与升华,当我们明白了一个类的设计原理,在一定的情境下我们可以借鉴其设计哲学,甚至针对我们自己特殊的业务场景对其进行改良与优化。

下面我就以这篇文章开启我的源码阅读之旅。总体而言,我会从这个类基本结构入手,然后分析原理,再看看已有的应用,并进行分析与理解。

我之前一篇文章里提到过java的显示锁ReentrantLock。此外,如果你编写过并发程序那你一般也应该用过CountDownLatch,Semaphore等等,这些都是同步器,而它们都基于AbstractQueuedSynchronizer(简称AQS)实现的,那么我们今天就来看看这个牛逼的AQS是怎么实现这么多功能的。

首先打开IDEA,随便新建一个类,然后输入CountDownLatch,在它上面敲下Ctrl+B,就打开了CountDownLatch的源码,然后发现有一个非常重要的静态内部类Sync继承了AbstractQueuedSynchronizer,再次Ctrl+B,我们就打开了AQS的源码,马上就可以解开它的神秘面纱了,哼哼。

映入眼帘的首先就是大段大段的文档,大意就是这个类 提供了一个基于FIFO队列的实现了阻塞锁和相关同步器(信号量,事件等)的框架...... 读完了大概就了解这个类到底是怎么工作的了。下面我们开始分类型研究源码,当然不可能全部分析一遍,这里只把重点的列出来。


一文理清21种设计模式:用实例分析和对比


设计模式无论是对于最底层的的编码实现还是较高层的架构设计都有着重要的指导作用。所谓光说不练假把式,今天我就把项目中常见的应用场景涉及到的主要设计模式及其相关设计模式总结一下,用实例分析和对比的方式在一片文章中就把最常见的21种设计模式梳理清楚。

Redis发布订阅

在项目中常常使用redis的发布/订阅功能,用来实现进程间通信甚至IM等业务。
使用 jedis 实现频道订阅的模式一般如下:

try( Jedis jedis =  RedisClient.getJedis() ) {
    JedisPubSub listener = new MySubListener();
    // 订阅
    jedis.subscribe(listener, "channel");
}


数据结构与算法——常用高级数据结构及其Java实现


前文 数据结构与算法——常用数据结构及其Java实现 总结了基本的数据结构,类似的,本文准备总结一下一些常见的高级的数据结构及其常见算法和对应的Java实现以及应用场景,务求理论与实践一步到位。

跳跃表

跳跃列表是对有序的链表增加上附加的前进链接,增加是以随机化的方式进行的,所以在列表中的查找可以快速的跳过部分列表。是一种随机化数据结构,基于并联的链表,其效率可比拟于红黑树和AVL树(对于大多数操作需要O(logn)平均时间),但是实现起来更容易且对并发算法友好。redis 的 sorted SET 就是用了跳跃表。

性质:

  1. 由很多层结构组成;
  2. 每一层都是一个有序的链表,排列顺序为由高层到底层,都至少包含两个链表节点,分别是前面的head节点和后面的nil节点;
  3. 最底层的链表包含了所有的元素;
  4. 如果一个元素出现在某一层的链表中,那么在该层之下的链表也全都会出现(上一层的元素是当前层的元素的子集);
  5. 链表中的每个节点都包含两个指针,一个指向同一层的下一个链表节点,另一个指向下一层的同一个链表节点;