当前位置: 首页 > news >正文

Spring WebFlux—Reactive 核心

一、概述

spring-web 模块包含以下对响应式Web应用的基础支持:

  • 对于服务器请求处理,有两个级别的支持。
    • HttpHandler: 用于HTTP请求处理的基本约定,具有非阻塞I/O和Reactive Streams背压,以及Reactor Netty、Undertow、Tomcat、Jetty和任何Servlet容器的适配器。
    • WebHandler API: 稍微高一点的,用于处理请求的通用 web API,在此基础上建立具体的编程模型,如注解controller和函数式端点。
  • 对于客户端来说,有一个基本的 ClientHttpConnector 约定来执行具有非阻塞I/O和Reactive Streams背压的HTTP请求,同时还有 Reactor Netty、reactive Jetty HttpClient 和 Apache HttpComponents 的适配器。应用中使用的更高层次的 WebClient 是建立在这个基本约定之上的。

二、HttpHandler

HttpHandler 是一个简单的契约,有一个处理请求和响应的单一方法。它有意做到最小,它的主要和唯一的目的是成为不同的HTTP服务器API的最小抽象。

下表描述了支持的服务器API:

服务器

使用的服务器API

Reactive Streams 的支持

Netty

Netty API

Reactor Netty

Undertow

Undertow API

spring-web: Undertow 到 Reactive Streams 的 bridge

Tomcat

Servlet非阻塞式I/O;Tomcat API读和写 ByteBuffers vs byte[]

spring-web: Servlet非阻塞I/O到Reactive Streams的bridge

Jetty

Servlet非阻塞式I/O;Jetty API写ByteBuffers vs byte[]

spring-web: Servlet非阻塞I/O到Reactive Streams的bridge

Servlet 容器

Servlet 非阻塞 I/O

spring-web:Servlet非阻塞I/O到Reactive Streams的bridge

下表描述了服务器的依赖(也见 支持的版本):

服务器

Group id

Artifact name

Reactor Netty

io.projectreactor.netty

reactor-netty

Undertow

io.undertow

undertow-core

Tomcat

org.apache.tomcat.embed

tomcat-embed-core

Jetty

org.eclipse.jetty

jetty-server, jetty-servlet

下面的代码片段显示了在每个服务器API中使用 HttpHandler 适配器(adapter):

1、Reactor Netty

Java

HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create().host(host).port(port).handle(adapter).bindNow();

2、Undertow

Java

HttpHandler handler = ...
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();

3、Tomcat

Java

HttpHandler handler = ...
Servlet servlet = new TomcatHttpHandlerAdapter(handler);Tomcat server = new Tomcat();
File base = new File(System.getProperty("java.io.tmpdir"));
Context rootContext = server.addContext("", base.getAbsolutePath());
Tomcat.addServlet(rootContext, "main", servlet);
rootContext.addServletMappingDecoded("/", "main");
server.setHost(host);
server.setPort(port);
server.start();

4、Jetty

Java

HttpHandler handler = ...
Servlet servlet = new JettyHttpHandlerAdapter(handler);Server server = new Server();
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
contextHandler.addServlet(new ServletHolder(servlet), "/");
contextHandler.start();ServerConnector connector = new ServerConnector(server);
connector.setHost(host);
connector.setPort(port);
server.addConnector(connector);
server.start();

5、Servlet 容器

要以WAR的形式部署到任何Servlet容器,你可以继承并在WAR中包含 AbstractReactiveWebInitializer。该类用 ServletHttpHandlerAdapter 包装了一个 HttpHandler,并将其注册为一个 Servlet。

三、WebHandlerAPI

org.springframework.web.server 包建立在 HttpHandler 契约的基础上,提供了一个通用的Web API,通过多个 WebExceptionHandler、多个 WebFilter 以及单个 WebHandler组件的链来处理请求。该链可以通过简单地指向 自动检测 组件的 Spring ApplicationContext,和/或通过向 builder 注册组件来与 WebHttpHandlerBuilder 组合。

虽然 HttpHandler 有一个简单的目标,即抽象出不同的HTTP服务器的使用,但 WebHandler API的目标是提供一套更广泛的在Web应用程序中常用的功能,如:

  • User session 和 attributes。
  • Request attributes。
  • 为 Request 解析 Locale 或 Principal。
  • 访问已解析和缓存的表单数据。
  • multipart data 的抽象。
  • 等 。。。

1、特殊的 Bean 类型

