11

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

 3 years ago
source link: https://www.cnblogs.com/wangiqngpei557/p/7898730.html
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.
neoserver,ios ssh client

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 服务,只需要将有性能瓶颈的接口改造下即可。


Recommend

  • 48

    我们在使用Redis时,总会碰到一些redis-server端CPU及内存占用比较高的问题。下面以几个实际案例为例,来讨论一下在使用Redis时容易忽视的几种情形。 一、短连接导致CPU高 某用户反映QPS不高,从监控看CPU确实偏高。...

  • 53

    前言 在我基于 beego 写博客的时候遇到一个很奇怪的问题,那就是在使用 Memory Cache 类型缓存的时候,缓存不生效很是奇怪,但是使用 redis 就没问题。由于时间问题我就没有深究,毕竟那时候实际上...

  • 30

    本文由 dbaplus 社群授权转载。 我们在使用 Redis 时,总会碰到一些 redis-server 端 CPU 及内存占用比较高的问题。下面以几个实际案例为例,来讨论一下在使用 Redis 时容易忽视的几种情形。 一、短连接导致 CPU 高

  • 44
    • 微信 mp.weixin.qq.com 5 years ago
    • Cache

    容易被忽视的MySQL字符集问题?

    在使用MySQL客户端书写SQL语句的时候,我们可以在字符串前边加 _charset_name 的符号,其中的 charset_name 对应着某个具体的字符集,废话不多说...

  • 46
    • 掘金 juejin.im 5 years ago
    • Cache

    Vue中容易被忽视的知识点

    前言 Vue的学习成本和难度不高,这除了和框架本身的设计理念有关之外,还和Vue完善的官方文档有着密不可分的关系,相信有很多的开发者和我一样,很长时间没有仔细去看过官方文档了,最近花时间仔细看了一下官方文档,将里面一些容易忽视的点整理出来和大家分享。...

  • 33
    • www.freebuf.com 5 years ago
    • Cache

    APP调试中容易被忽视的安全项

    本文未经作者同意不得转载。同时,本文所涉及内容均为科普性质,任何个人或组织不得利用本文内容牟利或实施违法行为。 一、前言 Android调试桥 (adb) 是一种命令行工具,可与安卓设备进行通信。

  • 17

    ToB最容易被忽视的几个增长问题笔记侠1小时前如何打造可自运营的ToB产品?“什么是自传播的ToB产品?做自传播,是一个典型的有ToC味道的说法。我们这...

  • 5

    容易被忽视的五个安全环节,比想象中的更危险!-51CTO.COM 容易被忽视的五个安全环节,比想象中的更危险! 作者:安全牛 2022-07-18 13:37:10

  • 13
    • www.cnblogs.com 2 years ago
    • Cache

    c#中容易被忽视的foreach - micDavid

    c#中容易被忽视的foreach 有句俗语:百...

  • 21

    警惕那些常见却又容易被忽视的网络安全威胁 2022-11-29 12:11:25 ​勒索软件、恶意软件、网络钓鱼攻击......现代企业在数字化转型发展中面临着诸多网络安全威胁与挑战,由此造成的危害也时有报道。然而当企业投入...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK