加载中...

使用Spring Boot和Rust生成二维码的性能比较(附代码)

使用Spring Boot和Rust生成二维码的性能比较(附代码)

比较分别使用虚拟线程的SpringBoot和使用Actix框架的Rust来实现QR码生成器API。

微信搜索关注《Java学研大本营》

本文重点比较使用虚拟线程的SpringBoot和使用Actix框架的Rust,来实现QR码生成器API。这两种技术都是成熟的,无需进一步介绍。接下来,让我们直接深入测试设置的细节。

New Image

1 测试设置

1.1 环境

所有测试都在装有16GB RAM的MacBook Pro M1上进行。使用的测试工具是Bombardier的定制版本,支持在请求体中包含随机URL。这些测试使用的软件版本如下:

  • SpringBoot 3.1.3,带有Java v20(启用预览以获取虚拟线程)

  • Rust 1.72.0

1.2 代码

这个QR码生成器应用程序被设计成接收一个JSON请求体,其中包含一个名为"urlToEmbed"的必需参数。该应用程序的主要功能是为指定的URL生成一个QR码,并在HTTP响应中以PNG格式传送QR码。为增加复杂性,该应用程序在HTTPS上运行。

SpringBoot(虚拟线程)

  1. server.port=3000
  2. server.ssl.certificate=/Users/mayankc/Work/source/certs/cert.pem
  3. server.ssl.certificate-private-key=/Users/mayankc/Work/source/certs/key.pem
  1. package com.example.qr;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
  5. import org.springframework.context.annotation.Bean;
  6. import java.util.concurrent.Executors;
  7. @SpringBootApplication
  8. public class QrApplication {
  9.   public static void main(String[] args) {
  10.     SpringApplication.run(QrApplication.class, args);
  11.   }
  12.   @Bean
  13.   public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
  14.     return protocolHandler -> {
  15.       protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
  16.     };
  17.   }
  18. }
  1. package com.example.qr;
  2. import org.springframework.web.bind.annotation.PostMapping;
  3. import org.springframework.web.bind.annotation.RequestBody;
  4. import org.springframework.http.ResponseEntity;
  5. import org.springframework.http.HttpStatus;
  6. import org.springframework.http.HttpHeaders;
  7. import org.springframework.web.bind.annotation.RestController;
  8. import java.util.Optional;
  9. import com.example.qr.QrRequest;
  10. import com.example.qr.QrGenerator;
  11. @RestController
  12. public class QrController {
  13.   @PostMapping("/qr")
  14.   public ResponseEntity handleRequest(@RequestBody QrRequest qrRequest) {
  15.     if(qrRequest.getUrlToEmbed() == null) {
  16.       return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
  17.     }
  18.     try {
  19.       HttpHeaders httpHeaders = new HttpHeaders();
  20.       httpHeaders.add(HttpHeaders.CONTENT_TYPE, "image/png");
  21.       return new ResponseEntity<byte[]>(
  22.          QrGenerator.generateQR(qrRequest.getUrlToEmbed(), 512512), 
  23.          httpHeaders,
  24.          HttpStatus.OK);
  25.     } catch (Exception e) {
  26.       return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
  27.     }
  28.   }
  29. }
  1. package com.example.qr;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import com.google.zxing.BarcodeFormat;
  5. import com.google.zxing.WriterException;
  6. import com.google.zxing.client.j2se.MatrixToImageConfig;
  7. import com.google.zxing.client.j2se.MatrixToImageWriter;
  8. import com.google.zxing.common.BitMatrix;
  9. import com.google.zxing.qrcode.QRCodeWriter;
  10. public class QrGenerator {
  11.   public static byte[] generateQR(String text, int width, int height) throws WriterException, IOException {
  12.     QRCodeWriter qrCodeWriter = new QRCodeWriter();
  13.     BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height);
  14.     ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream();
  15.     MatrixToImageConfig con = new MatrixToImageConfig() ;
  16.     MatrixToImageWriter.writeToStream(bitMatrix, "PNG", pngOutputStream, con);
  17.     byte[] pngData = pngOutputStream.toByteArray();
  18.     return pngData;
  19.   }
  20. }
  1. package com.example.qr;
  2. public class QrRequest {
  3.   private String urlToEmbed;
  4.   public String getUrlToEmbed() {
  5.     return this.urlToEmbed;
  6.   }
  7.   public void setUrlToEmbed(String urlToEmbed) {
  8.     this.urlToEmbed = urlToEmbed;
  9.   }
  10. }

