Vert.X拆包问题解决方案

最近项目中用Vert.X做了一层数据库代理,用来转发mysql的Query请求,并在代理中拦截sql,对sql进行改写,控制查询返回数据。

这里面就遇到了Vert.X的拆包问题,这个问题是Socket自带的问题,但并未找到Vert.X中对于拆包问题的较好解决方案。尝试过官方文档及各类博客中的方式,并未有效解决。

问题情景:

查询SQL太长,超过了Vert.X的缓存空间,导致一个SQL通过2+个包扔过来了。

解决方式:

1.增大Vert.X的缓存空间 【但其实不太理想,假如SQL无限大,你品品】

NetServerOptions serverOptions = new NetServerOptions()
                .setRegisterWriteHandler(true)
                .setLogActivity(true)
                .setSendBufferSize(1024 * 30)
                .setReceiveBufferSize(1024 * 30);

NetClientOptions clientOptions = new NetClientOptions()
                .setRegisterWriteHandler(true)
                .setLogActivity(true)
                .setSendBufferSize(1024 * 30)
                .setReceiveBufferSize(1024 * 30);

2.所以最好是不管缓存多大,解决拆包后的合并最好,代码如下。

// 定义个缓存空间,用来存放拆包后的多个包
final ByteArrayOutputStream cacheByteArray = new ByteArrayOutputStream();

clientSocket.handler(buffer -> {
            try {
                log.info("接收的信息:\n" + buffer.toString(Charset.forName("UTF-8")));
                log.info("原始数据包长度:" + buffer.length());
                byte[] bufferBytes = buffer.getBytes();
                // 只判断接收到的是不是mysql查询sql
                if (isMysqlProtocolQuery(bufferBytes) || cacheByteArray.size() > 0) {
                    log.info("是mysql Query查询");
                    // 处理拆包 begin
                    // 判断缓存空间中是否已经有部分包存在
                    if (cacheByteArray.size() > 0) {
                        byte[] cacheBufferBytes = cacheByteArray.toByteArray();
                        // 得到Mysql SQL的完整长度(这个其实要解析mysql的协议)
                        int inSqlBytesLength = NativeUtils.decodeMysqlThreeByteInteger(bufferBytes);
                        // SQL完整长度减去已经缓存好的上一个包的长度得到剩余长度
                        int lastSqlLength = inSqlBytesLength - cacheBufferBytes.length + 4;
                        // 如果剩余长度大于0,就接着往cacheByteArray缓存里面扔
                        if (lastSqlLength > 0) {
                            cacheByteArray.write(bufferBytes);
                            buffer = Buffer.buffer(cacheByteArray.toByteArray());
                        }
                    } else {
                        if (isUnpacking(bufferBytes)) { // 如果是拆包
                            log.info("是拆包");
                            cacheByteArray.write(bufferBytes);
                            // 如果是拆包,且不是最后一个包,直接return,接续接下面的包
                            return;
                        }
                    }
                    // 处理拆包 end

                    if (proxyLog != null) proxyLog.setDatasource_query_time_consumimg(System.currentTimeMillis());
                    String noHeadBufferSql = handleDorisSqlPacketHeader(buffer);
                    String handleSql = handleSqlCondition(noHeadBufferSql);
                    byte[] handleSqlBytes = handleSqlPacket(handleSql);
                    buffer = Buffer.buffer(handleSqlBytes);
                    log.info("修改数据包长度:" + buffer.length());
                    log.info("更改的信息:\n" + buffer.toString(Charset.forName("UTF-8")));
                    serverSocket.write(buffer);
                    // 接完所有包之后,清除自定义的缓存空间
                    cacheByteArray.reset();
                } else {
                    serverSocket.write(buffer);
                }
            } catch (Exception e) {
                // 为了避免代理在解析sql时报错,造成的整个链路失常,出现报错时候,直接返回固定错误查询,也为了出现错误时好排查
                log.error("write error: ", e);
                if (proxyLog != null) proxyLog.setSql_execute_status("ERROR");
                long errorTime = System.currentTimeMillis();
                String msg = "proxy error:解析sql异常-"+ errorTime;
                log.error("query error: ", msg);
                String errorSql = "select '" + msg + "' as error_msg";
                Buffer errorQueryBuffer = Buffer.buffer(errorSql);
                serverSocket.write(errorQueryBuffer);
            }
        }
);


/**
 * 判断是否是拆包
 * 这是mysql协议解析是否拆包,需要根据自己的协议情况,判断是否是拆包状态
 */
private boolean isUnpacking(byte[] bufferBytes) {
    int inSqlBytesLength = NativeUtils.decodeMysqlThreeByteInteger(bufferBytes);
    int size = inSqlBytesLength + 4;
    log.info("判断是否是拆包,接收buffer size:" + bufferBytes.length + ",完整sql size:" + size);
    return bufferBytes.length < size;
}

Vert.X貌似不用处理粘包,所以把拆包处理好后,不管扔的数据多长,也不管Vert.X的缓存空间多大,也不管拆了多少包,都能在handler(Buffer)中接收到完整的,再往Mysql Server中扔。

上面的内容写的不是很细,如果有需要可以留言沟通。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值