自定义ik分词加载无效的问题分析

问题背景

1.需求

最近有一些需求导致我们需要扩充IK的词库,按照IK的官方走了一遍,这里是官方的一些介绍
IK热更新配置方式
我们的ES版本为7.1.1

2. 使用tomcat提供一个接口

按照文档在tomcat服务中提供一个接口,来将自定义的热词数据写出去,结果在es中不能成功加载这些数据。
项目是一个springboot项目,接口的代码如下

@RequestMapping(value = "/remoteExtDict/select", method = RequestMethod.GET)
  public void select(HttpServletResponse response) {
        List<String> resultList = remoteExtDictMapper.selectRemoteExtDict();
        StringBuilder result = new StringBuilder();
        for (String value : resultList){
            result.append(value+"\n");
        }
        result.delete(result.length()-1,result.length());
        try {
            Long time = remoteExtDictMapper.getMaxTime();
            response.setHeader("Last-Modified", time.toString());
            response.setHeader("ETag",time.toString());
            response.setContentType("text/plain; charset=utf-8");

            String test = "上海堡垒\n陈默涵\n天际漫谈\n大西瓜\n人生如梦\n快手";
            PrintWriter writer = response.getWriter();
            writer.write(test);
            writer.flush();

        } catch (IOException e) {
            log.error("自定义词典更新报错" , e);
        }
    }

在浏览器端和crul请求中返回的结果都比较正常,编码等都是正确设置了的。所以很是疑惑,如果使用es本地文件的方式添加热词是可以的。所以感觉应该是哪个环节有问题。
因为官方建议是使用nginx代理文件的方式,所以决定先用nginx来搞一下看看能不能正确使用,然后再进行对比。

3. 使用nginx代理文件

3.1 nginx 配置
cat server.conf

server {
    listen 88;
    charset utf-8;

   location /analize/ {
	alias /home/deploy/search/log-search/analize_word/;
        access_log /data/nginx/log/analize_word.log api;

    }
}

对应的文件为 /home/deploy/search/log-search/analize_word/test.txt

cat /home/deploy/search/log-search/analize_word/test.txt
钟丽
陈港生
3.2 ES配置
cat elasticsearch-7.1.1/config/analysis-ik/IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 -->
	<entry key="ext_dict"></entry>
	 <!--用户可以在这里配置自己的扩展停止词字典-->
	<entry key="ext_stopwords"></entry>
	<!--用户可以在这里配置远程扩展字典 -->
	<entry key="remote_ext_dict">http://10.26.5.18:8090/search/manager/es/remoteExtDict/select</entry>
	<!--用户可以在这里配置远程扩展停止词字典-->
	<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

重启ES
在修改完后等了一会儿可以在es的日志中看到

[2019-08-15T17:57:51,001][INFO ][o.w.a.d.Monitor          ] [ES01] 重新加载词典...
[2019-08-15T17:57:51,002][INFO ][o.w.a.d.Monitor          ] [ES01] try load config from /home/deploy/search/log-search/elasticsearch-7.1.1/config/analysis-ik/IKAnalyzer.cfg.xml
[2019-08-15T17:29:51,083][INFO ][o.w.a.d.Monitor          ] [ES01] [Dict Loading] http://10.76.7.49:88/analize/test.txt
[2019-08-15T17:29:51,086][INFO ][o.w.a.d.Monitor          ] [ES01] 钟丽
[2019-08-15T17:29:51,086][INFO ][o.w.a.d.Monitor          ] [ES01] 陈港生
[2019-08-15T17:29:51,086][INFO ][o.w.a.d.Monitor          ] [ES01] 重新加载词典完毕...

说明确实成功了。

3.3 增加词汇不生效

接下来出现了比较诡异的东西。
当我再在文件中添加一个词的时候更新就无法成功了。

cat /home/deploy/search/log-search/analize_word/test.txt
钟丽
陈港生
陈默涵

对应的es的日志

[2019-08-15T17:57:51,001][INFO ][o.w.a.d.Monitor          ] [ES01] 重新加载词典...
[2019-08-15T17:57:51,002][INFO ][o.w.a.d.Monitor          ] [ES01] try load config from /home/deploy/search/log-search/elasticsearch-7.1.1/config/analysis-ik/IKAnalyzer.cfg.xml
[2019-08-15T17:57:51,092][INFO ][o.w.a.d.Monitor          ] [ES01] [Dict Loading] http://10.76.7.49:88/analize/test.txt
[2019-08-15T17:57:51,095][INFO ][o.w.a.d.Monitor          ] [ES01] 重新加载词典完毕...

反复试了几次都是这样,感觉很奇怪。冷静下来分析,应该是对数据长度的识别有问题。又去翻了翻IK上的issue。最后找到了这条
顿时觉得和我现在的状态十分的吻合。
简单描述一下,就是nginx默认会对响应进行压缩gzip和分片chuncked

进行验证,当文件中只有两个词,返回的response header是

