少年阿宾

那些青春的岁月

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks

#

最近因为项目需要,简单的试用了两款高可用开源方案:Keepalived和Heartbeat。两者都很流行,但差异还是很大的,现将试用过程中的感受以及相关知识点简单总结一下,供大家选择方案的时候参考。
1)Keepalived使用更简单:从安装、配置、使用、维护等角度上对比,Keepalived都比Heartbeat要简单得多,尤其是Heartbeat2.1.4后拆分成3个子项目,安装、配置、使用都比较复杂,尤其是出问题的时候,都不知道具体是哪个子系统出问题了;而Keepalived只有1个安装文件、1个配置文件,配置文件也简单很多;
2)Heartbeat功能更强大:Heartbeat虽然复杂,但功能更强大,配套工具更全,适合做大型集群管理,而Keepalived主要用于集群倒换,基本没有管理功能;
3)协议不同:Keepalived使用VRRP协议进行通信和选举,Heartbeat使用心跳进行通信和选举;Heartbeat除了走网络外,还可以通过串口通信,貌似更可靠;
4)使用方式基本类似:如果要基于两者设计高可用方案,最终都要根据业务需要写自定义的脚本,Keepalived的脚本没有任何约束,随便怎么写都可以;Heartbeat的脚本有约束,即要支持service start/stop/restart这种方式,而且Heartbeart提供了很多默认脚本,简单的绑定ip,启动apache等操作都已经有了;
使用建议:优先使用Keepalived,当Keepalived不够用的时候才选择Heartbeat
posted @ 2015-07-26 20:42 abin 阅读(786) | 评论 (1)编辑 收藏

1、StatefulJob
implements StatefulJob使Job成为有状态的,顺序执行 
同一个有状态的job实例不存在并发,无状态的job的并发数由上面配置的线程数决定。不想并发的话,设置成1,第二个线程在前一个执行完以后触发执行。
线程数大于1时,如果存在空闲线程,则到执行时间点即触发执行。

2、
MethodInvokingJobDetailFactoryBean
MethodInvokingJobDetailFactoryBean的并发问题
大家在使用quartz的时候,一般只设置了“targetObject”和“targetMethod”,MethodInvokingJobDetailFactoryBean类默认是并发执行的,这时候如果不设置“concurrent”为false,很可能带来并发或者死锁的问题,而且几率较小,不容易复现,请大家使用的时候注意设置“concurrent”。
 
    <bean id="cpm.MessageJobFactoryBean" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="cpm.MessageJob"/>
        <property name="targetMethod" value="execute"/>
        <property name="concurrent" value="false"/>
    </bean>
concurrent   同时发生
 concurrent:对于相同的JobDetail,当指定多个Trigger时, 很可能第一个job完成之前,第二个job就开始了。
 定concurrent设为false,多个job不会并发运行,第二个job将不会在第一个job完成之前开始




防止job并行运行的几种解决方案 
一、JOB State 
在通过MethodInvokingJobDetailFactoryBean在运行中动态生成的Job,配置的xml文件有个concurrent属性,表示job是否可以并行运行:如果一个job的业务处理发费的时间超过了job的启动的间隔时间(repeatInterval),这个属性非常有用。如果为false,那么,在这种情况下,当前job还在运行,那么下一个job只能延时运行。如果为true,那么job就会并行运行。在实际的应用中应该配置为true/false,要根据需要了(废话)。 
二、如果通过继承QuartzJobBean实现job的话,默认情况下QuartzJobBean是implements org.quartz.Job接口的,也就是说job示例是stateless的,会出现前面所述的并行情况。而代码中却要求job任务必需串行,解决办法:在job子类中继续implements org.quartz.StatefulJob。那么这个job实例变成了Stateful,job任务也就是串行的了。
注: 
在Quartz中,如果实现org.quartz.Job接口,那么这个job是stateless的,job实例的参数不能在多个任务之间共享,如果实现org.quartz.StatefulJob,这个job是个单例的,job实例的属性可以从当前任务传递到下一个任务。 


