当前位置: 首页 > news >正文

SpringBoot:使用HTTP2+protobuf实现高性能微服务调用

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

     基于HTTP2.0 + protobuf的微服务调用框架,将很好的解决上述问题,下面将介绍一下如何基于SpringBoot实现HTTP2.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报文的常用微服务调用的例子

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;}@Overridepublic 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;}@Overridepublic 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

		<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.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> {@Overrideprotected boolean supports(Class<?> clazz) {return true;}@Overrideprotected void writeInternal(Object t, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {byte[] buf = ProtoBufTools.serialize(t);outputMessage.getBody().write(buf);}@Overrideprotected 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 {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {ProtoBufHttpMessageConverter converter = new ProtoBufHttpMessageConverter();converter.setSupportedMediaTypes(Collections.singletonList(new MediaType("application", "protobuf")));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");  // #1ReqtObj 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


http://www.mrgr.cn/news/83648.html

相关文章:

  • IntelliJ IDEA 优化设置
  • Vue.js中使用emits完成数据子传父的组件事件
  • Oracle重启后业务连接大量library cache lock
  • Tomcat详解
  • 什么是MVCC
  • 腾讯云AI代码助手编程挑战赛-算法小助手
  • 【Linux】Linux开发:GDB调试器与Git版本控制工具指南
  • JVM:ZGC详解(染色指针,内存管理,算法流程,分代ZGC)
  • 拷贝构造函数
  • 基于深度学习的视觉检测小项目(十二) 使用线条边框和渐变颜色美化界面
  • pytest+request+yaml+allure搭建低编码调试门槛的接口自动化框架
  • 指针的进阶
  • 蓝耘:GPU算力云服务的技术探索与AIGC应用支持
  • 渗透Vulnhub-hackme靶机
  • Windows 11更新之后卡顿 (黑神话掉帧严重)问题探索
  • 聊聊AI Agent
  • windows及linux 安装 Yarn 4.x 版本
  • Linux的基础IO
  • SpringBoot之LazyInitializationBeanFactoryPostProcessor类源码学习
  • 金融项目实战 04|JMeter实现自动化脚本接口测试及持续集成
  • LLMBook 大模型数据集下载地址完整收集
  • Github配置ssh key,密钥配对错误怎么解决?
  • Open FPV VTX开源之第一次出图
  • Day05-后端Web基础——TomcatServletHTTP协议SpringBootWeb入门
  • 【AIDD药物研发】张载熙-生成式AI4药物发现
  • 《自动驾驶与机器人中的SLAM技术》ch8:基于 IESKF 的紧耦合 LIO 系统