下表列出了 WebHttpHandlerBuilder 可以在Spring ApplicationContext 中自动检测的组件,或者可以直接用它来注册:

Bean

Bean 类型

说明

说明

<any>

WebExceptionHandler

0..N

提供对来自 WebFilter 实例链和目标 WebHandler 的异常的处理。更多的细节,请看 异常(Exception)。

<any>

WebFilter

0..N

在过滤器链(filter chain)的其他部分和目标 WebHandler 之前和之后应用拦截式逻辑。更多细节,见 过滤器(Filter)。

webHandler

WebHandler

1

请求的处理程序(handler)。

webSessionManager

WebSessionManager

0..1

WebSession 实例的管理器,通过 ServerWebExchange 的一个方法暴露出来。默认情况下是 DefaultWebSessionManager。

serverCodecConfigurer

ServerCodecConfigurer

0..1

用于访问 HttpMessageReader 实例,以解析 form data 和 multipart data,然后通过 ServerWebExchange 上的方法公开。默认情况下,使用 ServerCodecConfigurer.create()。

localeContextResolver

LocaleContextResolver

0..1

LocaleContext 的解析器,通过 ServerWebExchange 的一个方法暴露出来。默认情况下是 AcceptHeaderLocaleContextResolver。

forwardedHeaderTransformer

ForwardedHeaderTransformer

0..1

用于处理转 forwarded type 的 header,可以提取和删除它们,也可以只删除它们。默认情况下不使用。

2、表单(Form)数据

ServerWebExchange 暴露了以下访问表单数据的方法:

Java

Mono<MultiValueMap<String, String>> getFormData();

DefaultServerWebExchange 使用配置的 HttpMessageReader 来解析表单数据(application/x-www-form-urlencoded)到 MultiValueMap。默认情况下,FormHttpMessageReader 被配置为由 ServerCodecConfigurer bean 使用(见 Web Handler API)。

3、Multipart Data

参见 Servlet 技术栈中的相应内容

ServerWebExchange 为访问 multipart data 暴露了以下方法:

Java

Mono<MultiValueMap<String, Part>> getMultipartData();

DefaultServerWebExchange 使用配置的 HttpMessageReader<MultiValueMap<String, Part>> 来解析 multipart/form-data、multipart/mixed 和 multipart/related 内容到 MultiValueMap。默认情况下,这是 DefaultPartHttpMessageReader,它没有任何第三方的依赖。另外,也可以使用 SynchronossPartHttpMessageReader,它是基于 Synchronoss NIO Multipart 库的。两者都是通过 ServerCodecConfigurer Bean来配置的(见 Web Handler API)。

为了以流的方式解析 multipart data,你可以使用从 PartEventHttpMessageReader 返回的 Flux<PartEvent>,而不是使用 @RequestPart,因为这意味着通过名称对单个 part 进行类似 Map 的访问,因此,需要完整地解析 multipart data。相比之下,你可以使用 @RequestBody 将内容解码为 Flux<PartEvent>,而无需收集到 MultiValueMap。

4、Forwarded Header

参见 Servlet 技术栈中的相应内容

当一个请求通过代理(如负载均衡器)时,host、port 和 scheme 可能会改变。这使得从客户的角度来看,创建指向正确的 host、port 和 scheme 的链接成为一种挑战。

RFC 7239 定义了 Forwarded HTTP头,代理可以用它来提供关于原始请求的信息。还有其他非标准的 header,包括 X-Forwarded-Host、X-Forwarded-Port、X-Forwarded-Proto、X-Forwarded-SSL 和 X-Forwarded-Prefix。

ForwardedHeaderTransformer 是一个组件,它根据 forwarded header 修改请求的host、port 和 scheme,然后删除这些header。如果你以 forwardedHeaderTransformer 为名将其声明为一个Bean,它将被 检测 并使用。

对 forwarded header 有安全方面的考虑,因为应用程序无法知道这些头信息是由代理按计划添加的,还是由恶意的客户添加的。这就是为什么在信任边界的代理应该被配置为删除来自外部的不被信任的转发流量。你也可以将 ForwardedHeaderTransformer 配置为 removeOnly=true,在这种情况下,它将删除但不使用这些 header。

在5.1版本中,ForwardedHeaderFilter 被弃用,并被 ForwardedHeaderTransformer 所取代,因此 forwarded header 可以在创建 exchange 之前被提前处理。如果过滤器(filter)被配置了,它就会从过滤器的列表中取出来,而使用 ForwardedHeaderTransformer。

四、过滤器(Filter)

参见 Servlet 技术栈中的相应内容

在 WebHandler API, 中,你可以使用 WebFilter 在过滤器和目标 WebHandler 的其他处理链之前和之后应用拦截式逻辑。当使用 WebFlux 配置 时,注册一个 WebFilter 就像把它声明为一个Spring Bean一样简单,并(可选择)通过在bean声明中使用 @Order 或实现 Ordered 来表达优先级。

1、CORS

参见 Servlet 技术栈中的相应内容

Spring WebFlux 通过 controller 上的注解为 CORS 配置提供了细粒度的支持。然而,当你将其与Spring Security一起使用时,我们建议依靠内置的 CorsFilter,它的顺序必须在 Spring Security 的过滤器链之前。

更多细节请参见 CORS 和 CORS WebFilter 部分。

五、 异常(Exception)

参见 Servlet 技术栈中的相应内容

在 WebHandler API 中,你可以使用一个 WebExceptionHandler 来处理来自 WebFilter 实例链和目标 WebHandler 的异常。当使用 WebFlux 配置 时,注册一个 WebExceptionHandler 就像把它声明为 Spring Bean 一样简单,并(可选择)通过在bean声明中使用 @Order 或实现 Ordered 来表达优先级。

下表描述了可用的 WebExceptionHandler 实现:

Exception Handler

说明

ResponseStatusExceptionHandler

为 ResponseStatusException 类型的异常提供处理,将响应设为异常的HTTP状态代码。

WebFluxResponseStatusExceptionHandler

ResponseStatusExceptionHandler 的扩展,也可以确定任何异常的 @ResponseStatus 注解的HTTP状态代码。

这个处理程序是在 WebFlux 配置 中声明的。

六、 编解码器(Codecs)

参见 Servlet 技术栈中的相应内容

spring-web 和 spring-core 模块通过非阻塞I/O与Reactive Streams背压,提供了对高层对象的字节内容序列化和反序列化的支持。下面描述了这种支持:

  • Encoder 和 Decoder 是独立于HTTP的编码和解码内容的低级别契约。
  • HttpMessageReader 和https://docs.spring.io/spring-framework/docs/6.0.8-SNAPSHOT/javadoc-api/org/springframework/http/codec/HttpMessageWriter.html[HttpMessageWriter] 是对HTTP消息内容进行编码和解码的契约。
  • 一个 Encoder 可以用 EncoderHttpMessageWriter 来包装,以使其适用于 Web 应用程序中,而一个 Decoder 可以用 DecoderHttpMessageReader 来包装。
  • DataBuffer 抽象了不同的字节缓冲区表示法(例如 Netty ByteBuf、java.nio.ByteBuffer 等),是所有编解码器的工作对象。请参阅 "Spring Core" 部分中的 Data Buffer 和 Codec,以了解更多关于这一主题的信息。

spring-core 模块提供 byte[]、ByteBuffer、DataBuffer、Resource 和 String 编码器和解码器的实现。spring-web 模块提供了 Jackson JSON、Jackson Smile、JAXB2、Protocol Buffers 和其他编码器和解码器,以及针对表单数据、multipart 内容、server-sent event 和其他的web专用HTTP消息读写器实现。

ClientCodecConfigurer and ServerCodecConfigurer are typically used to configure and customize the codecs to use in an application. See the section on configuring HTTP消息编解码器.

ClientCodecConfigurer 和 ServerCodecConfigurer 通常被用来配置和定制应用中使用的编解码器。参见配置 HTTP消息编解码器 的章节。

1、Jackson JSON

当Jackson库存在时,JSON和二进制JSON( Smile)都被支持。

Jackson2Decoder 的工作原理如下:

  • Jackson的异步、非阻塞解析器被用来将字节块流聚集到 TokenBuffer 中,每个字节块代表一个JSON对象。
  • 每个 TokenBuffer 被传递给 Jackson 的 ObjectMapper,以创建一个更高层次的对象。
  • 当解码到一个单值 publisher(例如 Mono)时,有一个 TokenBuffer。
  • 当解码到一个多值 publisher(如 Flux)时,每个 TokenBuffer 在收到足够的字节时就被传递给 ObjectMapper,以形成一个完整的对象。输入的内容可以是一个JSON数组,或任何 以行为单位的JSON 格式,如NDJSON,JSON行,或JSON文本序列。

Jackson2Encoder 的工作原理如下:

  • 对于一个单一的值 publisher(例如 Mono),只需通过 ObjectMapper 将其序列化。
  • 对于一个有 application/json 的多值 publisher,默认情况下用 Flux#collectToList() 来收集值,然后将得到的集合序列化。
  • 对于具有流媒体类型(如 application/x-ndjson 或 application/stream+x-jackson-smile)的多值 publisher,使用 以行为单位的JSON 格式对每个值进行编码、写入和刷出。其他流媒体类型可以在 encoder 上注册。
  • 对于SSE来说,Jackson2Encoder 在每个事件中被调用,output被刷出,以确保无延迟的交付。

默认情况下,Jackson2Encoder 和 Jackson2Decoder 都不支持 String 类型的元素。相反,默认的假设是一个字符串或一个字符串序列代表序列化的JSON内容,由 CharSequenceEncoder 来渲染。如果你需要的是从 Flux<String> 渲染一个JSON数组,使用 Flux#collectToList() 并编码一个 Mono<List<String>。

2、Form Data

FormHttpMessageReader 和 FormHttpMessageWriter 支持对 application/x-www-form-urlencoded 内容进行解码和编码。

在服务器端,表单内容经常需要从多个地方访问,ServerWebExchange 提供了一个专门的 getFormData() 方法,它通过 FormHttpMessageReader 解析内容,然后缓存结果以便重复访问。见 WebHandler API 部分的 表单(Form)数据 。

一旦使用 getFormData(),就不能再从请求体中读取原始的内容。由于这个原因,应用程序应该始终通过 ServerWebExchange 来访问缓存的表单数据,而不是从原始请求体中读取。

3、Multipart

MultipartHttpMessageReader 和 MultipartHttpMessageWriter 支持对 "multipart/form-data"、"multipart/mixed" 和 "multipart/related" 内容进行解码和编码。反过来, MultipartHttpMessageReader 委托给另一个 HttpMessageReader 来进行实际的解析到 Flux<Part>,然后简单地将这些 part 收集到一个 MultiValueMap 中。默认情况下,使用 DefaultPartHttpMessageReader,但这可以通过 ServerCodecConfigurer 改变。关于 DefaultPartHttpMessageReader 的更多信息,请参阅 DefaultPartHttpMessageReader 的javadoc。

在服务器端,如果 multipart 表单内容可能需要从多个地方访问,ServerWebExchange 提供了一个专门的 getMultipartData() 方法,该方法通过 MultipartHttpMessageReader 解析内容,然后缓存结果以便重复访问。参见 WebHandler API 部分的 Multipart Data。

一旦使用了 getMultipartData(),就不能再从请求体中读取原始的内容。由于这个原因,应用程序必须坚持使用 getMultipartData() 来重复、类似 map 的访问 part,或者依靠 SynchronossPartHttpMessageReader 来一次性访问 Flux<Part>。

4、边界(Limits)

缓存部分或全部 input stream 的 Decoder 和 HttpMessageReader 实现可以被配置为在内存中缓冲的最大字节数的限制。在某些情况下,缓冲的发生是因为输入被聚合并表示为一个单一的对象—​例如,一个带有 @RequestBody byte[] 的 controller 方法,x-www-form-urlencoded 数据,等等。缓冲也可能发生在流媒体中,当分割输入流时—​例如,限定的文本,JSON对象的流,等等。对于这些流的情况,限制适用于与流中一个对象相关的字节数。

为了配置缓冲区的大小,你可以检查一个给定的 Decoder 或 HttpMessageReader 是否暴露了一个 maxInMemorySize 属性,如果是的话,Javadoc 会有关于默认值的细节。在服务器端, ServerCodecConfigurer 提供了一个设置所有编解码器的单一位置,参见 HTTP消息编解码器。在客户端,所有编解码器的限制可以在 WebClient.Builder 中改变。

对于 Multipart 解析,maxInMemorySize 属性限制了非文件部分(part)的大小。对于文件 part,它决定了该 part 被写入磁盘的阈值。对于写入磁盘的文件 part,有一个额外的 maxDiskUsagePerPart 属性来限制每个 part 的磁盘空间量。还有一个 maxParts 属性,用于限制 multipart 请求中的总 part 数量。要在 WebFlux 中配置这三个属性,你需要向 ServerCodecConfigurer 提供一个预先配置的 MultipartHttpMessageReader 实例。

5、流(Stream)

参见 Servlet 技术栈中的相应内容