spring和quartz的整合对版本是有要求的。
spring3.1以下的版本必须使用quartz1.x系列,3.1以上的版本才支持quartz 2.x,不然会出错。

至于原因,则是spring对于quartz的支持实现,org.springframework.scheduling.quartz.CronTriggerBean继承了org.quartz.CronTrigger,在quartz1.x系列中org.quartz.CronTrigger是个类,而在quartz2.x系列中org.quartz.CronTrigger变成了接口,从而造成无法用spring的方式配置quartz的触发器(trigger)。

在Spring中使用Quartz有两种方式实现:第一种是任务类继承QuartzJobBean,第二种则是在配置文件里定义任务类和要执行的方法,类和方法可以是普通类。很显然,第二种方式远比第一种方式来的灵活。



MethodInvokingJobDetailFactoryBean中concurrent和shouldRecover属性的作用
解释 concurrent为true,则允许一个QuartzJob并发执行,否则就是顺序执行。例如QuartzJob A执行时间为15秒,配置为每10秒执行一次;如果concurrent为true,则0秒的时候启动一次A,10秒的时候再启动一次A,20秒的时候再启动一次A,不管前面启动的A有没有执行完;如果concurrent为false,则0秒的时候启动一次A,15秒的时候A执行完毕,再第二次启动A。
shouldRecover属性为true,则当Quartz服务被中止后,再次启动或集群中其他机器接手任务时会尝试恢复执行之前未完成的所有任务。例如QuartzJob B,在每次00秒的时候启动,假如在03:00的任务执行完之后服务器1被中止,服务器2在05:15的时候才接手;如果shouldRecover属性为true,则服务器2会尝试着补回原来在04:00和05:00的时候应该做的任务,如果shouldRecover属性为false,则服务器2只会从06:00的时候再执行B。


Quartz集群只支持JDBCJobStore存储方式,而MethodInvokingJobDetailFactoryBean不能序列化存储job数据到数据库,
重写 quartz 的 QuartzJobBean 类 
原因是在使用 quartz+spring 把 quartz 的 task 实例化进入数据库时,会产生: serializable 的错误,原因在于:
这个 MethodInvokingJobDetailFactoryBean 类中的 methodInvoking 方法,是不支持序列化的,因此在把 QUARTZ 的 TASK 序列化进入数据库时就会抛错。网上有说把 SPRING 源码拿来,修改一下这个方案,然后再打包成 SPRING.jar 发布,这些都是不好的方法,是不安全的。
必须根据 QuartzJobBean 来重写一个自己的类 。


posted @ 2015-07-26 01:39 abin 阅读(2092) | 评论 (0)编辑 收藏

