1、场景

1.1、依赖版本

  • avatica-core 1.11.0
  • druid 0.12.0

1.2、问题重现:

使用Avatica JDBC查询语句:SELECT score FROM student WHERE name='小明'

到Druid变成:SELECT score FROM student WHERE name='??'

2、解决过程

思路:检查请求发送前request body -> 检查收到请求后解析的文本

2.1、初步怀疑请求编码所致

初步怀疑请求的编码格式设置不正确。为了方便查看返回的结果是否是乱码,我们使用EXPLAIN PLAN FOR来调试。查看请求日志,可以知道avatica的内部使用了HttpClient实现的。

17:45:17.557 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> POST /druid/v2/sql/avatica/ HTTP/1.1
17:45:17.557 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Content-Length: 241
17:45:17.557 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Content-Type: application/octet-stream
17:45:17.557 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Host: localhost:8082
17:45:17.557 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Connection: Keep-Alive
17:45:17.557 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_131)
17:45:17.557 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Accept-Encoding: gzip,deflate
17:45:17.557 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "POST /druid/v2/sql/avatica/ HTTP/1.1[\r][\n]"
17:45:17.557 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "Content-Length: 241[\r][\n]"
17:45:17.557 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "Content-Type: application/octet-stream[\r][\n]"
17:45:17.557 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "Host: localhost:8082[\r][\n]"
17:45:17.557 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "Connection: Keep-Alive[\r][\n]"
17:45:17.557 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_131)[\r][\n]"
17:45:17.557 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "Accept-Encoding: gzip,deflate[\r][\n]"
17:45:17.557 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "[\r][\n]"
17:45:17.557 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "{"request":"prepareAndExecute","connectionId":"b4490330-c1c1-493a-b40d-a4303698cafa","statementId":1,"sql":"EXPLAIN PLAN FOR SELECT score FROM student WHERE name='[0xe6][0xb4][0x97][0xe5]'","maxRowsInFirstFrame":-1,"maxRowCount":-1}"

可以发现请求的sqlEXPLAIN PLAN FOR SELECT score FROM student WHERE name='小明'发送时变成了EXPLAIN PLAN FOR SELECT score FROM student WHERE name='[0xe6][0xb4][0x97][0xe5]',这是编码乱了吗?其实不是的,我们可以通过指定avatica的HttpClient,来调试请求发送前的数据。

通过复制avatica-core-1.11.0.jar的AvaticaCommonsHttpClientImpl类,改变public byte[] send(byte[] request)的实现,可以改变请求的方式。

同时jdbc调用的时候指定httpclient_impl,即可。

String url = "jdbc:avatica:remote:url=http://localhost:8082/druid/v2/sql/avatica/";
Properties connectionProperties = new Properties();
connectionProperties.setProperty("httpclient_impl", "com.test.MyAvaticaCommonsHttpClientImpl");
conn = DriverManager.getConnection(url, connectionProperties);

最后无论怎么改变send方法的实现,最后返回的结果始终带有??。

