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

Reactor 第十篇 定制一个生产的WebClient

1 为什么要用 WebClient

刚开始尝试使用 Spring WebFlux 的时候,很多人都会使用 Mono.fromFuture() 将异步请求转成 Mono 对象,或者 Mono.fromSupplier() 将请求转成 MOno 对象,这两种方式在响应式编程中都是不建议的,都会阻塞当前线程。

1.1 Mono.fromFuture() VS WebClient

Mono.fromFuture()方法和使用 WebClient 调用第三方接口之间存在以下区别:

  • 异步 vs. 非阻塞

Mono.fromFuture()方法适用于接收一个 java.util.concurrent.Future 对象,并将其转换为响应式的 Mono。这是一个阻塞操作,因为它会等待 Future 对象完成。而使用 WebClient 调用第三方接口是异步和非阻塞的,它不会直接阻塞应用程序的执行,而是使用事件驱动的方式处理响应。

可扩展性和灵活性:使用 WebClient 可以更灵活地进行配置和处理,例如设置超时时间、请求头、重试机制等。WebClient 还可以与许多其他 Spring WebFlux 组件集成,如 WebSockets、Server-Sent Events 等。而 Mono.fromFuture() 是适用于单个 Future 对象转化为 Mono 的情况,可扩展性较差。

  • 错误处理

WebClient 提供了更丰富的错误处理机制,可以通过 onStatus、onError 等方法来处理不同的 HTTP 状态码或异常。同时,WebClient 还提供了更灵活的重试和回退策略。Mono.fromFuture() 方法只能将 Future 对象的结果包装在 Mono 中,不提供特定的错误处理机制。

  • 阻塞操作

Mono.fromFuture() 会阻塞。当调用 Mono.fromFuture() 方法将 Future 转换为 Mono 时,它会等待 Future 对象的结果返回。在这个等待的过程中,Mono.fromFuture()方法会阻塞当前的线程。这意味着,如果 Future 的结果在运行过程中没有返回,则当前线程会一直阻塞,直到 Future 对象返回结果或者超时。因此,在使用 Mono.fromFuture() 时需要注意潜在的阻塞风险。另外,需要确保F uture 的任务在后台线程中执行,以免阻塞应用程序的主线程。

1.2 Mono.fromFuture VS Mono.fromSupplier

Mono.fromSupplier() 和 Mono.fromFuture() 都是用于将异步执行的操作转换为响应式的 Mono 对象,但它们的区别在于:

Mono.fromSupplier() 适用于一个提供者/生产者,可以用来表示某个操作的结果,该操作是一些纯计算并且没有阻塞的方法。也就是说,Mono.fromSupplier() 将其参数 (Supplier) 所提供的操作异步执行,并将其结果打包成一个 Mono 对象。

Mono.fromFuture() 适用于一个 java.util.concurrent.Future 对象,将其封装成 Mono 对象。这意味着调用 Mono.fromFuture() 方法将阻塞当前线程,直到异步操作完成返回一个 Future 对象。

因此,Mono.fromSupplier() 与 Mono.fromFuture() 的主要区别在于:

Mono.fromSupplier() 是一个非阻塞的操作,不会阻塞当前线程。这个方法用于执行计算型的任务,返回一个封装了计算结果的 Mono 对象。
Mono.fromFuture() 是阻塞操作,会阻塞当前线程,直到异步操作完毕并返回看,它适用于处理 java.util.concurrent.Future 对象。

需要注意的是,如果 Supplier 提供的操作是阻塞的,则 Mono.fromSupplier() 方法本身也会阻塞线程。但通常情况下,Supplier 提供的操作是纯计算型的,不会阻塞线程。

因此,可以使用 Mono.fromSupplier() 方法将一个纯计算型的操作转换为 Mono 对象,而将一个异步返回结果的操作转换为 Mono 对象时,可以使用 Mono.fromFuture() 方法。

2 定制化自己的 WebClient

2.1 初始化 WebClient

WebClient 支持建造者模式,使用 WebClient 建造者模式支持开发自己的个性化 WebClient,比如支持设置接口调用统一耗时、自定义底层 Http 客户端、调用链路、打印接口返回日志、监控接口耗时等等。

WebClient builder 支持以下方法