QRTZ_CALENDARS 以 Blob 类型存储 Quartz 的 Calendar 信息 
QRTZ_CRON_TRIGGERS 存储 Cron Trigger,包括 Cron表达式和时区信息 
QRTZ_FIRED_TRIGGERS 存储与已触发的 Trigger 相关的状态信息,以及相联 Job的执行信息QRTZ_PAUSED_TRIGGER_GRPS 存储已暂停的 Trigger 组的信息 
QRTZ_SCHEDULER_STATE 存储少量的有关 Scheduler 的状态信息,和别的 Scheduler实例(假如是用于一个集群中) 
QRTZ_LOCKS 存储程序的观锁的信息(假如使用了悲观锁) 
QRTZ_JOB_DETAILS 存储每一个已配置的 Job 的详细信息 
QRTZ_JOB_LISTENERS 存储有关已配置的 JobListener 的信息 
QRTZ_SIMPLE_TRIGGERS 存储简单的Trigger,包括重复次数,间隔,以及已触的次数 
QRTZ_BLOG_TRIGGERS Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候) 
QRTZ_TRIGGER_LISTENERS 存储已配置的 TriggerListener 的信息 
QRTZ_TRIGGERS 存储已配置的 Trigger 的信息 
--------------------------------------------------------------------------------------------------
quartz 持久化数据库表格字段解释
建表,SQL语句在quartz-1.6.6\docs\dbTables文件夹中可以找到,介绍下主要的几张表: 
       表qrtz_job_details: 保存job详细信息,该表需要用户根据实际情况初始化 
       job_name:集群中job的名字,该名字用户自己可以随意定制,无强行要求 
       job_group:集群中job的所属组的名字,该名字用户自己随意定制,无强行要求 
       job_class_name:集群中个note job实现类的完全包名,quartz就是根据这个路径到classpath找到该job类 
       is_durable:是否持久化,把该属性设置为1,quartz会把job持久化到数据库中 
       job_data:一个blob字段,存放持久化job对象 

       表qrtz_triggers: 保存trigger信息 
       trigger_name: trigger的名字,该名字用户自己可以随意定制,无强行要求 
       trigger_group:trigger所属组的名字,该名字用户自己随意定制,无强行要求 
       job_name: qrtz_job_details表job_name的外键 
       job_group: qrtz_job_details表job_group的外键 
       trigger_state:当前trigger状态,设置为ACQUIRED,如果设置为WAITING,则job不会触发 
       trigger_cron:触发器类型,使用cron表达式 

       表qrtz_cron_triggers:存储cron表达式表 
       trigger_name: qrtz_triggers表trigger_name的外键 
       trigger_group: qrtz_triggers表trigger_group的外键 
       cron_expression:cron表达式 
       
       表qrtz_scheduler_state:存储集群中note实例信息,quartz会定时读取该表的信息判断集群中每个实例的当前状态 
       instance_name:之前配置文件中org.quartz.scheduler.instanceId配置的名字,就会写入该字段,如果设置为AUTO,quartz会根据物理机名和当前时间产生一个名字 
       last_checkin_time:上次检查时间 
       checkin_interval:检查间隔时间 

步骤4
 配置quartz.properties文件:
#调度标识名 集群中每一个实例都必须使用相同的名称 org.quartz.scheduler.instanceName = scheduler
#ID设置为自动获取 每一个必须不同 org.quartz.scheduler.instanceId = AUTO
#数据保存方式为持久化 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库平台 org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate #数据库别名 随便取org.quartz.jobStore.dataSource = myXADS
#表的前缀 org.quartz.jobStore.tablePrefix = QRTZ_
#设置为TRUE不会出现序列化非字符串类到 BLOB 时产生的类版本问题 org.quartz.jobStore.useProperties = true
#加入集群 org.quartz.jobStore.isClustered = true
#调度实例失效的检查时间间隔 org.quartz.jobStore.clusterCheckinInterval = 20000 
#容许的最大作业延长时间 org.quartz.jobStore.misfireThreshold = 60000
#ThreadPool 实现的类名 org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#线程数量 org.quartz.threadPool.threadCount = 10
#线程优先级 org.quartz.threadPool.threadPriority = 5
#自创建父线程 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true 
#设置数据源org.quartz.dataSource.myXADS.jndiURL = CT
#jbdi类名 org.quartz.dataSource.myXADS.java.naming.factory.initial = weblogic.jndi.WLInitialContextFactory #URLorg.quartz.dataSource.myXADS.java.naming.provider.url = t3://localhost:7001

【注】:在J2EE工程中如果想用数据库管理Quartz的相关信息,就一定要配置数据源,这是Quartz的要求。
posted @ 2015-07-23 15:31 abin 阅读(646) | 评论 (0)编辑 收藏

1、下载一个jrebel的文件,解压到D:\Sys\jrebel6.0.0-crack,在eclipse的classpath路径下面配置rebel.xml

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd">
<classpath>
<dir name="D:\SystemFile\EclipseWorkspace\SpringAQConsume\target">
</dir>
</classpath>
<web>
<link target="/">
<dir name="D:\SystemFile\EclipseWorkspace\SpringAQConsume\src\main\webapp">
</dir>
</link>
</web>
</application>

2、在tomcat里面配置
 -javaagent:D:\Sys\jrebel6.0.0-crack\jrebel.jar -noverify  -Xbootclasspath/p:D:\Sys\jrebel6.0.0-crack\rebelboot.jar
 -javaagent:D:\Sys\jrebel-6.0.2\jrebel.jar -noverify  -Xbootclasspath/p:D:\Sys\jrebel-6.0.2\rebelboot.jar


