spring+redis哨兵模式-解决问题清单

   项目没有采用maven,所以出现版本问题需要去自己手动下载jar包才可以。

   采用哨兵模式的前提是redis已经成功搭建,哨兵模式的配置非常简单,网上也有很多,不详细讲述如何搭建redis。我简单列出来一下spring的配置,本篇主要讲述配置完哨兵模式产生的问题以及解决方式。

   在spring配置文件中加入

 <bean id="sentinelConfiguration" 
        class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
        <property name="master">
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <property name="name" value="mymaster"></property><!--为主的名字,一定要跟redis的配置一样,否则无法找到-->
            </bean>
        </property>
        <property name="sentinels">
            <set>

         <!--节点配置,我们是一主二从,这块要写哨兵的端口以及ip,不是节点的-->
                <bean  class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="192.168.1.126"></constructor-arg>
                    <constructor-arg name="port" value="27000"></constructor-arg>
                </bean>
            </set>
        </property>
    </bean>

 <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <!-- redis 配置 -->
        <property name="password" value="123456"/>
        <!-- <property name="poolConfig" ref="poolConfig"></property> -->
         <constructor-arg name="sentinelConfig" ref="sentinelConfiguration"/>
         <constructor-arg name="poolConfig" ref="poolConfig" />
    </bean>

 <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
           <property name="maxIdle" value="${redis.maxIdle}" />    
        <!--连接池的最大数据库连接数  -->  
        <property name="maxTotal" value="${redis.maxTotal}" />  
        <!--最大建立连接等待时间-->    
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />    
        <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟)-->  
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" />   
        <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3-->  
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" />   
        <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1-->  
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" />   
        <!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个-->    
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />    
        <!--在空闲时检查有效性, 默认false  -->  
        <property name="testWhileIdle" value="${redis.testWhileIdle}" />   
      </bean>  

一共三个bean的配置,缺一不可!

问题一:搭建完之后启动项目,报错无法注入sentinels,理由是类型不一致!查看源码发现,的确不一致。源码的定义是

private Set<RedisNode> sentinels;

public void setSentinels(Iterable<RedisNode> sentinels) {

        notNull(sentinels, "Cannot set sentinels to 'null'.");

        this.sentinels.clear();

        for (RedisNode sentinel : sentinels) {
            addSentinel(sentinel);
        }
    }

可以看的出来,定义的类型是set,而set的类型是iterable,虽然set跟iterable存在父子继承关系,但是还是无法注入,但是既然无法注入,为什么还要这么写呢!这是我在我头脑里面的问题。所以我大胆猜测,哨兵模式跟spring的版本有关系。于是开始搜索解决方案:

                    第一:由于项目中采用的是spring3.2的。网上好多人继承RedisSentinelConfiguration,参照了他们的方式,我发现存在一定的问题。为什么这么说,因为我也不清楚网上这么写的人到底是否真正的写成功了,但是的确存在继承问题。

修改spring的配置文件,变成自己定义的

<bean id="stConfiguration" class="com.cn.tool.RedisSentinelConfigurationChild">

看啊,要想解决继承问题,那么把重写set方法即可。把iterable变成set就解决了,于是我继承RedisSentinelConfiguration,重写下面的方法

public class RedisSentinelConfigurationChild extends RedisSentinelConfiguration {
    public void setSentinels(Set<RedisNode> sentinels) {

        notNull(sentinels, "Cannot set sentinels to 'null'.");

          this.sentinels.clear();

        for (RedisNode sentinel : sentinels) {
            addSentinel(sentinel);
        }
    }
}

请看我标注红色的地方,因为sentinels在父类中是私有方法,是不可以在子类中继承的。而大部分网上写继承的人,此处都没有改变,所以我才觉得他们应该是猜测这样,而没有真正的使用!当然,我们也可以通过父类的get方法来获取sentinels。比如这样

this.getSentinels().clear(); 写完之后也照样报错,报错是Collections导致的。

我也尝试了把父类所有的方法都重写一遍,但是我发现在其他spring的类里面创建的对象全部是用RedisSentinelConfiguration,如果我要改写,需要把所有用RedisSentinelConfiguration来创建对象的对方全部换成它的子类才可以,工作量大不说,解决起来也好难,所以,给后来人一个建议,继承RedisSentinelConfiguration不可用。此种方法抛弃。

       第二个:升级spring,那么需要升级到多少版本呢,有的说4.0版本以上就可以。但是事实真的如此吗,并不是。需要spring4.0.7版本以上。

此处要感谢https://aperise.iteye.com/blog/2342615,提供对jedis以及spring的版本对应关系。

把spring4.0.7的版本替换原来的spring3.2版本,进行升级,升级完毕之后重启。

报错

Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy581

无法继承cglib的子类。啥意思,就是无法代理。

简单说一下代理,代理分为两种。一种为jdk的接口代理,接口代理需要目标类实现接口才可以。一种为cglib的代理,也叫作类代理,是通过继承的方式来实现的,继承的方式那就有局限性,因为并不是所有的东西都可以继承的,比如上文说的private或者final之类的。cglib的代理需要导入cglib-nodep.jar包。

那么代理用到哪里了呢。aop啊,事物啊,都会用到。

jdk的接口代理是程序默认的,如果想要强制使用cglib代理,那么需要加入proxy-target-class="true"。

所以,当出现以上报错的时候,要查找程序要哪些地方采用的代理。经过查找,发现项目中存在两处aop代理的地方。一种是基于接口代理,类似于这种。

<aop:pointcut id="apiUserRegister" expression="execution(* com.cn.service.impl.UserServiceImpl.apiUserRegister(..) ) or 
                                                      execution(* com.cn.service.impl.UserServiceImpl.register(..) ) or
                                                      execution(* com.cn.service.impl.PhoneUserServiceImpl.register(..) ) or
                                                      execution(* com.cn.service.impl.PhoneUserServiceImpl.apiUserRegister(..) )"/>

还有一种

<bean id="methodCacheInterceptor"  class="com.cn.aop.TuijianAspect"></bean>
    <bean id="methodCachePointCut"  
          class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
        <property name="advice">  
            <ref local="methodCacheInterceptor"/>  
        </property>  
        <property name="patterns">  
            <list>  
                <value>.*getXTLast*</value>  
                <value>.*getXHLast*</value>  
            </list>  
        </property>  
    </bean>  

<bean id="productServiceTarget" class="com.cn.service.impl.ProductServiceImpl"/>  <!--定义代理类-->
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">  
            <ref local="productServiceTarget"/>  
        </property>  
        <property name="interceptorNames">  
            <list>  
                <value>methodCachePointCut</value>  
            </list>  
        </property>  
    </bean>

以上为两种aop的方式,定义拦截以及切入点。

经过查找,第二种方式这么写代理程序报错。开始解决

加入了 <property name="proxyInterfaces">
             <value>com.cn.service.ProductService</value>
        </property>

实现了接口,不好用。没有办法,只能重新写aop的拦截了,算是解决了问题。

但是一直有一个问题,为什么才spring的低版本下,没问题,而升级了高版本出现了问题了,所以也是猜测,只是猜测。

高版本的spring在程序没有指定代理方式的时候,会自己查找,如果目标类实现了接口,则用接口代理,如果没有接口,则使用类代理。但是目标类里面含有private或者final,无法继承导致报错,而低版本没有这个校验,甚至说低版本根本不查找代理方式,默认就是jdk的接口代理,如果指定了类代理,就用类代理。

当然,查找问题的过程中也走了很多的弯路,实在是网上查不到对应的解决方式。有些地方只能自己去看源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值