interface Builder {/*** 配置请求基础的url,如:baseUrl = "https://abc.go.com/v1";和 uriBuilderFactory 冲突,如果有 uriBuilderFactory ,则忽略 baseUrl*/Builder baseUrl(String baseUrl);/*** URI 请求的默认变量。也和 uriBuilderFactory 冲突,如果有 uriBuilderFactory ,则忽略 defaultUriVariables*/Builder defaultUriVariables(Map<String, ?> defaultUriVariables);/*** 提供一个预配置的UriBuilderFactory实例*/Builder uriBuilderFactory(UriBuilderFactory uriBuilderFactory);/*** 默认 header*/Builder defaultHeader(String header, String... values);/*** 默认cookie*/Builder defaultCookie(String cookie, String... values);/*** 提供一个 consumer 来定制每个请求*/Builder defaultRequest(Consumer<RequestHeadersSpec<?>> defaultRequest);/*** 添加一个filter,可以添加多个*/Builder filter(ExchangeFilterFunction filter);/*** 配置要使用的 ClientHttpConnector。这对于插入或自定义底层HTTP 客户端库(例如SSL)的选项非常有用。*/Builder clientConnector(ClientHttpConnector connector);/*** Configure the codecs for the {@code WebClient} in the* {@link #exchangeStrategies(ExchangeStrategies) underlying}* {@code ExchangeStrategies}.* @param configurer the configurer to apply* @since 5.1.13*/Builder codecs(Consumer<ClientCodecConfigurer> configurer);/*** 提供一个预先配置了ClientHttpConnector和ExchangeStrategies的ExchangeFunction。
这是对 clientConnector 的一种替代,并且有效地覆盖了它们。*/Builder exchangeFunction(ExchangeFunction exchangeFunction);/*** Builder the {@link WebClient} instance.*/WebClient build();// 其他方法}

2.2 日志打印及监控