当流式HTTP响应(例如,text/event-stream,application/x-ndjson)时,定期发送数据是很重要的,以便可靠地尽早检测到一个断开连接的客户端,而不是更晚。这样的发送可以是一个仅有 comment 的、空的SSE事件或任何其他 "无操作" 的数据,这将有效地作为一个心跳。

6、DataBuffer

DataBuffer 是 WebFlux 中字节缓冲区的代表。本参考文献的Spring Core部分在 Data Buffer 和 Codec 部分有更多的介绍。需要理解的关键点是,在一些服务器上,如Netty,字节缓冲区是池化和引用计数的,在消耗时必须释放以避免内存泄漏。

WebFlux应用程序一般不需要关注这些问题,除非它们直接消费或生产数据缓冲区(data buffer),而不是依靠编解码器来转换为更高级别的对象,或者它们选择创建自定义编解码器。对于这种情况,请查看 Data Buffer 和 Codec 中的信息,特别是 使用 DataBuffer 的部分。

七、日志

参见 Servlet 技术栈中的相应内容

Spring WebFlux中的 DEBUG 级别日志被设计成紧凑、简约和人性化的。它专注于高价值的信息,这些信息可以反复使用,而其他信息只有在调试某个特定问题时才会有用。

TRACE 级别的日志通常遵循与 DEBUG 相同的原则(例如也不应该是火线),但可以用于调试任何问题。此外,一些日志信息在 TRACE 与 DEBUG 下可能显示不同的细节。

好的日志来自于使用日志的经验。如果你发现任何不符合既定目标的地方,请让我们知道。

1、日志 ID

在WebFlux中,一个请求可以在多个线程上运行,线程ID对于关联属于特定请求的日志消息没有用。这就是为什么WebFlux的日志消息默认以特定请求的ID为前缀。

在服务器端,日志ID存储在 ServerWebExchange 属性中( LOG_ID_ATTRIBUTE),而基于该ID的完全格式化的前缀可以从 ServerWebExchange#getLogPrefix() 获得。在 WebClient 端,日志ID存储在 ClientRequest attribute 中( LOG_ID_ATTRIBUTE),而完全格式化的前缀可以从 ClientRequest#logPrefix() 中获得。

2、敏感数据

参见 Servlet 技术栈中的相应内容

DEBUG 和 TRACE 日志可以记录敏感信息。这就是为什么表单参数和 header 在默认情况下是被屏蔽的,你必须明确地完全启用它们的日志。

下面的例子显示了如何对服务器端的请求进行处理:

Java

@Configuration
@EnableWebFlux
class MyConfig implements WebFluxConfigurer {@Overridepublic void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {configurer.defaultCodecs().enableLoggingRequestDetails(true);}
}

下面的例子显示了如何对客户端的请求进行处理:

Java

Consumer<ClientCodecConfigurer> consumer = configurer ->configurer.defaultCodecs().enableLoggingRequestDetails(true);WebClient webClient = WebClient.builder().exchangeStrategies(strategies -> strategies.codecs(consumer)).build();

3、Appender

SLF4J 和 Log4J 2 等日志库提供了避免阻塞的异步 logger。虽然这些都有自己的缺点,比如可能会丢弃无法排队记录的消息,但它们是目前在响应式、非阻塞式应用中使用的最佳可用选项。

4、自定义编解码器

应用程序可以注册自定义编解码器,以支持额外的媒体类型,或默认编解码器不支持的特定行为。

开发者表达的一些配置选项在默认的编解码器上被强制执行。自定义编解码器可能希望得到一个与这些偏好相一致的机会,比如 强制执行缓冲限制 或 记录敏感数据。

下面的例子显示了如何对客户端的请求进行处理:

Java

WebClient webClient = WebClient.builder().codecs(configurer -> {CustomDecoder decoder = new CustomDecoder();configurer.customCodecs().registerWithDefaultConfig(decoder);}).build();

相关文章:

Spring WebFlux—Reactive 核心

一、概述 spring-web 模块包含以下对响应式Web应用的基础支持&#xff1a; 对于服务器请求处理&#xff0c;有两个级别的支持。 HttpHandler: 用于HTTP请求处理的基本约定&#xff0c;具有非阻塞I/O和Reactive Streams背压&#xff0c;以及Reactor Netty、Undertow、Tomcat、…...

由于找不到d3dx9_43.dll,无法继续执行代码要怎么解决

