56

dubbo坑

 5 years ago
source link: http://tech.dianwoda.com/2018/09/30/dubbokeng/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、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、解决方法

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

二、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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK