教程

Spring WebFlux 中的并发

1、简介 本文将带你了解 Spring WebFlux 响应式应用中的并发。 2、响应式编程的动机 一个典型的 Web 应用由多个复杂的交互部分组成。其中许多交互在本质上是阻塞性的,例如那些涉及数据库调用以获取或更新数据的交互。而其他几个部分则是独立的,可以 并发 执行,也可能是 并行 执行。 并发是多个任务在同一时间段内交替执行,而并行是多个任务同时执行。并发关注的是任务的调度和切换,以提高系统的效率和响应性,而并行关注的是任务的同时执行,以提高计算速度和性能。 例如,用户对 Web 服务器的两个请求可以由不同的线程处理。在多核平台上,这对整体响应时间有明显的好处。因此,这种并发模型被称 thread-per-request(每个请求一个线程)模型: 如上图,每个线程一次处理一个请求。 虽然基于线程的并发为我们解决了部分问题,但却无法解决单个线程内的大部分交互仍然是阻塞的这一事实。此外,在 Java 中使用原生线程来实现并发时,还需要付出上下文切换的巨大代价。 与此同时,随着 Web 应用面临的请求越来越多,thread-per-request 模式开始无法满足人们的期望。 因此,我们需要一种并发模型,它可以帮助我们用相对较少的线程数处理越来越多的请求。这也是采用响应式编程的主要动机之一。 3、响应式编程中的并发 响应式编程可以帮助我们根据数据流和变化的传播来构建程序。在完全无阻塞的环境中,这可以让我们实现更高的并发性和更好的资源利用率。 然而,响应式编程是否完全摒弃了基于线程的并发?虽然这种说法有些激烈,但响应式编程肯定与使用线程实现并发的方法截然不同。响应式编程带来的根本区别在于 异步。 换句话说,程序流程从一连串同步操作转变为异步事件流。 例如,在响应式模型下,对数据库的读取调用不会在获取数据时阻塞调用线程。调用会立即返回一个发布者(Publisher),其他人可以订阅该发布者。订阅者(Subscriber)可以在事件发生后对其进行处理,甚至可以自己进一步生成事件: 最重要的是,响应式编程并不强调应该生成和消耗哪个线程事件。相反,它强调的是将程序构造成异步事件流。 这里的发布者和订阅者不需要属于同一个线程。这有助于我们更好地利用可用线程,从而提高整体并发性。 4、Event Loop Event Loop(事件循环)模型是一种用于服务器的响应式异步编程模型: 上图是一个事件循环的抽象设计,展示了响应式异步编程的思想: Event Loop 在单个线程中连续运行,我们可以根据可用内核的数量设置多个 Event Loop。 Event Loop 按顺序处理来自事件队列的事件,并在向平台注册回调后立即返回。 Platform(平台)可以触发操作的完成,如数据库调用或外部服务调用。 Event Loop 可在 operation(操作)完成通知时触发 callback(回调),并将结果发回给原始调用者。 包括 Node.js、Netty 和 Ngnix 在内的许多平台都实现了 Event Loop 模型。与 Apache HTTP 服务器、Tomcat 或 JBoss 等传统平台相比,它们具有更好的可扩展性。 5、用 Spring WebFlux 进行响应式编程 对响应式编程及其并发模型有了足够的了解后,来看看 Spring WebFlux。它是 Spring 在 5.

在 YAML 中定义 POJO 中的 Map 属性

1、概览 本文将带你了解如何在 YAML 配置文件中定义 POJO 类中的 Map 属性。 2、POJO 和 YAML POJO 即 Plain Old Java Objects(普通旧 Java 对象)。YAML 是一种人类可读的结构化数据格式,使用缩进表示嵌套。 2.1、简单的 Map 假设我们正在经营一家网店,并且要创建一个服装尺码转换的服务。起初,我们只销售英国的服装。我们想知道标签 S、M、L 等代表的是英国的哪个尺码。 创建 POJO 配置类: @ConfigurationProperties(prefix = "t-shirt-size") public class TshirtSizeConfig { private Map<String, Integer> simpleMapping; public TshirtSizeConfig(Map<String, Integer> simpleMapping) { this.simpleMapping = simpleMapping; } //get、set } 注意 @ConfigurationProperties 注解的 prefix 值表示配置前缀,在下一章会定义该配置。 还要在 Application.class 上使用以下注解启用配置属性类: @EnableConfigurationProperties(TshirtSizeConfig.class) public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } 2.

Spring Boot 中的健康指标(Health Indicators)

1、概览 Spring Boot 提供了几种不同的方法来检查运行中的应用及其组件的状态和健康状况。在这些方法中,HealthContributor 和 HealthIndicator API 是值得注意的两种。 本文将带你了解这些 API 的原理以及如何向它们提供自定义信息。 2、依赖 Health Information Contributor(健康信息贡献者)是 Spring Boot Actuator 模块的一部分: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> 3、内置的 HealthIndicator Spring Boot 注册了许多开箱即用的 HealthIndicator 来报告应用特定方面的健康状况。 其中一些指标默认已经注册了,如 DiskSpaceHealthIndicator 或 PingHealthIndicator。前者报告磁盘的当前状态,后者则作为应用的 ping 端点。 Spring Boot 也会有条件地注册一些指标。也就是说,如果 classpath 上存在某些依赖或满足某些其他条件,Spring Boot 可能也会注册一些其他的 HealthIndicator。例如,如果使用了关系型数据库,那么 Spring Boot 就会注册 DataSourceHealthIndicator。同样,如果使用的是 Cassandra 作为数据存储,它会注册 CassandraHealthIndicator。 可以调用 /actuator/health 端点来检查 Spring Boot 应用的健康状态。该端点会报告所有已注册的 HealthIndicator 的汇总结果。 如果要查看某个特定指标的健康报告,可以调用 /actuator/health/{name} 端点。例如,调用 /actuator/health/diskSpace 端点将返回 DiskSpaceHealthIndicator 的状态报告: { "status": "UP", "details": { "total": 499963170816, "free": 134414831616, "threshold": 10485760, "exists": true } } 4、自定义 HealthIndicator 除了内置的 HealthIndicator,还可以注册自定义 HealthIndicator 来报告组件或子系统的健康状况。只需将 HealthIndicator 接口的实现注册为 Spring Bean 即可。

Spring Boot 中的 application.yml 和 application.properties

1、概览 Spring Boot 中的一个常见做法是使用外部配置来定义属性。这样,就可以在不同的环境中使用相同的应用程序代码。 外部配置可以使用 properties 文件、YAML 文件、环境变量和命令行参数。 本文将带你了解 properties 文件和 YAML 文件的主要区别。 2、Properties 配置 默认情况下,Spring Boot 可以访问在 application.properties 文件中设置的配置,该文件使用键值格式: spring.datasource.url=jdbc:h2:dev spring.datasource.username=SA spring.datasource.password=password 如上,每一行都是一个单独的配置,因此需要为 key 使用相同的前缀来表达分层数据。在本例中,每个 key 都属于 spring.datasource。 2.1、Properties 中的占位符 在值中,可以使用 ${} 语法的占位符来引用其他 key、系统属性或环境变量的内容: app.name=MyApp app.description=${app.name} is a Spring Boot application 2.2、列表 如果同类 properties 具有不同的值,可以用数组索引来表示列表结构: application.servers[0].ip=127.0.0.1 application.servers[0].path=/path1 application.servers[1].ip=127.0.0.2 application.servers[1].path=/path2 application.servers[2].ip=127.0.0.3 application.servers[2].path=/path3 2.3、多文档 自 2.4.0 版起,Spring Boot 支持创建多文档 properties 文件。简单地说,可以将单个物理文件拆分成多个逻辑文档。 这样,就可以在同一个 properties 文件中声明多个 Profile: logging.file.name=myapplication.log bael.property=defaultValue #--- spring.config.activate.on-profile=dev spring.datasource.password=password spring.datasource.url=jdbc:h2:dev spring.

Spring Boot 优雅停机

本文将带你了解如何在 Spring Boot 应用中配置优雅停机。 1、优雅停机 从 Spring Boot 2.3 开始,Spring Boot 所支持的四种嵌入式 Web 服务器(Tomcat、Jetty、Undertow 和 Netty)都支持在 Servlet 和响应式平台上的优雅关机功能。 只需在 application.properties 文件中将 server.shutdown 属性设置为 graceful,即可启用优雅停机: server.shutdown=graceful 在优雅停机阶段,Tomcat、Netty 和 Jetty 会停止接受新的 Web 请求。而 Undertow 会继续接受,但会立即向客户端发送 503 Service Unavailable 响应。 默认情况下,该属性的值等于 immediate,这意味着服务器会立即关闭。 在优雅关机时,可能有一些之前的请求仍在处理中。在这种情况下,服务器会等待这些活动请求在指定时间内完成。 可以使用 spring.lifecycle.timeout-per-shutdown-phase 配置属性来配置超时时间: spring.lifecycle.timeout-per-shutdown-phase=1m 如上,在优雅停机时,对于那些仍在处理的请求,服务器等待最多一分钟。该属性的默认值为 30 秒。 2、总结 本文介绍了如何在 Spring Boot 中配置优雅停机,以及如何配置服务器停机超时时间。 Ref:https://www.baeldung.com/spring-boot-web-server-shutdown

Spring MVC Async 和 WebFlux

1、概览 本文将带你了解 Spring Async 和 Spring WebFlux 之间的区别。 2、场景 本文分别用 Spring Async 和 Spring WebFlux 来实现一个简单的 Web 应用。 Web 请求会通过一个延迟时间为 200 毫秒的 Filter,然后 Controller 需要 500 毫秒来计算并返回结果。 最后使用 Apache ab 分别进行负载测试,并使用 JConsole 监控应用的行为。 3、Spring MVC Async Spring 3.0 引入了 @Async 注解。@Async 的目标是允许应用在单独的线程上运行重负载的任务。此外,调用方可以等待结果(如果感兴趣)。因此,返回类型不能是void,而可以是 Future、CompletableFuture 或 ListenableFuture 之一。 Spring 3.2 引入了 org.springframework.web.context.request.async 包,它与 Servlet 3.0 一起为 Web 层带来了异步的支持。因此,自 Spring 3.2 起,@Async 可以在 @Controller 或 @RestController 类中使用。 当客户端发起请求时,请求会经过过滤器链(filter chain)中所有匹配的过滤器(filter),直到到达 DispatcherServlet 实例。 然后,Servlet 会对请求进行异步调度。它通过调用 AsyncWebRequest#startAsync 将请求标记为已启动,将请求处理转移到 WebSyncManager 的实例,然后在不提交响应的情况下完成其工作。过滤器链也按相反的方向遍历执行到根节点。

在生产环境中关闭 Swagger-UI

1、概览 在开发环境下使用 Swagger UI 可以很方便地查看、测试 REST 服务。但是出于安全考虑,在生产环境中往往需要禁用 Swagger UI。 2、Swagger 配置 要 使用 SpringDoc 设置 Swagger,需要在配置 Bean 中对其进行定义。 创建 SwaggerConfig 类: @Configuration public class SwaggerConfig { @Bean public OpenAPI openAPI() { return new OpenAPI().info(new Info().title("SpringDoc Disable SwaggerUI example") .description("SpringDoc Disable SwaggerUI application") .version("v0.0.1")); } } 默认情况下,该配置 Bean 会添加到 Spring Context 中。这样,Swagger 可以在所有环境中使用。 3、使用 Spring Profile 在 Spring 中,可以使用 @Profile 注解来启用或禁用 Bean。 使用 SpEL 表达式来指定在哪些环境中激活 Swagger。 @Profile({"!prod && swagger"}) 如上,在 swagger Profile,且不是 prod Profile 的情况下才会启注解的 Bean。

自定义 Keycloak 的登录页面

1、概览 Keycloak 是第三方授权服务器,用于管理 Web 或移动应用的身份验证和授权。它为用户提供了一个默认的登录页面。 本文将带你了解如何自定义 Keycloak 服务器的登录页面。 本文在 《自定义 Keycloak 主题》 基础上进行实现。 2、独立 Keycloak 服务器 继续以 custom 主题为例,先看看独立服务器。 2.1、管理控制台设置 进入 Keycloak 目录,然后在 bin 文件夹中运行如下命令,启动服务器: kc.[sh|bat] start-dev --spi-theme-static-max-age=-1 --spi-theme-cache-themes=false --spi-theme-cache-templates=false 使用上述命令启动服务器后,只需刷新页面,就能看到更改。 现在,在 themes/custom 目录中新建一个名为 login 的文件夹。为了简单起见,先将 themes/keycloak/login 目录中的所有内容复制到这里。这是默认的登录页面主题。 然后,进入管理控制台(http://localhost:8080/admin/master/console),使用 initial1/zaq1!QAZ 凭证登录,并进入 Realm 的 “Themes” 选项卡: 为 Login Theme 选择 custom,然后保存更改。 现在就可以尝试一些自定义功能了。不过在此之前,先来看看默认登录页面: 2.2、添加自定义内容 现在,假设我们需要更改背景。 打开 login/resources/css/login.css 并更改 class 定义: .login-pf body { background: #39a5dc; background-size: cover; height: 100%; } 刷新页面即可看到效果: 接下来,尝试更改用户名和密码的标签。

自定义 Keycloak 主题

1、概览 Keycloak 是一个开源的身份和访问管理(Identity and Access Management,IAM)解决方案,可以作为第三方授权服务器来管理 Web 或移动应用的身份验证和授权。 本文将带你了解如何自定义 Keycloak 的主题,为终端用户的网页提供不同的外观。 本文以之前的文章为基础:《Keycloak 指南》和《在 Spring Boot 中嵌入 Keycloak》。因此,对于初学者来说,最好先阅读这两篇文章。 2、Keycloak 的主题 2.1、默认主题 Keycloak 中预置了几个主题,并与发行版绑定在一起。 对于单机版服务器,可在 ../lib/lib/main/org.keycloak.keycloak-themes-20.0.3.jar(可使用任何标准 ZIP 压缩工具打开)的主题目录下的不同文件夹中找到这些主题: base:包含 HTML 模板和 Message Bundle 的骨架主题;所有主题(包括自定义主题)一般都继承自 base 主题 keycloak:包含用于美化页面的图片和样式表;如果我们不提供自定义主题,则默认使用该主题 keycloak.v2:基于 React 的主题;新管理控制台的一部分;旧控制台已过时,将在 Keycloak 21 中移除。 不建议修改现有主题。相反,应该创建一个新主题,从上述两个主题中继承。 要创建一个新的自定义主题,需要在 themes 目录中添加一个新的文件夹,称之为 custom。如果想要完全重建,则建议从 base 文件夹中复制内容。 在本例中,我们并不打算替换所有内容,因此从 keycloak 目录中获取内容。 2.2、主题类型 Keycloak 支持五种主题: Welcome:用于欢迎页 Login:用于登录、OTP、授予、注册和忘记密码页面 Account:用于用户账户管理页面 Admin Console:用于管理控制台 Email:用于服务器发送的电子邮件 上述列表中的最后四个主题可以通过独立服务器的管理控制台进行设置。当我们在 themes 目录下创建一个新文件夹后,服务器重启后就可以选择该文件夹。 使用凭证 initial1 / zaq1!QAZ(在 上一文章 中设置的)登录管理控制台,并转到 Realm 的 “Themes” 选项卡:

使用 Arthas 在 Spring 运行时获取配置值和配置来源

背景 众所周之,Spring / Spring Boot 应用的配置注入方式非常多: System Properties / System Env application.properties / application.yaml Spring Profile Spring Cloud Config 还有很多配置注入的方式你可以参阅 中文文档,可谓是令人眼花缭乱。 获取运行时具体配置 对于开发人员来说,在运行时怎样确定某个配置是否生效?它的具体值是什么? 通过 Arthas,只要一行命令就可以获取到。 例如,获取 server.port 的具体值: vmtool --action getInstances --className org.springframework.context.ConfigurableApplicationContext --express 'instances[0].getEnvironment().getProperty("server.port")'@String[7001] 获取具体的配置来源 但是这个配置是从哪里来的? 对于 spring boot 应用,可以打开一个新的 terminal 窗口,执行 telnet 127.0.0.1 3658 连接上Arthas。 直接 watch 下面的函数。 在原来窗口用上面的 vmtool 命令来获取 server.port 的值。 从 watch 返回结果中可以看到,server.port 值来源于 application.yml: watch org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource findConfigurationPropertyPress Q or Ctrl+C to abort.Affect(class count: 1 , method count: 2) cost in 217 ms, listenerId: 5method=org.