D3DX9_43.dll是一个动态链接库文件&#xff0c;它是DirectX的一个组件&#xff0c;主要用于支持一些旧版本的游戏和软件。当电脑缺少这个文件时&#xff0c;可能会导致这些游戏和软件无法正常运行。例如&#xff0c;一些老游戏可能需要D3DX9_43.dll来支持图形渲染等功能。此外&…...

git安装配置教程

目录 git安装配置1. 安装git2. git 配置3.生成ssh key:4. 获取生产的密钥3. gitee或者github添加ssh-key4.git使用5. git 使用-本地仓库与远程仓库建立连接第一步&#xff1a;进入项目文件夹&#xff0c;初始化本地仓库第二步&#xff1a;建立远程仓库。 建立远程连接的小技巧 …...

要如何选择报修工单管理系统?需要注意哪些核心功能?

现如今&#xff0c;越来越多的企业已经离不开报修工单管理系统&#xff0c;但市面上的产品繁多&#xff0c;很难寻找到一款特别符合企业需求的系统。企业采购报修工单管理系统的主要目的在于利用其核心功能&#xff0c;如工单流转等&#xff0c;来解决工作事件的流程问题&#…...

面对大数据量渲染,前端工程师如何保证页面流畅性?

一、问题背景 在web前端开发中,需要渲染大量数据是很常见的需求。拿一般的业务系统来说,一个模块中往往需要显示成百上千条记录,这已经属于比较大的数据量。而一些大型系统,如数据分析平台、监控系统等,需要同时渲染的 数据量可能达到几十万甚至上百万。 面对大数据量渲染的需…...

2023年浙工商MBA新生奖学金名单公布,如何看待?

浙工商MBA项目官方最新公布了2023年的非全日制新生奖学金名单&#xff0c;按照政策约定&#xff0c;共分为特等奖学金1名&#xff0c;一等奖学金10名&#xff0c;二等奖学金15名&#xff0c;三等奖学金30名&#xff0c;额度对应3万、1万、0.8万、0.5万不等&#xff0c;主要名单…...

关于时空数据的培训 GAN:实用指南(第 02/3 部分)

一、说明 在本系列关于训练 GAN 实用指南的第 1 部分中&#xff0c;我们讨论了 a&#xff09; 鉴别器 &#xff08;D&#xff09; 和生成器 &#xff08;G&#xff09; 训练之间的不平衡如何导致模式崩溃和由于梯度消失而导致静音学习&#xff0c;以及 b&#xff09; GAN 对超参…...

UNIAPP利用canvas绘制图片和文字,并跟随鼠标移动

最近有个项目&#xff0c;要触摸组件&#xff0c;产生一条图片跟随移动&#xff0c;并显示相应的文字&#xff0c;在网上找了一些资料&#xff0c;终于完成构想&#xff0c;废话少说&#xff0c;直接上代码&#xff08;测试通过&#xff09; <template> <view>…...

【智能电表数据接入物联网平台实践】

智能电表数据接入物联网平台实践 设备接线准备设备调试代码实现Modbus TCP Client 读取电表数据读取寄存器数据转成32bit Float格式然后使用modbusTCP Client 读取数据 使用mqtt协议接入物联网平台最终代码实现 设备接线准备 设备调试 代码实现 Modbus TCP Client 读取电表数…...

Docker--network命令的用法

原文网址&#xff1a;Docker--network命令的用法_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Docker的network网络命令的用法。 官网网址 docker network | Docker Documentation 命令概述 所有命令 命令名称 说明 docker network connect 将容器连接到网络 dock…...

优维低代码实践:图片和搜索

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 优维…...

[Qt]控件

文章摘于 爱编程的大丙 文章目录 1. 按钮类型控件1.1 按钮基类 QAbstractButton1.1.1 标题和图标1.1.2 按钮的 Check 属性1.1.3 信号1.1.4 槽函数 1.2 QPushButton1.2.1 常用API1.2.2 按钮的使用 1.3 QToolButton1.3.1 常用API1.3.2 按钮的使用 1.4 QRadioButton1.4.1 常用API…...

GEE:快速实现时间序列线性趋势和变化敏感性计算(斜率、截距)以NDVI时间序列为例

作者:CSDN @ _养乐多_ 本博客将向您介绍如何使用Google Earth Engine(GEE)平台来处理Landsat 5、7和8的卫星图像数据,构建时间序列,以NDVI为例,计算NDVI时间序列的斜率和截距,以及如何导出这些结果供进一步分析使用。 文章目录 一、代码详解1.1 核心代码详解1.2 核心代…...

LC1713. 得到子序列的最少操作次数(java - 动态规划)