*********6.2.2需要以下设置**********
-noverify
-Djavaagent:D:/Sys\server/JRebel/JRebel6.2.0/jrebel.jar
-DXbootclasspath/p:D:\Sys\server\JRebel\JRebel6.2.0\rebelboot.jar
-Drebel.generate.show=true
-Drebel.spring_plugin=true
-Drebel.aspectj_plugin=true
-Drebel.cxf_plugin=true
-Drebel.logback_plugin=true
-Drebel.mybatis_plugin=true
-Xdebug -Djava.compiler=NONE -DXrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n
-Drebel.dirs=D:\SystemFile\EclipseWorkspace\integrate-svr\integrate-main\target\classes
-Dmyproject.root=D:\SystemFile\EclipseWorkspace\integrate-svr
-Drebel.disable_update=true
posted @ 2015-06-24 11:43 abin 阅读(3286) | 评论 (0)编辑 收藏

《秒杀系统架构优化思路》


上周参加Qcon,有个兄弟分享秒杀系统的优化,其观点有些赞同,大部分观点却并不同意,结合自己的经验,谈谈自己的一些看法。


一、为什么难

秒杀系统难做的原因:库存只有一份,所有人会在集中的时间读和写这些数据。

例如小米手机每周二的秒杀,可能手机只有1万部,但瞬时进入的流量可能是几百几千万。

又例如12306抢票,亦与秒杀类似,瞬时流量更甚。


二、常见架构


流量到了亿级别,常见站点架构如上:

1)浏览器端,最上层,会执行到一些JS代码

2)站点层,这一层会访问后端数据,拼html页面返回给浏览器

3)服务层,向上游屏蔽底层数据细节

4)数据层,最终的库存是存在这里的,mysql是一个典型


三、优化方向

1)将请求尽量拦截在系统上游:传统秒杀系统之所以挂,请求都压倒了后端数据层,数据读写锁冲突严重,并发高响应慢,几乎所有请求都超时,流量虽大,下单成功的有效流量甚小【一趟火车其实只有2000张票,200w个人来买,基本没有人能买成功,请求有效率为0】

2)充分利用缓存:这是一个典型的读多些少的应用场景【一趟火车其实只有2000张票,200w个人来买,最多2000个人下单成功,其他人都是查询库存,写比例只有0.1%,读比例占99.9%】,非常适合使用缓存


四、优化细节

4.1)浏览器层请求拦截

点击了“查询”按钮之后,系统那个卡呀,进度条涨的慢呀,作为用户,会不自觉的再去点击“查询”,继续点,继续点,点点点。。。有用么?平白无故的增加了系统负载(一个用户点5次,80%的请求是这么多出来的),怎么整?

a)产品层面,用户点击“查询”或者“购票”后,按钮置灰,禁止用户重复提交请求

b)JS层面,限制用户在x秒之内只能提交一次请求

如此限流,80%流量已拦


4.2)站点层请求拦截与页面缓存

浏览器层的请求拦截,只能拦住小白用户(不过这是99%的用户哟),高端的程序员根本不吃这一套,写个for循环,直接调用你后端的http请求,怎么整?

a)同一个uid,限制访问频度,做页面缓存,x秒内到达站点层的请求,均返回同一页面

b)同一个item的查询,例如手机车次,做页面缓存,x秒内到达站点层的请求,均返回同一页面

如此限流,又有99%的流量会被拦截在站点层


4.3)服务层请求拦截与数据缓存

站点层的请求拦截,只能拦住普通程序员,高级黑客,假设他控制了10w台肉鸡(并且假设买票不需要实名认证),这下uid的限制不行了吧?怎么整?

a)大哥,我是服务层,我清楚的知道小米只有1万部手机,我清楚的知道一列火车只有2000张车票,我透10w个请求去数据库有什么意义呢?对于写请求,做请求队列,每次只透过有限的写请求去数据层,如果均成功再放下一批,如果库存不够则队列里的写请求全部返回“已售完”

