spring rest 容易被忽视的后端服务 chunked 性能问题

简介:

spring boot 容易被忽视的后端服务 chunked 性能问题

标签(空格分隔): springboot springmvc chunked


  • 背景
  • spring boot 创建的默认 spring mvc 项目
  • 集成 JAX-RS 规范框架 Jersey

背景

在之前的一次性能压测的时候我们发现一个细节问题,我们使用 spring boot 创建的 web rest 项目,使用默认 spring mvc 作为 web rest 框架。

这在使用上没有太大问题,但是有一个影响性能的细节问题被发现了,说实话这个问题很难被发现。

spring boot 创建的默认 spring mvc 项目

我们来看一个简单的 demo,我使用 IDEA 创建一个 spring boot 项目,创建过程中没有什么特别的选项需要调整,一路 next 。然后我们创建一个简单的 controller 。

package springboot.demo.controller;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springboot.demo.model.User;

/**
 * Created by plen on 2017/11/25.
 */

@RestController
public class SpringMvcController {

    @RequestMapping("/user/{id}")
    public User hello(@PathVariable  Long id) {

        User user = new User();
        user.setID(id);
        user.setUserName("mvc.");

        return user;
    }
}

再创建一个简单的 model 。

package springboot.demo.model;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * Created by plen on 2017/11/25.
 */
@Data
@EqualsAndHashCode
public class User {
    private Long ID;
    private String userName;
}

然后启动访问这个 controller ,注意看下返回的 http 信息里多了一个 Transfer-Encoding:chunked 。Transfer-Encoding:chunked 在 HTTP 协议里的意思是无法计算 Content-Length 长度,需要分块传输。

这是 spring mvc 的默认 complex object 传输方式,如果我们返回的是一个简单的对象就不会有这个问题。

Transfer-Encoding:chunked 带来的性能问题就是访问一次数据在 http层面看确实是一次 http 请求,而通过 tcp 抓包工具查看会发现多了一次 tcp 传输。

集成 JAX-RS 规范框架 Jersey

解决这个问题两个层面都可以,一种是采用比较粗暴的方式在 servlet 容器层面解决,但是这个会带来一个后果就是当我们计算 complex object 大小的时候会比较复杂而且容易出错,也会影响项目未来的分块传输功能,效果不太好。

还有一种就是在应用层面解决,比较柔性也易于扩展,我们可以集成一个 rest 框架,最好是符合 JAX-RS 规范,本文我们集成 Jersey 框架。

jersey 集成如果通过 @Component 方式那么 jersey 会默认接管所有的 web servlet 请求处理,所以就需要我们手动的配置专门用来处理 jersey servlet 的容器。

spring boot 解决了以前 spring 繁重的配置,提供了 auto config 功能,原来通过 web.xml 配置 servlet 的,现在需要用代码来配置。spring boot 提供了让我们手动注册 servlet bean 的方式。

org.springframework.boot.web.servlet.ServletRegistrationBean

ServletRegistrationBean 可以让我们注册servlet,我们来看下完整代码。

package springboot.demo.config;

import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.servlet.ServletProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * Created by plen on 2017/11/25.
 */
@Component
public class JerseyServletBeanConfig {

    @Bean
    public ServletRegistrationBean jerseyServlet() {

        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new ServletContainer(), "/rest/v1/*");
        registrationBean.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyResourceConfig.class.getName());

        return registrationBean;
    }
}

这和原来在 web.xml 配置的是一样的,设置 routing 地址,设置 Init 初始化参数,对应的 servlet class name 。

所有的 "rest/v1/*" 请求都将被 ServletContainer jersey servlet 容器接管。

package springboot.demo.config;

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
import springboot.demo.controller.JerseyController;

/**
 * Created by plen on 2017/11/25.
 */
public class JerseyResourceConfig extends ResourceConfig {

    public JerseyResourceConfig() {
        register(JerseyController.class);
        register(RequestContextFilter.class);
    }
}

ResourceConfig 其实是一个 jersey Application 类型。这是 jersey 注册 servlet__ 时规定的。

package springboot.demo.controller;

import springboot.demo.model.User;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

/**
 * Created by plen on 2017/11/25.
 */

@Path("/user/")
public class JerseyController {

    @Path("{id}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public User hello(@PathParam("id") Long id) {

        User user = new User();
        user.setID(id);
        user.setUserName("jersey.");

        return user;
    }
}

这是我们应用代码 Controller ,使用 JAX-RS 规范的注解进行设置即可。

这样就解决了 sprng mvc 和 jersey rest 共同存在的问题,我们也不需要将所有的返回 chunked 的接口都改成 JAX-RS 的 rest 服务,只需要将有性能瓶颈的接口改造下即可。




 本文转自 王清培 51CTO博客,原文链接:http://blog.51cto.com/wangqingpei557/2044336,如需转载请自行联系原作者



相关文章
|
11月前
|
前端开发 Java 数据安全/隐私保护
用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证(带前后端源码)全方位全流程超详细教程
文章通过一个简单的SpringBoot项目,详细介绍了前后端如何实现用户登录功能,包括前端登录页面的创建、后端登录逻辑的处理、使用session验证用户身份以及获取已登录用户信息的方法。
1501 2
用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证(带前后端源码)全方位全流程超详细教程
|
Ubuntu 网络安全 数据安全/隐私保护
百度搜索:蓝易云【docker通过dockerfile安装sftp教程。】
现在,你已经通过 Dockerfile 成功安装了 SFTP,并且可以使用指定的用户名和公钥进行远程访问。请确保替换示例中的用户名、密码和公钥为自己的实际值。
274 1
|
8月前
|
监控 编译器 Go
1 行命令引发的Go应用崩溃
这篇文章分析了Go编译时插桩工具导致go build -race竞态检测产生崩溃的原因。
656 179
|
运维 Linux Apache
Docker详解(八)——Docker镜像制作
Docker详解(八)——Docker镜像制作
480 1
|
存储 Java 数据处理
定义和使用String数组
定义和使用String数组
|
NoSQL 安全 Java
Jedis 使用详解(官方原版)
Jedis 使用详解(官方原版)
812 0
|
SQL 监控 Java
Java Web应用中数据库连接池的配置与优化
Java Web应用中数据库连接池的配置与优化
692 1
|
Java 关系型数据库 MySQL
定时任务Quzrtz:Failed to override connection auto commit/transaction isolation
定时任务Quzrtz:Failed to override connection auto commit/transaction isolation
270 0
|
JSON Java fastjson
restTemplate 调用第三方接口获取json数据
restTemplate 调用第三方接口获取json数据
|
JavaScript 数据安全/隐私保护
jQuery 使用 md5 加密效果demo(整理)
jQuery 使用 md5 加密效果demo(整理)