cas3.5.2集群化部署及定制开发

来源:互联网 发布:机械加工工艺编程员 编辑:程序博客网 时间:2024/06/11 20:46

原文及更多文章请见个人博客:http://heartlifes.com

集群化方案:

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>
原创粉丝点击