Spring Boot + Open Telemetry 实现 Kafka 追踪

本文将带你了解如何使用 Spring Boot 和 Open Telemetry 为 Kafka 生产者和消费者配置追踪功能。我们会使用 Micrometer 库发送追踪信息,并使用Jaeger来存储和可视化这些数据。Spring Kafka内置了与 Micrometer 的集成,用于 KafkaTemplate 和监听容器。本文还会介绍何配置 Spring Kafka observability (可观察性),以在追踪中添加自定义标签(Tag)。 源码 你可以在 GitHub 上找到完整的源码。Clone 项目后,进入 kafka 目录,按照说明进行操作即可。 依赖 添加如下依赖,其中 Spring Boot Starter 和 Spring Kafka 用于发送或接收消息,Spring Boot Actuator 和 Micrometer Tracing Open Telemetry 桥接器用于自动生成与每条消息相关的追踪,最后是 opentelemetry-exporter-otlp,用于将追踪导出到应用外。 对于本文中的两个示例 Spring Boot 应用,依赖都是相同的。 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-tracing-bridge-otel</artifactId> </dependency> <dependency> <groupId>io.

Spring Boot 与 Gzip 压缩

响应压缩是 Web 应用一种常见的优化手段,通过压缩算法减小传输数据的体积,提高传输效率、节约带宽。客户端接收到数据后,使用相同的算法对数据进行解压从而获取到原始数据。 客户端和服务器需要通过 Header 来协商双方支持的压缩算法。 Accept-Encoding:请求头,告诉服务器客户端支持的压缩算法(多个使用逗号分割)。例如:Accept-Encoding: gzip, deflate。 Content-Encoding:响应头,告诉客户端当前 Payload 使用的编码方式(压缩算法)。例如:Content-Encoding: gzip。 常用的压缩算法如下: gzip deflate br JDK 提供了对 GZIP 压缩算法的实现:GZIPOutputStream 和 GZIPInputStream,我们可以用它们来实现 Gzip 压缩和解压缩。 使用 Gzip 压缩响应 在 Spring Boot 应用中创建一个 Controller,使用 GZIPOutputStream 把一张图片文件(20 KB)压缩后响应给客户端。 package cn.springdoc.demo.web.controller; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; import java.util.zip.GZIPOutputStream; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping public void file (HttpServletRequest request, HttpServletResponse response) throws IOException { // 20.

在 Spring 中使用 DeferredResult 实现长轮询

1、概览 长轮询(Long polling)通常用于在 B/S 架构的应用中保持客户端和服务器的连接,直到信息可用。通常在服务器必须调用下游服务以获取信息并等待结果时使用。 本文将带你了解如何在 Spring MVC 应用中使用 DeferredResult 实现长轮询,以及如何处理错误和超时。 2、使用 DeferredResult 进行长轮询 可以在 Spring MVC 中使用 DeferredResult 来异步处理入站 HTTP 请求。它允许释放 HTTP 工作线程来处理其他入站请求,并将工作转移到另一个工作线程。因此,它可以帮助处理需要较长计算或任意等待时间的请求,提高服务的可用性。 2.1、Publisher 使用 DeferredResult 创建一个 Publisher(发布者)应用。 首先,定义一个 Spring @RestController,它可以使用 DeferredResult,但不会将工作转移到另一个工作线程: @RestController @RequestMapping("/api") public class BakeryController { @GetMapping("/bake/{bakedGood}") public DeferredResult<String> publisher(@PathVariable String bakedGood, @RequestParam Integer bakeTime) { DeferredResult<String> output = new DeferredResult<>(); try { Thread.sleep(bakeTime); output.setResult(format("Bake for %s complete and order dispatched. Enjoy!", bakedGood)); } catch (Exception e) { // .

在 Spring Security 6 中实现动态权限管理

在 Spring Boot 3 之后,Spring Security 现在也升级到 Spring Security 6 了。 Spring Security 6 的用法跟之前比起来还是有很大差异,例如:动态权限定义的方式。 1、权限开发思路 先来说权限开发的思路,当设计好 RBAC 权限之后,具体到代码层面,有两种实现思路: 直接在接口/Service 层方法上添加权限注解,这样做的好处是实现简单,但是有一个问题就是权限硬编码,每一个方法需要什么权限都是代码中配置好的,后期如果想通过管理页面修改是不可能的,要修改某一个方法所需要的权限只能改代码。 将请求和权限的关系通过数据库来描述,每一个请求需要什么权限都在数据库中配置好,当请求到达的时候,动态查询,然后判断权限是否满足,这样做的好处是比较灵活,将来需要修改接口和权限之间的关系时,可以通过管理页面点击几下,问题就解决了,不用修改代码。 有人觉得第二种方案无法做到按钮级别的权限控制,这其实是一个误解。想要做到按钮级别的权限控制,只需要数据库中细化配置即可。 2、具体实践 2.1、旧方案回顾 在 vhr 项目中,通过重写两个类来和实现动态权限的。 第一个类是收集权限元数据的类: @Component public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { //... } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> clazz) { return true; } } 在 getAttributes 方法中,根据当前请求的 URL 地址(从参数 Object 中可提取出来)和根据权限表中的配置,分析出来当前请求需要哪些权限并返回。

WebClient 超时设置

1、概览 WebClient 是一个响应式的 HTTP 客户端,它基于 Reactor 项目提供了函数式 API。 本文将带你了解 WebClient 的超时设置,学习如何正确地设置不同的超时,既包括整个应用程序的全局超时,也包括特定请求的超时。 2、WebClient 和 HTTP 客户端 WebClient 还需要一个 HTTP 客户端库才能正常工作。Spring 为其中一些提供了 内置支持,默认使用的是 Reactor Netty。 大多数配置,包括超时,都可以通过这些客户端完成。 3、通过 HTTP 客户端配置超时 如前所述,在应用中设置不同 WebClient 超时的最简单方法是通过底层 HTTP 客户端进行全局设置。这也是最有效的方法。 由于 Netty 是 Spring WebFlux 的默认客户端库,本文将以 Reactor Netty HttpClient 类 作为示例。 3.1、响应超时 响应超时是指发送请求后等待接收响应的时间。可以使用 responseTimeout() 方法为客户端配置它: HttpClient client = HttpClient.create() .responseTimeout(Duration.ofSeconds(1)); 在本例中,配置的超时时间为 1 秒。Netty 默认不设置响应超时。 然后,就可以使用 HttpClient 来构建 Spring WebClient。 WebClient webClient = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build(); 之后,WebClient 会继承底层 HttpClient 提供的所有配置,适用于发送的所有请求。

使用 Spring Boot + React 开发 CRUD 应用

1、简介 本文将会带你学习如何使用 Spring Boot 以及 React JavaScript 框架开发一个简单的 RESTful CRUD 应用。 2、Spring Boot 2.1、Maven 依赖 在 pom.xml 中添加如下依赖: <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.4.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.4.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>2.4.4</version> <scope>test</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.200</version> <scope>runtime</scope> </dependency> </dependencies> 如上,添加了 Web、Test、Spring Data JPA 以及 H2 依赖。 2.2、创建 Model 创建 Client 实体类,有 id、name 和 email 属性: @Entity @Table(name = "client") public class Client { @Id @GeneratedValue private Long id; private String name; private String email; // 省略构造函数和 get/set 方法 } 2.

Spring Boot 整合 SQLite 数据库

SQLite 是一种嵌入式关系型数据库管理系统(RDBMS),使用 C 语言开发,以其简单性、轻量级和零配置而闻名。不需要独立的服务器,可以直接嵌入到应用中。支持事务,支持各种编程语言。是移动应用和嵌入式系统的首选数据库解决方案。 本文将会带你了解如何在 Spring Boot 中配置和使用 SQLite。 创建 Spring Boot 应用 创建一个示例 Spring Boot 应用。在 pom.xml 添加 spring-boot-starter-jdbc 和 SQLite 驱动 sqlite-jdbc 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.xerial</groupId> <artifactId>sqlite-jdbc</artifactId> </dependency> sqlite-jdbc 已经被 Spring Boot 纳入了版本控制,所以在项目继承了 spring-boot-starter-parent 的情况下,添加此依赖不用声明版本号。 配置属性 在 application.yaml 中配置如下属性: spring: datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: org.sqlite.JDBC url: "jdbc:sqlite:D:\\app.db" 使用默认的数据源实现 HikariDataSource。通过 driver-class-name属性指定 SQLite 的驱动类。url 配置指定了 SQLite 数据库文件的位置,可以是相对路径或者绝对路径。如果文件不存在,会被创建。 测试 得益于 Spring Boot 对数据源开箱即用的支持,完成上述配置后,SQLite 数据库已经就绪可以使用了。 创建测试类: import java.time.LocalDateTime; import java.

Spring WebFlux 中的重试

1、概览 在云上构建分布式应用时,需要考虑到服务故障,这通常会涉及到重试。 Spring WebFlux 提供了一些失败后重试的工具。 本文将会带你了解如何在 Spring WebFlux 添加和配置重试功能。 2、用例 本文使用一个 MockWebServer 来模拟外部系统暂时不可用,然后又变为可用的情况。 连接到 REST 服务的组件: @Test void givenExternalServiceReturnsError_whenGettingData_thenRetryAndReturnResponse() { mockExternalService.enqueue(new MockResponse() .setResponseCode(SERVICE_UNAVAILABLE.code())); mockExternalService.enqueue(new MockResponse() .setResponseCode(SERVICE_UNAVAILABLE.code())); mockExternalService.enqueue(new MockResponse() .setResponseCode(SERVICE_UNAVAILABLE.code())); mockExternalService.enqueue(new MockResponse() .setBody("stock data")); StepVerifier.create(externalConnector.getData("ABC")) .expectNextMatches(response -> response.equals("stock data")) .verifyComplete(); verifyNumberOfGetRequests(4); } 3、添加重试 Mono 和 Flux API 中内置了两个关键的 retry 操作。 3.1、使用 retry retry 可以防止应用立即返回错误,并重新订阅指定次数: public Mono<String> getData(String stockId) { return webClient.get() .uri(PATH_BY_ID, stockId) .retrieve() .bodyToMono(String.class) .retry(3); } 无论 Web 客户端返回什么错误,都会重试最多三次。 3.2、使用 retryWhen retryWhen 方法可用来创建一个可配置的重试策略:

RESTful API 中的 PUT 和 POST 请求

1、概览 在设计 RESTful API 的时候,往往会纠结于到底是用 PUT 还是 POST 请求? 本文将会带你了解在 RESTful API 中 PUT 和 POST 请求之间的区别以及它们的应用场景。 2、PUT 与 POST 在典型的 REST 架构中,客户端以 HTTP 方法的形式向服务器发送请求,以创建、检索、修改或删除资源。虽然可以使用 PUT 和 POST 来创建资源,但它们在预期应用方面有很大的不同。 根据 RFC 2616 标准,POST 方法应该用于请求服务器将所附实体作为现有资源(由请求 URI 标识)的子级接受。这意味着使用 POST 方法调用将在资源集合下创建一个子资源。 相反,PUT 方法应该用于请求服务器将所附实体存储在请求的 URI 下。如果请求 URI 指向服务器上的现有资源,则提供的实体将被视为现有资源的修改版本。因此,PUT 方法调用要么创建一个新资源,要么更新现有资源。 这两种方法的另一个重要区别是,PUT 是一种幂等方法,而 POST 不是。例如,多次调用 PUT 方法将创建或更新同一资源。相比之下,多次 POST 请求将导致多次创建同一资源。 3、示例应用 使用 Spring Boot 创建一个简单的 RESTful Web 应用来演示 PUT 和 POST 请求的区别。 3.1、Maven 依赖 需要在 pom.xml 中添加 Spring Web、Spring Data JPA 和 H2 内存数据库依赖:

Spring 应用中的 HandlerInterceptor 和 Filter

1、概览 本文将会带你了解 Spring MVC HandlerInterceptor 和 Servlet Filter 之间的区别和各自的应用场景。 2、Filter Filter 是 Web 服务器的一部分,而不是 Spring 框架的组件。对于传入请求,可以使用 Filter 来操作甚至阻止请求到达任何 Servlet。反之亦然,也可以阻止响应到达客户端。 Spring Security 就是使用 Filter 进行身份认证和授权的一个很好的例子。要配置 Spring Security,只需添加一个 Filter,即 DelegatingFilterProxy。这样,Spring Security 就能拦截所有进出流量。这就是 Spring Security 可以在 Spring MVC 之外使用的原因。 2.1、创建 Filter 创建一个实现 jakarta.servlet.Filter 接口的 Filter 实现类: public class LogFilter implements Filter { private Logger logger = LoggerFactory.getLogger(LogFilter.class); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { logger.info("Hello from: " + request.