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

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

作者:王清培(Plen wang) 沪江Java资深架构师


  • 背景
  • 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:chunkedTransfer-Encoding:chunkedHTTP 协议里的意思是无法计算 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 mvcjersey rest 共同存在的问题,我们也不需要将所有的返回 chunked 的接口都改成 JAX-RSrest 服务,只需要将有性能瓶颈的接口改造下即可。

spring rest 容易被忽视的后端服务 chunked 性能问题的更多相关文章

  1. 容易被忽视的后端服务 chunked 性能问题

    容易被忽视的后端服务 chunked 性能问题 标签(空格分隔): springboot springmvc chunked 背景 spring boot 创建的默认 spring mvc 项目 集成 ...

  2. Spring Cloud(二):Web服务客户端之Ribbon

    上文介绍了服务如何通过Eureka实现注册,以及如何从Eureka获取已经注册的服务列表.那么拿到注册服务列表后, 如何进行服务调用?一个简单的实现是可以从被调用服务的实例列表中选择一个服务实例,通过 ...

  3. mormort 土拨鼠,做后端服务那是杠杠的,基于http.sys

    http.sys你可以用 mormort 土拨鼠,做后端服务那是杠杠的,基于http.sys并且还是开源的,作者天天更新代码,非常勤奋,官方论坛提问,回答也快其实,稍微看看,就能玩的挺好的

  4. 使用Ratpack和Spring Boot打造高性能的JVM微服务应用

    使用Ratpack和Spring Boot打造高性能的JVM微服务应用 这是我为InfoQ翻译的文章,原文地址:Build High Performance JVM Microservices wit ...

  5. 基于Spring Cloud和Netflix OSS构建微服务,Part 2

    在上一篇文章中,我们已使用Spring Cloud和Netflix OSS中的核心组件,如Eureka.Ribbon和Zuul,部分实现了操作模型(operations model),允许单独部署的微 ...

  6. Apple使用Apache Mesos重建Siri后端服务

    苹果公司宣布,将使用开源的集群管理软件Apache Mesos,作为该公司广受欢迎的.基于iOS的智能个人助理软件Siri的后端服务.Mesosphere的博客指出,苹果已经创建了一个命名为J.A.R ...

  7. Spring Cloud 入门教程 - 搭建配置中心服务

    简介 Spring Cloud 提供了一个部署微服务的平台,包括了微服务中常见的组件:配置中心服务, API网关,断路器,服务注册与发现,分布式追溯,OAuth2,消费者驱动合约等.我们不必先知道每个 ...

  8. 快速新建简单的koa2后端服务

    既然前端工程化是基于NodeJS,那么选择NodeJs做前后端分离部署也是理所应当的.其实只需要实现静态资源和代理的话,用nginx才是最好的选择,用NodeJS是为了日后能进一步在服务端上实现自动构 ...

  9. vue,vuex的后台管理项目架子structure-admin,后端服务nodejs

    之前写过一篇vue初始化项目,构建vuex的后台管理项目架子,这个structure-admin-web所拥有的功能 接下来,针对structure-admin-web的不足,进行了补充,开发了具有登 ...

随机推荐

  1. pip安装其他包报错

    pip安装时报错  Unknown or unsupported command 'install 一.是否配置了路径 配置了看下面的方法. 二.有多个pip系统不知道调用哪个. 1.where pi ...

  2. Fiddler的安装与使用

    Fiddler是位于客户端和服务器端之间的代理,也是目前最常用的抓包工具之一 .它能够记录客户端和服务器之间的所有 请求,可以针对特定的请求,分析请求数据.设置断点.调试web应用.修改请求的数据,甚 ...

  3. BZOJ2212 [Poi2011]Tree Rotations 线段树合并 逆序对

    原文链接http://www.cnblogs.com/zhouzhendong/p/8079786.html 题目传送门 - BZOJ2212 题意概括 给一棵n(1≤n≤200000个叶子的二叉树, ...

  4. BZOJ3772 精神污染 主席树 dfs序

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3772 题意概括 给出一个树,共n个节点. 有m条互不相同的树上路径. 现在让你随机选择2条路径,问 ...

  5. day7 [id],[is],编码

    老师的笔记: 字典:dic = {'name':'alex'} 1,增 dic['k'] = 'v' 有键值对,则覆盖 setdefault 有键值对,不添加 dic.setdefault('k1', ...

  6. sql查询月的数据

    https://zhidao.baidu.com/question/557935059.html

  7. Python traceback 模块,追踪错误

    Python traceback 模块,追踪错误 import traceback try: your code except: traceback.print_exc()

  8. multiprocssing,threading,多进程多线程初识

    1. multiprocessing 多进程: import multiprocessing p = multiprocessing.Process(target = func,name = &quo ...

  9. LIS+二分法

    http://poj.org/problem?id=3903 数列里是存从小到大排的数,二分也是为了这个服务的,不断更新.而len才是所求长度 #include <iostream> #i ...

  10. Codeforces 852G Bathroom terminal 【Trie树】

    <题目链接> 题目大意: 现在给定出n个字符串,并且进行m此询问,每次询问给出一个匹配串,每次询问都给出该匹配串能够匹配的字符串个数(题目只出现字符'a'~'e').'?'可以看成任意字符 ...