SpringBoot:使用HTTP2+protobuf实现高性能微服务调用(一)服务器端实现

      在当前的企业IT系统建设中,基于HTTP/1.1 + JSON的微服务架构因其实现简单、部署灵活和维护方便等特性而广受青睐,逐渐成为主流选择。然而,随着业务需求的增长,特别是高并发场景的出现,这种传统架构的局限性也日益凸显:
(1)报文转换效率较低:由于JSON格式的解析和序列化过程相对复杂,导致了较高的CPU和内存消耗。
(2)传输数据量较大:JSON文本格式较为冗长,增加了网络传输的数据量,尤其是在处理大批量数据时,这一问题更为显著。
(3)HTTP连接资源占用多:HTTP/1.1协议创建的连接一次只能处理一个请求或响应,若需要同时处理多个请求则需要创建多个连接,导致占用较多的内存和线程资源,容易造成性能瓶颈。
       在这些问题共同影响下,往往会引起计算机资源消耗过高、调用处理时间延长、单机TPS(每秒事务处理数)上限被拉低等一系列性能挑战,影响了微服务应用的整体响应速度和服务质量,难以充分满足对性能有较高要求的应用场景的需求。

     基于HTTP/2.0 + protobuf的微服务调用框架,将很好的解决上述问题,下面将介绍一下如何基于SpringBoot实现HTTP/2.0 + protobuf。

1、服务器端配置

从SpringBoot2.x.x开始都已默认支持HTTP2.0功能,只需要配置中开启即可。

若要支持HTTP2.0+SSL,则使用如下配置

server.http2.enabled=true

 若只想支持HTTP2.0,不想使用SSL,则配置如下

server.http2.enabled=false

且要添加代码,下面以tomcat9举例:

@Bean
public TomcatConnectorCustomizer connectorCustomizer() {
    return (connector) -> connector.addUpgradeProtocol(new Http2Protocol());
}

其它服务器的配置方式,可在SpringBoot的如下文档中找到:

【“How-to” Guides】>【3. Embedded Web Servers】>【3.8. Configure HTTP/2】>【3.8.5. HTTP/2 Cleartext with supported servers】 

配置完后,启动应用,会在日志中查看到如下记录

09:21:00.898 [main] INFO  org.apache.coyote.http11.Http11NioProtocol - The ["http-nio-8080"] connector has been configured to support HTTP upgrade to [h2c]

2、服务器端代码

先给出一个采用JSON报文的常用微服务调用的例子。需要说明的是,对于已有的大量Controller层代码,几乎不用做任何修改,就可以支持HTTP/2.0 + protobuf。

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.test.model.ReqtObj;
import com.test.model.RespObj;

@RestController
public class ObjectController {

	@PostMapping(value = "/object/doTest")
	public RespObj doTest(@RequestBody ReqtObj reqtObj) {

		RespObj resp = new RespObj();
		resp.setNo(101);
		resp.setCode("ERR");
		resp.setMsg("something is wrong");

		return resp;
	}
}
public class ReqtObj {

	private String name;

	private String sex;

	private int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "ReqtObj [name=" + name + ", sex=" + sex + ", age=" + age + "]";
	}

}
public class RespObj {

	private int no;

	private String code;

	private String msg;

	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	@Override
	public String toString() {
		return "RespObj [no=" + no + ", code=" + code + ", msg=" + msg + "]";
	}

}

 通过执行如下命令并查看运行结果:

$ curl -H 'Content-Type: application/json' 127.0.0.1:8080/object/doTest  -d '{"name": "xxxx", "sex": "male", "age": 123}' -s
{"no":101,"code":"ERR","msg":"something is wrong"}

若要支持protobuf,需要在pom.xml中添加如下内容。其中protostuff库的最大特点是不用生成.proto文件,protostuff会自动根据对象生成.proto文件对应的内存结构,从而免去了维护.proto文件的工作。

pom.xml

		<dependency>
			<groupId>com.google.protobuf</groupId>
			<artifactId>protobuf-java</artifactId>
			<version>4.29.3</version>
		</dependency>
		<dependency>
			<groupId>com.dyuproject.protostuff</groupId>
			<artifactId>protostuff-runtime</artifactId>
			<version>1.3.1</version>
		</dependency>
		<dependency>
			<groupId>com.dyuproject.protostuff</groupId>
			<artifactId>protostuff-core</artifactId>
			<version>1.3.1</version>
		</dependency>

