除了分身做beetl(http://beetl.sourceforge.net/) 外,实际上还做了一个Spring相关的,技术含量不高,但还是很有用。所以推荐给大家
Spring-Dumpling(简称 SD) 是在基于 Spring 框架下的提供协作服务,正如 Spring 自带的 @Service,SD 提供 @CooperationService 以及如下注解:
@Publish / @Subscribe
@ClusterSync
@RemotePublish / @RemoteSubscriber
@RemoteNotify / @RemoteWait
@Process / @Task
来完成业务方法的协作。
1 https://sourceforge.net/projects/spring-dumpling/ 下载最新版本spring-dumpling.0.1.jar 或者下载工程自个打包
2 放入 classpath 里
3 在 spring 做如下配置即可
<!-- springdumpling 配置 --> < bean id = "spring-dumpling" class = "com.bee.spring.dumpling.SpringBowl" >
< property name = "psProvider" ref = "defaultPublisScribeProvider" /> <!-- <property name="nwProvider" ref ="jmsNotifyWaitProvider" /> <property name="remotePSProvider" ref ="jmsRemotePSProvider" /> <property name="clusterSyncProvider" ref ="zkClusterSyncProvider" />
-->
</ bean > < bean id = "defaultPublisScribeProvider" class = "com.bee.spring.dumpling.DefaultPSProviderImpl" /> < bean id = "zkClusterSyncProvider" class = "com.bee.spring.dumpling.ZkClusterSyncProvider" > < property name = "zkConf" ref = "zkConf" /> </ bean >
< bean id = "jmsNotifyWaitProvider" class = "com.bee.spring.dumpling.jms.activemq.JMSNotifyWaitProvider" > < property name = "jmsConf" ref = "jmsConf" /> </ bean >
< bean id = "jmsRemotePSProvider" class = "com.bee.spring.dumpling.jms.activemq.JMSRemotePSProvider" > < property name = "jmsConf" ref = "jmsConf" /> </ bean >
<!-- zk 配置 --> < bean id = "zkConf" class = "com.bee.spring.dumpling.clustersync.zk.ZKConf" > < property name = "server" value = "127.0.0.1:2181" /> <!-- <property name="useACL" value="1"/> --> </ bean > <!-- JMS消息配置 --> < bean id = "jmsConf" class = "com.bee.spring.dumpling.jms.activemq.JmsConfig" > < property name = "url" value = "failover://(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617)?randomize=false" /> </ bean >
<!-- enable aop --> < aop:aspectj-autoproxy /> <!-- <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" /> --> < context:component-scan base-package = "com.netease" />
<!-- springdumpling 配置结束 --> |
目前采用ZK 作为 ClusterSync 的协调服务提供者, ActivityMQ 作为 RemotePublish , RemoteNotify 的服务提供者
需要实现协调服务的类,必须在类上申明@CooperationService ,如下例子
@CooperationService @Service public class UserServiceImpl implements UserService ,ApplicationContextAware { |
|
@Publish,有如下属性
path: 一个逻辑路径,必须要
ruleExp: 根据输入值,输出值的表达式判断是否需要 Publish, 否则,总是通知其他机器 . 如规则 rule="returnValue==true" ,默认是发送
argExp:一个参数表达式列表,如果没有,则按照输入参数和输出参数作为参数列表,传递个 sub 。参数格式如: argExp="args[0].orderId,args[1],returnValue;
@Transactional @Publish (path = "/user/ login " , ruleExp = "returnValue!=null" , argExp = "args[0],returnValue" ) public UserVo updateLoginTime(String userName) { System. out .println( "update login userName for " + userName); userDao .updateLoginTime(userName); return userDao .login(userName); } |
|
@Subscribe
path: 一个逻辑路径,必须要
runPolicy: 运行时机,有俩个值,一个是 sameTransacion, 表示于 publish 方法在同一事物里,另外一个是 afterCommint, 这是默认值,表示当事物成功提交后异步执行,如果 Publish 没有在事物上下文里,则 @publish 方法执行完毕后立刻执行
@Subscribe (path = "/user/ login " ,runPolicy= Subscribe . AFTER_COMMIT ) @Transactional public void auditLogin(String name, UserVo vo) { //do nothing,just log System. out .println( "user " + vo.getName() + "last login time:" + vo.getLastLoginTime() + " in subscribe UserCreditServiceImpl.auditLogin" ); userCreditDao .addLoginTimes(name); // throw new UnsupportedOperationException("just test,no roll back"); } |
|
path:一个逻辑路径,一旦机器( JVM )获取锁,将永久占有锁,机器宕机或者失去连接,将导致其他机器中的某一个占用
allowAcessAsFistTime: false或者 true ,默认 false 。如果为 true ,则允许第一次调用忽略锁
@ClusterSync(path="/testsys/job/xxxjob") Public void doit(){ |
@RemotePublish,同 Publish ,但是发布到远程
path 一个路径
ruleEx: 根据输入值,输出值判断是否需要 Notify ,默认是 returnValue!=null ,否则,总是通知其他机器 . 如规则 rule="returnValue==true"
argExp:一个参数表达式列表,如果没有,则按照输入参数和输出参数作为参数列表,传递个 sub 。参数格式如: argExp="arg0=input[0].orderId;arg1=input[0].cash;arg2=returnValue;"
p ersisit :是否持久化消息
@RemotePublish (path = "/user/age12" , argExp = "args[0],args[1]" ) public void updateAge(String userName, int age) { System. out .println( "update age" + userName + "," + age); } |
@RemoteSubscriber
path:一个路径。
@RemoteSubscribe (path = "/user/age12" ) public void addAge(String userName, int age) { System. out .println( "add age " + userName + "," + age); } |
@RemoteNotify,远程只能有一个被执行
path 一个路径
rule: 根据输入值,输出值的表达式判断是否需要 Notify ,否则,总是通知其他机器 . 如规则 rule="returnValue==true" ,默认是发送
argExp:一个参数表达式列表,如果没有,则按照输入参数和输出参数作为参数列表,传递个 sub 。参数格式如: argExp="arg0=input[0].orderId;arg1=input[0].cash;arg2=returnValue;"
p ersisit :是否持久化消息
@ RemoteNotify(path= "/test/mq2" ,persisit= true ) public void sendMsg(String str){ sendList.add(str); } |
@RemoteWait ,
path:一个路径。
@RemoteWait (path= "/test/mq2" ) public void receiveMsg(String str){ receiverList .add(str); } |
每组annotation 可以有自己的协作服务提供者,或者共用一种协作服务提供者(如果服务提供者都支持)。
协作服务提供时机应该是系统启动成功后(也包括系统各个组件初始化成功后)
Remote的协作服务实现可以通过 JMS , ZK, 甚至数据库表共享来实现。推荐使用 ZK ,但 ZK 对 RemotePublish 支持并不好。不适合线上业务,只适合一些数据同步和管理功能。
@Publish,@Process是基于 Local 的,则不需一个第三方协作服务提供者。
@ClusterSync 使用了Zookeeper, 只需要安装 Zookeeper ,并在配置文件中指定 server 的 IP 地址即可
@RemotePublis @RemoteNotify使用了 ActivyMQ ,同样只需要安装好 MQ ,并设定 URL 的值即可
1 多台机器上只有一台能执行某个 job ,则使用 @RemoteSynronized
2 主业务调用后会调用一些次要业务,不希望次要业务影响主业务的性能和牺牲可维护性 主业务使用 @Publish, 多个次业务使用 @Subscribe 。
3 数据需要同步到多台机器上,使用 @RemotePublish 和 @RemoteSubscriber 标签
4 数据需要交给远程的任一台机器处理,使用 @RemoteNotify 然后结合 @RemoteWait 标签一起用
5 主业务和次要业务处理后,还要求交给远端一个机器处理 可以在使用 @Publish,@Subscrbie 后,可以结合 @RemoteNotify , @RemoteWait 来处理
欢迎使用和建议