从BDF2-2.0.1开始,我们在BDF2-WEBSERVICE模块当中添加了利用Webservice来同步集群环境下各个节点权限缓存信息的功能,利用这个功能,在集群环境下使用BDF2就不需要再配置诸如Terracotta Server之类的缓存服务器。利用Webservice来同步缓存,本质上,缓存还是集群中各个节点应用自已处理,相比使用缓存服务器效率要高出很多(缓存服务器本质是利用网络将各个节点缓存信息同步到缓存服务器当中再进行二次分发,对网络及服务器内存消耗过大),但因为可以实现缓存的同步,所以同样可以达到只有配置了Terracotta Server才能达到的功能。
需要特别指出的是,这里利用Webservice来同步缓存,实际上是将业务方法调用拦截功能与调用SpringBean中特定方法的Webservice结合而实现。要使用这一功能,我们需要配置下面两个属性:
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
bdf2.syncCacheremoteServerUrls | String | 空 | 用于指定在当前应用当中用户手工触发权限模块当中刷新缓存操作时,需要调用哪些其它节点应用的Webservice来同步刷新权限缓存,比如: bdf2.syncCacheremoteServerUrls=http://www.bstek.com:82/bdf2-demo/,http://www.bstek.com:81/bdf2-demo/ 可以看到上面配置了两个URL,中间使用逗号分隔,这样当前应用在刷新权限缓存时就是更新这两个URL上对应的权限缓存信息,可以看到这个URL在定义时要包含我们应用的ContextPath,比如这里的bdf2-demo。除了可以在dorado-home目录下的configure.properties当中配置bdf2.syncCacheremoteServerUrls这个属性值以外,我们还可以将其配置成JVM变量,JVM变量名就是bdf2.syncCacheremoteServerUrls。 |
bdf2.syncCacheRemoteServerUsernamePassword | String | 空 | 调用目标节点应用Webservice时需要的验证的用户名密码信息,它们中间用逗号分隔(username,password,顺序不能颠倒),同时这个属性值除了可以在dorado-home目录下的configure.properties当中外,还可以配置到我们应用所在JVM变量当中,变量名就叫bdf2.syncCacheRemoteServerUsernamePassword。 |
如果在使用的时候我们没有配置这两个属性值,那么系统将不会在户手工触发权限模块当中刷新缓存操作时刷新其它节点的缓存信息。同时,要使这个功能运转正常,我们还需要正确配置bdf2.globalMethodIntercetporBeanNamesPattern属性值(该属性作用参见业务方法调用拦截功能中相关介绍),保证该属性值中包含*Maintain,否则系统将无法拦截用户手工执行的刷新缓存操作。
实际上,利用类似的方法,我们还可以根据业务的需要实现集群环境下其它信息的同步,具体实现方法,与我们这里的权限缓存同步基本相同,下面的代码当中,列出了我们实现缓存同步操作的具体源代码,大家要实现其它操作可以模仿这种写法:
package com.bstek.bdf2.webservice.sync.cache; import java.lang.reflect.Method; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.InitializingBean; import com.bstek.bdf2.core.aop.IMethodInterceptor; import com.bstek.bdf2.core.business.IUser; import com.bstek.bdf2.core.context.ContextHolder; import com.bstek.bdf2.webservice.client.WebServiceClient; import com.bstek.bdf2.webservice.jaxb.IWebservice; import com.bstek.bdf2.webservice.rpc.data.DataRequest; import com.bstek.bdf2.webservice.rpc.data.DataResponse; /** * @author Jacky.gao * @since 2013年7月12日 */ public class SyncRefreshSecurityCache implements IMethodInterceptor,InitializingBean{ private static final String C1="com.bstek.bdf2.core.view.role.component.RoleComponentMaintain"; private static final String C2="com.bstek.bdf2.core.view.role.RoleMaintain"; private static final String C3="com.bstek.bdf2.core.view.role.url.RoleUrlMaintain"; private static final String C4="com.bstek.bdf2.authoritydelegation.view.role.url.RoleUrlMaintain"; private static final String M1="refreshUrlSecurityMetadata"; private static final String M2="refreshAllSecurityMetadata"; private static final String M3="refreshComponentSecurityMetadata"; private String remoteServerUrls; private String remoteServerUsernamePassword; private String username; private String password; public boolean support(Class<?> objectClass, Method method) { if(objectClass==null)return false; if(ContextHolder.getRequest()==null)return false; Object loginWay=ContextHolder.getHttpSession().getAttribute(ContextHolder.USER_LOGIN_WAY_KEY); if(loginWay!=null && loginWay.equals(IWebservice.WS_LOGIN_WAY.toString()))return false; if(StringUtils.isEmpty(remoteServerUrls))return false; String className=objectClass.getName(); if(className.equals(C1) || className.equals(C2) || className.equals(C3) || className.equals(C4)){ String methodName=method.getName(); if(methodName.equals(M1) || methodName.equals(M2) || methodName.equals(M3)){ return true; } } return false; } public void doBefore(Class<?> objectClass, Method method, Object[] arguments) throws Exception { //do nothing } public void doAfter(Class<?> objectClass, Method method,Object[] arguments, Object returnValue) throws Exception { DataRequest req=new DataRequest(); req.setBeanId("bdf2.roleMaintain"); req.setMethodName(method.getName()); String suffix="dorado/webservice/SpringBeanRPC"; if(StringUtils.isEmpty(remoteServerUsernamePassword)){ IUser user=ContextHolder.getLoginUser(); this.username=user.getUsername(); this.password=user.getPassword(); } for(String url:remoteServerUrls.split(",")){ if(url.endsWith("/")){ url=url+suffix; }else{ url=url+"/"+suffix; } WebServiceClient client=new WebServiceClient(url); client.setUsernameToken(username, password, true); client.setMarshallerClasses(new Class<?>[]{DataRequest.class,DataResponse.class}); DataResponse res=(DataResponse)client.sendAndReceive(req); if(!res.isSuccessful()){ throw new RuntimeException(res.getReturnValue()); } } } public void afterPropertiesSet() throws Exception { if(StringUtils.isEmpty(remoteServerUsernamePassword)){ remoteServerUsernamePassword=System.getProperty("bdf2.syncCacheRemoteServerUsernamePassword"); } if(StringUtils.isNotEmpty(remoteServerUsernamePassword)){ String[] account=remoteServerUsernamePassword.split(","); if(account.length!=2){ throw new IllegalArgumentException("Username and password must be separated by commas"); } this.username=account[0]; this.password=account[1]; } if(StringUtils.isEmpty(remoteServerUrls)){ remoteServerUrls=System.getProperty("bdf2.syncCacheremoteServerUrls"); } } public void setRemoteServerUrls(String remoteServerUrls) { this.remoteServerUrls = remoteServerUrls; } public void setRemoteServerUsernamePassword(String remoteServerUsernamePassword) { this.remoteServerUsernamePassword = remoteServerUsernamePassword; } }
类编写完成,不要忘记配置到Spring环境当中。