17:45:18.907 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "HTTP/1.1 200 OK[\r][\n]"
17:45:18.908 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "Date: Tue, 15 May 2018 09:45:30 GMT[\r][\n]"
17:45:18.908 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "Content-Type: application/json;charset=utf-8[\r][\n]"
17:45:18.908 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "Content-Length: 1641[\r][\n]"
17:45:18.908 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "Server: Jetty(9.3.19.v20170502)[\r][\n]"
17:45:18.908 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "[\r][\n]"
17:45:18.909 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "{"response":"executeResults","missingStatement":false,"rpcMetadata":{"response":"rpcMetadata","serverAddress":"master:8082"},"results":[{"response":"resultSet","connectionId":"b4490330-c1c1-493a-b40d-a4303698cafa","statementId":1,"ownStatement":false,"signature":{"columns":[{"ordinal":0,"autoIncrement":false,"caseSensitive":true,"searchable":false,"currency":false,"nullable":0,"signed":true,"displaySize":-1,"label":"PLAN","columnName":"PLAN","schemaName":null,"precision":-1,"scale":-2147483648,"tableName":null,"catalogName":null,"type":{"type":"scalar","id":12,"name":"VARCHAR","rep":"STRING"},"readOnly":true,"writable":false,"definitelyWritable":false,"columnClassName":"java.lang.String"}],"sql":"EXPLAIN PLAN FOR SELECT score FROM student WHERE name='??'","parameters":[],"cursorFactory":{"style":"LIST","clazz":null,"fieldNames":null},"statementType":"SELECT"},"firstFrame":{"offset":0,"done":true,"rows":[["DruidQueryRel(query=[{\"queryType\":\"scan\",\"dataSource\":{\"type\":\"table\",\"name\":\"student\"},\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},\"virtualColumns\":[],\"resultFormat\":\"compactedList\",\"batchSize\":20480,\"filter\":{\"type\":\"selector\",\"dimension\":\"name\",\"value\":\"??\",\"extractionFn\":null},\"columns\":[\"score\"],\"legacy\":false,\"context\":{},\"descending\":false,\"granularity\":{\"type\":\"all\"}}], signature=[{name:STRING])\n"]]},"updateCount":-1,"rpcMetadata":{"response":"rpcMetadata","serverAddress":"master:8082"}}]}[\n]"
17:45:18.910 [main] DEBUG org.apache.http.headers - http-outgoing-0 << HTTP/1.1 200 OK
17:45:18.910 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Date: Tue, 15 May 2018 09:45:30 GMT
17:45:18.911 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Content-Type: application/json;charset=utf-8
17:45:18.911 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Content-Length: 1641
17:45:18.911 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Server: Jetty(9.3.19.v20170502)

2.2、检查Druid收到的请求以及返回

既然检查发送前的请求没问题,那么接下来检查请求接收后的情况。

通过全局搜索Druid的源码,搜索/druid/v2/sql/avatica/,可以知道我们刚才的请求被DruidAvaticaHandler类接收并处理,最后实际处理的avatica-server,而这里用的是1.10.0版本,通过版本对比,没版本的问题。

通过检查avatica-server的AvaticaJsonHandler类的方法:

public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)

看到不和谐的地方:

final String jsonRequest = new String(rawRequest.getBytes("ISO-8859-1"), "UTF-8");,

UTF-8编码传来的请求居然用ISO-8859-1转换?原来问题就出现在这里。

3、解决方法

3.1、修改avatica-server源码

上github克隆avatica-server 1.10.0的代码,找到AvaticaJsonHandler类的方法:

public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)

修改:

final String jsonRequest = new String(rawRequest.getBytes("ISO-8859-1"), "UTF-8");

=> final String jsonRequest = new String(rawRequest.getBytes("UTF-8"), "UTF-8");

3.2、重新编译avatica-server

运行clean install -Dmaven.test.skip=true -Dcheckstyle.skip=true,重新生成avatica-server的jar

3.3、覆盖Druid的依赖

上Druid所在的服务器,进入lib,把avatica-server-1.10.0.jar备份并覆盖,重启服务。

编译好的jar包下载:https://download.csdn.net/download/yongjian_pan/10417162

