dubbo坑

一、dubbo默认的序列化遇上泛型

1、背景

dubbo默认使用序列化方式是hessian lite,它是基于hession修改而来的,而hessian本身是一种跨语言的高效二进制序列化方式。

2、问题

hessian lite不能很好的支持泛型对象的序列化和反序列化。

因为 java 的泛型擦除,而hessian lite又没有相应的处理,所以无法将数据准确地反序列化为目标类型

所以采用dubbo默认的序列化可能会出现类型转换失败,尤其是在与泛型一起用的时候。


import com.alibaba.com.caucho.hessian.io.Hessian2Input;  
import com.alibaba.com.caucho.hessian.io.Hessian2Output;  
import com.google.common.collect.Maps;  
import org.junit.Test;

import java.io.ByteArrayInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.IOException;  
import java.util.Map;

/** * @author Isen * @date 2018/9/30 16:30 * @since 1.0 */
public class HessianLiteTest {

    @Test
    public void test() throws IOException {
        Map<String, Byte> map = Maps.newHashMap();
        map.put("age", (byte) 18);
        byte[] bytes = serialize(map);
        Map<String, Byte> object = (Map<String, Byte>) deserialize(bytes);
// Byte age = object.get("age");//无法从Integer转为Byte
// System.out.println(age);

        Byte by = (byte)2233;
        bytes = serialize(by);
        Object ob = deserialize(bytes);
        System.out.println("ob" + ob.getClass());//类型是Integer
    }