b)对于读请求,还用说么?cache来抗,不管是memcached还是redis,单机抗个每秒10w应该都是没什么问题的

如此限流,只有非常少的写请求,和非常少的读缓存mis的请求会透到数据层去,又有99.9%的请求被拦住了


4.4)数据层闲庭信步

到了数据这一层,几乎就没有什么请求了,单机也能扛得住,还是那句话,库存是有限的,小米的产能有限,透过过多请求来数据库没有意义。


五、总结

没什么总结了,上文应该描述的非常清楚了,对于秒杀系统,再次重复下笔者的两个架构优化思路:

1)尽量将请求拦截在系统上游

2)读多写少的常用多使用缓存

posted @ 2015-05-23 15:50 abin 阅读(1361) | 评论 (0)编辑 收藏

java 有for(;;)和 for(Object obj : List/Array)

最明显的一个:前者是有范围;后者是全部。

就编码来说各有好处:for更灵活,foreach更简便

for和foreach都是java中重要的集合遍历方法 
for循环中 你可以选择从前往后遍历,也可以从后往前遍历,也可以不遍历默写值
但是foreach只能从前往后遍历,而且每一个都会遍历一次,他们之间的选择得看你项目程序中的需求而定
JVM在解释执行行,都会将for与foreach解释成iterator。

总结如下:

1.如果只是遍历集合或者数组,用foreach好些,快些。

2.如果对集合中的值进行修改,就要用for循环了。
其实foreach的内部原理其实也是Iterator,但它不能像Iterator一样可以人为的控制,而且也不能调用iterator.remove();
更不能使用下标来访问每个元素,所以不能用于增加,删除等复杂的操作。



posted @ 2015-05-21 23:59 abin 阅读(373) | 评论 (0)编辑 收藏

    Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。

生成的ID是64Bits整型数,同时满足高性能(>10K ids/s),低延迟(<2ms)和高可用。

在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位+机器ID 10位+毫秒内序列12位。

该项目地址为:https://github.com/twitter/snowflake是用Scala实现的。

python版详见开源项目https://github.com/erans/pysnowflake

核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:

0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---0000000000 00

在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。

这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。

1. 41位的时间序列(精确到毫秒,41位的长度可以使用69年) 

2. 10位的机器标识(10位的长度最多支持部署1024个节点,支持多机房的分布式,需要使用zookeeper) 

3. 12位的计数顺序号(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号) 最高位是符号位,始终为0。

//64--------63-----------22-----------12----------0
//符号位 |41位时间 |10位机器码 |12位自增码|

对twitter而言这样的ID生成方案满足:

1.每秒能够生成足够的ID数。 2.生成的ID按照时间大致有序。

用zookeeper的原因是需要获取一个workerId,当然你也可以给分布式节点手工指定不同的workderId,那样就不需要用zookeeper了。

一个server一个workerid, 用zookeeper做保证.


除了最高位bit标记为不可用以外,其余三组bit占位均可浮动,看具体的业务需求而定。默认情况下41bit的时间戳可以支持该算法使用到2082年,10bit的工作机器id可以支持1023台机器,序列号支持1毫秒产生4095个自增序列id
















posted @ 2015-05-17 13:20 abin 阅读(1201) | 评论 (0)编辑 收藏

1.Http作为web服务的首选协议,居有4大优点:
   1)http非常简单,以纯文本(超文本)形式编码的请求和响应组成
   2)http是无状态的。一旦发送了一个http请求,客户和服务器之间的连接信息就会被释放,有利于减少服务器资源的消耗。
   3)http的运行端口80,在大多数防火墙上是公开的
   4)行业认可。
  但是Http的缺点:
   1)缺少对异步消息的支持
   2)消息传输的不可靠性

web service相对http (post/get)有好处吗?

 

1.接口中实现的方法和要求参数一目了然

2.不用担心大小写问题

3.不用担心中文urlencode问题

4.代码中不用多次声明认证(账号,密码)参数

5.传递参数可以为数组,对象等...

http和webservice的区别:
1、http是采用get,post等方式传输数据,而webservice是采用xml格式打包数据,传输是基于http协议进行传输。
2、http直接传输数据,而webservice是采用xml编解码数据,所以能速度上面有些慢。
3、webservice可以直接传输数组或者对象的数据格式,实际现在常用的http+json也可以的,只是需要进行字符串和各种格式的转换。
4、http传输占用的带宽要比webservice占用的带宽少。
5、webservice支持用户权限的验证,而http不支持直接的用户权限验证。
6、webservice接口中实现的方法和要求参数一目了然。


HTTPS和HTTP的区别:
https协议需要到ca申请证书,一般免费证书很少,需要交费。
http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议
http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议 要比http协议安全


SSL协议基础

SSL协议位于TCP/IP协议与各种应用层协议之间,本身又分为两层:

SSL记录协议(SSL Record Protocol):建立在可靠传输层协议(TCP)之上,为上层协议提供数据封装、压缩、加密等基本功能。

SSL握手协议(SSL Handshake Procotol):在SSL记录协议之上,用于实际数据传输前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。


HTTPS通信过程:
1.在服务器端存在一个公钥及私钥
2.客户端从服务器取得这个公钥
3.客户端产生一个随机的密钥
4.客户端通过公钥对密钥加密(非对称加密)
5.客户端发送到服务器端
6.服务器端接受这个密钥并且以后的服务器端和客户端的数据全部通过这个密钥加密(对称加密)





















posted @ 2015-05-10 11:27 abin 阅读(1289) | 评论 (0)编辑 收藏

Zookeeper有两种运行模式:

独立模式(standalone mode):只运行在一台服务器上,适合测试环境

复制模式(replicated mode):运行于一个集群上,适合生产环境,这个计算机集群被称为一个“集合体”(ensemble)。Zookeeper通过复制来实现高可用性,只要集合体中半数以上的机器处于可用状态,它就能够保证服务继续。为什么一定要超过半数呢?这跟Zookeeper的复制策略有关:zookeeper确保对znode树的每一个修改都会被复制到集合体中超过半数的机器上。

生产环境,zookeeper集群的服务器数目应该是奇数。

Zookeeper集群中的角色及其职责

领导者

  1.管理写请求

跟随者

  1.响应客户端的读请求

  2.负责把客户端提交的写请求转发给领导者

 

znode的观察机制

 

znode以某种方式发生变化时,“观察”(watch)机制可以让客户端得到通知。可以针对ZooKeeper服务的“操作”来设置观察,该服务的其他操作可以触发观察。比如,客户端可以对某个客户端调用exists操作,同时在它上面设置一个观察,如果此时这个znode不存在,则exists返回false,如果一段时间之后,这个znode被其他客户端创建,则这个观察会被触发,之前的那个客户端就会得到通知。

sync: 将客户端的znode视图与ZooKeeper同步
SYNC消息:返回SYNC结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。

跨客户端视图的并发一致性:

ZooKeeper并不保证在某时刻,两个不同的客户端具有一致的数据视图。因为网络延迟的原因,一个客户端可能在另一个客户端得到修改通知之前进行更新。假定有两个客户端AB。如果客户端A将一个节点/a的值从0修改为1,然后通知客户端B读取/a,客户端B读取到的值可能还是0,这取决于它连接到了哪个服务器。如果客户端AB读取到相同的值很重要,那么客户端B应该在执行读取之前调用sync()方法。

所以,ZooKeeper本身不保证修改在多个服务器间同步地发生,但是可以使用ZooKeeper原语来构建高层功能,提供有用的客户端同步。



 设计目的
1.最终一致性:client不论连接到哪个Server,展示给它都是同一个视图,这是zookeeper最重要的性能。
2 .可靠性:具有简单、健壮、良好的性能,如果消息m被到一台服务器接受,那么它将被所有的服务器接受。
3 .实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。
4 .等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效的等待。
5.原子性:更新只能成功或者失败,没有中间状态。
6 .顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面。


