使用protostuff自定义编解码器优化springcloud-feign性能

摘要:
前言Spring Cloudforeign是一种伪RPC方法,用于解决微服务之间的调用。在序列化方面,默认情况下,spring引导中的HttpMessageConverter使用jackson2方法进行序列化和反序列化。所以我决定用原材料代替它。在forge中,protobuff默认提供了一个编解码器,因此您可以参考其他教程来使用它。然而,protostuf没有默认值,因此我们必须自己执行。在手读之前,我们必须首先了解protostuff和foreign提供的protobuff编解码器。毕竟,似乎没有关于protostuff编解码器的在线教程。代码如下:/***@作者黄新宇*@Date2018/10/30 PM 1:24*@描述中的protostuff编解码器foreign call**/publicclassProtostuffHttpMessageConverterextendsAbstractHttpMessageConverter<Object>{privatestaticfinalLoggerlogger=LoggerFactory.getLogger;publicstaticfinalCharsetDEFAULT_CHARSET=Charge.forName;publicstaticfinalMediaTypePROTOBUF;static{PROTOBUF=新媒体类型;}publicProtostuffHttpMessageConverter(){super;}@过载保护工具和支架(等级˂?
前言

Spring Cloud feign是伪RPC方式解决微服务间的调用。翻看FeignCloudFeign源码,可以看到Feign默认使用HttpUrlConnection; 代码在DefaultFeignLoadBalancedConfiguration 的Client.Default。

这里特意说明下,其他替代方式有OKhttp和HTTPClient,这两种方式有连接池,可以减少创建连接的性能损耗,但是多家实测效果表明,HttpUrlConnection的效率上是最高的,这也是feign默认的原因,如果在轻型应用不考虑速度考虑CPU的情况下,可以考虑替换成OKhttp和HttpClient。

在序列化方面,springboot中HttpMessageConverters 默认使用jackson2方式进行序列化和反序列化。 jackson的效率在于GSON和fastjson之上。

正常情况下使用jackson2支持前后端开发基本没有什么问题,但是如果是微服务间频频通信,使用jackson2序列化和反序列化会占用不少系统资源,并且效率较差。 这里有个git地址来对比各种序列化和反序列化框架的性能 https://github.com/eishay/jvm-serializers/wiki,部分内容如下:


Ser Time+Deser Time (ns)
使用protostuff自定义编解码器优化springcloud-feign性能第1张


Size, Compressed size [light] in bytes

 使用protostuff自定义编解码器优化springcloud-feign性能第2张

可见jackson在各种测试中都不占优势,网上现在很多的教程使用protobuf来替换jackson提升feign的性能,但是由于nafos中使用的是更加方便的protostuff,而且由图中可以看出protostuff的性能比protobuf更胜一筹。所以我决定用protostuff来替换。

在feign中,protobuff有默认提供的编解码器,因此参考其他教程使用即可,但是protostuf却没有默认提供,所以我们必须自己动手撸了。

在手撸之前,我们肯定是需要先了解protostuff和feign提供的protobuf编解码器的,毕竟protostuff的编解码器,在网上貌似还没得教程。

Protostuff在这里就不做赘述,可以参考我博客的另一篇文章:https://www.cnblogs.com/tohxyblog/p/8974636.html

通过查看feign提供的protobuff编解码器的源码,可以看到他是继承AbstractHttpMessageConverter<Object>,主要实现了supports,readInternal,writeInternal这三个方法。分别是返回类的校验,解码器,和编码器。

代码如下:

/**
 * @Author 黄新宇
 * @Date 2018/10/30 下午1:24
 * @Description feign调用中protostuff编解码器
 **/
public class ProtostuffHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
    private static final Logger logger = LoggerFactory.getLogger(ProtostuffHttpMessageConverter.class);

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    public static final MediaType PROTOBUF;

    static {
        PROTOBUF = new MediaType("application", "x-protobuf", DEFAULT_CHARSET);
    }

    public ProtostuffHttpMessageConverter() {
        super(new MediaType[]{PROTOBUF, MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML});
    }


    @Override
    protected boolean supports(Class<?> aClass) {
       return Object.class.isAssignableFrom(aClass);
    }

    @Override
    protected Object readInternal(Class<?> aClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        MediaType contentType = inputMessage.getHeaders().getContentType();
        if (contentType == null) {
            contentType = PROTOBUF;
        }
        if (!PROTOBUF.isCompatibleWith(contentType)) {
            logger.error("不支持的解码格式,请用x-protobuf作为contentType");
        }

        try {
            return ProtoUtil.deserializeFromByte(inputMessage.getBody(),aClass);
        } catch (Exception var7) {
            throw new HttpMessageNotReadableException("Could not read Protobuf message: " + var7.getMessage(), var7);
        }
    }

    @Override
    protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        MediaType contentType = outputMessage.getHeaders().getContentType();
        if (contentType == null) {
            contentType = PROTOBUF;
        }

        Charset charset = contentType.getCharset();
        if (charset == null) {
            charset = DEFAULT_CHARSET;
        }

        if (!PROTOBUF.isCompatibleWith(contentType)) {
            logger.error("不支持的编码格式,请用x-protobuf作为contentType");
        }


        FileCopyUtils.copy(ProtoUtil.serializeToByte(object), outputMessage.getBody());

    }
}