Druid.io SQL乱码问题的更多相关文章

  1. Druid.io启用SQL支持

    Druid.io的SQL功能虽然在试验阶段,但是也支持了大部分的功能,而且还可以通过 Avatica JDBC查看请求的json,有助于我们理解Druid.io的语法.Druid.io有个比较坑的是, ...

  2. Druid.io系列(一):简介

    原文链接: https://blog.csdn.net/njpjsoftdev/article/details/52955676 Druid.io(以下简称Druid)是面向海量数据的.用于实时查询与 ...

  3. Druid.io系列(五):查询过程

    原文链接: https://blog.csdn.net/njpjsoftdev/article/details/52956194 Druid使用JSON over HTTP 作为底层的查询语言,不过强 ...

  4. druid.io 海量实时OLAP数据仓库 (翻译+总结) (1)

    介绍 我是NDPmedia公司的大数据OLAP的资深高级工程师, 专注于OLAP领域, 现将一个成熟的可靠的高性能的海量实时OLAP数据仓库介绍给大家: druid.io NDPmedia在2014年 ...

  5. druid.io 海量实时OLAP数据仓库 (翻译+总结) (1)——分析框架如hive或者redshift(MPPDB)、ES等

    介绍 我是NDPmedia公司的大数据OLAP的资深高级工程师, 专注于OLAP领域, 现将一个成熟的可靠的高性能的海量实时OLAP数据仓库介绍给大家: druid.io NDPmedia在2014年 ...

  6. SpringMVC4+MyBatis+SQL Server2014+druid 监控SQL运行情况

    前言 在基于SpringMVC+MyBatis的开发过程中,我们希望能看到自己手写SQL的执行情况,在开发阶段我们可以配置log4j在控制台里基于debug模式查看,那么上线后,在生产声我们想查看SQ ...

  7. druid.io本地集群搭建 / 扩展集群搭建

    druid.io 是一个比较重型的数据库查询系统,分为5种节点 . 在此就不对数据库进行介绍了,如果有疑问请参考白皮书: http://pan.baidu.com/s/1eSFlIJS 单台机器的集群 ...

  8. Druid.io通过NiFi摄取流数据

    NiFi是一个易于使用,功能强大且可靠的系统来处理和分发数据. 本文讲述如何用NiFi将Http的Json数据传到Druid.国外的一篇文章讲到如何用NiFi将推文传到Druid,https://co ...

  9. Druid.io系列(九):数据摄入

    1. 概述 Druid的数据摄入主要包括两大类: 1. 实时输入摄入:包括Pull,Push两种 - Pull:需要启动一个RealtimeNode节点,通过不同的Firehose摄取不同种类的数据源 ...

随机推荐

  1. flex 布局能解决的问题

    flex 布局,可以解决元素在容器中的对齐.方向.顺序,甚至它们是动态的或者不确定大小的新布局模型.Flex容器的主要特征是能够调整其子元素在不同的屏幕大小中能够用最适合的方法填充合适的空间 . 转载 ...

  2. 正确理解 SqlConnection 的连接池机制[转]

    作者: eaglet 转载请注明出处 .net 中通过 SqlConnection 连接 sql server,我们会发现第一次连接时总是很耗时,但后面连接就很快,这个其实和SqlConnection ...

  3. linux 无交互生成ssh rsa免秘证书

    [root@xxx tmp]# man ssh-keygen NAME ssh-keygen - authentication key generation, management and conve ...

  4. [转帖]常见USB种类

    随着 USB Type-C 接口被苹果推上热门话题,那么对于我们普通的消费者来说,各种 USB 接口类型我们知道多少?买一个设备回来我们是否会遇到各种接口各种线用不了的情况呢? 那么我们泪雪网新开的一 ...

  5. [转帖] 固定硬盘接口 U.2和M.2

    U.2接口 U.2接口别称SFF-8639,是由固态硬盘形态工作组织(SSD Form Factor Work Group)推出的接口规范.U.2不但能支持SATA-Express规范,还能兼容SAS ...

  6. yiled(),wait(),sleep()方法区别

    yiled():让步 wait():等待 sleep():休眠 yiled是让步,会使当前线程由运行状态进入到就绪状态,让其他优先级高线程先执行,但是如果是同一优先级的线程,那么谁先执行就不确定了.它 ...

  7. Caffe使用step by step:r-cnn目标检测代码

    深度学习算法火起来之后,基于深度学习各种模型都如雨后春笋一般在各个领域广泛应用. 由于想把深度学习算法应用在在视频目标检测方向,得到一个较好的结果.由于视频数据的复杂性,因此使用深度学习算法在视频中的 ...

  8. 【bzoj5084】hashit 广义后缀自动机+树链的并+STL-set

    题目描述 你有一个字符串S,一开始为空串,要求支持两种操作 在S后面加入字母C 删除S最后一个字母 问每次操作后S有多少个两两不同的连续子串 输入 一行一个字符串Q,表示对S的操作 如果第i个字母是小 ...

  9. codevs1839洞穴勘测

    题目链接:http://codevs.cn/problem/1839/ 题目描述 Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉 ...

  10. ActiveMQ反序列化漏洞(CVE-2015-5254)复现

      0x00 漏洞前言 Apache ActiveMQ是美国阿帕奇(Apache)软件基金会所研发的一套开源的消息中间件,它支持Java消息服务,集群,Spring Framework等.Apache ...