系统集成CRedis遇到的坑

1个月前 (08-20) wang JAVA 0评论 已收录 74℃ 浏览数:39

今日完成任务

  • 系统集成credis,遇到了不少问题,总结一下。

maven依赖冲突

  • 首先按照文档引入weshop-ad-component包,然后报错,删除这个包后就不报错了,初步定为依赖冲突。
  • 然后查看报错的日志。

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'orderPushController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.weshop.galileo.service.order.impl.NewOrderDTOServiceImpl com.weshop.galileo.web.controller.OrderPushController.newOrderDTOService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'newOrderDTOServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.weshop.galileo.service.order.open.corgi.producer.SynOrderMessageProducer com.weshop.galileo.service.order.impl.NewOrderDTOServiceImpl.synOrderMessageProducer; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'synOrderMessageProducer': Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: com/mogujie/corgi/net/util/V4AddressResolver

看到了corgi,打开weshop-ad-component的pom文件,看到了

<corgi.version>1.0.8</corgi.version>

于是觉得可能是这个包的冲突,于是代码中排除corgi的包,重启应用。

  • 再次报错,但是报错内容不一样了。

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'settleCommission' defined in file [/Users/wangyang/Documents/apache-tomcat-9.0.10/webapps/ROOT/WEB-INF/classes/spring/spring-common.xml]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: org/apache/curator/retry/RetryForever

然后坑就在这了,我看报错日志,好像是retry这个的问题,于是我就去maven tree里搜了一下,还真找到了

org.springframework.retry:spring-retry:jar:1.2.0.RELEASE:compile

这个包。然后我就排除了这个包,结果发现还是报错了。这里说明冲突的应该不是这个包。

  • 然后我继续看日志 看到了这个

    Caused by: java.lang.NoClassDefFoundError: org/apache/curator/retry/RetryForever
    at com.mogujie.vacuum.JobScheduler.initZkClient(JobScheduler.java:380) ~[vacuum-core-1.1.4.6.jar:na]
    at com.mogujie.vacuum.JobScheduler.afterPropertiesSet(JobScheduler.java:100) ~[vacuum-core-1.1.4.6.jar:na]
    at com.mogujie.vacuum.JobScheduler.(JobScheduler.java:89) ~[vacuum-core-1.1.4.6.jar:na]
    at com.mogujie.vacuum.spring.SpringJobScheduler.afterPropertiesSet(SpringJobScheduler.java:65) ~[vacuum-spring-1.1.4.6.jar:na]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    ... 57 common frames omitted

  • 然后我又看到了我自己认识的 vacuum,于是我觉得找到源头了。于是又觉得很稳的试了一下,发现还是同样的错。然后我就发现应该排除的包还是不对。 然后我又自己试了半天,怎么弄都是不对。于是去问了一下@厘子。最后发现冲突的包是curator,排除掉就跑的起来了。

  • 其实已经提示的很清楚了:

    Caused by: java.lang.NoClassDefFoundError: org/apache/curator/retry/RetryForever

但是我自己却只看到了retry,然后就各种试但是都走错了方向。weshop-ad-component的pom文件中也有

<curator.version>2.8.0</curator.version>
  • 以后遇到了这种问题,就要多想想,可能还是经验丰富一点,就能看到哪些包是引用过的,就可以一一排除尝试,而不能单纯的去看自己认识哪些,往往自己看问题是有局限性的。这样折腾一番还是感觉很有用的,下次如果再遇到类似的问题。那么我觉得可以很快的去解决。
  • ps 我看了下这两个冲突的报错,分别是org/apache/curator/retry/RetryForever 和org/apache/curator/retry/RetryForever,打印的信息的第三个,corgi、curator恰好就是冲突包名,是否以后遇到类似的问题,去看报错日志的第三个词呢? 仅仅猜测哈。

父子容器

  • 初始化

    @PostConstruct
    public void init(){
    redisTemplate = redisTemplateFactory.createComponent("test");
    }

结果方法报错。

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisFactory': Invocation of init method failed; nested exception is java.lang.ArrayIndexOutOfBoundsException: 1

查看了一下构造方法,定位到了具体的错误代码。

    private void initConnectionFactory(String redisConfigs) {
        int i = 0;
        String[] redisConfigArray = redisConfigs.split(",");
        for (String redisConfig : redisConfigArray) {
            logger.info("init config redisConfig {}", new Object[]{redisConfig});
            String[] configs = redisConfig.split(":");
            System.out.println(Arrays.toString(configs));
            String host = configs[0];
            int port = Integer.parseInt(configs[1]); // 报错在这一行
            int db = 0;
            int weight = 100;
            String password = configs[2];
            String name = i == 0 ? "master" : ("slave-" + i);
            JedisShardInfo shardInfo = new JedisShardInfo(host, name, port, getTimeout(), weight);
            shardInfo.setPassword(password);
            logger.info("init jedis connection factory, name:{}, host:{}:{}", new Object[] { name, host, port });
            if (i == 0) {
                masterConnectFactory = createConnectionFactory(shardInfo, db);
            } else {
                slaveConnectFactories.add(createConnectionFactory(shardInfo, db));
            }
            i++;
        }
    }

应该是configs数组解析报错了,我们在配置文件中配置的如下

# Redis
test.redis.configs=1.1.1.1:666:123456

也就是上面那个方法,拿到配置,然后按照:进行解析,进行一系列初始化操作。然后我们反向查找代码,向上看。现在的信息还不够,