用法:

添加如下配置,ProtostuffHttpMessageConverter在nafos.remote.feign.ProtostuffHttpMessageConverter下

使用protostuff自定义编解码器优化springcloud-feign性能第3张View Code

然后在需要用protostuff的feignClient上注明configuration = ProtoFeignConfiguration.class)即可

@FeignClient(value = "service",path ="/nafosRemoteCall/test" , configuration = ProtoFeignConfiguration.class)

详细代码请参考nafos-network: https://gitee.com/huangxinyu/BC-NETTYGO


使用protostuff自定义编解码器优化springcloud-feign性能第4张

NAFOS

一个基于netty的轻量级高性能服务端框架。 

简介

nafos是一个基于netty的高性能服务器框架,其目的在于易上手,易扩展,让开发人员更致力于业务开发。 在前后端分离的web架构上,或者APP,手游,nafos都是一个很不错的选择。

除此之外,sample中也给出了超简单的扩展方案,使得nafos在分布式扩展上能更胜一筹。

文档

特点

    • 1、简单易用:通过简单的配置文件即可建立完善的启动方案,然后就可以开心的关注业务代码了;
    • 2、串行设计 :单用户的所有请求都是串行进行,完美解决单用户并发问题,减少锁的使用;
    • 3、高性能:网络层采用netty作为中间件,同等配置及优化条件下,相比tomcat性能可提升一倍;
    • 4、易扩展:整合了springBoot,可完美支持spring大家族系列;
    • 5、强兼容: 可单机同时支持HTTP,TCP,websocket等服务,小规模应用下不用多开占用资源;
    • 6、工具类丰富:封装所有开发中常见工具类可直接调用;
    • 7、房间策略:封装常见游戏的房间策略,开房,比赛,聊天可直接调用,无需多写;
    • 8、模块化:多个模块之间相互解耦,喜欢哪个用哪个,不喜欢直接丢弃。
    • 9、脚本支持:内有现成的shell脚本可以直接使用,开关机,数据库备份等;
    • 10、自带分布式限流器,有IP策略和总流量策略等漏桶限流,抵御攻击。
    • 11、自带protostuff的feign编解码器,操作简单的同时可以极大程度优化feign端对端的通信问题。
    • 12、封装了kafa和rabbitMQ,工具类一般超简单使用,无需关注内部业务;
    • 13、丰富教程:除了详细文档外,在sample模块中还有多模块使用案例,开发无忧~

免责声明:文章转载自《使用protostuff自定义编解码器优化springcloud-feign性能》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇解决Maven项目构建后配置文件在target文件夹中不存在的问题根据IP查询所在城市接口(查询用户所在城市)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C#穿透session隔离———Windows服务启动UI交互程序 be

在Windows服务里面启动其他具有界面的应用程序,需要穿透session隔离,尝试了很多种方法,都可行,现在一一列举下来,并写下几个需要注意的地方。 需要注意的地方 首先要将服务的Account属性设置为LocalSystem,安装服务后的登录身份则为本地系统账户         再一个需要注意的是不要把Windows服务的程序放在C:\Users\Ad...

乱码

以前一直处理乱码的问题,总是how,不知道why. 现在总结:1.jsp的编码方式:pageEncodiing; 它是负责页面编码的,与发生请求没有关系.(可是为什么实际测试的时候发现它和发送请求的编码有关系呢?继续看下边!) 2.jsp发送给web servlet容器的编码 contextType 的charset. 如果不指定,charset=page...

c++与C# winform的消息通讯--(结构体与byte数组的使用)

转载:http://www.cnblogs.com/lizhaoduo/p/3870809.html 近期正在做一个蓝牙驱动的使用程序,其中有一块从c++发送数据到C#的部分,网上查了很多资料,大多都是介绍如何通过调用函数获取用户数据。并且在消息发送中,很少介绍如何发送一个结构体,并且结构体里面有 byte数组(硬件开发常用)等如何进行处理。 首先c++里...

flask第30篇——宏macro和import标签

宏是Jinja2特有的,像Django则没有这个。 先新建一个项目macroDemo: 然后在templates文件夹中新建index.html文件,并在代码中返回渲染后的文件: 然后回到index.html,现在假设我们要写一个登录的表单: 代码: <!DOCTYPE html><html lang="en"><he...

C# 互操作性入门系列(二):使用平台调用调用Win32 函数

本文转载自:https://www.cnblogs.com/zhili/archive/2013/01/21/PInvoke.html 本专题概要: 引言 如何使用平台调用Win32 函数——从实例开始 当调用Win32函数出错时怎么办?——获得Win32函数的错误信息 小结 一、引言 上一专题对.NET 互操作性做了一个全面的概括,其中讲到.NET平...

protostuff的坑

1、Protostuff 序列化/反序列化集合等对象 定义一个包装类: public class SerializeDeserializeWrapper<T> { private T data; public static <T> SerializeDeserializeWrapper<T> buil...