前提:
我想通过Durid Data Source 来管理Ignite 的Jdbc连接,然后却抱了这个错误
create connection SQLException, url: jdbc:ignite:thin://127.0.0.1:10800, errorCode 0, state 08004
java.sql.SQLException: Handshake failed [driverProtocolVer=ClientListenerProtocolVersion [major=2, minor=5, maintenance=0], remoteNodeProtocolVer=null, err=Handshake error: Can not perform the operation because the authentication is not enabled for the cluster.]
at org.apache.ignite.internal.jdbc.thin.JdbcThinTcpIo.handshake(JdbcThinTcpIo.java:372)
at org.apache.ignite.internal.jdbc.thin.JdbcThinTcpIo.start(JdbcThinTcpIo.java:223)
at org.apache.ignite.internal.jdbc.thin.JdbcThinTcpIo.start(JdbcThinTcpIo.java:144)
at org.apache.ignite.internal.jdbc.thin.JdbcThinConnection.ensureConnected(JdbcThinConnection.java:148)
at org.apache.ignite.internal.jdbc.thin.JdbcThinConnection.<init>(JdbcThinConnection.java:137)
at org.apache.ignite.IgniteJdbcThinDriver.connect(IgniteJdbcThinDriver.java:157)
at com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl.java:156)
at com.alibaba.druid.filter.stat.StatFilter.connection_connect(StatFilter.java:218)
at com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl.java:150)
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1560)
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1623)
at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2468)
环境
ignite 2.6
ignite 不需要用户名密码
jdbc 连接的 代码:(不需要用户名密码)
Class.forName("org.apache.ignite.IgniteJdbcThinDriver");
Connection conn = DriverManager.getConnection("jdbc:ignite:thin://localhost:10800");
DuridDataSource连接的相关配置
spring:
datasource:
ignite:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.apache.ignite.IgniteJdbcThinDriver
url: jdbc:ignite:thin://127.0.0.1:10800
问题追踪
1. 首先我测试过直接通过jdbc连接是可行的,ignite也支持jdbc连接,那么这个问题就跟DuridDataSource相关。
2. 报错的地方在ignite的JdbcThinTcpIo这个类中,接下来我就进行debug为什么会报这个错误
下面是报错的相关的代码片段
BinaryReaderExImpl reader = new BinaryReaderExImpl(null, new BinaryHeapInputStream(read()),
null, null, false);
boolean accepted = reader.readBoolean();
if (accepted) {
if (reader.available() > 0) {
byte maj = reader.readByte();
byte min = reader.readByte();
byte maintenance = reader.readByte();
String stage = reader.readString();
long ts = reader.readLong();
byte[] hash = reader.readByteArray();
igniteVer = new IgniteProductVersion(maj, min, maintenance, stage, ts, hash);
}
else
igniteVer = new IgniteProductVersion((byte)2, (byte)0, (byte)0, "Unknown", 0L, null);
srvProtocolVer = ver;
}
else {
short maj = reader.readShort();
short min = reader.readShort();
short maintenance = reader.readShort();
String err = reader.readString();
ClientListenerProtocolVersion srvProtoVer0 = ClientListenerProtocolVersion.create(maj, min, maintenance);
if (srvProtoVer0.compareTo(VER_2_5_0) < 0 && !F.isEmpty(connProps.getUsername())) {
throw new SQLException("Authentication doesn't support by remote server[driverProtocolVer="
+ CURRENT_VER + ", remoteNodeProtocolVer=" + srvProtoVer0 + ", err=" + err
+ ", url=" + connProps.getUrl() + ']', SqlStateCode.CONNECTION_REJECTED);
}
if (VER_2_4_0.equals(srvProtocolVer) || VER_2_3_0.equals(srvProtocolVer) ||
VER_2_1_5.equals(srvProtocolVer))
handshake(srvProtocolVer);
else if (VER_2_1_0.equals(srvProtocolVer))
handshake_2_1_0();
else {
throw new SQLException("Handshake failed [driverProtocolVer=" + CURRENT_VER +
", remoteNodeProtocolVer=" + srvProtocolVer + ", err=" + err + ']',
SqlStateCode.CONNECTION_REJECTED);
}
}
从上面的代码片段中,boolean accepted = reader.readBoolean(); 如果用DuridDataSource连接,这一行代码返回了false,如果直接通过jdbc连接,返回的是true。这个时候找到了关键点,那么为什么两种连接方式会有不同的结果呢?接下来继续debug比较两种连接方式的差异,最终我发现通过jdbc连接的时候这里的username是空的,然而通过DuridDataSource连接的时候这里的username是“sa”. 通过这个发现,那么肯定是DuridDataSource默认赋值username为“sa”导致的。
既然找到了原因,那就改下配置加上username和password再测试一把,下面是修改后的配置
spring:
datasource:
ignite:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.apache.ignite.IgniteJdbcThinDriver
url: jdbc:ignite:thin://127.0.0.1:10800
username:
password:
改完配置重新测试,问题得到了解决。
那么DuridDataSource是怎么赋值username为“sa”的。这个时候又要重新debug代码进行查找,经过多翻debug发现在DruidDataSourceWrapper这个类中有以下一段代码
@Override
public void afterPropertiesSet() throws Exception {
//if not found prefix 'spring.datasource.druid' jdbc properties ,'spring.datasource' prefix jdbc properties will be used.
if (super.getUsername() == null) {
super.setUsername(basicProperties.determineUsername());
}
if (super.getPassword() == null) {
super.setPassword(basicProperties.determinePassword());
}
if (super.getUrl() == null) {
super.setUrl(basicProperties.determineUrl());
}
if(super.getDriverClassName() == null){
super.setDriverClassName(basicProperties.getDriverClassName());
}
}
这段代码告诉我吗如果配置文件里没有配置username就会从basicProperties里取username (basicProperties.determineUsername())
继续看basicProperties.determineUsername里面的代码,这里已经很明显了如果username为空,驱动类不为空就会赋值username默认值“sa”
public String determineUsername() {
if (StringUtils.hasText(this.username)) {
return this.username;
}
if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName())) {
return "sa";
}
return null;
}