ik分词器(es8,13,4)基于Mysql(8.0.16)数据库热词更新实现方法及遇到的问题和解决方案(jdk17)
项目开发过程中需要在elasticsearch中使用ik分词器实现扩展词库的热更新,ik分词器本身支持将扩展词写入文件中进行读取,通过自己搭建服务器访问文件实现实时获取扩展词词库更新数据,但是由于扩展词数据多,文件读取的方式性能低,所以想通过在mysql中建立扩展词库的数据库表,ik分词器通过读取数据库获得扩展词库数据,并实现热更新。
环境信息:elasticsearch 8.13.4,jdk17,mysql 8.0.16
首先从github上下载ik分词器的源码,网址:https://github.com/infinilabs/analysis-ik
在idea中打开源码,更新好maven仓库,代码结构如下图所示。

打开core中java下的Dictionary类,路径如下

添加加载基于Mysql的远程词库的方法loadMySQLExtDict,如下:
private void loadMySQLExtDict(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
Path file = configuration.getPath(getDictRoot(), "jdbc-reload.properties");
prop.load(new FileInputStream(file.toFile()));
conn = DriverManager.getConnection(
prop.getProperty("jdbc.url"),
prop.getProperty("jdbc.user"),
prop.getProperty("jdbc.password")
);
stmt = conn.createStatement();
rs = stmt.executeQuery(prop.getProperty("jdbc.reload.sql"));
while (rs.next()){
String word = rs.getString("word");
_MainDict.fillSegment(word.trim().toCharArray());
}
Thread.sleep(Long.getLong(prop.getProperty("jdbc.reload.internal")));
}catch (Exception e){
logger.error("error:",e);
}finally {
try{
if(rs!=null){
rs.close();
}
if(stmt!=null){
stmt.close();
}
}catch (Exception e){
logger.error("error in finally is : ",e);
}
}
}
在项目的config文件夹中添加数据库配置文件jdbc-reload.properties,目录位置如下:

jdbc-reload.properties内容包含数据库连接url,用户名,密码,查询词库的语句,以及实时访问数据库的时间间隔,具体内容如下:
jdbc.url=jdbc:mysql://localhost:3306/es_dict?serverTimezone=UTC&useSSL=false jdbc.user=root jdbc.password=root jdbc.reload.sql=select word from es_extword jdbc.reload.internal=1000
loadMySQLExtDict方法中的prop是在Dictionary类中定义的全局变量:

为了获取数据库连接需要加载数据库驱动,在Dictionary类中定义静态类获取:
static{
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}

查看Monitor类中的runUnprivileged方法,该方法是ik分词器实时获取远程词库的热更新方法,其中远程词库有更新,需要重新加载词典时,调用了Dictionary.getSingleton().reLoadMainDict();如下:

我们按照这个方式,新建HotDic类,调用该方法,具体如下:

回到Dictionary类中,在loadMainDict方法最后添加调用loadMySQLExtDict方法:

同样在Dictionary类中找到initial方法,在其中添加代码启动HotDict线程:

代码修改完成后,点击项目的pom.xml文件,修改其中elasticsearch的版本号,为自己es的版本号

之后通过maven先clean再package,生成ik分词器jar包

将该压缩文件解压,解压后的文件内容移动至es安装目录下plugins中,替换原有ik文件,

打开文件夹,将mysql驱动器拷贝至该文件夹下

启动ES,此时遇到了各种坑。
问题1:Caused by: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "setContextClassLoader")
启动后控制台报错,查看logs文件夹下的日志信息,报错如下:


在网上搜了一圈这个问题,是因为jdk有一个安全权限设置,提到的方法基本都是jdk8中jre->lib->security文件夹中有一个java.policy文件,在其中加上
permission java.security.AllPermission;
或者加上
permission java.lang.RuntimePermission "setContextClassLoader";
参考文章:
https://blog.csdn.net/BLADCS/article/details/119600373
https://blog.51cto.com/u_15952602/6037786
但是我的jdk17中没有jre,我在jdk安装目录中找到lib 其中的security文件夹中没有java.policy,但是有一个default.policy,我在这个文件中添加权限的设置,不生效,我在该目录中新建java.policy文件,并按照文章中所说的添加权限也不生效,

default.policy:

java.policy

上述配置均不生效。
(⭐解决方法)后来在阅读源码时发现elasticsearch模块的resource中有一个plugin-security.policy,目录如下:

在这个文件中grant中添加,
permission java.lang.RuntimePermission "setContextClassLoader"; 再次运行该错误解决。

问题2:java.sql.SQLNonTransientConnectionException: Could not create connection to database server.
Caused by: java.security.AccessControlException: access denied ("java.net.SocketPermission" "127.0.0.1:3306" "connect,resolve")
启动后控制台报错,查看logs文件夹下的日志信息,报错如下:

网上找了一圈基本都是同样在jdk的安全权限文件中增加一条语句:
permission java.net.SocketPermission "127.0.0.1:3306", "connect,resolve";
但是配置之后不生效,包括在前面问题解决方法中提到的plugin-security.policy文件中修改也不生效,最后参考该文章:https://blog.csdn.net/MRzhenglea/article/details/118328651 以及https://blog.csdn.net/goodluck_mh/article/details/92845023
这篇文章中提到了3中解决方法,前两个都是我前面提到的权限配置,我这里都不生效,最后通过第三种方法,解决该问题,在文章的评论区中看到,这段代码意思应该在ik配置中绕过权限认证,(可能理解的不对)具体如下:
(⭐解决方法)在Dictionary类中找到loadMainDict()方法,在原有调用我们自己写的this.loadMySQLExtDict()方法的地方,对代码进行调整如下,

补充:
参考文章:https://blog.csdn.net/BLADCS/article/details/119600373
在elasticsearch模块的assemblies包中plugin.xml中添加如下内容:

错误问题没有记录,我记得是在ik插件中加入mysql的驱动但是没有生效。
更多推荐




所有评论(0)