JdonFramework-6.6.2发布

13-03-13 banq
自从基于Disruptor的Jdon框架6.4版本发布以来,经过一年多持续不断调优,JdonFramework迎来了成熟稳定的6.6.2版本

6.6.2版本主要有两处改进:

1. 使用Guava Cache 替代原先的EHcache,替换以后吞吐量throughput大大提高。

Google的Guava是一种函数风格的集合Collection和事件功能库包,经过初步分析,Guava比EHcache性能提升的原因:可能是因为EHCache使用了锁,虽然保证了读写安全,但也牺牲了读写性能,特别是吞吐量;而Guava Cache读吞吐量相当高,但是同时读写有些堵塞。

在实际应用中,支持大量并发读是使用缓存的主要目的。业界正在通过不断地努力,寻找无锁却能大幅度提高吞吐量的算法和技术,最新布谷鸟算法:

用于并发读密集型的乐观Cuckoo(布谷鸟)哈希

如果有移除读锁的布谷鸟Java库推出,Jdon框架将率先立即切换使用,保证Jdonframework始终跟踪业界最先进技术。也能保证基于Jdon框架开发的各种应用无缝享受最新科技发展成果。

2.JdonFramework 6.6.2引入了Disruptor 最新2.10.4版本。

该版本删除了BlockingWaitStrategy中的numWaiters,该BUG导致繁忙时CPU负载增高几十倍,由于解道Jdon网站使用基于其开发的JiveJdon,故运行一段时间后,常常因为CPU负载被numWaiters死循环推得很高,uptime 20分钟数据高峰能够达到1.50,修复BUG后最高不过是0.15左右,经常为零。CPU负载大幅度降低。

线程高并发且无锁,且占CPU少,这是业界正在不断追求的极致完美目标。Jdon框架对读写操作分离的高并发性能追求保证了基于其开发的CQRS质量。

下图是Jdon框架6.4和6.6.2在jivejdon中两张测试图比较,虽然6.6.2采样线程多了,吞吐量throughput还是明显比以前增加很多,特别是写操作(add/update):

6.4:

6.6.2:

[该贴被admin于2013-03-19 16:53修改过]


23
banq
2013-03-13 09:32
Guava提供了简单的事件编程,而Jdon框架也基于Disruptor提供了基于模型对象的事件编程,两种区别在于:Disruptor的线程切换是无锁并发性能高,且能够指定事件订阅器顺序执行。

但是,Guava事件模型用在除了模型事件以外的组件事件领域还是很方便的。举例两者结合案例:

下面是Jdon框架响应模型(Prodcuer)事件的消费者(Consumer)代码:

@[author]Consumer[/author]("subscriptionSender")
public class SubscriptionSender implements DomainEventHandler {
	private AsyncEventBus eventBus;

	public SubscriptionSender(SubscriptionNotify subscriptionNotify) {
		eventBus = new AsyncEventBus(Executors.newScheduledThreadPool(1));
		eventBus.register(subscriptionNotify);
	}

       //该方法将类似观察者模式disruptor立即激活
	public void onEvent(EventDisruptor event, boolean endOfBatch) throws Exception {

		try {
			Subscribed subscribed = (Subscribed) event.getDomainMessage().getEventSource();
      //注意这是Guava的事件发生者
			eventBus.post(subscribed);
			//可实现其他事务性关键业务
		} catch (Exception e) {
		}

	}
}
<p>

Guava的事件的订阅响应者代码如下:

@Component("subscriptionNotify")
public class SubscriptionNotify {

	@Subscribe   //这是Guava的事件订阅者元注解
	public void sendSub(Subscribed subscribed) {
		          //可实现业务上真正异步的操作。
	}
  ....
}
<p>

这两种事件使用有什么区别?

在Jdon框架的事件响应器Consumer中主要实现关键操作,比如当前模型状态的持久化,由于Disruptor强有力支持,Consumer中的动作将可靠快速完成。

而推迟到Guava的事件订阅者中执行,主要适合那些耗时动作,比如发送微博 发送Email通知等等,这样这些耗时的操作摆脱Disruptor的控制之外,节省CPU占用量(Disruptor的BLOCK策略)。

总之,Jdonframework不断致力于将最新架构技术和最新建模思想完美结合在一起,打造国人自己的极客开源作品。让我们尽情享用科技带来的快感!

Jdonframework 6.6.2 下载地址:

http://www.jdon.com/jdonframework/download.html

[该贴被admin于2013-03-19 16:54修改过]

lostalien
2013-03-14 10:00
祝贺一下~

banq
2013-03-16 09:41
谢谢。

今天周六上午测试一下网站速度:

经过@360网站安全检测,我的网站 http://www.jdon.com 响应时间为:0.217 秒,打败了全国 75% 的网站,你也来试试吧,地址: http://webscan.360.cn/tools/http

