zoukankan      html  css  js  c++  java
  • cas3.5.2集群化部署及定制开发

    原文及更多文章请见个人博客: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>
    
  • 相关阅读:
    【poj2396】 Budget
    【bzoj3876】 Ahoi2014—支线剧情
    【uoj207】 共价大爷游长沙
    【bzoj3064】 CPU监控
    【codeforces 103E】 Buying Sets
    【bzoj3938】 Robot
    【bzoj1568】 JSOI2008—Blue Mary开公司
    【hdu5306】 Gorgeous Sequence
    【bzoj2229】 Zjoi2011—最小割
    【bzoj2007】 Noi2010—海拔
  • 原文地址:https://www.cnblogs.com/heartlifes/p/6971009.html
Copyright © 2011-2022 走看看