技术阅读周刊第十一期

news2024/9/28 17:35:26

8e7f8fd63ca2050d0c9bb84b8c6a7ec8.png

技术阅读周刊,每周更新。

历史更新

  • 20231124:第七期

  • 20231201:第八期

  • 20231215:第十‍期

A Comprehensive guide to Spring Boot 3.2 with Java 21, Virtual Threads, Spring Security, PostgreSQL, Flyway, Caching, Micrometer, Opentelemetry, JUnit 5, RabbitMQ, Keycloak Integration, and More! (10/17) | by Jonathan Chevalier | Nov, 2023 | Medium

URL: https://medium.com/@jojoooo/exploring-a-base-spring-boot-application-with-java-21-virtual-thread-spring-security-flyway-c0fde13c1eca#551c

本文讲解了基于最新的 Spring Boot3.2 和 Java 21 所使用到的技术栈

数据库

数据库使用 Postgres15 和 flyway 来管理数据库 schema 的迁移。8fbbd8a4bc2deed561ad191d89cb0a27.png

异常处理

Spring6 实现了新的 RFC9457规范,实现以下接口:

@Slf4j
@ControllerAdvice
@RequiredArgsConstructor
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

  // Process @Valid
  @Override
  protected ResponseEntity<Object> handleMethodArgumentNotValid(
      @NonNull final MethodArgumentNotValidException ex,
      @NonNull final HttpHeaders headers,
      @NonNull final HttpStatusCode status,
      @NonNull final WebRequest request) {
    log.info(ex.getMessage(), ex);

    final List<ApiErrorDetails> errors = new ArrayList<>();

    for (final ObjectError err : ex.getBindingResult().getAllErrors()) {
      errors.add(
          ApiErrorDetails.builder()
              .pointer(((FieldError) err).getField())
              .reason(err.getDefaultMessage())
              .build());
    }

    return ResponseEntity.status(BAD_REQUEST)
        .body(this.buildProblemDetail(BAD_REQUEST, "Validation failed.", errors));
  }


  private ProblemDetail buildProblemDetail(
      final HttpStatus status, final String detail, final List<ApiErrorDetails> errors) {
  
    final ProblemDetail problemDetail =
        ProblemDetail.forStatusAndDetail(status, StringUtils.normalizeSpace(detail));
    
    // Adds errors fields on validation errors, following RFC 9457 best practices.
    if (CollectionUtils.isNotEmpty(errors)) {
      problemDetail.setProperty("errors", errors);
    }
  
   
    return problemDetail;
  }
{
    "type": "about:blank",
    "title": "Bad Request",
    "status": 400,
    "detail": "Validation failed.",
    "instance": "/management/companies",
    "errors": [
        {
            "pointer": "name",
            "reason": "must not be blank"
        },
        {
            "pointer": "slug",
            "reason": "must not be blank"
        }
    ]
}

应用异常

@Getter
public class RootException extends RuntimeException {

  @Serial private static final long serialVersionUID = 6378336966214073013L;

  private final HttpStatus httpStatus;
  private final List<ApiErrorDetails> errors = new ArrayList<>();

  public RootException(@NonNull final HttpStatus httpStatus) {
    super();
    this.httpStatus = httpStatus;
  }

  public RootException(@NonNull final HttpStatus httpStatus, final String message) {
    super(message);
    this.httpStatus = httpStatus;
  }
}

@ExceptionHandler(RootException.class)
public ResponseEntity<ProblemDetail> rootException(final RootException ex) {
  log.info(ex.getMessage(), ex);
  
  // Uses default message, can be adapted to use ex.getMessage().
  final ProblemDetail problemDetail =
      this.buildProblemDetail(
          ex.getHttpStatus(), API_DEFAULT_REQUEST_FAILED_MESSAGE, ex.getErrors());
  
  return ResponseEntity.status(ex.getHttpStatus()).body(problemDetail);
}

{
    "type": "about:blank",
    "title": "Internal Server Error",
    "status": 500,
    "detail": "Request failed.",
    "instance": "/back-office/hello-world"
}

异常降级

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class)
public ProblemDetail handleAllExceptions(final Throwable ex, final WebRequest request) {
  log.warn(ex.getMessage(), ex);

  this.slack.notify(format("[API] InternalServerError: %s", ex.getMessage()));

  return this.buildProblemDetail(HttpStatus.INTERNAL_SERVER_ERROR, API_DEFAULT_ERROR_MESSAGE);
}

{
    "type": "about:blank",
    "title": "Internal Server Error",
    "status": 500,
    "detail": "Something went wrong. Please try again later or enter in contact with our service.",
    "instance": "/back-office/hello-world"
}

