ReactiveStreams、Reactor、SpringWebFlux
注意: 本文内容于 2024-12-28 21:22:12 创建,可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容,请访问原文地址:ReactiveStreams、Reactor、SpringWebFlux。感谢您的关注与支持!
ReactiveStreams是一个处理异步流的规范,定义了Publisher、Subscriber、Subscription、Processor接口。
Reactor是ReactiveStreams的实现,对于Publisher提供了两个核心实现——Mono与Flux。
SpringWebFlux是构建在Reactor之上的响应式Web框架。
本文源码
一、Reactive Streams
Reactive Streams 是一个用于处理异步流数据的标准规范,特别适合处理非阻塞、背压控制的场景。
所谓的背压控制,是指在异步数据流中,消费者根据自身的能力向生产者获取数据进行消费,以避免数据积压导致系统过载或者崩溃。
TCP中的拥塞控制,也可以看作是背压控制的一种实现。
1.1 API规范
Reactive Streams 的四大API接口如下
org.reactivestreams.Publisher
: 发布者接口,提供数据流。- void subscribe(Subscriber<? super T> subscriber)
org.reactivestreams.Subscriber
: 订阅者接口,接收数据流。- void onSubscribe(Subscription subscription)
- void onNext(T item)
- void onError(Throwable throwable)
- void onComplete()
org.reactivestreams.Subscription
: 订阅关系接口,提供控制机制。- void request(long n)
- void cancel()
org.reactivestreams.Processor
: 继承Publisher和Subscriber的接口。
简单绘制一个时序图,加深对整个链路的理解。
使用Publisher、Subscriber、Subscription实现一个简单的订阅功能,示例如下
以下代码,并没有异步相关的内容。只是为了学习整个API流转链路。
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;public class Example01 {private static final Logger log = LoggerFactory.getLogger(Example01.class);/*** 订阅关系*/public static Subscription getSubscription(Subscriber<? super String> subscriber, String... items) {return new Subscription() {private final AtomicBoolean canceled = new AtomicBoolean(false);private final AtomicInteger sendItems = new AtomicInteger(0);/*** request数据* 内部onNext会request后面的数据,而onComplete应该要等所有的数据消费完毕后,才会执行。* 故需要加锁保证线程安全,此处采取CAS。源码参考reactor.core.publisher.Operators.ScalarSubscription#request(long)*/@Overridepublic void request(long n) {if (n > 0) {if (canceled.get()) {return;}if (sendItems.get() >= items.length) {subscriber.onComplete();} else {subscriber.onNext(items[sendItems.getAndIncrement()]);}}}@Overridepublic void cancel() {canceled.compareAndSet(true, true);}};}/*** 发布者*/private static Publisher<String> getPublisher(String... items) {return new Publisher<String>() {@Overridepublic void subscribe(Subscriber<? super String> subscriber) {subscriber.onSubscribe(getSubscription(subscriber, items));}};}/*** 订阅者*/private static Subscriber<String> getSubscriber() {return new Subscriber<String>() {private Subscription subscription;@Overridepublic void onSubscribe(Subscription s) {this.subscription = s;log.info("Subscribed to {}", s);// 请求第一个元素subscription.request(1);}@Overridepublic void onNext(String s) {log.info("Received {}", s);// 请求下一个元素subscription.request(1);}@Overridepublic void onError(Throwable t) {log.error("Error occurred", t);}@Overridepublic void onComplete() {log.info("All items received");}};}public static void main(String[] args) {// 订阅Flux// Flux.just("first", "second", "third").delayElements(Duration.ofSeconds(2))// .subscribe(getSubscriber());/*** org.reactivestreams.Publisher: 发布者* org.reactivestreams.Subscriber: 订阅者* org.reactivestreams.Subscription: 发布者和订阅者之间的桥梁,数据流控制的核心机制。*/// 订阅自定义PublishergetPublisher("first", "second", "third", "fourth", "fifth").subscribe(getSubscriber());while (true) {}}
}
运行结果
1.2 API实现库
Reactive Streams实现如下
- Java9+ java.util.concurrent.Flow
- RxJava: Reactive Extension Java
- Reactor: Reactor Framework
Java9+中提供了java.util.concurrent.Flow
,在标准库中提供ReactiveStreams规范的接口。
ReactiveStreams内部也提供了适配JDK中Flow的适配器org.reactivestreams.FlowAdapters
。
RxJava以及Reactor,分别用于Java开发中不同领域。RxJava一般用于Android开发,Reactor一般用于Spring开发。
二、Reactor
Reactor提供了两个核心类
- reactor.core.publisher.Flux:发布0或N个元素的异步数据流
- reactor.core.publisher.Mono:发布0或1个元素的异步数据流
这两者都是Publisher,主要区别在于发送数据的数量。因此在使用上,相关的API都是差不多的。
2.1 Mono
Mono中的静态方法,用于创建Mono实例。
Mono实例中的成员方法如下
方法名 | 说明 |
---|---|
and | 合并多个 Mono 实例,等所有 Mono 完成后返回一个新的 Mono 。 |
as | 用指定的类型包裹当前 Mono ,通常用于类型转换。 |
block | 阻塞并获取 Mono 的结果,直到执行完成。 |
blockOptional | 类似于 block ,但返回 Optional 包裹的结果。 |
cache | 缓存当前 Mono 的值,使得未来的订阅者可以共享相同的结果。 |
cacheInvalidateIf | 缓存失效条件满足时重新缓存,适用于动态失效策略。 |
cacheInvalidateWhen | 在指定条件下使缓存失效。 |
cancelOn | 当给定的 Publisher 发出信号时,取消当前 Mono 。 |
cast | 强制类型转换为指定的类型。 |
checkpoint | 在流的执行过程中插入检查点,用于调试。 |
concatWith | 与另一个 Mono 或 Flux 连接,按顺序执行。 |
contextWrite | 修改 Mono 的上下文。 |
defaultIfEmpty | 如果 Mono 为空,返回默认值。 |
delayElement | 延迟发出元素的时间。 |
delaySubscription | 延迟订阅,等到指定的时间或事件发生才开始订阅。 |
delayUntil | 延迟直到指定的 Publisher 发出信号时才开始执行。 |
dematerialize | 将一个包含 Signal 的 Mono 转换为原始值的 Mono 。 |
doAfterSuccessOrError | 在执行成功或出错后执行的操作。 |
doAfterTerminate | 在 Mono 结束时执行的操作,不论成功或失败。 |
doFinally | 在 Mono 完成时执行的最终操作。 |
doFirst | 在 Mono 执行前执行的操作。 |
doOnCancel | 当订阅者取消时执行的操作。 |
doOnDiscard | 当元素被丢弃时执行的操作。 |
doOnEach | 对每个发出的信号执行操作。 |
doOnError | 当发生错误时执行的操作。 |
doOnNext | 每次元素发出时执行的操作。 |
doOnRequest | 在请求信号到达时执行的操作。 |
doOnSubscribe | 在订阅时执行的操作。 |
doOnSuccess | 当成功完成时执行的操作。 |
doOnSuccessOrError | 无论成功还是失败,都执行的操作。 |
doOnTerminate | 在终止时执行的操作。 |
elapsed | 返回每个信号的时间戳。 |
expand | 展开 Mono ,生成新的 Mono ,直到满足某个条件。 |
expandDeep | 深度展开 Mono ,通常递归调用直到满足条件。 |
filter | 过滤元素,只有符合条件的元素才会发出。 |
filterWhen | 使用 Publisher 的元素条件来过滤当前 Mono 。 |
flatMap | 转换元素,返回新的 Mono 或 Flux 。 |
flatMapIterable | 将每个元素转换为一个可迭代的元素。 |
flatMapMany | 将元素转换为 Flux 。 |
flux | 将 Mono 转换为 Flux 。 |
handle | 基于元素的条件来决定如何处理流。 |
hasElement | 判断是否包含元素。 |
hide | 隐藏 Mono 的实现细节,返回一个不可观察的 Mono 。 |
ignoreElement | 忽略元素,只关心是否完成。 |
log | 记录 Mono 中的信号,便于调试。 |
map | 将元素映射为另一个元素。 |
mapNotNull | 映射并排除空值。 |
materialize | 将信号转化为一个 Signal 对象。 |
mergeWith | 合并当前 Mono 和另一个 Mono 。 |
metrics | 获取流的度量信息。 |
name | 为 Mono 设置名称,用于调试和监控。 |
ofType | 根据类型过滤信号。 |
onErrorContinue | 在发生错误时继续执行。 |
onErrorMap | 将错误映射为其他类型。 |
onErrorResume | 在发生错误时恢复操作。 |
onErrorReturn | 在发生错误时返回默认值。 |
onErrorStop | 在发生错误时终止流。 |
onTerminateDetach | 在终止时解除与订阅者的连接。 |
or | 连接另一个 Mono ,如果当前 Mono 没有值或为空时执行。 |
publish | 启动 Mono 并返回一个共享的流。 |
publishOn | 指定在哪个线程调度上下文中执行 Mono 。 |
repeat | 重复执行 Mono ,直到满足某个条件。 |
repeatWhen | 基于另一个 Publisher 的信号来控制重复。 |
repeatWhenEmpty | 当 Mono 为空时重复执行。 |
retry | 在发生错误时重试操作。 |
retryWhen | 基于另一个 Publisher 来控制重试。 |
share | 共享执行的结果,避免重复执行。 |
single | 获取 Mono 中唯一的元素。 |
subscribe | 启动流的执行并订阅。 |
subscribeOn | 指定在哪个线程调度上下文中订阅 Mono 。 |
subscribeWith | 通过指定的 Subscriber 订阅 Mono 。 |
subscriberContext | 获取或修改订阅时的上下文。 |
switchIfEmpty | 如果 Mono 为空,则切换到另一个 Mono 。 |
tag | 为 Mono 打上标签,用于调试和日志。 |
take | 限制只获取前 N 个元素。 |
takeUntilOther | 当另一个 Publisher 发出信号时停止当前 Mono 。 |
then | 在当前 Mono 执行完后执行另一个操作。 |
thenEmpty | 在当前 Mono 执行完后返回一个空的 Mono 。 |
thenMany | 在当前 Mono 执行完后返回一个 Flux 。 |
thenReturn | 在当前 Mono 执行完后返回指定的值。 |
timed | 返回元素和其时间戳。 |
timeout | 如果 Mono 在指定时间内没有发出信号,则触发超时。 |
timestamp | 返回元素及其时间戳。 |
toFuture | 将 Mono 转换为 Future 。 |
toProcessor | 将 Mono 转换为 Processor ,适用于与 Flux 的结合。 |
toString | 返回 Mono 的字符串表示。 |
transform | 使用转换函数修改 Mono 。 |
transformDeferred | 延迟转换,直到订阅发生。 |
transformDeferredContextual | 延迟转换并访问上下文。 |
zipWhen | 与另一个 Mono 的信号配对,形成 Mono 的组合。 |
zipWith | 与另一个 Mono 的信号进行合并,形成 Mono 的组合。 |
2.2 Flux
Flux中的静态方法,用于创建Flux实例。
Flux实例中的成员方法如下
方法名 | 说明 |
---|---|
all | 判断 Flux 中的所有元素是否满足给定条件。 |
any | 判断 Flux 中是否有任何一个元素满足给定条件。 |
as | 将 Flux 转换为指定类型的 Publisher 。 |
blockFirst | 阻塞并返回 Flux 中的第一个元素。 |
blockLast | 阻塞并返回 Flux 中的最后一个元素。 |
buffer | 将 Flux 中的元素分成固定大小的缓冲区。 |
bufferTimeout | 按照指定的时间或缓冲区大小将元素分块。 |
bufferUntil | 在满足某个条件时开始一个新的缓冲区。 |
bufferUntilChanged | 将相邻相同的元素合并到同一个缓冲区。 |
bufferWhen | 根据外部 Publisher 切换缓冲区。 |
bufferWhile | 按照指定条件将元素分组为缓冲区。 |
cache | 缓存 Flux 的值,使得未来的订阅者可以共享相同的结果。 |
cancelOn | 当另一个 Publisher 发出信号时取消当前的 Flux 。 |
cast | 将 Flux 强制转换为指定的类型。 |
checkpoint | 在执行流中插入检查点,用于调试和分析。 |
collect | 收集流中的元素,按给定规则生成结果。 |
collectList | 收集 Flux 中的所有元素并返回一个 List 。 |
collectMap | 将 Flux 中的元素收集为一个 Map 。 |
collectMultimap | 将 Flux 中的元素收集为一个多值 Map 。 |
collectSortedList | 将 Flux 中的元素收集为排序的 List 。 |
concatMap | 将元素转换为 Mono ,按顺序处理。 |
concatMapDelayError | 与 concatMap 类似,但在错误发生时延迟处理。 |
concatMapIterable | 将每个元素转换为可迭代的元素,并按顺序合并。 |
concatWith | 与另一个 Flux 连接,按顺序执行。 |
concatWithValues | 连接多个值作为新的 Flux 。 |
contextWrite | 修改 Flux 的上下文。 |
count | 统计 Flux 中元素的数量。 |
defaultIfEmpty | 如果 Flux 为空,则返回默认值。 |
delayElements | 延迟元素的发出。 |
delaySequence | 延迟整个序列的发出。 |
delaySubscription | 延迟订阅,直到指定的时间或事件发生。 |
delayUntil | 延迟直到另一个 Publisher 发出信号。 |
dematerialize | 将一个包含 Signal 的 Flux 转换为原始元素的 Flux 。 |
distinct | 过滤掉重复的元素,保持唯一性。 |
distinctUntilChanged | 过滤掉相邻重复的元素。 |
doAfterTerminate | 在 Flux 完成后执行的操作。 |
doFinally | 在 Flux 终止时执行的操作。 |
doFirst | 在 Flux 执行前执行的操作。 |
doOnCancel | 在 Flux 被取消时执行的操作。 |
doOnComplete | 在 Flux 完成时执行的操作。 |
doOnDiscard | 在元素被丢弃时执行的操作。 |
doOnEach | 对 Flux 发出的每个元素执行操作。 |
doOnError | 在发生错误时执行的操作。 |
doOnNext | 每次 Flux 发出元素时执行的操作。 |
doOnRequest | 在请求信号到达时执行的操作。 |
doOnSubscribe | 在订阅时执行的操作。 |
doOnTerminate | 在 Flux 终止时执行的操作。 |
elapsed | 获取每个元素的时间戳和持续时间。 |
elementAt | 获取指定索引处的元素。 |
expand | 对每个元素进行展开,生成新的元素流。 |
expandDeep | 深度展开 Flux ,通常递归展开元素。 |
filter | 过滤出符合条件的元素。 |
filterWhen | 使用外部 Publisher 的信号过滤 Flux 中的元素。 |
flatMap | 将元素转换为 Flux ,并合并其发出的所有元素。 |
flatMapDelayError | 在发生错误时延迟元素的转换。 |
flatMapIterable | 将元素转换为可迭代的 Flux 。 |
flatMapSequential | 顺序地将元素转换为 Flux 。 |
flatMapSequentialDelayError | 顺序转换,并在发生错误时延迟。 |
getPrefetch | 获取 Flux 的预取量。 |
groupBy | 将元素按指定的键分组。 |
groupJoin | 类似 groupBy ,但用于联接多个流。 |
handle | 根据元素的条件进行流的处理。 |
hasElement | 判断 Flux 中是否包含某个元素。 |
hasElements | 判断 Flux 中是否包含多个元素。 |
hide | 隐藏 Flux 的实现细节,返回不可观察的流。 |
ignoreElements | 忽略 Flux 中的所有元素,只关心终止信号。 |
index | 返回元素在流中的索引。 |
join | 将多个 Flux 中的元素合并为一个字符串。 |
last | 获取 Flux 中的最后一个元素。 |
limitRate | 限制从流中请求的元素数量。 |
limitRequest | 限制从流中请求的最大元素数量。 |
log | 记录流中的元素,用于调试。 |
map | 将元素映射为新的类型。 |
mapNotNull | 映射并排除空值。 |
materialize | 将信号转换为 Signal 对象。 |
mergeComparingWith | 将两个 Flux 合并并根据比较条件排序。 |
mergeOrderedWith | 将两个有序的 Flux 合并。 |
mergeWith | 合并当前 Flux 和另一个 Flux 。 |
metrics | 获取流的度量信息。 |
name | 为 Flux 设置名称,便于调试。 |
next | 获取 Flux 中的下一个元素。 |
ofType | 根据类型过滤信号。 |
onBackpressureBuffer | 在背压时缓存元素。 |
onBackpressureDrop | 在背压时丢弃元素。 |
onBackpressureError | 在背压时触发错误。 |
onBackpressureLatest | 在背压时保留最新的元素。 |
onErrorContinue | 在发生错误时继续执行。 |
onErrorMap | 在错误时将其映射为其他类型。 |
onErrorResume | 在错误时恢复操作。 |
onErrorReturn | 在错误时返回默认值。 |
onErrorStop | 在错误时终止流。 |
onTerminateDetach | 在终止时分离与订阅者的连接。 |
or | 连接另一个 Flux ,如果当前 Flux 为空时执行。 |
parallel | 将 Flux 分发到多个线程进行并行处理。 |
publish | 启动 Flux 并返回一个共享流。 |
publishNext | 在流的每个元素发出时开始新的发布。 |
publishOn | 指定在哪个线程调度上下文中执行流。 |
reduce | 将流中的所有元素合并为单一值。 |
reduceWith | 使用指定初始值对元素进行合并。 |
repeat | 重复执行 Flux 直到满足某个条件。 |
repeatWhen | 基于另一个 Publisher 的信号来控制重复。 |
replay | 缓存并重播流中的元素。 |
retry | 在发生错误时重试操作。 |
retryWhen | 基于另一个 Publisher 来控制重试。 |
sample | 每隔指定时间间隔取一个元素。 |
sampleFirst | 获取流中的第一个元素。 |
sampleTimeout | 超过指定时间间隔时触发超时操作。 |
scan | 对流中的元素执行累加操作。 |
scanWith | 使用给定的初始值对元素执行累加操作。 |
share | 共享流的执行,避免重复执行。 |
shareNext | 将下一个发出的元素共享给多个订阅者。 |
single | 获取 Flux 中唯一的元素。 |
singleOrEmpty | 获取 Flux 中唯一的元素,如果为空返回空。 |
skip | 跳过流中的前 N 个元素。 |
skipLast | 跳过流中的最后 N 个元素。 |
skipUntil | 跳过直到满足某个条件的元素。 |
skipUntilOther | 跳过直到另一个 Flux 发出信号时的元素。 |
skipWhile | 跳过直到满足条件的元素。 |
sort | 对流中的元素进行排序。 |
startWith | 在流的开始处添加额外元素。 |
subscribe | 订阅并启动 Flux 。 |
subscribeOn | 指定在哪个线程调度上下文中订阅流。 |
subscribeWith | 通过指定的 Subscriber 订阅流。 |
subscriberContext | 获取或修改订阅时的上下文。 |
switchIfEmpty | 如果 Flux 为空,则切换到另一个 Flux 。 |
switchMap | 将元素转换为另一个 Flux 并切换执行。 |
switchOnFirst | 在流开始时选择一个 Flux 进行切换。 |
tag | 为 Flux 打标签,便于调试和日志。 |
take | 限制只获取前 N 个元素。 |
takeLast | 获取流中的最后 N 个元素。 |
takeUntil | 获取直到满足条件为止的元素。 |
takeUntilOther | 获取直到另一个 Flux 发出信号时的元素。 |
takeWhile | 获取满足条件的元素,直到条件不满足为止。 |
then | 在当前流完成后执行另一个操作。 |
thenEmpty | 在当前流完成后返回一个空流。 |
thenMany | 在当前流完成后返回另一个 Flux 。 |
timed | 返回每个元素的时间戳和持续时间。 |
timeout | 如果 Flux 在指定时间 |
三、SpringWebFlux
3.1 WebHandler与WebFilter
在SpringMVC中,有Servlet、Filter。
在SpringWebFlux中,有WebHandler、WebFilter,对标的其实就是Servlet API中的Servlet、Filter。甚至执行链也是相似的设计。
Servlet相关知识阅读Servlet - 言成言成啊
Filter相关知识阅读Filter和Listener - 言成言成啊
WebFilter的注册如下
@Bean
@Order(0) // 值越小,优先级越高
@ConditionalOnProperty(name = "allowAllCors.learnFilter", havingValue = "true")
public WebFilter aFilter() {/*** 在servlet中。请求的扭转是 aFilter-->bFilter-->servlet-->bFilter-->aFilter* 在webflux中同理。Filter对应WebFilter,Servlet对应WebHandler*/return (exchange, chain) -> {log.info("aFilter start");return chain.filter(exchange).doOnSuccess(t -> log.info("aFilter end"));};
}
3.2 实际案例
跨域配置
@Bean
@Order(Integer.MIN_VALUE)
@ConditionalOnProperty(name = "allowAllCors.personal", havingValue = "true")
public WebFilter personalCorsFilter(WebSocketHandlerAdapter webFluxWebSocketHandlerAdapter) {WebFilter webFilter = (exchange, chain) -> {ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();HttpHeaders headers = response.getHeaders();//用*会导致范围过大,浏览器出于安全考虑,在allowCredentials为true时会不认*这个操作,因此可以使用如下代码,间接实现允许跨域headers.set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, request.getHeaders().getFirst("origin"));headers.set(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");headers.set(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "*");//允许跨域发送cookieheaders.set(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");if ("OPTIONS".equalsIgnoreCase(request.getMethod().name())) {response.setStatusCode(HttpStatus.OK);return Mono.empty();} else {return chain.filter(exchange);}};log.info("allowAllCors.personal is set to true");return webFilter;}
全局异常拦截/定义响应格式
首先,定义通用响应格式
import lombok.Data;
import reactor.core.publisher.Mono;@Data
public class Resp<T> {private int code;private String msg;private T data;public static <T> Resp<T> ok(T t) {Resp<T> resp = new Resp<>();resp.setCode(0);resp.setMsg("成功");resp.setData(t);return resp;}public static Resp<Void> failure(String msg) {Resp<Void> resp = new Resp<>();resp.setCode(1);resp.setMsg("失败: " + msg);return resp;}public static Resp<Void> error() {Resp<Void> resp = new Resp<>();resp.setCode(500);resp.setMsg("服务器内部错误");return resp;}public static <T> Mono<Resp<T>> getSuccessResp(Mono<T> mono) {return mono.map(Resp::ok);}public static Mono<Resp<Void>> getFailureResp(String msg) {return Mono.just(failure(msg));}public static Mono<Resp<Void>> getErrorResp() {return Mono.just(error());}
}
其次,定义自定义异常DIYException。
最后,配置全局异常拦截。
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import reactor.core.publisher.Mono;
import top.meethigher.utils.Resp;@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public Mono<Resp<Void>> handleException(Exception e) {log.error("api occurred exception", e);return Resp.getErrorResp();}@ExceptionHandler(DIYException.class)public Mono<Resp<Void>> handleDiyException(DIYException e) {log.error("api occurred exception", e);return Resp.getFailureResp(e.getMessage());}
}
相关文章:
ReactiveStreams、Reactor、SpringWebFlux
注意: 本文内容于 2024-12-28 21:22:12 创建,可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容,请访问原文地址:ReactiveStreams、Reactor、SpringWebFlux。感谢您的关注与支持! ReactiveStreams是…...
【潜意识Java】探寻Java子类构造器的神秘面纱与独特魅力,深度学习子类构造器特点
目录 一、子类构造器的诞生背景 (一)为啥要有子类构造器? (二)子类与父类构造器的关系 二、子类构造器的调用规则 (一)默认调用父类无参构造器 (二)显式调用父类构…...
OpenCV调整图像亮度和对比度
【欢迎关注编码小哥,学习更多实用的编程方法和技巧】 1、基本方法---线性变换 // 亮度和对比度调整 cv::Mat adjustBrightnessContrast(const cv::Mat& src, double alpha, int beta) {cv::Mat dst;src.convertTo(dst, -1, alpha, beta);return dst; }// 使用…...
HarmonyOs DevEco studio小技巧40--应用名称、图标与启动动画修改全攻略
一、引言 随着 HarmonyOS 的日益普及,越来越多的开发者投身于这个充满潜力的生态之中。而 DevEco Studio 作为 HarmonyOS 官方推出的集成开发环境,为开发者提供了一站式的开发体验。在应用开发过程中,一些细节上的设置,如应用名称…...
ESP-IDF学习记录(2)ESP-IDF 扩展的简单使用
傻瓜式记录一个示例的打开,编译,运行。后面我再一个个运行简单分析每个demo的内容。 1.打开示例代码 2.选择项目,文件夹 3.选择串口 4.选择调试方式 5.根据硬件GPIO口配置menuconfig 6.构建项目 7.烧录设备,选择串口UART方式 运行…...
2024 年最新 windows 操作系统搭建部署 nginx 服务器应用详细教程(更新中)
nginx 服务器概述 Nginx 是一款高性能的 HTTP 和 反向代理 服务器,同时是一个 IMAP / POP3 / SMTP 代理服务器。Nginx 凭借其高性能、稳定性、丰富的功能集、简单的配置和低资源消耗而闻名。 浏览 nginx 官网:https://nginx.org/ Nginx 应用场景 静态…...
基于ArcGIS Pro的SWAT模型在流域水循环、水生态模拟中的应用及案例分析;SWAT模型安装、运行到结果读取全流程指导
目前,流域水资源和水生态问题逐渐成为制约社会经济和环境可持续发展的重要因素。SWAT模型是一种基于物理机制的分布式流域水文与生态模拟模型,能够对流域的水循环过程、污染物迁移等过程进行精细模拟和量化分析。SWAT模型目前广泛应用于流域水文过程研究…...
Quartz任务调度框架实现任务动态执行
说明:之前使用Quartz,都是写好Job,指定一个时间点,到点执行。最近有个需求,需要根据前端用户设置的时间点去执行,也就是说任务执行的时间点是动态变化的。本文介绍如何用Quartz任务调度框架实现任务动态执行…...
10.MySQL事务
目录 什么是事务为什么有事务存在事务的版本支持事务的提交方式事务常见的操作方式事务异常验证与产出结论事务隔离性理论事务隔离级别的设置与查看事务隔离级别 - 读未提交事务隔离级别 - 读提交事务隔离级别 - 可重复读事务隔离级别 - 串行化MVCC机制3个记录隐藏字段undo日志…...
1.若依介绍
若依框架 好处: 1.快速构建 2.通用模块(登录、权限分配和校验、操作日志功能) 3.代码生成(定义好数据库表的结构,就能自动生成前后端对应的代码) 位置:系统工具-> 代码生成 若依版本 R…...
计算机网络实验室建设方案
一、计算机网络实验室拓扑结构 计算机网络综合实验室解决方案,是面向高校网络相关专业开展教学实训的综合实训基地解决方案。教学实训系统采用 B/S架构,通过公有云教学实训平台在线学习模式,轻松实现网络系统建设与运维技术的教学…...
【Rust自学】6.4. 简单的控制流-if let
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 6.4.1. 什么是if let if let语法允许将if和let组合成一种不太冗长的方式来处理与一种模式匹配的值,同时忽略其余模式。 可以…...
4-1 输出一组成绩中的最高分和最低分
第一行输入人数n,第二行输入每个人的成绩,用空格分开。输出所有成绩中的最高分和最低分。 输入格式: 第一行输入n,大于0的整数;第二行输入n个大于等于0,小于等于100的整数,用空格分开。 输出格式: 最高…...
数据结构:二叉树部分接口(链式)
目录 二叉树的遍历 1.通过前序遍历的数据构造二叉树 2.二叉树销毁 3. 二叉树节点个数 4. 二叉树叶子节点的个数 5.二叉树第k层节点个数 6.二叉树查找值为x的节点 7.二叉树的前/中/后序遍历 8.层序遍历 9.判断二叉树是否是完全二叉树 二叉树的遍历 前序、中序以及后序…...
音视频入门基础:MPEG2-PS专题(1)——MPEG2-PS官方文档下载
一、引言 MPEG2-PS(又称PS,Program Stream,程序流,节目流)是一种多路复用数字音频、视频等的封装容器。MPEG2-PS将一个或多个分组但有共同的时间基准的基本数据流 (PES)合并成一个整体流。它是…...
overleaf中文生僻字显示不正确,显示双线F
我是不想换全文字体的,只是一个生僻字显示不出来,就想要像word一样,把这个生僻字用包含这个生僻字的字体来显示就好了。 解决步骤: 1、使用如下宏包: \usepackage{xeCJK} %声明宏包,主要用于支持在XeTeX…...
代理arp(proxy arp)原理 及配置
openwrt下打开 arp代理方法 proxy arp概念打开方法openwrt下打开 arp代理方法proxy arp概念 定义 Proxy ARP(代理地址解析协议)是一种网络技术,它允许一个设备(通常是路由器)代表另一个设备来回应 ARP(地址解析协议)请求。工作原理 ARP 回顾:在正常的 ARP 过程中,当主…...
torch.tensor
torch.tensor 通过复制数据构造一个张量 (构造出的张量是一个没有自动微分(autograd )历史的张量,也称为叶张量,参考Autograd mechanics)。 torch.tensor(data, *, dtypeNone, deviceNone, requires_gra…...
Lucene 漏洞历险记:修复损坏的索引异常
作者:来自 Elastic Benjamin Trent 有时,一行代码需要几天的时间才能写完。在这里,我们可以看到工程师在多日内调试代码以修复潜在的 Apache Lucene 索引损坏的痛苦。 做好准备 这篇博客与往常不同。它不是对新功能或教程的解释。这是关于花…...
Github优质项目推荐(第十期)
文章目录 Github优质项目推荐(第十期)一、【postiz-app】,14.6k stars - 您的终极 AI 社交媒体调度工具二、【lobe-chat】,50.1k stars - AI 聊天框架三、【cobalt】,22.1k stars - 媒体下载器四、【build-your-own-x】…...
【已解决】“Content-Security-Policy”头缺失
1、作用 简称CSP,意为内容安全策略,通过设置约束指定可信的内容来源,降低异源文件攻击,例如:js/css/image等 2、相关设置值 指令名 demo 说明 default-src self cdn.example.com 默认策略,可以应用于js文件/图片…...
【每日学点鸿蒙知识】Web高度适配、变量声明规范、动画取消、签名文件、包体积优化相关
1、HarmonyOS Web页面高度适配? 在Web页面设置高度100%时,发现和Web控件的高度不一致,这个需要设置什么可以达到页面高度和Web容器高度一致 目前只支持两种web布局模式,分别为Web布局跟随系统WebLayoutMode.NONE和Web基于页面大…...
呼叫中心中间件免费体验测试和freeswitch部署方案
文章目录 前言联系我们部署freeswitch常见问题汇总 前言 大部分的用户想体验呼叫中心中间件的功能,却没有门路。这里可以分享呼叫中心中间件的部署链接,可供用户们免费体验测试。 联系我们 有意向了解呼叫中心中间件的用户,点击该链接可添加…...
游戏开发线性空间下PS工作流程
前言 使用基于物理的渲染,为了保证光照计算的准确,需要使用线性空间; 使用线性空间会带来一个问题,ui 在游戏引擎中的渲染结果与 PS 中的不一致: PS(颜色空间默认是sRGB伽马空间):…...
Mono里运行C#脚本7—MonoImageStorage结构解析
Mono里运行C#脚本7—MonoImageStorage结构解析 定义一个结构来保存EXE文件加载到内存的表示。 typedef struct { MonoRefCount ref; //引用计数,如果这个文件引用计数为0就可以删除。 /* key used for lookups. owned by this image storage. */ char *key; //HASH…...
Mac 查询IP配置,网络代理
常用命令 1.查询IP ifconfig | grep "inet" 2.ping查询 ping 172.18.54.19(自己IP) 3.取消代理,通过在终端执行以下命令,可以取消 Git 的代理设置 git config --global --unset http.proxy git config --global …...
WebRTC 环境搭建
主题 本文主要描述webrtc开发过程中所需的环境搭建 环境: 运行环境:ubuntu20.04 Node.js环境搭建 安装编译 Node.js 所需的依赖包: sudo apt-get updatesudo apt-get install -y build-essential libssl-dev下载 Node.js 源码: curl -sL https://…...
Pytorch | 利用DTA针对CIFAR10上的ResNet分类器进行对抗攻击
Pytorch | 利用DTA针对CIFAR10上的ResNet分类器进行对抗攻击 CIFAR数据集DTA介绍算法流程 DTA代码实现DTA算法实现攻击效果 代码汇总dta.pytrain.pyadvtest.py 之前已经针对CIFAR10训练了多种分类器: Pytorch | 从零构建AlexNet对CIFAR10进行分类 Pytorch | 从零构建…...
基于STM32的智能路灯系统控制的Proteus仿真
文章目录 一、智能路灯系统控制1.题目要求2.思路3.电路仿真3.1 未仿真3.2 开始仿真,显示屏显示初始化连接界面后,转为正常显示界面3.3 按下模式按键,切换为AUTO1模式3.4 再次按下模式按键,切换为HAND模式3.5 切换为时间设置界面&a…...
Kivy App开发遇到的问题
Python的安装 如图示,不要安装在带空格的路径下,Program Files 错误,后面安装kivy部件时导致找不到路径, 只能卸载重装. Python重装后将之前kivy的安装拷贝到新的目录下,不用重新安装 安装kivy,kivy的库都会安装在python的目录下,所以kivy项目设置编译器指向python 安装…...
如何在 ONLYOFFICE 中使用智谱 AI 人工智能插件以及其它实用插件来写文章
如何在 ONLYOFFICE 中使用智谱 AI 人工智能插件以及其它实用插件来写文章 书接上文: 为什么 F-35 拥有更大推力的引擎,只能达到 1.6 马赫速度,然而 F-16 却能达到 2.0 马赫? 这一片其实是我和人工智能一起合写的东西࿰…...
Clickhouse使用基础
# 查看操作系统版本 cat /etc/os-release# clickhouse版本 clickhouse -V# 登录clickhouse客户端 clickhouse-client -u xxx --password xxx -m # -m 或 --multiline:进入客户端后,运行输入多行sql语句建表 # 创建数据库 CREATE DATABASE IF NOT EXIST…...
Docker基础知识 Docker命令、镜像、容器、数据卷、自定义镜像、使用Docker部署Java应用、部署前端代码、DockerCompose一键部署
目录 1.Docker 2.镜像和容器 2.1 定义 2.2 开机自动启动容器 3.docker命令 3.1 docker run 参数说明 3.2 常见命令 3.3 命令演示 3.4 命令别名 4.Docker命令详解 5.数据卷 5.1 定义 5.2 数据卷的相关命令 5.3 数据卷命令 5.4 挂载本地目录或文件 5.4.1 定义 5.4.2 mysql容器目录…...
b站ip属地评论和主页不一样怎么回事
在浏览B站时,细心的用户可能会发现一个有趣的现象:某些用户的评论IP属地与主页显示的IP属地并不一致。这种差异引发了用户的好奇和猜测,究竟是什么原因导致了这种情况的发生呢?本文将对此进行深入解析,帮助大家揭开这一…...
【最新】沃德协会管理系统源码+uniapp前端+环境教程
一.系统介绍 一款基于FastAdminThinkPHPUniapp开发的商协会系统,新一代数字化商协会运营管理系统,以“智慧化会员体系、智敏化内容运营、智能化活动构建”三大板块为基点,实施功能全场景覆盖,一站式解决商协会需求壁垒࿰…...
Cookie+Redis+自定义参数解析器+AOP+自定义校验注解实现鉴权+改动CustomException
文章目录 1.数据库表设计2.基础环境搭建1.目录2.MD5Util.java 加密加盐工具类3.CookieUtil.java4.其余的都是使用EasyCode自动生成的,不再赘述5.测试是否可以访问 3.用户注册1.LoginUserController.java2.UserConstant.java 用户常量3.LoginUserReq.java 请求4.Logi…...
低代码开发 实战转型案例一览
数字浪潮澎湃,企业应用开发需求呈井喷之势。传统全栈开发虽底蕴深厚,然其漫长周期与高昂成本,难以追赶市场快速交付的急切步伐。无代码与低代码平台顺势崛起,宛如暗夜明灯,吸引非技术人员纷至沓来,投身应用…...
Spring Boot实战:构建一个简单的RESTful API
Spring Boot是一个开源框架,旨在简化Spring应用的创建与开发过程。通过Spring Boot,你可以轻松地创建独立、生产级的Spring应用,而不需要复杂的配置。本教程将带领大家一步一步构建一个简单的RESTful API,演示Spring Boot的核心功…...
电力场景配网缺陷系列之销钉缺失检测数据集VOC+YOLO格式3095张2类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):3095 标注数量(xml文件个数):3095 标注数量(txt文件个数):3095 …...
FastAPI中的数据库应用介绍
FastAPI中的数据库应用介绍 FastAPI 是一个高性能和易用的日现应用框架,在构建数据库操作时同样具备高效性和完善性。本文将分为初级和高级应用,帮助你熟练从基础到高级的数据库操作。 一、初级应用 1.安装和配置 FastAPI 通常搭配 ORM 工具来操作数据…...
wordpres当前分类调用父分类的名称和链接
在WordPress中,如果你想在当前分类页面调用并显示父分类的名称和链接,你可以使用以下代码片段: <?php // 获取当前分类的ID $cat_id get_queried_object_id();// 获取当前分类的父分类ID $parent_id get_term($cat_id, category)->…...
自动驾驶3D目标检测综述(六)
停更了好久终于回来了(其实是因为博主去备考期末了hh) 这一篇接着(五)的第七章开始讲述第八章的内容。第八章主要介绍的是三维目标检测的高效标签。 目录 第八章 三维目标检测高效标签 一、域适应 (一)…...
RDFS—RDF模型属性扩展解析
目录 前言1. 什么是RDFS?1.1 RDFS的核心概念1.2 RDFS与RDF的区别 2. RDFS的基础概念2.1 类(Class)2.2 属性(Property)2.3 关系(Relation)2.4 定义域(Domain)2.5 值域&…...
应用层2——FTP文件传输协议
FTP文件传输协议 FTP文件传输协议 TFTP简单文件传输协议 提供不同种类主机系统(软硬件体系不同)之间的文件传输能力 即屏蔽底层操作系统的差异 FTP以C/S模式进行服务,FTP使用TCP协议传输 FTP服务器进程: 一个主进程,n个从属进…...
《探索PyTorch计算机视觉:原理、应用与实践》
《探索PyTorch计算机视觉:原理、应用与实践》 一、PyTorch 与计算机视觉的奇妙相遇二、核心概念解析(一)张量:计算机视觉的数据基石(二)神经网络:视觉任务的智慧大脑(三)…...
【Linux学习五】时间日期指令与查找指令
目录 一、时间日期指令 1.1 显示日期 1.2 设置日期 1.3 日历指令 二、搜索查找类指令 2.1 find指令 2.2 locate指令 2.3 grep指令和管道符号| 一、时间日期指令 1.1 显示日期 date (功能描述:显示当前时间)date %Y࿰…...
程序环境和预处理
程序环境和预处理 1.程序环境1.1如何从test.c到test.exe1.2.翻译环境1.2.1翻译过程1.2.2详细过程 1.3.运行环境 2.预处理2.1 预定义符号2.2 define2.2.1 define定义标识符2.2.2 define定义宏2.2.3 带副作用的宏参数2.2.4 宏和函数的对比 2.3 #undef2.4 命令行定义2.5 条件编译2…...
SpringCloud整合skywalking实现链路追踪和日志采集
1.部署skywalking https://blog.csdn.net/qq_40942490/article/details/144701194 2.添加依赖 <!-- 日志采集 --><dependency><groupId>org.apache.skywalking</groupId><artifactId>apm-toolkit-logback-1.x</artifactId><version&g…...
【Java】面试题 并发安全 (2)
文章目录 可重入锁(ReentrantLock)知识总结1. 可重入锁概念与特点2. 基本语法与使用注意事项3. 底层实现原理4. 面试回答要点 synchronized与lock的区别死锁相关面试题讲解死锁产生的四个条件ConcurrentHashMap2. JDK1.7的ConcurrentHashMap结构添加数据…...
面试场景题系列:设计URL短链
1.场景需求界定 1.缩短URL:提供一个长URL,返回一个短很多的URL。 2.重定向URL:提供一个缩短了的URL,重定向到原URL。 3.高可用、可扩展性和容错性考量。 •写操作:每天生成1亿个URL。 •每秒的写操作数:…...