LC1713. 得到子序列的最少操作次数 题目描述LIS 动态规划 二分法代码演示 题目描述 难度 - 困难 LC1713.得到子序列的最少操作次数 给你一个数组 target &#xff0c;包含若干 互不相同 的整数&#xff0c;以及另一个整数数组 arr &#xff0c;arr 可能 包含重复元素。 每一次…...

vr飞机驾驶舱模拟流程3D仿真演示加大航飞安全法码

众所周知&#xff0c;航空航天飞行是一项耗资大、变量参数很多、非常复杂的系统工程&#xff0c;因此可利用虚拟仿真技术经济、安全及可重复性等特点&#xff0c;进行飞行任务或操作的模拟&#xff0c;以代替某些费时、费力、费钱的真实试验或者真实试验无法开展的场合&#xf…...

一、八大排序(sort)

文章目录 一、时间复杂度&#xff08;一&#xff09;定义&#xff1a;常数操作 二、空间复杂度&#xff08;一&#xff09;定义&#xff1a; 三、排序&#xff08;一&#xff09;选择排序1.定义2.代码3.特性 &#xff08;二&#xff09;冒泡排序1.定义2.代码3.特性 &#xff08…...

【AWS】AI 代码生成器—Amazon CodeWhisperer初体验 | 开启开挂编程之旅

使用 AI 编码配套应用程序更快、更安全地构建应用程序 文章目录 1.1 Amazon CodeWhisperper简介1.2 Amazon CodeWhisperer 定价2.1 打开VS Code2.2 安装AWS ToolKit插件 一、前言 1.1 Amazon CodeWhisperper简介 1️⃣更快地完成更多工作 CodeWhisperer 经过数十亿行代码的训…...

【Mysql主从配置方法---单主从】

Mysql主从 主服务器 创建用户 create user “for_rep”“从服务器IP地址” IDENTIFIED by “123456” 授权 grant replication slave on . to “for_rep”“从服务器IP地址” IDENTIFIED by “123456” 查看用户权限 SHOW GRANTS FOR “for_rep”“从服务器IP地址”; 修改M…...

⼀⽂读懂加密资产交易赛道的新锐⼒量Bitdu

交易所&#xff0c;仍然是加密资产赛道的皇冠级赛道。围绕这个领域展开的商业竞争&#xff0c;最能引起⼴⼤⽤⼾的关注。 经历了数轮资产价格涨跌的⽜熊之后&#xff0c;⼀批批创业者也在不断地思考这⼀议题 — 如何在去中⼼化的世界中&#xff0c;最⾼效率地集结流量、资本和…...

万里牛与金蝶云星空对接集成查询调拨单连通调拨单新增(万里牛调拨单-金蝶【直接调拨单】)

万里牛与金蝶云星空对接集成查询调拨单连通调拨单新增(万里牛调拨单-金蝶【直接调拨单】) 源系统:万里牛 万里牛是杭州湖畔网络技术有限公司旗下SaaS软件品牌&#xff0c;主要针对电商、外贸、实体门店等业务群体&#xff0c;帮助企业快速布局新零售&#xff0c;提升订单处理效…...

虚拟DOM与diff算法

虚拟DOM与diff算法 snabbdom虚拟DOMdiff算法 snabbdom 是什么&#xff1a;snabbdom是著名的虚拟DOM库&#xff0c;是diff算法的鼻祖&#xff0c;Vue源码借鉴了snabbdom 虚拟DOM 是什么&#xff1a;本质上是存在内存里的 JavaScript 对象 作用&#xff1a;用来描述真实DOM的层…...

K8S:pod资源限制及探针

文章目录 一.pod资源限制1.pod资源限制方式2.pod资源限制指定时指定的参数&#xff08;1&#xff09;request 资源&#xff08;2&#xff09; limit 资源&#xff08;3&#xff09;两种资源匹配方式 3.资源限制的示例&#xff08;1&#xff09;官网示例&#xff08;2&#xff0…...

CSS中的定位

position 的属性与含义 CSS 中的 position 属性用于控制元素在页面中的定位方式&#xff0c;有四个主要的取值&#xff0c;每个取值都会影响元素的布局方式&#xff0c;它们是&#xff1a; static&#xff08;默认值&#xff09;&#xff1a; 这是所有元素的初始定位方式。在静…...

二、链表(linked-list)