相关主题:

为什么解道访问速度这么快

abbasky
2013-03-17 18:54
一直有个问题困惑着我,那就是服务端如何实现异步操作。例如客户端提交了查询数据的请求,该请求不管是从数据库读数据,还是从Cache读数据,都必须要返回数据给客户端的。能不能说服务端收到了请求,然后说知道了你等着通知呢?

如果用Disruptor来构建数据查询请求的序列,该如何做呢?另我查看了jdon6.2的源码,没找到象我说的这种用法。

数据库读写的竞争如何通过Disruptor来实现协调呢?Disruptor构建Ringbuffer时,其事件类型是一个类型的,即一类操作实现顺序化。我认为读和写要整合到一个顺序序列中才能解决读写竞争问题。

请大神们帮忙解惑。

[该贴被abbasky于2013-03-17 18:56修改过]

banq
2013-03-17 21:15
2013-03-17 18:54 "@abbasky

"的内容

我认为读和写要整合到一个顺序序列中才能解决读写竞争问题 ...

如果读写整到一起,虽然解决了读写竞争,但是也变成性能瓶颈了,锁模式就是这种解决办法,比如数据库锁或ORM的悲观或乐观锁等。

将”客户端提交数据然后返回结果“这种方式可以看成一种函数调用Lambda架构

“query = function(all data)”

或者说,将客户端提交数据看成是一种写操作,在服务器端对写操作进行切分并行化或异步处理,如下图:

客户端提交了数据给领域模型,模型完成业务后立即将结果返回客户端;同时领域模型将其他处理如数据库存储等通过Disruptor这个并发框架立即开启。

打个比喻,快递送物品过来,你就立即处理一下快递想要的结果,比如签字等等活动,等快递走了,你同时打开包裹,检查商品。这样效率是不是很高?如果你在签字之前,要打开包裹,验机等等一系列操作,快递肯定等不及,不耐烦了,但是你认为这样保险,而快递说有问题可和商城联系,和他们无关。

D D D 缓 存何时持久化,领域模型打发完客户端(快递)请求后,要进行持久化(验机),通过Jdonframework基于事件的模型可以并发立即持久化,这是由Disruptor完成的,持久化动作和签字动作几乎是同时并发发生。

实现无锁异步的事件编程的好处,原先有三个操作在一个方法中顺序执行,以jivejdon论坛发帖为例:

A. 插入新贴数据库

B. 加入Lucene搜索

C. 通知关注作者的所有订阅者更新信息

这就容易碰到问题,B动作因为是加入搜索文件还要优化索引,比较费CPU,当发帖作者关注人数很多时,能几百个几千条数据要插入数据库。这些因素合并在一起,致使发一次贴,CPU负载相当高,甚至影响正常功能,比如发完新贴,理应给发贴者显示一下其发帖的新内容,结果因为CPU忙于B和C两个操作,无法显示发帖内容了,有些系统甚至出现Session丢失,连接拒绝等等问题。

而引入事件编程后,我们有两种手段来对付,并发和异步,如下处理:

A.通过无锁并发,让另外一个线程插入数据库。

B.让另外一个线程延时60秒插入搜索索引

C.加入一个队列定时器,两分钟以后每隔5分钟向成百上千订阅者发布通知,包括邮件 微博通知等等。这个队列大小可根据服务器硬件配置设置一定大小,硬件弱的,队列小些。这样还可以缓冲短时间大量发帖(写操作)引起的对系统冲击。

关键是,因为A B C三个动作我们是在EventHandler事件处理器中实现,那么进行这些重构,丝毫不会影响触发事件那一端的代码,两者松耦合,修改相当轻松,几分钟完成。

具体代码可见:

Jdon框架+Guava的EventBus实现

[该贴被banq于2013-03-26 17:30修改过]

abbasky
2013-03-21 22:03
首先感谢banq的帮助。

你在文中提到的解决方案都是针对写数据。如果出现查询数据与写数据的并发。那会不会出现死锁,或数据不正确的情况?

banq
2013-03-22 08:18
2013-03-21 22:03 "@abbasky

"的内容

如果出现查询数据与写数据的并发。那会不会出现死锁,或数据不正确的情况? ...

出现这个情况还是锁模式下,在读写分离的CQRS模式根本不存在,首先,操作者发出写操作命令,修改的是内存中模型,返回结果也是模型的结果,所以他能够立即查询到自己的修改结果。

如果是单机,其他用户查询也是内存中这个模型,如果担心内存中这个模型修改时有查询会出现不一致,前面讲的布谷鸟算法就是应对这种情况的无锁算法,有简单并发锁的比如JDK的的ReadWriteLock都支持。

