dubbo使用redis注册中心异常
kl 人气:0前言
dubbo支持zookeeper,reids,multicast等注册中心注册服务信息,使用redis作为注册中心时,因为reids作为注册中心使用并不广泛,早期reids由于定位内网访问,使用密码验证也不怎么重视,导致框架本身设计缺陷,会有很多坑,如1.没有考虑到带密码验证的redis,2.集群容错模式判断错误 3.不可以设置redisdbindex等。其中部分问题,博主已经提交给dubbo官方仓库了,但是还没有完全解决掉,其实这些问题无需等官方修复,对源码稍加改造就ok了。
1.不支持带密码,设置indexdb的reids
2.5.6以及以前的会有这个问题,最新的版本已经解决了这个问题了,但是还是存在一个坑,就是必须得设置用户名(大家都知道redis验证不需要用户名),如URL的构造方法有如下判断
这会导致,如果只设置了密码,没有设置用户名,就会抛Invalid url, password without username的异常。
解决方法:
1.打开RedisRegistry.java,设置jedispool时判断下,如果设置密码,使用带密码,indexdb入参的构造方法,具体如下:
if(StringUtils.isEmpty(url.getPassword())){ this.jedisPools.put(address, new JedisPool(config, host, port, url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT),null,url.getParameter("db.index",0))); }else { this.jedisPools.put(address, new JedisPool(config, host, port, url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT),url.getPassword(),url.getParameter("db.index",0))); }
2.配置注册中心的时候得把username加上,如:
二,集群容错模式异常
这个问题,不开启服务监控不会有问题,在开启dubbo服务监控后,就会抛异常:Unsupported redis cluster: Failsafe. The redis cluster only supported failover or replicate,问题是由如下图红框内的if判断造成的,因为监控模块服务默认的集群容错模式为Failsafe,而且写死了,不可通过配置更改,如图:
如下图箭头所指为添加了排除监控中心的if判断逻辑:
三,jedis连接池连接的坑
在修改了dubbo后,由于没有更新到修改后本地打包的dubbo依赖,一度报如下异常:
at java.lang.Thread.run(Thread.java:748) Caused by: redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool at redis.clients.util.Pool.getResource(Pool.java:51) at redis.clients.jedis.JedisPool.getResource(JedisPool.java:226) at com.alibaba.dubbo.registry.redis.RedisRegistry.doSubscribe(RedisRegistry.java:342) ... 43 more Caused by: java.util.NoSuchElementException: Unable to validate object at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:506) at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363) at redis.clients.util.Pool.getResource(Pool.java:49) ... 45 more
Unable to validate object的问题网上有各种说法,博主也找了好久的问题,最终通过猜想+读jedis源码+本地debug调试才解决的问题。其实网上的说法都正确,原因是jedis内一段代码导致的,dubbo默认设置了连接池的test.on.borrow为true,所有在拿连接前都会验证一遍,验证的逻辑如下:
如上图,前面两个判断100%不会有问题,网上大多是因为redis服务本身出问题了,ping的时候没有返回PONG。博主这边是以为jedis.isConnected()报错了,但是jedis是个坑,虽然返回了false,但是具体的异常信息并没有抛出来,其实这个地方,具体的异常:redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required.。很明显是reids密码验证失败了。因为一开始修改的dubbo密码设置没有依赖到
四,服务超过8个应用启动卡死
这个最终的问题还是jedis导致的,dubbo默认初始化的jedis连接池,最大链接数是8个,然后默认的从连接池里拿连接的超时时间为-1,又因为使用redis作为注册中心时,通过订阅暴露的service 的变更来做服务治理的,而jedis里的服务订阅是阻塞占用连接的,也就是说有多少个服务,就会被占用多少个链接。这就导致了,当暴露的服务数量大于8个时,从连接池中获取不到资源,又永不超时,造成应用启动卡死的现象
解决方案:手动设置jedis的最大连接数,如:
spring.dubbo.registry.parameters.max.total = 200
文末结语
使用开源的产品,还是要多读读开源产品代码,至少架构设计,模块划分要了解,这样遇到啥问题,才不会手足无措,才能举一反三。bug或异常一点都不可怕,遇到异常,解决异常就问两个问题。1.在哪里抛出的异常(找到抛异常的代码),2.为什么抛这个异常(找出抛异常的原因,一般有逻辑,如if判断等,没有的逻辑的异常一般都是系统级别的),然后通读下异常周边代码,基本上问题就搞定了
加载全部内容