文章目录 一、定义二、经典例题&#xff08;一&#xff09;[21.合并两个有序链表](https://leetcode.cn/problems/merge-two-sorted-lists/description/)1.思路2.复杂度分析3.注意4.代码 &#xff08;二&#xff09;[86.分割链表](https://leetcode.cn/problems/partition-list…...

Android EditText筛选+选择功能开发

在日常开发中经常会遇到这种需求&#xff0c;EditText既需要可以筛选&#xff0c;又可以点击选择。这里筛选功能用的是AutoCompleteTextView&#xff0c;选择功能使用的是第三方库https://github.com/kongzue/DialogX。 Android AutoCompleteTextView(自动完成文本框)的基本使用…...

Linux 信号 alarm函数 setitimer函数

/*#include <unistd.h>unsigned int alarm(unsigned int seconds);功能&#xff1a;设置定时器。函数调用&#xff0c;开始倒计时&#xff0c;0的时候给当前的进程发送SIGALARM信号参数&#xff1a;倒计时的时长。。单位&#xff1a;秒 如果参数为0&#xff0c;无效返回…...

自主设计,模拟实现 RabbitMQ - 实现发送方消息确认机制

目录 一、实现发送方消息确认 1.1、需求分析 什么是发送方的消息确认? 如何实现?...

【数据结构】二叉树的·深度优先遍历(前中后序遍历)and·广度优先(层序遍历)

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …...

优彩云采集器下载-免费优彩云采集器下载地址

免费优彩云采集器。您是否曾为了数据采集而感到头疼不已&#xff1f;是否一直在寻找一种能够轻松、高效地获取所需数据的方法&#xff1f;别着急&#xff0c;让我们一起来了解如何通过优彩云采集器解决这些问题&#xff0c;从而让您产生购买的欲望。 免费全自动采集发布批量管理…...

【Python】OJ 常用函数

这里写目录标题 一. math1. 求阶乘 - factorial()2. 绝对值 - fabs() 二. 容器的方法1. reverse() 三. Python 内置函数1. sort() 一. math 需要引入 math 包&#xff1a;import math 1. 求阶乘 - factorial() import math print(math.factorial(5))--------运行结果-------…...

丽水做网站企业/网站推广计划方案

在我们开发的过程中&#xff0c;难免遇到一种场景&#xff0c;把某个表中的的某个列的值拼接成用逗号隔开的一行数据 如图&#xff1a;我们把UserId列拼接成一行数据 -------》 为此我写了一个存储过程来解决此类问题。 -- Author: LHM -- Create date: 2015-01-15 -- D…...

自动做设计的网站/长沙互联网推广公司

1.建立工程文件夹:my_jade 2.下载express和jade包到本地.我个人不喜欢下载成全局的,我喜欢下到工程文件夹中去. 3.建立相关的文件夹和文件. index.js: style.css gys.jade代码: index.jade: layout.jade app.js 运行app.js:node app.js; 在浏览器中预览: 在看一个不使用页面…...

做外贸哪些网站可以发免费信息/济南百度推广优化

描述 可以下载QQ音乐免费音乐和绿钻音乐&#xff0c;但是不能下载付费音乐。 运行程序后会在同级目录自动创建名为&#xff1a;歌曲下载的文件夹&#xff0c;下载的文件将被放置在此文件夹 如果下载的文件大小为1KB&#xff0c;则表示下载失败&#xff0c;这首歌不支持下载 源…...

电子配件 技术支持 东莞网站建设/关键词名词解释

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品经营单位安全管理人员考试内容是由公众号安全生产模拟考试一点通提供&#xff0c;危险化学品经营单位安全管理人员证模拟考试题库是根据危险化学品经营单位安全管理人员最新版教材汇编出危险化学品经营单位…...

毕业设计做网站应该学什么/页优化软件

DB_USER>root,DB_PWD>123456,至此&#xff0c;能够成功登录了&#xff01;2.如何进入修改密码后的phpmyadmin经过上面的修改后&#xff0c;发现登录不了phpmyadmin了&#xff0c;再打开phpMyAdmin就显示&#xff1a;“phpMyAdmin 试图连接到 MySQL 服务器&#xff0c;但服…...

常州全景网站制作/东莞网络推广平台

从2.0开始Spring Security对服务层的方法的安全有了实质性的改善。他提供对JSR-250的注解安全支持象框架原生的Secured注解一样好。从3.0开始你也可以使用新的基于表达式的注解。你可以应用安全到单独的bean,使用拦截方法元素去装饰Bean声明&#xff0c;或者你可以在整个服务层…...