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

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

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

   在spring配置文件中加入

          class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
        
            
                
            

        

        
            

         
                
                    
                    
                

            
        
    

 
        
        
        
        
         
    

 
               
         
         
           
           
         
          
         
          
         
          
           
           
         
          
     
 

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

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

private Set sentinels;

public void setSentinels(Iterable 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的配置文件,变成自己定义的

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

public class RedisSentinelConfigurationChild extends RedisSentinelConfiguration {
    public void setSentinels(Set 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的版本对应关系。

spring+redis哨兵模式-解决问题清单_第1张图片

把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代理的地方。一种是基于接口代理,类似于这种。

还有一种


               class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
         
             
       
 
         
             
                .*getXTLast*  
                .*getXHLast*  
           
 
       
 
   
 

productServiceTarget" class="com.cn.service.impl.ProductServiceImpl"/> 
   
         
             
       
 
         
             
                methodCachePointCut  
           
 
       
 
   

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

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

加入了 
             com.cn.service.ProductService
       

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

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

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

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

你可能感兴趣的:(spring+redis哨兵模式-解决问题清单)