技术阅读周刊第十一期

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.mfbz.cn/a/268698.html

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

相关文章

第19章总结

一.Java绘图类 1.Graphics类 Graphics类是所有图形上下文的抽象基类&#xff0c;它允许应用程序在组件以及闭屏图像上进行绘制。Graphics类封装了Java支持的基本绘图操作所需的状态信息&#xff0c;主要包括颜色、字体、画笔、文本、图像等。 2.Graphics2D类 Graphics2…

MFC 工具栏

目录 工具栏概述 工具栏的使用 添加工具栏资源 创建&#xff0c;加载工具栏 设置工具栏停靠 工具栏概述 CToolBarCtrl-父类CWnd&#xff0c;封装了关于工具栏控件的各种操作。 CToolBar一父类CControlBar&#xff0c;封装了关于工具栏的操作&#xff0c;以及和框架窗口的…

【力扣周赛】第 373 场周赛(交换得到字典序最小的数组 ⭐分解质因子+前缀和+哈希表)

文章目录 竞赛链接Q1&#xff1a;2946. 循环移位后的矩阵相似检查竞赛时代码——模拟 2947. 统计美丽子字符串 I竞赛时代码——前缀和暴力枚举 Q3&#xff1a;2948. 交换得到字典序最小的数组竞赛时代码——排序后判断相似题目——1202. 交换字符串中的元素&#xff08;使用并查…

【C++练级之路】【Lv.5】动态内存管理(都2023年了,不会有人还不知道new吧?)

目录 一、C/C内存分布二、new和delete的使用方式2.1 C语言内存管理2.2 C内存管理2.2.1 new和delete操作内置类型2.2.2 new和delete操作自定义类型 三、new和delete的底层原理3.1 operator new与operator delete函数3.2 原理总结3.2.1 内置类型3.2.2 自定义类型 四、定位new表达…

MFC读取文件数据,添加信息到列表并保存到文件

打开并读取文件信息 添加&#xff1a; BOOL infoDlg::OnInitDialog() {CDialogEx::OnInitDialog();// TODO: 在此添加额外的初始化AfxMessageBox("欢迎查看学生信息");SetList();return TRUE; // return TRUE unless you set the focus to a control// 异常: OCX 属…

Spark编程语言选择:Scala、Java和Python

在大数据处理和分析领域&#xff0c;Apache Spark已经成为一种非常流行的工具。它提供了丰富的API和强大的性能&#xff0c;同时支持多种编程语言&#xff0c;包括Scala、Java和Python。选择合适的编程语言可以直接影响Spark应用程序的性能、可维护性和开发效率。在本文中&…

Json和Xml

一、前言 学习心得&#xff1a;C# 入门经典第8版书中的第21章《Json和Xml》 二、Xml的介绍 Xml的含义&#xff1a; 可标记性语言&#xff0c;它将数据以一种特别简单文本格式储存。让所有人和几乎所有的计算机都能理解。 XML文件示例&#xff1a; <?xml version"1.…

自动驾驶学习笔记(二十二)——自动泊车算法

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo开放平台9.0专项技术公开课》免费报名—>传送门 文章目录 前言 感知算法 定位算法 规划算法…

分享70个Java源码总有一个是你想要的

分享70个Java源码总有一个是你想要的 学习知识费力气&#xff0c;收集整理更不易。 知识付费甚欢喜&#xff0c;为咱码农谋福利。 链接&#xff1a;https://pan.baidu.com/s/1s8ZVYHb5B1GgXMlpG-6-Iw?pwd6666 提取码&#xff1a;6666 项目名称 admin、cms、console 等多…

nodejs+vue+微信小程序+python+PHP的4s店客户管理系统-计算机毕业设计推荐

系统的功能结构是系统实现的框架&#xff0c;本系统的主要结构为管理员和用户、员工。管理员的功能为车辆信息管理、用户管理、售后服务管理、售后安排管理、完成售后管理等。 本系统实现了售后的在线申请与处理&#xff0c;方便了用户和管理员、员工三方的利益&#xff0c;提高…

Linux 宝塔mysql莫名其妙数据库不见了恢复数据库

起因&#xff1a;宝塔安装的mysql 线上运行突然表包括库都不见了&#xff0c;想办法恢复数据库 登陆mysql cd /www/server/mysql/binmysql -u root -p查看binlog日志是否打开 show variables like log_%;log_bin如果为 ON 则为开启状态&#xff0c;如果开启了才可以进行下一…

【SD】差异值 生成 同一人物 制作 表情包 【1】

说明&#xff1a;只对AI生成的人物&#xff0c;效果稳定。 Reference差异值 生成表情 首先生成一张图片。 测试命令&#xff1a;1 man,chibi,full body, 模型&#xff1a;envyclarityxl02_v10.safetensors [f6c13197db] 种子&#xff1a;2704867166 》》测试命令&#xff1a…

智能优化算法应用:基于金豺算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于金豺算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于金豺算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.金豺算法4.实验参数设定5.算法结果6.参考文献7.MA…

力扣:51. N 皇后

题目&#xff1a; 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的…

网络7层架构

网络 7 层架构 什么是OSI七层模型&#xff1f; OSI模型用于定义并理解数据从一台计算机转移到另一台计算机&#xff0c;在最基本的形式中&#xff0c;两台计算机通过网线和连接器相互连接&#xff0c;在网卡的帮助下共享数据&#xff0c;形成一个网络&#xff0c;但是一台计算…

Unity重写Inspector简化分组配置文件

Unity重写Inspector简化分组配置文件 重写Inspector创建分组管理配置文件创建修改参数参数对应类工程在我的资源中名为CreateConfig&#xff0c;免费下载 重写Inspector创建分组管理配置文件 创建 修改参数 参数对应类 using UnityEngine;public class GameConfig : Scriptab…

【UML】第13篇 序列图(2/2)——建模的方法

目录 三、序列图建模 3.1 概述 3.2 建模的步骤 3.3 举例说明步骤 1.确定主要场景和流程 2.确定参与的对象 3.绘制序列图 4.注意事项 3.4 特殊的情况 序列图是我个人认为&#xff0c;UML中最重要的图之一。 而且序列图&#xff0c;对于业务建模&#xff0c;也有非常好…

【Filament】立方体贴图(6张图)

1 前言 本文通过一个立方体贴图的例子&#xff0c;讲解三维纹理贴图&#xff08;子网格贴图&#xff09;的应用&#xff0c;案例中使用 6 张不同的图片给立方体贴图&#xff0c;图片如下。 读者如果对 Filament 不太熟悉&#xff0c;请回顾以下内容。 Filament环境搭建绘制三角…

java调用GDAL实现栅格数据的重采样的一种方法

目录 1.关于重采样 1.1概念 1.2用途 1.3常见算法 2.关于GDAL 2.1GDAL中的重采样算法 3.实现重采样 3.1思路 3.2完整代码 3.3使用QGIS验证效果 1.关于重采样 1.1概念 重采样是以原始图像的像元值或者导出的值填充到新的图像的每个像元的的过程。 1.2用途 在地理信…

数据库开发表操作案例的详细解析

2.3.1.4 案例 需求&#xff1a;根据产品原型/需求创建表((设计合理的数据类型、长度、约束) 产品原型及需求如下&#xff1a; 步骤&#xff1a; 阅读产品原型及需求文档&#xff0c;看看里面涉及到哪些字段。 查看需求文档说明&#xff0c;确认各个字段的类型以及字段存储数据…
最新文章