    private static byte[] serialize(Object obj) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        Hessian2Output hessian2Output = new Hessian2Output(os);
        byte[] buff = null;
        try {
            hessian2Output.writeObject(obj);
            hessian2Output.flushBuffer();
            buff = os.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            hessian2Output.close();
        }
        return buff;
    }

    private static Object deserialize(byte[] buff) throws IOException {
        try {
            ByteArrayInputStream is = new ByteArrayInputStream(buff);
            Hessian2Input hessian2Input = new Hessian2Input(is);
            return hessian2Input.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

Hessian2Input部分源码

public Object readObject(List<Class<?>> expectedTypes) throws IOException {  
    ...
    /* byte int */
    case 0xc0:
    case 0xc1:
    case 0xc2:
    case 0xc3:
    case 0xc4:
    case 0xc5:
    case 0xc6:
    case 0xc7:
    case 0xc8:
    case 0xc9:
    case 0xca:
    case 0xcb:
    case 0xcc:
    case 0xcd:
    case 0xce:
    case 0xcf:
        return Integer.valueOf(((tag - BC_INT_BYTE_ZERO) << 8) + read());
    ...
}

由源码可知,Byte会被转为Integer

所以当provider的方法中的参数或者返回值中含有泛型,可能出现类型转换失败异常。

3、解决方法

在接口的实现中手动添加强制类型转换逻辑。不雅、不灵活、存在样板代码,并且需要接口实现者保障进行了强制类型转换。
避开泛型,使用数组代替集合。但是数组在元素类型管控方面弱于泛型。例如用String[] 代替 List 。
更换序列化方式。


二、dubbo使用kryo序列化

kryo是一种成熟的序列化实现,在Twitter、Yahoo、Hive等众多项目中广泛使用。

dubbo使用kryo作为序列化方式。

1、在协议中配置kryo

<dubbo:protocol name="dubbo" port="20880" serialization="kryo" />

2、注册被序列化的类(非必须,仅仅是更好的发挥kryo性能)

package com.isen;

import com.alibaba.dubbo.common.serialize.support.SerializationOptimizer;  
import com.isen.domain.Foo;  
import java.util.Collection;  
import java.util.LinkedList;  
import java.util.List;

/** * @author Isen * @date 2018/9/30 17:40 * @since 1.0 */
public class SerializationOptimizerImpl implements SerializationOptimizer {

    @Override
    public Collection<Class> getSerializableClasses() {
        List<Class> classes = new LinkedList<Class>();
        classes.add(Foo.class);
        return classes;
    }
}

生产者和消费者两方均要有com.isen.SerializationOptimizerImpl

3、配置序列化优化实现类(依赖于2)

<dubbo:protocol name="dubbo" port="20880" serialization="kryo" optimizer="com.isen.SerializationOptimizerImpl"/>

4、添加maven依赖

生产者和消费者两端均要添加kryo-serializers依赖

<!-- https://mvnrepository.com/artifact/de.javakaffee/kryo-serializers -->  
<dependency>  
  <groupId>de.javakaffee</groupId>
  <artifactId>kryo-serializers</artifactId>
  <version>0.42</version>
</dependency>

如果没有上述依赖在dubbo2.6.2下会报如下错误

Caused by: com.alibaba.dubbo.remoting.RemotingException: Failed to send message Request
Caused by: java.lang.NoClassDefFoundError: com/esotericsoftware/kryo/io/Output

如果在产者和消费者两端均直接添加kryo依赖

<dependency>  
  <groupId>com.esotericsoftware</groupId>
  <artifactId>kryo</artifactId>
  <version>4.0.2</version>
</dependency>

而没有添加kryo-serializers依赖,会报如下错误

Caused by: com.alibaba.dubbo.remoting.RemotingException: Failed to send message Request
Caused by: java.lang.NoClassDefFoundError: Could not initialize class com.alibaba.dubbo.common.serialize.kryo.utils.KryoUtils

三、dubbo调试

如果在provider方开启调试模式,并且执行到断点处,consumer方会报如下错误

Caused by: com.alibaba.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持

全部评论

相关推荐

03-29 14:19
门头沟学院 Java
你背过凌晨4点的八股文么:加油同学,人生的容错率很高,只是一个暑期罢了,后面还有很多机会!
点赞 评论 收藏
分享
从输入URL到页面加载发生了什么:总体来说分为以下几个过程:&nbsp;1.DNS解析&nbsp;2.TCP连接&nbsp;3.发送HTTP请求&nbsp;4.服务器处理请求并返回HTTP报文&nbsp;5.浏览器解析渲染页面&nbsp;6.连接结束。简述了一下各个过程的输入输出作用:以下是对从输入&nbsp;URL&nbsp;到页面加载各过程的输入、输出或作用的一句话描述:DNS&nbsp;解析:&nbsp;输入:用户在浏览器地址栏输入的域名(如&nbsp;www.example.com)。输出:对应的&nbsp;IP&nbsp;地址(如&nbsp;192.168.1.1)。作用:将易于记忆的域名转换为计算机能够识别和用于网络通信的&nbsp;IP&nbsp;地址,以便浏览器与目标服务器建立连接。TCP&nbsp;连接:&nbsp;输入:浏览器获得的服务器...
明天不下雨了:参考一下我的说法: 关键要讲出输入网址后涉及的每一个网络协议的工作原理和作用: 涉及到的网络协议: HTTP/HTTPS协议->DNS协议->TCP协议->IP协议->ARP协议 面试参考回答: 第一次访问(本地没有缓存时): 一般我们在浏览器地址栏输入的是一个域名。 浏览器会先解析 URL、解析出域名、资源路径、端口等信息、然后构造 HTTP 请求报文。浏览器新开一个网络线程发起HTTP请求(应用层) 接着进行域名解析、将域名解析为 IP 地址 浏览器会先检查本地缓存(包括浏览器 DNS 缓存、操作系统缓存等)是否已解析过该域名 如果没有、则向本地 DNS 服务器请求解析; 本地服务器查不到会向更上层的 DNS 服务器(根域名服务器->顶级域名服务器->权威域名服务器询问)递归查询 最终返回该域名对应的 IP 地址。(应用层DNS协议)DNS 协议的作用: 将域名转换为 IP 地址。 由于 HTTP 是基于 TCP 传输的、所以在发送 HTTP 请求前、需要进行三次握手、在客户端发送第一次握手的时候、( 浏览器向服务器发送一个SYN(同步)报文、其中包含客户端的初始序列号。TCP头部设置SYN标志位、并指定客户端端口 同时填上目标端口和源端口的信息。源端口是浏览器随机生成的、目标端口要看是 HTTP 还是 HTTPS、如果是 HTTP 默认目标端口是 80、如果是 HTTPS 默认是 443。(传输层) 然后到网络层:涉及到(IP协议) 会将TCP报文封装成IP数据包、添加IP头部,包含源IP地址(浏览器)和目标IP地址(服务器)。IP 协议的作用: 提供无连接的、不可靠的数据包传输服务。 然后到数据链路层、会通过 ARP 协议、获取目标的路由器的 MAC 地址、然后会加上 MAC 头、填上目标 MAC 地址和源 MAC 地址。 然后到物理层之后、直接把数据包、转发给路由器、路由器再通过下一跳、最终找到目标服务器、然后目标服务器收到客户的 SYN 报文后,会响应第二次握手。 当双方都完成三次握手后、如果是 HTTP 协议、客户端就会将 HTTP 请求就会发送给目标服务器。如果是 HTTPS 协议、客户端还要和服务端进行 TLS 四次握手之后、客户端才会将 HTTP 报文发送给目标服务器。 目标服务器收到 HTTP 请求消息后、就返回 HTTP 响应消息、浏览器会对响应消息进行解析渲染、呈现给用户
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务