cas3.5.2集群化部署及定制开发
2016-01-20 13:42
691 查看
集群化方案:
1.tomcat集群共享session 2.持久化票根st及tgt 3.持久化service 4.修改ServiceManager,从内存共享改为redis共享
tomcat集群共享session
之所以要共享session,是因为cas使用了spring-webflow,而webflow使用session存储中间变量,如果不共享session,会直接导致登录流程因为缺少中间变量而失败,具体表现为输入正确用户名密码后,界面刷新重新进入登录界面。session共享在tomcat中有三种方案可供选择,分别是:1.tomcat原始集群共享。2.redis session持久化共享。3.memcache session持久化共享。
这里我选用了tomcat原始的集群共享,原因是redis session持久化实验失败,session是成功持久化到redis中了,但是登录流程还是失败,memcache由于没有环境,没有试验。
配制tomcat集群
1.在server.xml的Engine元素下加入以下配制<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" dropTime="3000" mcastTTL="1"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="xxx" port="4001" autoBind="0" selectorTimeout="100" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=".*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster>
上面的address="xxx",替换成你自己的服务器ip地址
2.在工程web.xml的开头加入配置项
<distributable />
持久化票根
票根的持久化,cas默认就提供了支持,我们所要做的就是把相应的持久化类使用起来,在配置文件中替换掉原来的内存存储类。 1.pom中增加以下依赖:<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.18</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.6.2</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.5.0.RELEASE</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <scope>runtime</scope> <version>4.2.0.Final</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.1</version> </dependency>
以上依赖包括mysql驱动,hibernate相关包,druid数据连接池及redis驱动(redis用于后面service持久化)
2.applicationContext.xml文件修改 增加以下配置项:
<tx:annotation-driven transaction-manager="transactionManager" /> <context:component-scan base-package="com" /> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass --> <property name="driverClassName" value="${jdbc.driver}" /> <!-- 基本属性 url、user、password --> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="${jdbc.pool.minIdle}" /> <property name="minIdle" value="${jdbc.pool.minIdle}" /> <property name="maxActive" value="${jdbc.pool.maxActive}" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="30000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="30000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="90000" /> <property name="validationQuery" value="SELECT 'x'" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaVendorAdapter"> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">${database.dialect}</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.jdbc.batch_size">${database.batchSize}</prop> </props> </property> </bean> <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" p:generateDdl="true" p:showSql="true" /> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" /> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
2.ticketRegistry.xml文件修改 查找ticketRegistry,修改原bean声明如下:
<bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.JpaTicketRegistry" />
持久化service
1.deployerConfigContext.xml文件修改 查找serviceRegistryDao,修改bean声明<bean id="serviceRegistryDao" class="org.jasig.cas.services.JpaServiceRegistryDaoImpl" p:entityManagerFactory-ref="entityManagerFactory" />
修改ServiceManger
cas自带的DefaultServicesManagerImpl的集群模式,是通过直接将所有的service存在内存的一个set中,然后通过quartz,每两分钟reload一把全量service,这种2分钟同步一次service的集群模式,显然不能正式上线使用,这里我们通过自己实现ServiceManager,采用redis,对所有service进行统一管理。1.增加RedisServicesManagerImpl类:
public class RedisServicesManagerImpl implements ServicesManager { private final Logger log = LoggerFactory.getLogger(getClass()); private final static String SPLIT = ","; private final static String REDIS_KEY = "registedService"; @NotNull private ServiceRegistryDao serviceRegistryDao; private RegisteredService disabledRegisteredService; private RedisTemplate<String, RegisteredService> redisTemplate; private RegexRegisteredService defaultService = new RegexRegisteredService(); public RedisServicesManagerImpl(final ServiceRegistryDao serviceRegistryDao, final RedisTemplate<String, RegisteredService> redisTemplate) { this.serviceRegistryDao = serviceRegistryDao; this.disabledRegisteredService = constructDefaultRegisteredService(new ArrayList<String>()); this.redisTemplate = redisTemplate; constructDefaultService(); } @Transactional(readOnly = false) @Audit(action = "DELETE_SERVICE", actionResolverName = "DELETE_SERVICE_ACTION_RESOLVER", resourceResolverName = "DELETE_SERVICE_RESOURCE_RESOLVER") public synchronized RegisteredService delete(final long id) { final RegisteredService r = findServiceBy(id); if (r == null) { return null; } log.info("delete service by id..." + r.getServiceId() + "..." + r.getId()); this.serviceRegistryDao.delete(r); String key = r.getId() + SPLIT + r.getServiceId(); // redisTemplate.opsForValue().getOperations().delete(key); redisTemplate.opsForHash().delete(REDIS_KEY, key); return r; } public RegisteredService findServiceBy(final Service service) { if (service != null) { log.info("find service by service..." + service.getId() + "..." + service.getClass()); } Collection<RegisteredService> c = getAllServices(); if (c.isEmpty()) { log.info("find service by service...service is blank"); return this.disabledRegisteredService; } for (final RegisteredService r : c) { if (r.matches(service)) { log.info("find service by service...service is a match...in service..." + service.getId() + "...with redis..." + r.getServiceId()); return r; } } log.info("find service by service...service not match"); return null; } public RegisteredService findServiceBy(final long id) { log.info("find service by id..." + id); Map<Object, Object> map = redisTemplate.opsForHash().entries(REDIS_KEY); Set<Entry<Object, Object>> set = map.entrySet(); Iterator<Entry<Object, Object>> it = set.iterator(); while (it.hasNext()) { Entry<Object, Object> entry = it.next(); String key = entry.getKey().toString(); RegisteredService value = (RegisteredService) entry.getKey(); log.info("find service by id...service in redis..." + key); if (String.valueOf(id).equals(key.split(SPLIT)[0])) { log.info("find service by id...match..." + key); try { return (RegisteredService) value.clone(); } catch (final CloneNotSupportedException e) { return value; } } } return null; } public Collection<RegisteredService> getAllServices() { log.info("get all services..."); Set<RegisteredService> services = new TreeSet<RegisteredService>(); Map<Object, Object> map = redisTemplate.opsForHash().entries(REDIS_KEY); Set<Entry<Object, Object>> set = map.entrySet(); Iterator<Entry<Object, Object>> it = set.iterator(); while (it.hasNext()) { Entry<Object, Object> entry = it.next(); log.info("get all services...service in redis..." + entry.getKey() + "..." + entry.getValue().getClass()); String key = entry.getKey().toString(); if (entry.getValue() instanceof RegisteredService) { RegisteredService value = (RegisteredService) entry.getValue(); log.info("get all services...service in redis..." + key); services.add(value); } } if (!services.contains(defaultService)) { services.add(defaultService); } return services; } public boolean matchesExistingService(final Service service) { return findServiceBy(service) != null; } @Transactional(readOnly = false) @Audit(action = "SAVE_SERVICE", actionResolverName = "SAVE_SERVICE_ACTION_RESOLVER", resourceResolverName = "SAVE_SERVICE_RESOURCE_RESOLVER") public synchronized RegisteredService save( final RegisteredService registeredService) { log.info("save service..." + registeredService.getServiceId()); final RegisteredService r = this.serviceRegistryDao .save(registeredService); String key = registeredService.getId() + SPLIT + registeredService.getServiceId(); log.info("save service in redis..." + key); // redisTemplate.opsForValue().set(key, registeredService); redisTemplate.opsForHash().put(REDIS_KEY, key, registeredService); return r; } private RegisteredService constructDefaultRegisteredService( final List<String> attributes) { final RegisteredServiceImpl r = new RegisteredServiceImpl(); r.setAllowedToProxy(true); r.setAnonymousAccess(false); r.setEnabled(true); r.setSsoEnabled(true); r.setAllowedAttributes(attributes); if (attributes == null || attributes.isEmpty()) { r.setIgnoreAttributes(true); } return r; } private void constructDefaultService() { defaultService.setId(0); defaultService.setName("HTTP and IMAP"); defaultService.setDescription("Allows HTTP(S) and IMAP(S) protocols"); defaultService.setServiceId("^(https?|imaps?)://.*"); defaultService.setEvaluationOrder(10000001); } }
2.applicationContext.xml文件修改 增加以下配置项:
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="${redis.pool.maxActive}" /> <property name="maxIdle" value="${redis.pool.maxIdle}" /> <property name="maxWaitMillis" value="${redis.pool.maxWait}" /> <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /> </bean> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.hostname}" /> <property name="port" value="${redis.port}" /> <property name="password" value="${redis.password}" /> <property name="poolConfig" ref="jedisPoolConfig" /> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory" /> </bean>
修改以下配置项:
<bean id="servicesManager" class="com.wondersgroup.cas.services.RedisServicesManagerImpl"> <constructor-arg index="0" ref="serviceRegistryDao" /> <constructor-arg index="1" ref="redisTemplate" /> </bean>
相关文章推荐
- sublime text中文乱码解决方法?
- IIS 编译器错误消息: CS0016未能写入输出文件“c:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\Temporary ASP.NET Files\roo
- 一个很详细的web.xml讲解
- 山坡羊。鹏程怀春
- android query的用法
- 读小敏子简书《程序员的呐喊》读书笔记(上)有感
- SD卡, EMMC固化 ,关于bootloader linux
- 递归求gcd(a,b)
- Python算法题----Valid Palindrome
- Java泛型擦除
- poj-3026 -Borg Maze -bfs+prim(MST)
- hbase 远程调试问题
- Socket通信编程___实例二___client端
- avl树重平衡的简化算法
- Android单元测试
- android常用的设计模式
- UITableView取消某一个cell的分割线
- hbase-site.xml 配置详解
- Windows 远程桌面连接命令
- 菜鸟程序员如何才能快速提高自己的技术