技术阅读周刊第十一期

news/2024/10/17 6:24:27/

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@Overrideprotected 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}")@OverrideOptional<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}"),})@Overridevoid 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)@Overridevoid 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* */@Beanpublic TaskExecutor simpleAsyncTaskExecutor() {final SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());return taskExecutor;}
}

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

management:  tracing:sampling:probability: 1otlp: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.ppmy.cn/news/1283294.html

相关文章

mybatis的一级缓存使用以及禁用

目录 验证代码如下 mappper 代码 xml 中代码 实际执行代码 执行结果 DefaultSqlSession CachingExecutor BaseExecutor PerpetualCache 总结 禁用一级缓存 mapper 对应的 xml 的 select 查询设置 flushCache 属性为 true MappedStatement 的内部类 Builder 向外部变…

SpringMVC:整合 SSM 上篇

文章目录 SpringMVC - 03整合 SSM 上篇一、准备工作二、MyBatis 层1. dao 层2. service 层 三、Spring 层四、SpringMVC 层五、执行六、说明 SpringMVC - 03 整合 SSM 上篇 用到的环境&#xff1a; IDEA 2019&#xff08;JDK 1.8&#xff09;MySQL 8.0.31Tomcat 8.5.85Maven…

list集合

List集合 List集合的概述 有序集合&#xff08;也称之为序列&#xff09;&#xff0c;用户可以精确的控制列表中的每个元素的插入位置。用户可以通过整数索引访问元素&#xff0c;并搜索列表中的元素 与 Set 集合不同&#xff0c;列表通常允许重复的元素 List 集合的特点 有…

7.7、kali linux环境下搭建DVWA

目录 一、资料下载准备工作 1.1、DVWA源代码下载 二、开启Apache、mysql服务 2.1、下载Apache2文件 2.2、开启Apache2服务 方法一&#xff1a;开启Apache2服务&#xff08;手动&#xff09; 方法二&#xff1a;开启Apache2服务&#xff08;系统自启动&#xff09; 2.3、…

【AI】人工智能本地环境集成安装

目录 1、基础安装 1.1 GPU安装 1.1.1 GPU版本支持 1.1.2 下载CUDA 1.1.3安装CUDA 1.1.4配置环境变量 1.1.5检测CUDA是否安装成功 1.2 CUDNN安装 1.2.1 下载CUDNN 1.2.2 添加配置 1.2.3验证结果 2、pytorch安装

centos 安装oracle 11.2.04 并配置数据库自启动操作记录,一次完成

环境&#xff1a; centos版本7.3&#xff0c;安装的有图形化界面 Oracle11.2.04&#xff0c;之所以选择这个版本是因为网上有人说11其他版本的在安装的过程中会出现这样或那样的问题&#xff0c;下载地址放到文章下面 步骤&#xff0c;按顺序&#xff1a; 1、创建安装Oracle…

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

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

云计算:现代技术的基本要素

众所周知&#xff0c;在儿童教育的早期阶段&#xff0c;幼儿园都会传授塑造未来行为的一些基本准则。 今天&#xff0c;我们可以以类似的方式思考云计算&#xff1a;它已成为现代技术架构中的基本元素。云现在在数字交互、安全和基础设施开发中发挥着关键作用。云不仅仅是另一…