  • 打印参数、url、返回
  • 参数和返回需要转成json
  • 需要打印正常返回日志和异常
  • 正常监控、异常监控、总监控以及响应时间
.doOnSuccess(response-> {log.info("get.success, url={}, response={}, param={}", url, response);
})
.doOnError(error-> {log.info("get.error, url={}", url, error);// 监控
})
.doFinally(res-> {//监控
})

2.3 返回处理

retrieve() // 声明如何提取响应。例如,提取一个ResponseEntity的状态,头部和身体:

.bodyToMono(clazz) 将返回body内容转成clazz对象,clazz 对象可以自己指定类型。如果碰到有问题的无法转化的,也可以先转成String,然后自己实现一个工具类,将String转成 class 对象。

2.3.1 get

public <T> Mono<T> get(String url, Class<T> clazz, T defaultClass) {
long start = System.currentTimeMillis();
return webClient.get().uri(url).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(clazz).doOnSuccess(response-> {log.info("get.success, url={}, response={}, param={}", url, response);}).doOnError(error-> {log.info("get.param.error, url={}", url, error);}).onErrorReturn(defaultClass).doFinally(res-> {}).publishOn(customScheduler);
}

2.3.2 get param 请求

public <T> Mono<T> getParam(String url, MultiValueMap<String, String> param, Class<T> clazz, T defaultClass) {
long start = System.currentTimeMillis();
URI uri = UriComponentsBuilder.fromUriString(url).queryParams(param).build().toUri();return webClient.get().uri(uri).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(clazz).doOnSuccess(response-> {log.info("get.param.success, url={}, response={}, param={}", url, response, JsonUtil.toJson(param));}).doOnError(error-> {log.error("get.param.error, url={}, param={}", url, JsonUtil.toJson(param), error);}).onErrorReturn(defaultClass).doFinally(res-> {// 监控 or 打印日志 or 耗时}).publishOn(customScheduler);
}

2.3.3 post json 请求

public <T> Mono<T> postJson(String url, final HttpParameter4Json parameter, Class<T> clazz, T defaultClass) {
final long start = System.currentTimeMillis();
return webClient.post().uri(url).contentType(MediaType.APPLICATION_JSON).cookies(cookies -> cookies.setAll(parameter.getCookies())).body(Mono.just(parameter.getJsonBody()), ParameterizedTypeReference.forType(parameter.getBodyType())).headers(headers -> headers.setAll(parameter.getHeaders())).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(clazz).doOnSuccess(response-> {log.info("post.json.success, url={}, response={}, param={}", url, response, parameter.getJsonBody());}).doOnError(error-> {log.error("get.param.error, url={}, param={}", url, parameter.getJsonBody(), error);}).onErrorReturn(defaultClass).doFinally(res-> {}).publishOn(customScheduler);}

2.3.4 post form Data 请求

public <T> Mono<T> postFormData(String url, HttpParameter parameter, Class<T> clazz, T defaultClass) {final long start = System.currentTimeMillis();return webClient.post().uri(url).contentType(MediaType.APPLICATION_FORM_URLENCODED).cookies(cookies -> cookies.setAll(parameter.getCookies())).body(BodyInserters.fromFormData(parameter.getMultiValueMapParam())).headers(headers -> headers.setAll(parameter.getMapHeaders())).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(clazz).doOnSuccess(response-> {log.info("post.fromData.success, url={}, response={}, param={}", url, response, JsonUtil.toJson(parameter));}).doOnError(error-> {log.info("get.param.error, url={}, param={}", url, JsonUtil.toJson(parameter), error);}).onErrorReturn(defaultClass).doFinally(res-> {}).publishOn(customScheduler);
}

2.4 异常处理

2.4.1 异常返回兜底

onErrorReturn 发现异常返回兜底数据

2.4.2 异常处理

状态码转成异常抛出

.onStatus(HttpStatus::isError, response -> Mono.error(new RuntimeException("Request failed with status code: " + response.statusCode())))

监控异常

.doOnError(error -> {// log and monitor
})

3 完整的 WebClient


package com.geniu.reactor.webclient;import com.geniu.utils.JsonUtil;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import reactor.netty.resources.LoopResources;
import reactor.netty.tcp.SslProvider;
import reactor.netty.tcp.TcpClient;import java.net.URI;
import java.time.Duration;
import java.util.function.Function;/*** @Author: prepared* @Date: 2023/8/15 11:05*/
@Slf4j
public class CustomerWebClient {public static final CustomerWebClient instance = new CustomerWebClient();/*** 限制并发数 100*/Scheduler customScheduler = Schedulers.newParallel("CustomScheduler", 100);private final WebClient webClient;private CustomerWebClient() {final SslContextBuilder sslBuilder = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE);final SslProvider ssl = SslProvider.builder().sslContext(sslBuilder).defaultConfiguration(SslProvider.DefaultConfigurationType.TCP).build();final int cpuCores = Runtime.getRuntime().availableProcessors();final int selectorCount = Math.max(cpuCores / 2, 4);final int workerCount = Math.max(cpuCores * 2, 8);final LoopResources pool = LoopResources.create("HCofSWC", selectorCount, workerCount, true);final Function<? super TcpClient, ? extends TcpClient> tcpMapper = tcp -> tcp.option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000).option(ChannelOption.SO_TIMEOUT, 10000).secure(ssl).runOn(pool);ConnectionProvider.Builder httpClientOfSWC = ConnectionProvider.builder("HttpClientOfSWC").maxConnections(100_000).pendingAcquireTimeout(Duration.ofSeconds(6));final ConnectionProvider connectionProvider = httpClientOfSWC.build();final HttpClient hc = HttpClient.create(connectionProvider).tcpConfiguration(tcpMapper);final Function<HttpClient, HttpClient> hcMapper = rhc -> rhc.compress(true);final WebClient.Builder wcb = WebClient.builder().clientConnector(new ReactorClientHttpConnector(hcMapper.apply(hc)));
//				.filter(new TraceRequestFilter()); 可以通过Filter 增加trace追踪this.webClient = wcb.build();}public <T> Mono<T> get(String url, Class<T> clazz, T defaultClass) {long start = System.currentTimeMillis();return webClient.get().uri(url).accept(MediaType.APPLICATION_JSON).retrieve().onStatus(HttpStatus::isError, response -> Mono.error(new RuntimeException("Request failed with status code: " + response.statusCode()))).bodyToMono(clazz).doOnSuccess(response-> {log.info("get.success, url={}, response={}, param={}", url, response);}).doOnError(error-> {log.info("get.param.error, url={}", url, error);}).onErrorReturn(defaultClass).doFinally(res-> {}).publishOn(customScheduler);}public <T> Mono<T> getParam(String url, MultiValueMap<String, String> param, Class<T> clazz, T defaultClass) {long start = System.currentTimeMillis();URI uri = UriComponentsBuilder.fromUriString(url).queryParams(param).build().toUri();return webClient.get().uri(uri).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(clazz).doOnSuccess(response-> {log.info("get.param.success, url={}, response={}, param={}", url, response, JsonUtil.toJson(param));}).doOnError(error-> {log.error("get.param.error, url={}, param={}", url, JsonUtil.toJson(param), error);}).onErrorReturn(defaultClass).doFinally(res-> {}).publishOn(customScheduler);}public <T> Mono<T> postJson(String url, final HttpParameter4Json parameter, Class<T> clazz, T defaultClass) {final long start = System.currentTimeMillis();return webClient.post().uri(url).contentType(MediaType.APPLICATION_JSON).cookies(cookies -> cookies.setAll(parameter.getCookies())).body(Mono.just(parameter.getJsonBody()), ParameterizedTypeReference.forType(parameter.getBodyType())).headers(headers -> headers.setAll(parameter.getHeaders())).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(clazz).doOnSuccess(response-> {log.info("post.json.success, url={}, response={}, param={}", url, response, parameter.getJsonBody());}).doOnError(error-> {log.error("get.param.error, url={}, param={}", url, parameter.getJsonBody(), error);}).onErrorReturn(defaultClass).doFinally(res-> {}).publishOn(customScheduler);}public <T> Mono<T> postFormData(String url, HttpParameter parameter, Class<T> clazz, T defaultClass) {final long start = System.currentTimeMillis();return webClient.post().uri(url).contentType(MediaType.APPLICATION_FORM_URLENCODED).cookies(cookies -> cookies.setAll(parameter.getCookies())).body(BodyInserters.fromFormData(parameter.getMultiValueMapParam())).headers(headers -> headers.setAll(parameter.getMapHeaders())).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(clazz).doOnSuccess(response-> {log.info("post.fromData.success, url={}, response={}, param={}", url, response, JsonUtil.toJson(parameter));}).doOnError(error-> {log.info("get.param.error, url={}, param={}", url, JsonUtil.toJson(parameter), error);}).onErrorReturn(defaultClass).doFinally(res-> {}).publishOn(customScheduler);}}

相关文章:

Reactor 第十篇 定制一个生产的WebClient

1 为什么要用 WebClient 刚开始尝试使用 Spring WebFlux 的时候&#xff0c;很多人都会使用 Mono.fromFuture() 将异步请求转成 Mono 对象&#xff0c;或者 Mono.fromSupplier() 将请求转成 MOno 对象&#xff0c;这两种方式在响应式编程中都是不建议的&#xff0c;都会阻塞当…...

桃子叶片病害识别(Python代码,pyTorch框架,深度卷积网络模型,很容易替换为其它模型,带有GUI识别界面)

1.分为三类 健康的桃子叶片 &#xff0c;251张 桃疮痂病一般&#xff0c;857张 桃疮痂病严重&#xff0c;770 张 2. GUI界面识别效果和predict.py识别效果如视频所示桃子叶片病害识别&#xff08;Python代码&#xff0c;pyTorch框架&#xff0c;深度卷积网络模型&#xff0…...

matlab使用教程(21)—求函数最值

1. 求函数最优值 1.1求一元函数的最小值 如果给定了一个一元数学函数&#xff0c;可以使用 fminbnd 函数求该函数在给定区间中的局部最小值。例如&#xff0c;请考虑 MATLAB 提供的 humps.m 函数。下图显示了 humps 的图。 x -1:.01:2; y humps(x); plot(x,y) xlabel(x)…...

Redis中 为什么Lua脚本可以保证原子性?

Redis中 为什么Lua脚本可以保证原子性&#xff1f;...

tda4 videnc-test-app: CONTINUOUS and STEPWISE FRAMEINTERVALS not supported

/* videnc-test-app */ https://git.ti.com/cgit/jacinto7_multimedia/ git clone https://git.ti.com/git/jacinto7_multimedia/videnc-test-app.git // 编译 ./autogen.sh ./configure --enable-maintainer-mode --buildi386-linux --hostaarch64-none-linux CC/home/share…...

[已解决] libGL error: MESA-LOADER: failed to open swrast

在新的服务器中配置好虚拟环境后&#xff0c;利用已有的预训练模型test后&#xff0c;可视化时遇到&#xff1a; libGL error: MESA-LOADER: failed to open swrast: /usr/lib/dri/swrast_dri.so: cannot open shared object file: No such file or directory (search paths /u…...

JVM及垃圾回收机制

文章目录 1、JVM组成&#xff1f;各部分作用&#xff1f;1.1 类加载器&#xff08;Class Loaders&#xff09;1.2 运行时数据区&#xff08;Runtime Data Area&#xff09;1.3 执行引擎&#xff08;Execution Engine&#xff09;1.4 本地方法接口&#xff08;Native Interface&…...

windows11不允许安装winpcap4.1.3

问题&#xff1a;下载安装包后在安装时显示与电脑系统不兼容&#xff0c;不能安装。 原因&#xff1a;winpcap是一个用于Windows操作系统的网络抓包库&#xff0c;有一些安全漏洞&#xff0c;存在被黑客攻击的风险。Windows11为了加强系统安全而禁用了这个库&#xff0c;因此不…...

matlab使用教程(23)—优化函数的参数

本博客向您介绍如何存储或访问向 MATLAB 复合函数&#xff08;如 fzero 或 integral&#xff09;传递的数学函数的额外参数。 MATLAB 复合函数基于某个值范围计算数学表达式。这些函数之所以称为复合函数是因为它们是接受函数句柄&#xff08;函数的指针&#xff09;作为输入…...

基于“互联网+ 服务供应链”的汽车道路救援系统对策分析

1。 建立“互联网服务供应链”背景下汽车道路救援系统 基于互联网的汽车道路救援&#xff0c;两级服务供应链结构是由服务提供商、服务 集成商和客户组成。“互联网服务供应链”背景下汽车道路救援系统组成&#xff0c; 它是一种 B2B2C 的形式&#xff0c;与前述传统汽车道路…...

浅谈泛在电力物联网在电力设备状态在线监测中的应用

安科瑞 华楠 摘要&#xff1a;随着信息化水平的不断发展&#xff0c;泛在电力物联网的建设提上日程&#xff0c;这对提升变电站电力设备在线监测水平&#xff0c;推动智能电网发展具有重要的指导意义。对基于物联网的电力设备状态监测系统进行了研究&#xff0c;概括了泛在电力…...

低通滤波器和高通滤波器

应用于图像低通滤波器和高通滤波器的实现 需要用到傅里叶变换 #include <opencv2/opencv.hpp> #include <Eigen> #include <iostream> #include <vector> #include <cmath> #include <complex>#define M_PI 3.14159265358979323846…...

VS中插入Qt插件后配置项目笔记

Project下要创建四个文件夹: bin(输出目录\工作目录) 、include(头文件目录) 、lib(动态库目录) 、src(源码目录) 一、主项目模块配置&#xff1a; 1.配置属性——>常规——>输出目录加入(..\..\bin\) 2.配置属性——>调试——>工作目录加入($(OutDir)) 备注&am…...

Hugo·Stack主题·使用及修改

代码折叠 cp themes/hugo折-themt-saick/exampleSlte/config.yamsclass"codefold"><summary class"codefold__title"><span class"codefold__title-text">" {{ with .Get 0}}{{.}}{{else}}click to expand{{ end }} "&…...

实战:大数据Spark简介与docker-compose搭建独立集群

文章目录 前言技术积累Spark简介Spark核心功能及优势Spark运行架构 Spark独立集群搭建安装docker和docker-composedocker-compose编排docker-compose编排并运行容器 Spark集群官方案例测试写在最后 前言 很多同学都使用过经典的大数据分布式计算框架hadoop&#xff0c;其分布式…...

嵌入性视角下的企业集成创新网络演化过程

从嵌入性角度来看&#xff0c;集成创新网络以社会关系嵌入或结构嵌入的联结方式&#xff0c;实 现创新资源共享。由于规模经济和能力的差异&#xff0c;较高的信息复杂程度往往更强调网 络化和外部组织之间的联合而不是一体化。企业集成创新网络依靠创新网络结点上 企业的合…...

回归预测 | MATLAB实现FA-ELM萤火虫算法优化极限学习机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现FA-ELM萤火虫算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现FA-ELM萤火虫算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍…...

数据结构数组栈的实现

Hello&#xff0c;今天我们来实现一下数组栈&#xff0c;学完这个我们又更进一步了。 一、栈 栈的概念 栈是一种特殊的线性表&#xff0c;它只允许在固定的一端进行插入和删除元素的操作。 进行数据的插入和删除只在栈顶实现&#xff0c;另一端就是栈底。 栈的元素是后进先出。…...

成集云 | 抖店连接器客户静默下单催付数据同步钉钉 | 解决方案

源系统成集云目标系统 方案介绍 随着各品牌全渠道铺货&#xff0c;主播在平台上直播时客户下了订单后不能及时付款&#xff0c;第一时间客户收不到提醒&#xff0c;不仅造成了客户付款率下降&#xff0c;更大量消耗了企业的人力成本和经济。而成集云与钉钉深度合作&#xff0…...

【算法专题突破】双指针 - 复写零(2)

目录 1. 题目解析 2. 算法原理 3. 代码编写 写在最后&#xff1a; 1. 题目解析 题目链接&#xff1a;1089. 复写零 - 力扣&#xff08;Leetcode&#xff09; 我先来读题&#xff0c; 题目的意思非常的简单&#xff0c;其实就是&#xff0c; 遇到 0 就复制一个写进数组&a…...

一文搞懂JTT1078:车载视频监控协议科普+开发入门

之前聊过JTT808&#xff0c;很多朋友私信问我&#xff0c;车载监控里的视频画面、语音对讲靠什么实现的&#xff1f;答案很简单——JTT1078协议。如果说JTT808是车载监控的“骨架”&#xff0c;负责定位和基础状态传输&#xff0c;那JTT1078就是“神经”&#xff0c;专门管音视…...

免费好用的去水印工具推荐:哪个效果最好?免费去水印工具对比 2026 实测

免费好用的去水印工具推荐&#xff1a;哪个效果最好&#xff1f;免费去水印工具对比 2026 实测 去水印这件事&#xff0c;真的是越来越高频了。自媒体剪素材、收藏喜欢的短视频、整理图片资料……一旦碰到带水印的内容&#xff0c;找个顺手的工具就成了刚需。网上工具多&#x…...

RootlessJamesDSP:无Root环境下的Android全局音频处理方案解析

1. 项目概述&#xff1a;在无根环境中驯服音频的“魔法师”如果你是一个对手机音质有追求的安卓用户&#xff0c;或者是一个喜欢折腾音频处理插件的玩家&#xff0c;那么你很可能听说过或者用过 JamesDSP。它是一款功能强大的音频处理引擎&#xff0c;能够通过复杂的算法&#…...

Intel Quark SoC X1000:物联网边缘计算的核心技术解析

1. Intel Quark SoC X1000&#xff1a;物联网边缘计算的小型化革命在工业自动化现场&#xff0c;一台装备了温度传感器的风机正在持续监测轴承状态。传统方案需要将每秒数百个采样点全部上传云端&#xff0c;不仅占用带宽&#xff0c;延迟更是达到秒级。而采用Intel Quark SoC …...

别再只用GitHub了!手把手教你用GitLab搭建团队专属代码仓库(从群组到项目实战)

别再只用GitHub了&#xff01;手把手教你用GitLab搭建团队专属代码仓库&#xff08;从群组到项目实战&#xff09; 在开源生态中&#xff0c;GitHub无疑是代码托管平台的代名词。但对于需要私有化部署和精细权限控制的团队而言&#xff0c;GitLab提供了更完整的DevOps解决方案。…...

AITranslate:本地化AI翻译工作流框架,构建可编程翻译管道

1. 项目概述与核心价值最近在折腾一个挺有意思的项目&#xff0c;叫AITranslate。这名字一看就知道&#xff0c;它想用AI来干翻译的活儿。但说实话&#xff0c;现在市面上翻译工具多如牛毛&#xff0c;从老牌的谷歌翻译、DeepL&#xff0c;到各种大厂出的AI翻译插件&#xff0c…...

三星48层3D V-NAND深度拆解:从电荷陷阱架构到存储密度革命

1. 初探三星48层3D V-NAND&#xff1a;一次深度拆解与工艺解析作为一名长期关注半导体存储技术的从业者&#xff0c;每次拿到业界巨头的新品进行物理层面的拆解分析&#xff0c;都像是一次充满惊喜的“寻宝”之旅。2016年初&#xff0c;当三星将其早在2015年8月就已预告的256Gb…...

别再死记硬背段码了!用Python脚本自动生成数码管显示码表(支持共阳/共阴)

用Python解放双手&#xff1a;动态生成数码管段码的工程实践 数码管作为电子设计中最基础的显示元件之一&#xff0c;其驱动原理看似简单却暗藏玄机。传统开发流程中&#xff0c;工程师需要反复查阅手册或记忆十六进制段码&#xff0c;这种低效模式在复杂项目中将消耗大量时间。…...

深度解析开源项目:Cursor Pro破解工具技术架构与实战应用完整指南

深度解析开源项目&#xff1a;Cursor Pro破解工具技术架构与实战应用完整指南 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reach…...

Crystal语言Web框架实战:构建高性能API服务的轻量级方案

1. 项目概述&#xff1a;一个轻量级、高性能的Crystal语言Web框架最近在探索一些新兴的编程语言生态时&#xff0c;我注意到了Crystal语言&#xff0c;以及一个名为jvpflum/Crystal的GitHub仓库。乍一看这个标题&#xff0c;可能会让人有些困惑&#xff1a;这究竟是Crystal语言…...