因为读写分离,可以针对读优化,有很多台服务器专门读,分布式读取,那么一台服务器上模型在写操作中被修改后,通过发出事件消息可以改变其他读服务器上数据,达到最终一致。

banq
2013-04-09 19:27
jdon框架支持并发异步等概念,能够根据服务器自身处理量,动态调节并发量,如果服务器比较弱,可以删除或延时一些细枝末节的处理。

Jdon框架让延迟和吞吐量之间有一个自主选择,而这些概念传统的JavaEE如EJB/Spring根据就没有。

推荐一篇文章:构建可伸缩系统 Scala vs Java,JdonFramework其实类似这个案例中的Scala,虽然文章作者倾向于Scala,但是使用Jdon框架完全可以做到Scala这种吞吐量等三个指标的平衡。

该文章作者是用Jmeter进行测试,Jdon框架也是使用Jmeter测试了两个写场景和多个读场景,可以与其index payments和search比较。

普通Java的测试图:

由于搜索请求耗费CPU,同时拖累了Index等其他查询,搜索请求实际在独占CPU,同时搜索自己的吞吐量也大幅度缩小,意味着程序中可能有同步锁(当然,我没有看源码,只是凭以往经验)。

下面是Scala的测试图:

在Scala中,Median 和90%line和吞吐量三个指标在三个模块中都没有因为搜索请求处理而影响,文章归结于异步因素,这也是我认同的。

当然文章也不否认Java能够做到,关键是否优雅地做到,而Jdonframework则依靠DDD+ EventSourcing +CQRS不但可以优雅做大,而且性能吞吐量等三个指标因为Jdon框架的异步同样不受影响,下图是Jdon框架Jmeter的测试结果,当然没有转换成文章中竖条图,从Median 90%line和吞吐量三个指标值可以看出:

我也不想好事,所以也就不想针对该文章案例用Jdonframework做一个案例,再PK一下,从道理上了然即可。

英文:Scalable scala vs java

bingyang
2013-07-11 09:26
祝贺了;

CRQS越来越来流行,bang 你的jdonframework 是不是可以克隆个非java的版本,比如.net,让更多人学到这个技术,呵呵;

tangxuehua
2013-07-11 09:44
我也转java了,啥时候看看jdonframework源码,学习下。

qiu768
2013-07-25 13:53
6.6.2版本支持的最低jdk版本是多少,1.5支持吗

banq
2013-09-24 07:56
JdonFramework-6.6.6版本在原来成熟稳定的6.6.4版本上主要增加了对CQRS的command异步并发支持。

也就是说,原来聚合根实体模型发出的领域事件是通过异步并发的Disruptor输出,6.6.6版本增加了使用Disruptor进行异步并发的command输入,这样聚合根实体模型有自己的与外界进行异步无堵塞的输入输出通讯,更加完整,更类似Scala/Akka Erlang的Actors模型了。如下图:

关于Actors模型的好处见文章:http://www.jdon.com/45728

在Jdon框架中有两个模型: Component(组件) 和 Model(领域模型). 分别以@Component, 和 @Model 标注。

当一个Model被外部组件访问,它一般是DDD中的聚合根实体,因为根据DDD只有聚合根实体才能被外界访问引用,外部不能直接访问聚合边界内其它对象,必须通过聚合根,这样聚合根才能保证聚合边界内各个对象变化的一致性。

如果一个Model被其他领域模型引用,它就肯定不是聚合根,因为聚合根之间不能直接相互引用,它可能是聚合内一个对象,或者是实体或者是值对象。

领域模型Model实例生活在in-memory内存缓存中, 而组件Component实例的生命周期是应用级别,比如和Web容器相同,一个容器内缺省是一个单例。

Component能够用来实现DDD的服务service或其他应用管理器,如邮件发送等。

Jdon框架也提供一种类似Component的Service类型 (标注为@Service),它是面向外部客户端,而不是面向内部,可用来实现SOA的粗粒度大服务。

Jdon在这两个模型(Component和Model)之间提供四种异步并发的通讯方式,也是一种Producer/Consumer模式。

  1.组件和模型 Component -----> model

  2. 模型和组件 model ------->Component

  3. 组件与组件 Compponent ------> Component

  4. 模型与模型 model------> model

当一个组件或服务Component/Service发送消息给领域模型Model(也就是聚合根aggregate root), 在CQRS中我们称这个消息携带的是命令command, 当一个领域模型model发送消息给组件Component, 我们称它为事件,代表已经在领域模型中发生什么事情::

关于这四个方式如何具体实现,可见:http://www.jdon.com/jdonframework/cqrs.html

另外推荐道友@tangxuehua 的在.net上实现的CQRS框架:enode框架,两者可合并一起参考: http://www.cnblogs.com/netfocus/p/3179060.html

猜你喜欢