当有无法处理的异常时,就需要配置一个兜底的异常。

缓存

<dependency>  
 <groupId>org.springframework.boot</groupId>  
 <artifactId>spring-boot-starter-cache</artifactId>  
</dependency>
public interface CompanyRepository extends JpaRepository<Company, Long> {

  String CACHE_NAME = "company";

  @NonNull
  @Cacheable(value = CACHE_NAME, key = "{'byId', #id}")
  @Override
  Optional<Company> findById(@NonNull Long id);

  @Cacheable(value = CACHE_NAME, key = "{'bySlug', #slug}")
  Optional<Company> findBySlug(String slug);

  @Caching(
      evict = {
        @CacheEvict(value = CACHE_NAME, key = "{'byId', #entity.id}"),
        @CacheEvict(value = CACHE_NAME, key = "{'bySlug', #entity.slug}"),
      })
  @Override
  <S extends Company> @NonNull S save(@NonNull S entity);

  /*
   * This cache implementation is only valid if the table is not
   * frequently updated since it will clear the cache at every update operation
   * If you want to be more performant you can use something like https://github.com/ms100/cache-as-multi
   * */
  @NonNull
  @CacheEvict(cacheNames = CACHE_NAME, allEntries = true)
  @Override
  <S extends Company> List<S> saveAll(@NonNull Iterable<S> entities);

  @Caching(
      evict = {
        @CacheEvict(value = CACHE_NAME, key = "{'byId', #entity.id}"),
        @CacheEvict(value = CACHE_NAME, key = "{'bySlug', #entity.slug}"),
      })
  @Override
  void delete(@NonNull Company entity);

  /*
   * This cache implementation is only valid if the table is not
   * frequently updated since it will clear the cache at every delete operation
   * If you want to be more performant you can use something like https://github.com/ms100/cache-as-multi
   * */
  @CacheEvict(cacheNames = CACHE_NAME, allEntries = true)
  @Override
  void deleteAll(@NonNull Iterable<? extends Company> entities);
}

Spring 提供了标准的缓存接口,即便是后续需要切换到 Redis,使用的 API 和注解都不会发生改变。

线程

Java21 后支持了虚拟线程,几乎可以无限的实现线程,在 Spring Boot 3.2 需要单独开启。

spring.threads.virtual.enabled

可观测性

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
spring:
  endpoints:
    web:
      exposure:
        include: info, health, prometheus, metrics
47a2b41e5f02f32403aef1c66fed76df.png
image.png

注意在生成环境不要暴露管理 API

Trace

<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
  <groupId>net.ttddyy.observation</groupId>
  <artifactId>datasource-micrometer-spring-boot</artifactId>
  <version>${datasource-micrometer.version}</version>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-exporter-otlp</artifactId>
  <version>${opentelemetry-exporter-otlp.version}</version>
</dependency>

同步请求的时候每个请求都会带上 traceIdspanId ,如果是异步请求时候需要配置:spring.reactor.context-propagation=true

如果使用 @Async时:

@Configuration
public class TaskExecutorConfig {

  /*
   * Override default SimpleAsyncTaskExecutor to provide context propagation in @Async function
   * */
  @Bean
  public TaskExecutor simpleAsyncTaskExecutor() {
    final SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
    taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
    return taskExecutor;
  }
}

本地测试时候可以使用 Otel Desktop Viewer

management:  
  tracing:
    sampling:
      probability: 1
  otlp:
    tracing:
      endpoint: http://localhost:4317
ef844879e1f1c20a055ea79efda76353.png
image.png

Rust Vs Go: A Hands-On Comparison

URL: https://www.shuttle.rs/blog/2023/09/27/rust-vs-go-comparison

动手比较 Rust 和 Go

664e13eacef4fc883db9001d55a29730.png
image.png

本文是通过编写一个 web 服务来进行比较的。

  • Go 更加简单易学,同时标准库非常强大,只需要配合 gin+sqlx 这两个第三方库就能实现一个 web 服务

  • Rust也可以快速的构建一个安全的 web 服务,但需要依赖许多第三方库,比如http/JSON/模板引擎/时间处理等

  • 但 Rust 在异常处理方面心智负担更低,代码更容易阅读。

  • 如果是一个初创小团队,使用 Go 的上手难度确实更低;

  • 但如果团队愿意花时间投入到 Rust 中,结合他出色的错误处理,和强大的编译检查,长时间来看会得到更好的效果。

为什么要使用 Go 语言?Go 语言的优势在哪里?- 知乎

URL: https://www.zhihu.com/question/21409296/answer/1040884859

图文并茂,讲解了 G-M-P 各自之间的关系,以及调度模型。