Rust

  1. [package]
  2. name = "actix_qr_generator"
  3. version = "0.1.0"
  4. edition = "2021"
  5. [dependencies]
  6. actix-web = { version = "4", features = ["openssl"] } 
  7. qrcode-generator = "4.1.8"
  8. serde = { version = "1.0", features = ["derive"] }
  9. serde_json = "1"
  10. openssl = { version = "0.10" , features = ["vendored"] }
  1. use actix_web::{web, post, App, HttpServer, HttpResponse, Responder};
  2. use qrcode_generator::QrCodeEcc;
  3. use serde::Deserialize;
  4. use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
  5. #[derive(Deserialize)]
  6. struct QrRequest {
  7.   urlToEmbed: String,
  8. }
  9. #[post("/qr")]
  10. async fn generate_qr(qr_request: web::Json<QrRequest>) -> impl Responder {
  11.   if qr_request.urlToEmbed.is_empty() {
  12.     return HttpResponse::BadRequest().into();
  13.   }
  14.   let result: Vec<u8> = qrcode_generator::to_png_to_vec(qr_request.urlToEmbed.clone(), QrCodeEcc::Low, 512)
  15.     .unwrap();
  16.   return HttpResponse::Ok()
  17.     .content_type("image/png")
  18.     .body(result);
  19. }
  20. #[actix_web::main]
  21. async fn main() -> std::io::Result<()> {
  22.   let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
  23.   builder
  24.     .set_private_key_file("/Users/mayankc/Work/source/perfComparisons/certs/key.pem", SslFiletype::PEM)
  25.     .unwrap();
  26.   builder
  27.     .set_certificate_chain_file("/Users/mayankc/Work/source/perfComparisons/certs/cert.pem")
  28.     .unwrap();
  29.   HttpServer::new(|| App::new().service(generate_qr))
  30.     .bind_openssl("127.0.0.1:3000", builder)?
  31.     .run()
  32.     .await
  33. }
  34. // 注意 ================================================
  35. // 该应用程序已在发布模式下构建。
  36. // =====================================================

2 结果

为了全面评估性能,这里进行了一系列细致的检查。每个检查包括10万个请求,并在10、50和100个并发连接的范围内评估它们的效率。考虑到QR码生成的资源密集型特性,故意保持了稍低的请求量,与其他场景相比。

结果如下:

New Image

New Image

New Image

根据以下公式,还生成了一个得分卡。对于每个测量,获取获胜的差距。如果获胜的差距是:

  • < 5%,不给予任何分数

  • 在5%到20%之间,获胜者得1分

  • 在20%到50%之间,获胜者得2分

  • 50%,获胜者得3分

得分卡如下:

New Image

New Image

推荐书单

《名师讲坛:Java微服务架构实战(SpringBoot+SpringCloud+Docker+RabbitMQ)》

Java微服务架构是当下流行的软件架构设计方案,可以快速地进行代码编写与开发,维护起来也非常方便。利用微架构技术,可以轻松地实现高可用、分布式、高性能的项目结构开发,同时也更加安全。   《名师讲坛:Java微服务架构实战(SpringBoot+SpringCloud+Docker+RabbitMQ)》一共15章,核心内容为SpringBoot、SpringCloud、Docker、RabbitMQ消息组件。其中,SpringBoot 是SpringMVC技术的延伸,使用它进行程序开发会更简单,服务整合也会更容易。SpringCloud是当前微架构的核心技术方案,属于SpringBoot的技术延伸,它可以整合云服务,基于RabbitMQ和GITHUB进行微服务管理。除此以外,该书还重点分析了OAuth统一认证服务的应用。   《名师讲坛:Java微服务架构实战(SpringBoot+SpringCloud+Docker+RabbitMQ)》适用于从事Java开发且有架构与项目重构需求的读者,也适用于相关技术爱好者,同时也可作为应用型高等院校及培训机构的学习教材。

《名师讲坛:Java微服务架构实战(SpringBoot+SpringCloud+Docker+RabbitMQ)》New Imagehttps://item.jd.com/12793864.html

New Image

精彩回顾

6个Spring Boot处理异常的小技巧

Spring Boot是如何处理HTTP请求的?

只需5步,将Spring Boot服务迁移到Kubernetes

精通Spring Autowiring,解决Bean数据冲突

团队协作开发中,5个强大的VS Code插件

微信搜索关注《Java学研大本营》

访问【IT今日热榜】,发现每日技术热点