HTTP/1.1 200 OK
Server: openresty
Date: Thu, 15 Aug 2019 09:49:11 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 17
Last-Modified: Thu, 15 Aug 2019 09:29:05 GMT
Connection: keep-alive
ETag: "5d5525e1-11"
Accept-Ranges: bytes

这里可以看到有Content-Length 这个header

当编程三个词的时候response header变成了这样

HTTP/1.1 200 OK
Server: openresty
Date: Thu, 15 Aug 2019 10:53:39 GMT
Content-Type: text/plain; charset=utf-8
Last-Modified: Thu, 15 Aug 2019 09:57:15 GMT
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
ETag: W/"5d552c7b-1b"
Content-Encoding: gzip

这里没有了Content-Length 这个header,
有了一个Transfer-Encoding:chunked 和一个Content-Encoding:gzip
这两个header是为了提升传输的效率和对内容进行压缩的。
IK插件可能是因为某种原因未对这中情况做支持,只有有Content-Length的时候才能正确的识别。

3.4 修改nginx配置进行验证
server {
    listen 88;
    charset utf-8;

   location /analize/ {
	alias /home/deploy/search/log-search/analize_word/;
        access_log /data/nginx/log/analize_word.log api;
	chunked_transfer_encoding       off;
	gzip off;

    }
}

这个时候再从浏览器请求,观察返回的header

HTTP/1.1 200 OK
Server: openresty
Date: Fri, 16 Aug 2019 03:26:59 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 27
Last-Modified: Thu, 15 Aug 2019 09:57:15 GMT
Connection: keep-alive
ETag: "5d552c7b-1b"
Accept-Ranges: bytes

这个时候有了Conten-Length,当然,如果想要ES加载必须编辑一下这个文件,再增加一些东西。
修改文件

cat /home/deploy/search/log-search/analize_word/test.txt
钟丽
陈港生
陈默涵
流水人间

在ES的日志中等待一会儿,便可以看到


[2019-08-16T11:37:51,001][INFO ][o.w.a.d.Monitor          ] [ES01] 重新加载词典...
[2019-08-16T11:37:51,020][INFO ][o.w.a.d.Monitor          ] [ES01] try load config from /home/deploy/search/log-search/elasticsearch-7.1.1/config/analysis-ik/IKAnalyzer.cfg.xml
[2019-08-16T11:37:51,105][INFO ][o.w.a.d.Monitor          ] [ES01] [Dict Loading] http://10.76.7.49:88/analize/test.txt
[2019-08-16T11:37:51,108][INFO ][o.w.a.d.Monitor          ] [ES01] 钟丽
[2019-08-16T11:37:51,108][INFO ][o.w.a.d.Monitor          ] [ES01] 陈港生
[2019-08-16T11:37:51,108][INFO ][o.w.a.d.Monitor          ] [ES01] 陈默涵
[2019-08-16T11:37:51,108][INFO ][o.w.a.d.Monitor          ] [ES01] 流水人间
[2019-08-16T11:37:51,108][INFO ][o.w.a.d.Monitor          ] [ES01] 重新加载词典完毕...

ok至此,nginx的基于文件的热词更新设置完成。

4.spring boot tomcat的设置

上面把nginx的调通也就找到了原因,知道原因了问题就好解决了。再调试原有的springboot代码,发现返回的header中果然没有Content-Length
研究一下tomcat的相关特性。

在tomcat当中,是使用一个respone buffer的缓存来存储即将发回的数据,如果这个buffer没有使用完,默认的情况下,tomcat使用的就是常规的方式,就是一次性返回,这个时候在response header当中是有Content-Length。如果这个buffer写满了而你还有数据要歇的时候,这个时候就先要进行一次会写,这个时候tomcat的响应就变成了chuncked的模式了。还有一种情况,如果显示的进行flush操作,就是response.gerWriter wirter.flush也会导致变成chuncked响应。
因此,在springboot tomcat项目中,需要将buffer size设置的更大(具体看自己的业务需求),同时不能显式的去调用flush操作。 这样就ok了。

对应的代码

@RequestMapping(value = "/remoteExtDict/select", method = RequestMethod.GET)
  public void select(HttpServletResponse response) {
        List<String> resultList = remoteExtDictMapper.selectRemoteExtDict();
        StringBuilder result = new StringBuilder();
        for (String value : resultList){
            result.append(value+"\n");
        }
        result.delete(result.length()-1,result.length());
        try {
            Long time = remoteExtDictMapper.getMaxTime();
            response.setHeader("Last-Modified", time.toString());
            response.setHeader("ETag",time.toString());
            response.setContentType("text/plain; charset=utf-8");
            response.setBufferSize(500000);

            String test = "上海堡垒\n陈默涵\n天际漫谈\n大西瓜\n人生如梦\n快手";
            PrintWriter writer = response.getWriter();
            writer.write(test);
        } catch (IOException e) {
            log.error("自定义词典更新报错" , e);
        }
    }

至此,问题解决

这个其实算是IK插件的一个小小bug,估计后面就该修复了。

参考
https://imququ.com/post/transfer-encoding-header-in-http.html

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值