76e96da497713fbb4ba7b9eb41b941e4.png
image.png
  • G: Goroutine,用户创建的协程,图中搬运的砖头。

  • M: Machine,OS 内核的线程的抽象,代表真正执行的资源;对应到就是图中的地鼠,地鼠不能用户直接创建;得是砖头 G 太多,地鼠 M 本身太少,同时还有空闲的小车 P,此时就会从其他地方借一些地鼠 M 过来直到把小车 P 用完为止。

  • P: Processor 处理器,G 只有绑定到 P 才能被调度;P 是图中的小车,由用户设置的 GoMAXPROCS 决定小车的数量。

文章链接:

  • https://blog.canopas.com/golang-14-shorthand-tricks-you-might-not-know-8d8d21954c49

  • https://medium.com/@jojoooo/exploring-a-base-spring-boot-application-with-java-21-virtual-thread-spring-security-flyway-c0fde13c1eca#551c

  • https://www.zhihu.com/question/21409296/answer/1040884859

    往期推荐

    如何给开源项目发起提案

    如何编写一个 Pulsar Broker Interceptor 插件

    老炮新作,大一统的监控探针采集器 cprobe 开源了

    五分钟 k8s 实战-滚动更新与优雅停机

    d4ceee0b26d867abb8ade8c8ab8baf76.gif

    点分享

    2249a8efdc814b1c3e43dd7013c2d634.gif

    点收藏

    ee977054406151dbad443535ad9fe81e.gif

    点点赞

    4f6f0a90d7375be403e0397b2a939ee6.gif

    点在看

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

61.SVN版本控制系统

SVN&#xff08;Subversion&#xff09;是一种集中式版本控制系统&#xff0c;它有一个中央仓库用于存储代码库的完整历史记录。相对于分布式版本控制系统&#xff08;例如 Git&#xff09;&#xff0c;SVN 不支持本地仓库。 一、SVN 安装。 &#xff08;1&#xff09;在windo…

Sharding JDBC 学习了解 - 总览和概念

第一部分&#xff1a;概述 ShardingSphere是一个由一套分布式数据库中间件解决方案组成的开源生态圈&#xff0c;包括Sharding-JDBC、Sharding-Proxy和Sharding-Proxy 3个独立产品。它们都提供了数据分片、分布式事务、数据库编排等功能&#xff0c;适用于Java同构、异构语言、…

【音视频】remb twcc原理

目录 twcc简介 WebRTC REMB 参考文档 twcc简介 TWCC全称是Transport wide Congestion Control&#xff0c;是webrtc的最新的拥塞控制算法。其原理是在接收端保存数据包状态&#xff0c;然后构造RTCP包反馈给发送端&#xff0c;反馈信息包括包到达时间、丢包状态等&#xff…

开源路由工具NextTrace Web

什么是 NextTrace &#xff1f; NextTrace 是一个由 Golang 语言开发的开源可视路由工具。它不仅支持 IPv4 和 IPv6 协议&#xff0c;而且在轻量级的同时&#xff0c;提供了快速、准确的路由信息。不论您是网络管理员、开发者还是普通用户&#xff0c;NextTrace 都是您网络问题…

【English】水果单词小小汇总~~

废物研究生&#xff0c;只要不搞科研干啥都是开心的&#xff0c;啊啊啊啊啊科研要命。作为一个水果怪&#xff08;每天不吃水果就要命的那种哈哈哈哈&#xff09;突然发现竟然就知道什么apple、banana、orange&#xff01;惭愧惭愧&#xff0c;正好兴致正浓&#xff0c;来整理一…

编写第一个APP自动化脚本 appium_helloworld ,将脚本跑起来

一、前置说明 我们把学习 Appium 的第一个脚本称为 appium_helloworld&#xff0c;它用于展示 Appium 的基本用法&#xff0c;验证配置和环境是否正确。 Appium 自动化操作 APP 的基本流程&#xff08;Android平台&#xff09;&#xff1a; 启动 Appium Serveradb 连接设备&…

分类预测 | Matlab实现MTF-CNN-Mutilhead-Attention基于马尔可夫转移场-卷积神经网络融合多头注意力多特征数据分类预测

分类预测 | Matlab实现MTF-CNN-Mutilhead-Attention基于马尔可夫转移场-卷积神经网络融合多头注意力多特征数据分类预测 目录 分类预测 | Matlab实现MTF-CNN-Mutilhead-Attention基于马尔可夫转移场-卷积神经网络融合多头注意力多特征数据分类预测分类效果基本描述程序设计参考…

Python的基本数据类型和数据类型的转换