java代码

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;

public class ProtoBufTools {

	public static <T> byte[] serialize(T obj) {
		@SuppressWarnings("unchecked")
		Schema<T> schema = (Schema<T>) RuntimeSchema.getSchema(obj.getClass());
		return ProtostuffIOUtil.toByteArray(obj, schema, LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
	}

	public static <T> T deserialize(byte[] data, Class<T> cls) throws Exception {
		T message = cls.getDeclaredConstructor().newInstance();
		Schema<T> schema = RuntimeSchema.getSchema(cls);
		ProtostuffIOUtil.mergeFrom(data, message, schema);
		return message;
	}

	public static byte[] getBytes(InputStream in) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();

		byte[] buf = new byte[1024];
		int len = 0;
		while ((len = in.read(buf)) != -1) {
			bos.write(buf, 0, len);
		}

		return bos.toByteArray();
	}
}
import java.io.IOException;
import java.io.InputStream;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

public class ProtoBufHttpMessageConverter extends AbstractHttpMessageConverter<Object> {

	public ProtoBufHttpMessageConverter() {
		this.setSupportedMediaTypes(Collections.singletonList(new MediaType("application", "protobuf")));
	}

	@Override
	protected boolean supports(Class<?> clazz) {
		return true;
	}

	@Override
	protected void writeInternal(Object t, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {
		byte[] buf = ProtoBufTools.serialize(t);
		outputMessage.getBody().write(buf);
	}

	@Override
	protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {
		InputStream in = inputMessage.getBody();
		try {
			byte[] buf = ProtoBufTools.getBytes(in);
			return ProtoBufTools.deserialize(buf, clazz);
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return null;
	}
}
import java.util.Collections;
import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		ProtoBufHttpMessageConverter converter = new ProtoBufHttpMessageConverter();

		converters.add(converter);
	}
}

创建ProtoBufHttpMessageConverter,处理请求时,将protobuf报文转换成对象,处理响应时,将对象转换成protobuf报文。

3、客户器端测试代码

3.1、使用apacheHttpClient4

pom.xml

		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.14</version>
		</dependency>

代码:

import java.io.InputStream;
import java.nio.charset.Charset;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.HttpClientBuilder;

import com.test.model.ReqtObj;
import com.test.model.RespObj;

public class ProtoBufTester {

	private static void doTest() {
		try {
			HttpClient httpClient = HttpClientBuilder.create().build();

			String url = "http://127.0.0.1:8080/object/doTest";
			HttpPost httpPost = new HttpPost(url);
			httpPost.setHeader("Content-Type", "application/protobuf");
			httpPost.setHeader("Accept", "application/protobuf");  // #1

			ReqtObj reqt = new ReqtObj();
			reqt.setName("PPPPPP");
			reqt.setSex("female");
			reqt.setAge(13);

			byte[] data = ProtoBufTools.serialize(reqt);

			System.out.println("======================" + reqt.toString());
			System.out.println("reqtLen=" + data.length);

			httpPost.setEntity(
					new ByteArrayEntity(data, ContentType.create("application/protobuf", Charset.forName("UTF-8"))));

			HttpResponse httpResponse = httpClient.execute(httpPost);
			InputStream in = httpResponse.getEntity().getContent();

			byte[] buf = ProtoBufTools.getBytes(in);

			System.out.println("respLen=" + buf.length);

			RespObj resp = ProtoBufTools.deserialize(buf, RespObj.class);
			System.out.println("======================" + resp.toString());
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		doTest();
	}
}

#1,这行代码非常关键,如果没有这行代码,服务器端生成的响应报文还会是JSON的

apacheHttpClient4只支持HTTP/1.1,不支持HTTP/2.0,因此若要使用HTTP/2.0,则看下面的代码

3.2、使用reactorHttpClient

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import reactor.core.publisher.Flux;
import reactor.netty.http.HttpProtocol;
import reactor.netty.http.client.HttpClient;

public class ReactorHttpClientTester {

