Open afredlyj opened 8 years ago
可以参考Jedis,利用TreeMap
实现,哈希算法采用MurmurHash
。
每个Shard都有权重,权重越大,分配的虚拟节点越多,命中的概率也就越大。
ShardedJedis并没有实现节点动态新增和移除,参考文档如下:
项目在新增业务功能之后,数据源一直初始化失败,异常日志如下:
### Cause: java.lang.IllegalArgumentException: For logger [org.logicalcobwebs.proxool] child name [org.logicalcobwebs.proxool.${mysql passed as parameter, may not include '.' after index27
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:104)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:95)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:354)
... 12 more
Caused by: java.lang.IllegalArgumentException: For logger [org.logicalcobwebs.proxool] child name [org.logicalcobwebs.proxool.${mysql passed as parameter, may not include '.' after index27
at ch.qos.logback.classic.Logger.createChildByName(Logger.java:357)
at ch.qos.logback.classic.LoggerContext.getLogger(LoggerContext.java:143)
at ch.qos.logback.classic.LoggerContext.getLogger(LoggerContext.java:45)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:277)
at org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(SLF4JLogFactory.java:156)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:704)
at org.logicalcobwebs.proxool.ConnectionPool.<init>(ConnectionPool.java:113)
at org.logicalcobwebs.proxool.ConnectionPoolManager.createConnectionPool(ConnectionPoolManager.java:93)
at org.logicalcobwebs.proxool.ProxoolFacade.registerConnectionPool(ProxoolFacade.java:109)
at org.logicalcobwebs.proxool.ProxoolDataSource.registerPool(ProxoolDataSource.java:140)
at org.logicalcobwebs.proxool.ProxoolDataSource.getConnection(ProxoolDataSource.java:94)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:80)
at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:66)
at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:279)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:69)
at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:56)
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:267)
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:141)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:105)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:81)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:101)
... 18 more
直觉是配置文件有问题,对比线上和线下配置,线上缺少配置节点mysql.name
,而dataSource配置文件需要该数据:
<property name="alias" value="${mysql.name}"></property>
生成线程dump文件,用VisualVM打开,通过OQL控制台查询
select x from org.logicalcobwebs.proxool.ProxoolDataSource x
找到alias
属性,发现值为${mysql.name}
,也就是说未读取到配置文件的数据,验证了之前的猜想。
新增功能没有改动数据库这块的代码,为什么会有影响?
查看代码提交记录,发现间接引用了两个日志相关的包log4j-over-slf4j
和jcl-over-slf4j
,尝试将这两个包排除,服务启动正常。所以解决办法有两个:
mysql.name
;log4j-over-slf4j
和jcl-over-slf4j
两个依赖。运用JDK的动态代理,如果要创建某个对象(该对象的类是RealSubject)的代理对象,JDK会做如下工作:
使用JDK动态代理时,需要关注两个类:
// InvocationHandler.java
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
其中proxy
为生成的代理类对象,method
为代理类实现的接口方法,args
为调用该方法的参数。另外还有:
// Proxy.java
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
其中,loader
为加载代理类的ClassLoader,interfaces
为该代理类要实现的接口,对代理对象的所有方法调用最终都被转到h
中。
如果想看将代理类的Class对象保存到磁盘,可以使用sun.misc.ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces)
,需要注意的是,Proxy类是被public final
修饰的,包括类和方法。
与JDK提供的动态代理方案相比,CGLIB不需要实现接口。在JDK的方案中,某个类必须有实现的接口,并且生成的代理类直接实现接口的方法,如果RealSubject
中有自己的方法,在动态代理中是无法使用的。
关于以上两种动态代理,这里说得清楚,也是我借鉴的文章。
类初始化包括三个步骤:
根据java虚拟机规范,所有java虚拟机实现必须在每个类或接口被java程序首次主动使用时才初始化。 主动使用有以下6种:
类与接口的初始化不同,如果一个类被初始化,则其父类或父接口也会被初始化,但如果一个接口初始化,则不会引起其父接口的初始化。