节点宕机:

应用集群中,我们常常需要让每一个机器知道集群中(或依赖的其他某一个集群)哪些机器是活着的,并且在集群机器因为宕机,网络断链等原因能够不在人工介入的情况下迅速通知到每一个机器。
Zookeeper同样很容易实现这个功能,比如我在zookeeper服务器端有一个znode叫/APP1SERVERS,那么集群中每一个机器启动的时候都去这个节点下创建一个EPHEMERAL类型的节点,比如server1创建/APP1SERVERS/SERVER1(可以使用ip,保证不重复),server2创建/APP1SERVERS/SERVER2,然后SERVER1和SERVER2都watch /APP1SERVERS这个父节点,那么也就是这个父节点下数据或者子节点变化都会通知对该节点进行watch的客户端。因为EPHEMERAL类型节点有一个很重要的特性,就是客户端和服务器端连接断掉或者session过期就会使节点消失,那么在某一个机器挂掉或者断链的时候,其对应的节点就会消失,然后集群中所有对/APP1SERVERS进行watch的客户端都会收到通知,然后取得最新列表即可。


zookeeper是一个高可用性,高性能的协调服务
解决哪些问题
在分布式应用中,经常会出现部分失败的情况,即当节点间传递消息的时候由于网络或者接收者进程死掉等原因,发送者无法知道接收者是否收到消息。
由于部分失败是分布式系统固有的特征因此zookeeper并不能避免部分失败,但是它可以帮你在部分失败的时候进行正确处理
为了解决这个问题zookeeper具有以下特征:
1:zookeeper提供丰富的构件(building block)来实现很多协调数据结构和协议
2:访问原子性,客户端要么读到所有数据,要么读取失败,不会出现只读取部分的情况
3:zookeeper运行在一组机器上,具有高可用性,帮助系统避免单点故障,同时删掉故障服务器
4:顺序一致性:任意客户端的更新请求会被按照发送顺序提交
5:单一系统映像:当一台服务器故障,导致它的客户端需要连接其它服务器的时候,所有更新晚于故障服务器的服务器都不会接收请求,一直到更新赶上故障服务器
6:及时性:任何客户端能看到的滞后都是有限的,不会超过几十秒,且提供sync操作强制客户端所连的服务器与领导者同步
7:会话:每个客户端连接时会尝试连接到配置列表中的一台服务器,一旦失败会自动连接另一台服务器依次类推,知道成功连接一台服务器,从而创建一个会话,客户端可以位每个会话设置超时时间,一旦会话过期,则所有短暂znode会丢失,因为zookeeper会自动发送心跳包,所以很少发生
8:约会机制(rendezvous),在交互的过程中,被协调的各方不许要事先彼此了解,甚至不必同时存在
9:ACL:zookeeper提供了digest(通过用户名密码),host(通过主机名),ip(通过ip地址)3种身份验证模式,依赖与zookeeper的身份验证机制每个ACL都是一个身份对应一组权限,如果我们要给demo.com的客户端域一个读权限在java语言中可以这样创建:
new ACL(Perms.READ, new Id("host", "demo.com"));
Ids.OPEN_ACL_UNSAFE是将所有ADMIN之外的权限授予每个人
另zookeeper还可以集成第三方的身份验证系统
10:提供关于通用协调模式的开源共享资源库
11:高性能的(官方数据)对以写为主的工作负载来说使用5台不错的机器基准吞吐量达到10000+




posted @ 2015-05-08 22:10 abin 阅读(643) | 评论 (0)编辑 收藏

各种设计模式--应用场景:
装饰器模式:
1、类继承会导致类的膨胀,这时候装饰器就派上用场了
责任链模式:
1、ifelse 用责任链来实现
状态模式:
1、ifelse
适配器模式:目的是在原来代码的基础上面,增加一些修饰的东西。
1、订单信息,比如增加了活动了之后,返回结果中要包含活动信息,在原来代码的基础上面给返回的Bean里面增加一些活动信息。
代理模式:
 比如吧,我有一个业务,同时要调用外部系统的http实现的接口和webservice实现的接口,可以做一个代理类, 代理webservice接口和http接口, 代理类帮我判断该用哪个, 我直接调用代理类就行了。代理类专门屏蔽后面的接口或者协议。
模板方法模式:
1、比如订单的下单还有退款操作,都需要同时判断使用的金额和红包。
2、支付的时候,调用不同的支付方式,都需要去做判断。
策略模式:





策略模式和装饰器模式区别:
策略模式偏向于对实现方法或策略的封装,调用者不需要考虑具体实现,只要指定使用的策略即可。
装饰器模式一般用于需要对功能进行扩展的场合,每一种装饰都是一种扩展或增强。

看起来两个模式好像没有必然的联系,但是在实际使用过程中,发现了一个让我困惑的地方。
先看一个典型的场景:
            商场对客户打折,老客户8折,新客户9折,新客户购物满3000,打8.5折
对这个基本场景,一般给的经典模式是策略模式,很多书也以这个作为策略模式的的经典案例。
但是,如果我把每一种折扣看作是一种对原有价格的装饰,这个场景也是可以用装饰器模式实现的。
两个模式都需要花费一些代码去判断策略或装饰器的类型,而且实现难度也旗鼓相当。

我用两种模式都实现了相同的功能,但是却没有发现明显的区别,不知道大家对这两个模式怎么看,
欢迎讨论。


策略模式更倾向是N选1的模式,也即根据条件选择相应的算法,但各种算法是互斥的,比如:团体客户和个人客户的优惠政策必然是非此即彼的;

装饰模式是在主体逻辑的基础上的附加逻辑,比如,个人客户有的属于同城客户,支持送货上门。

谢谢您的回复,如果按照策略模式,每一种打折方案是一种策略,而且只能选择一个,这是没有问题的。
按照装饰模式,每一种折扣都是在购买金额上的附加,在没有折上折或者送货上门这些附加值的时候,我感觉装饰模式也是实用的,当然,当折上折和送货这种附加体现的时候,装饰起的模式就体现去来了。

所以,我感觉在当前描述的问题中,这两个模式应该都可以很恰当的实现需求,但是没感觉到本质的区别。
于是就有些困惑了,看了您的总结,我感觉自己有点钻牛角了。
如果这个场景新增附加需求,比如新增vip客户,那么策略模式就比较合适了。
但是如果进行折上折或者送货上门这类附加需求,很明显装饰模式会更好一些了。
看来具体的模式还得根据实际需求确定,不能死搬硬套。
尽管楼主可以使用两种模式实现自己说的场景,但是两者还是有本质的区别。

策略模式,已经说的很清楚, 就不多说了。

装饰模式是主题逻辑的基础上的加强。可以看看JAVA IO的设计。
就像楼上说的, 如果客户购买满5000, 不只可以享受7折优惠, 还可以送货上门。
这里有两项功能: 1) 7折优惠, 2)送货上门
如果使用策略模式, 我们势必把两项功能都写在一个策略的实现类里面。
假使现在有新的场景出现,就是老客户购买满3000, 也享受送货上门。(或者说这里面的还蕴藏一些其他的优惠,比如说返券等等)
难道我们又把这些功能添加到我们的策略里面, 这样代码就很生硬而且不容易修改。

但是使用装饰模式就不一样,装饰模式能动态的给对象增加特有的功能。 比如说IO里面可以添加Buffer的功能。 同样在我们的场景里面,我们也可以将送货上门、返券等也动态的增强, new 送货上门(new 返券())...., 这样子就很灵活了。

策略实现可能类似:
do7折();
do送货上门();
do返券()

装饰的实现可能了类似:
new 7折( new 送货上门(new 返券())), 能随意组合;

所有的优惠都享受上了, 看上去还是爽一点。 

其实装饰模式, 还是更符合设计的一条原则: 少继承, 多组合 
posted @ 2015-05-08 17:32 abin 阅读(477) | 评论 (0)编辑 收藏

仅列出标题
共50页: 上一页 1 2 3 4 5 6 7 8 9 下一页 Last