	public static void main(String[] args) {

		ReqtObj reqt = new ReqtObj();
		reqt.setName("PPPPPP");
		reqt.setSex("female");
		reqt.setAge(13);

		try {
			byte[] data = ProtoBufTools.serialize(reqt);

			HttpClient client = HttpClient.create().protocol(HttpProtocol.H2C).headers(headers -> {
				headers.add("Content-Type", "application/protobuf");
				headers.add("Accept", "application/protobuf");
			});

			Flux<ByteBuf> bufFlux = Flux.just(data).map(Unpooled::wrappedBuffer);

			byte[] retData = client.post().uri("http://127.0.0.1:8080/object/doTest").send(bufFlux)
					.responseSingle((resp, bytes) -> {
						System.out.println(resp.status());
						return bytes.asByteArray();
					}).block();

			RespObj resp = ProtoBufTools.deserialize(retData, RespObj.class);
			System.out.println("======================" + resp.toString());
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}
}

      这个例子中HTTP2和protobuf就都支持了。但美中不足的是这个客户端没有跟SpringBoot中的RestTemplate或WebClient相结合,也没有自动做对象到protobuf的相互转换,后续会有文章专门解决这个问题。

参考文档
“How-to” Guides

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/954717.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Facebook 隐私风波:互联网时代数据安全警钟

在社交媒体飞速发展的今天&#xff0c;个人数据的隐私保护已成为全球关注的焦点。作为全球最大的社交平台之一&#xff0c;Facebook面临的隐私问题&#xff0c;尤其是数据泄露事件&#xff0c;频繁引发公众的广泛讨论。从用户信息被滥用到数据泄漏&#xff0c;Facebook的隐私挑…

HTML5 网站模板

HTML5 网站模板 参考 HTML5 Website Templates

Web前端对于登陆注册界面的实现

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>请注册登陆</title> </head> <body&…

Linux初识:【shell命令以及运行原理】【Linux权限的概念与权限管理】

目录 一.shell命令以及运行原理 二.Linux权限的概念与权限管理 2.1Linux权限的概念 sudo普通用户提权 2.2Linux权限管理 2.2.1文件访问者的分类&#xff08;人&#xff09; 2.2.2文件类型和访问权限&#xff08;事物属性&#xff09; 2.2.3文件权限值的表示方法 字符…

UML系列之Rational Rose笔记七:状态图

一、新建状态图 依旧是新建statechart diagram&#xff1b; 二、工作台介绍 接着就是一个状态的开始&#xff1a;开始黑点依旧可以从左边进行拖动放置&#xff1a; 这就是状态的开始&#xff0c;和活动图泳道图是一样的&#xff1b;只能有一个开始&#xff0c;但是可以有多个…

Java内存与缓存

Java内存管理和缓存机制是构建高性能应用程序的关键要素。它们之间既有联系又有区别&#xff0c;理解这两者对于优化Java应用至关重要。 Java 内存模型 Java内存模型&#xff08;JMM&#xff09;定义了线程如何以及何时可以看到其他线程修改过的共享变量的值&#xff0c;并且规…

带头双向循环链表(数据结构初阶)

文章目录 双向链表链表的分类概念与结构实现双向链表定义链表结构链表打印判空申请结点初始化头插尾插头删尾删查找指定位置插入和删除销毁链表 顺序表和链表的分析结语 欢迎大家来到我的博客&#xff0c;给生活来点impetus&#xff01;&#xff01; 这一节我们学习双向链表&a…

持续集成 01|Gitee介绍、Pycharm使用Gitee

目录 一、理论 二、 git的简介与安装 三、Gitee 1、注册网易163邮箱 2、注册Gitee账号 3、git和gitee管理代码工作原理 三、PyCharm安装配置Gitee 四、Pycharm使用Gitee插件的五种场景 1、将 Gitee的新仓库 Checkout&#xff08;检出&#xff09;到 Pycharm中 2、推送…

金融项目实战 05|Python实现接口自动化——登录接口

目录 一、代码实现自动化理论及流程 二、脚本实现的理论和准备工作 1、抽取功能转为自动化用例 2、搭建环境(测试工具) 3、搭建目录结构 三、登录接口脚本实现 1、代码编写 1️⃣api目录 2️⃣script目录 2、断言 3、参数化 1️⃣编写数据存储文件&#xff1a;jso…

【git】-3 github创建远程仓库,上传自己的项目,下载别人的项目

一、如何使用Github 1、创建远程仓库 2、使用github拉取/推送代码 克隆仓库 向远程仓库推送代码-git push 二、上传我们自己的项目到github 方法一&#xff1a;直接上传 方法二&#xff1a;使用git命令 方法三&#xff1a; 将仓库拉取到本地上传 三、下载别人的项目 …

Java算法 数据结构基础 并查集 模版 [洛谷-P3367]

目录 题目地址 题目描述 输入输出样例 并查集模版 介绍 1. 路径压缩&#xff08;Path Compression&#xff09; 2. 按秩合并&#xff08;Union by Rank / Size&#xff09; 代码讲解 操作讲解 时间复杂度分析 应用场景 题目地址 【模板】并查集 - 洛谷 题目描述 输…

PyCharm文档管理

背景&#xff1a;使用PyCharmgit做文档管理 需求&#xff1a;需要PyCharm自动识别docx/xslx/vsdx等文件类型&#xff0c;并在PyCharm内点击文档时唤起系统内关联应用(如word、excel、visio) 设置步骤&#xff1a; 1、file -》 settings -》file types 2、在Files opened i…

卷积神经05-GAN对抗神经网络

卷积神经05-GAN对抗神经网络 使用Python3.9CUDA11.8Pytorch实现一个CNN优化版的对抗神经网络 简单的GAN图片生成 CNN优化后的图片生成 优化模型代码对比 0-核心逻辑脉络 1&#xff09;Anacanda使用CUDAPytorch2&#xff09;使用本地MNIST进行手写图片训练3&#xff09;…

基于springboot的租房网站系统

作者&#xff1a;学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等 文末获取“源码数据库万字文档PPT”&#xff0c;支持远程部署调试、运行安装。 项目包含&#xff1a; 完整源码数据库功能演示视频万字文档PPT 项目编码&#xff1…

创建 WordPress 插件(第一部分):添加管理页面

WordPress 是互联网上最受欢迎的内容管理系统之一。它是用 PHP 创建的&#xff0c;可以处理从博客到商业网站的一切需求。事实上&#xff0c;我们的博客和网站都使用 WordPress。在本文中&#xff0c;我将向你展示如何创建一个 WordPress 插件&#xff0c;该插件会在管理员控制…

「港科技」联手「地平线」打造GPT风格的自动驾驶世界模型:DrivingWorld

摘要 最近在自回归&#xff08;AR&#xff09;生成模型方面的成功&#xff0c;例如自然语言处理中的GPT系列&#xff0c;激发了在视觉任务中复制这一成功的努力。一些研究尝试将这种方法扩展到自动驾驶中&#xff0c;通过构建基于视频的世界模型来生成逼真的未来视频序列和预测…

FPGA工程师成长四阶段

朋友&#xff0c;你有入行三年、五年、十年的职业规划吗&#xff1f;你知道你所做的岗位未来该如何成长吗&#xff1f; FPGA行业的发展近几年是蓬勃发展&#xff0c;有越来越多的人才想要或已经踏进了FPGA行业的大门。很多同学在入行FPGA之前&#xff0c;都会抱着满腹对职业发…

SOME/IP协议详解 基础解读 涵盖SOME/IP协议解析 SOME/IP通讯机制 协议特点 错误处理机制

车载以太网协议栈总共可划分为五层&#xff0c;分别为物理层&#xff0c;数据链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;应用层&#xff0c;其中今天所要介绍的内容SOME/IP就是一种应用层协议。 SOME/IP协议内容按照AUTOSAR中的描述&#xff0c;我们可以更进一步…

为ARM64架构移植Ubuntu20.04换源的发现

在为ARM64架构(RK3566)移植ubuntu20.04的时候发现在更换为国内源之后&#xff0c;无法正常完成apt update,报错为: Ign:25 http://mirrors.aliyun.com/ubuntu focal-updates/main arm64 Packages …

Playwright vs Selenium:全面对比分析

在现代软件开发中&#xff0c;自动化测试工具在保证应用质量和加快开发周期方面发挥着至关重要的作用。Selenium 作为自动化测试领域的老牌工具&#xff0c;长期以来被广泛使用。而近年来&#xff0c;Playwright 作为新兴工具迅速崛起&#xff0c;吸引了众多开发者的关注。那么…