@Override
    public void afterPropertiesSet() {
        init();
    }

    public void init() {
        if (null != redisConfigs) {
            initConnectionFactory(redisConfigs);
        }
        if (this.getMasterConnectFactory() == null) {
            throw new IllegalArgumentException("Property 'masterConnectFactory' is required");
        }

        hasSlave = null != getSlaveConnectFactories() && !getSlaveConnectFactories().isEmpty();
        if (hasSlave) {
            slaves = getSlaveConnectFactories().size();
        }
    }

只有这里调用了,也就是这个类拿到的值就是已经获取到的配置。我们现在的错误应该就是获取到的配置就有问题,也就是说我们需要找到哪里是获取配置的地方。我们找哪里new的这个类。

@Override
public void afterPropertied(MSRedisConnectionFactory t) {
     t.init();
}

@Override
public MSRedisConnectionFactory doCreateComponent(String k) {
     return new MSRedisConnectionFactory();
}

这里是new这个类,然后调用init方法,我们继续找。

@Override
    public void afterGetProps(String k, Map<String, Object> props) {
        String configs = (String) springPropertyResolver.resolveDefaultValue(k
                + ".redis.configs");
                + 
        boolean usepool = true;

        ConnectionFactoryManager factoryManager = new ConnectionFactoryManager(
                configs, usepool, jedisPoolConfig);
        MSRedisConnectionFactory connectionFactory = factoryManager
                .createComponent(k);
        props.put("connectionFactory", connectionFactory);
    }

我们可以看到,这里把我们初始化传的参数,k,拼接成 k.redis.configs,然后调用第三方类,从Spring中获取配置。

文档中给的demo是Spring Boot的,里面的配置文件Srping会自动扫描并读取,我们galileo的项目是Spring MVC的。我们平常写配置文件注入值,是直接在xml中将参数注入到类中。但是这个Redis的初始化是在Spring 中找配置。所以我们就把配置文件注入到Spring里。

<context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"
               ignore-resource-not-found="true"/>

再次启动项目,结果还是同样的错。当时就觉得是不是我没有把配置文件写入到Spring中。于是添加了一个属性,测试一下能不能获取到我的配置值。

@Value("#{test.redis.configs}")
private String value;

结果打印出来的值是对的。我就有点迷,那这么说,这个配置文件已经加载到Spring中,我可以获取到,那么构造方法也可以获取到,那能获取到为什么会数组无法解析呢?这个说不通啊,然后我又详细的看了下是不是我写的配置文件格式不对,多加了空格什么的。结果发现也没有。我就有点迷,没找到问题在哪了。

然后我就在报错的方法前面打印了一下这个值,我习惯输出的时候前面加上########类似的,这样一堆日志中看起来比较显眼。然后我看日志的时候,发现###出现了两行?我第一次以为我看错了,然后我又看了一次,是出现了两行。我以为是项目的问题,我又重启了一次。结果发现他就是两行。然后我还看到了第一次出现的时候,它的初始化是成功的。也就是没有报错,但是出现第二次的时候,报错数组越界。

现在问题比较明显了,这个初始化代码执行了两次,第一次执行的时候,是可以获取到配置的,第二次就获取不到了。我当时的想法就是为什么这个类被初始化了两次,然后就有点迷。我看了看我的这个类的代码,只有一个初始化没有其他的代码。(我这里没有想到父子容器的问题,这个东西我以前完全没有接触过,我思考的全部是他正确是应该加载一次,他为什么加载了两次)然后我就开始查原因。

首先我把这个类的代码都删了,就留了一个空壳子和一句输出。测试了一下,打印了两次。然后我查了查@PostConstruct这个注解被执行了两次,然后有人说什么类不能用@Service注解,要用@Component注解,然后我就试了试,发现不是这个问题。然后我把注解也删了,就一个方法,结果还是初始化了两次。那说明不是我这个类的问题。

那么我觉得应该就是Spring xml配置的问题,让整个类被扫描了两次,然后我就开始找扫描的配置。然后果然我找到了两处。我把一处代码删了,然后就好了。

然后觉得不太对,整体的项目配置文件不应该会出现这种问题(我当时以为是代码有问题),整体的代码也是经过review才可以上线的,所以我就跟铸剑学长说了一下,我说整体的项目被加载了两次,然后我才了解到父子容器这个概念。我才看到两个都有扫描的配置的地方都是配置文件,我只在一个地方添加了我自己redis配置文件的扫描,然后我在两处都添加了配置文件的扫描,再跑起来就正常了。

  • 现在想想我觉得我整体的思路是没错的,遇到了问题先看报错,然后看哪里的问题。发现是Sring获不到配置文件,那就去配置,然后还是不行。就测试能不能获到那个值,然后可以获取到。然后就多看看报错,然后再试试,找一下其他的原因。然后发现被初始化了两次,然后按照我当时的理解,这个是错误的,所以去找为什么初始化了两次。中途遇到了超过我目前知识领域的问题。其实想想有些地方是说不通的。为什么第一次能获取到,第二次获取不到。我当时甚至是以为用一次就被销毁了。因为按照我当时只有那么说才能说的通。至于父子容器,我也看了一点文章,还是要以后慢慢的去理解。
  • 遇到了问题,然后去解决问题,然后去各种折腾才是很好的学习的途径。只有瞎折腾,然后才会不断的试错,这样才能成长。感觉还是很意思的。
博主

Just do it. Now or never.

相关推荐

嗨、骚年、快来消灭0回复。