TOC 数据类型 类型查看 type 可以使用type内置函数查看变量所指的对象类型 a1 b1.0 c"1" d1, e[1] f{1:1} g{1}print(type(a)) print(type(b)) print(type(c)) print(type(d)) print(type(e)) print(type(f)) print(type(g))isinstance **如字面意思,isinstance()…

Flask+Mysql项目docker-compose部署(Pythondocker-compose详细步骤)

一、前言 环境&#xff1a; Linux、docker、docker-compose、python(Flask)、Mysql 简介&#xff1a; 简单使用Flask框架写的查询Mysql数据接口&#xff0c;使用docker部署&#xff0c;shell脚本启动 优势&#xff1a; 采用docker方式部署更加便于维护&#xff0c;更加简单快…

多维时序 | MATLAB实CNN-Mutilhead-Attention卷积神经网络融合多头注意力机制多变量时间序列预测

多维时序 | MATLAB实CNN-Mutilhead-Attention卷积神经网络融合多头注意力机制多变量时间序列预测 目录 多维时序 | MATLAB实CNN-Mutilhead-Attention卷积神经网络融合多头注意力机制多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 多维时序 | …

CUMT--Java复习--文件及IO流

目录 一、文件 1、文件系统和路径 2、File类 3、FilenameFilter接口 二、IO流 1、流的分类 2、流的体系结构 三、字节流 1、InputStream 2、OutputStream 四、字符流 1、Reader 2、Writer 五、过滤流和转换流 1、过滤流 2、转换流 六、序列化 1、对象序列化…

继承易错总结

1.继承会将所有的成员继承下来&#xff0c;但是继承方式限定的是继承下来成员的可见类型(如果是private继承&#xff0c;那么他不论哪里都是不可见的&#xff1b;如果是protected继承在类中是可见的&#xff0c;在类外是不可见的&#xff1b;如果是public继承&#xff0c;在任何…

[机器人-2]:开源MIT Min cheetah机械狗设计(二):机械结构设计

目录 1、四肢朝向的选择 2、电机布局形式的选择 3、电机的选型及测试&#xff08;非常重要&#xff09; 4、结构优化 5、尺寸效应 6、其他 1、四肢朝向的选择 机械狗的结构设计&#xff0c;第一个摆在我们面前的就说四肢的朝向问题&#xff0c;如下图&#xff0c;我们是…

白龙地铁消费项目(地铁消费系统,包括用户端、管理端)

大一学的C#可视化项目文件&#xff0c;所有功能均可使用。可以直接下载 下方是演示照片

Vue 3 Composition API:让组件开发更高效、灵活(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

修改第三方npm包

文章目录 一、前言二、补丁方案2.1、patch-package2.2、pnpm patch 三、换日方案四、总结五、最后 一、前言 在开发过程中&#xff0c;发现某个npm包有Bug&#xff0c;应该怎么办&#xff1f;可以试试下面这2种方案&#xff1a; 代码量少&#xff0c;可以直接修改npm包代码的&…

【计算机四级(网络工程师)笔记】操作系统运行机制

目录 一、中央处理器&#xff08;CPU&#xff09; 1.1CPU的状态 1.2指令分类 二、寄存器 2.1寄存器分类 2.2程序状态字&#xff08;PSW&#xff09; 三、系统调用 3.1系统调用与一般过程调用的区别 3.2系统调用的分类 四、中断与异常 4.1中断 4.2异常 &#x1f308;嗨&#xff…

【LeetCode】每日一题 2023_12_25 不浪费原料的汉堡制作方案(数学,解二元一次方程)

文章目录 刷题前唠嗑题目&#xff1a;不浪费原料的汉堡制作方案题目描述代码与解题思路 结语 刷题前唠嗑 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 题目&#xff1a;不浪费原料的汉堡制作方案 题目链接&#xff1a;1276. 不浪费原料的汉堡制作方案 题…

详解—数据结构—<常用排序>基本实现和代码分析

目录 一.排序的概念及其运用 1.1排序的概念 1.2排序运用​编辑 1.3 常见的排序算法​编辑 二.常见排序算法的实现 2.1 插入排序 2.1.1基本思想&#xff1a; 2.1.2直接插入排序&#xff1a; 2.1.3 希尔排序( 缩小增量排序 ) 2.2 选择排序 2.2.1基本思想&#xff1a; …

大模型工具:LangChain 原理与实战案例

LangChain 是什么&#xff1f; LangChain是一个用于开发由语言模型驱动的应用程序的框架。它使得可以构建以下类型的应用程序&#xff1a; 数据感知&#xff1a;将语言模型与其他数据源连接起来 智能&#xff1a;允许语言模型与其环境进行交互 LangChain的主要价值在于&…