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

Redission · 可重入锁(Reentrant Lock)

前言

Redisson是一个强大的分布式Java对象和服务库,专为简化在分布式环境中的Java开发而设计。通过Redisson,开发人员可以轻松地在分布式系统中共享数据、实现分布式锁、创建分布式对象,并处理各种分布式场景的挑战。

Redisson的设计灵感来自于Redis,但它不仅仅是Redis的Java客户端,更是在分布式环境下构建分布式系统所需的一套工具。无论是分布式集合、分布式锁、还是分布式调度器,Redisson都提供了简单而强大的API,使得开发者能够专注于业务逻辑而不必担心复杂的分布式细节。

底层原理

通过提供易于使用的API和丰富的功能集,Redisson旨在帮助开发者更轻松地构建可靠的、高性能的分布式系统。
Redisson的底层原理主要基于Redis的分布式特性和Java的高级特性。以下是一些关键的底层原理:

  1. Redis协议: Redisson使用Redis协议进行与Redis服务器的通信。这意味着它能够与任何遵循Redis协议的Redis服务器进行交互。通过利用Redis的分布式特性,Redisson实现了分布式对象和服务。
  2. Java序列化: Redisson使用Java对象的序列化和反序列化机制将Java对象转化为Redis数据结构。这使得在Java应用程序和Redis之间传递对象变得简单。默认情况下,Redisson使用标准的Java序列化,但也支持其他序列化方式,如JSON、Jackson等。
  3. 分布式锁的实现: Redisson的分布式锁是通过Redis的SETNX(set if not exists)命令实现的。它利用了Redis的原子性操作,确保在分布式环境中只有一个客户端能够成功获取锁。
  4. 监听器和事件通知: Redisson通过订阅/发布机制实现事件通知。当分布式对象发生变化时,Redisson会发布相应的事件,已注册的监听器将得到通知。这基于Redis的PUB/SUB功能,使得分布式环境下的事件通知成为可能。
  5. 分布式集群: 对于Redis集群,Redisson使用了Redis的集群模式。它能够识别集群中的不同节点,并根据需要进行数据分片和分布式操作。
  6. 线程模型: Redisson使用异步的线程模型来处理与Redis服务器的通信。这有助于提高性能,允许多个操作同时执行而不阻塞主线程。

总体而言,Redisson利用了Redis强大的分布式功能,并通过Java的特性将其封装为易于使用的API。底层的实现涵盖了分布式锁、分布式对象、事件通知等方面,以满足在分布式环境中构建高性能应用程序的需求。

Redisson分布式锁类型

Redisson提供了多种类型的分布式锁,以满足不同场景的需求。以下是一些常见的Redisson分布式锁类型:

  1. 可重入锁(Reentrant Lock): 可以被同一个线程重复加锁的锁。同一个线程在持有锁的情况下可以再次加锁,而不会引起死锁。
  2. 公平锁(Fair Lock): 公平锁按照请求加锁的顺序进行获取锁,即先来先得。这有助于避免某些线程长时间等待的问题,提高公平性。
  3. 联锁(MultiLock): 可以同时获取多个锁,且在释放锁时可以选择全部释放或部分释放。适用于需要操作多个资源的场景。
  4. 红锁(RedLock): RedLock是一种分布式锁算法,使用多个Redis节点来确保锁的强一致性。通过在不同的节点上创建锁,即使其中一个节点失效,其他节点依然可以工作。
  5. 读写锁(ReadWrite Lock): 读写锁分为读锁和写锁,多个线程可以同时持有读锁,但只有一个线程能够持有写锁。适用于读多写少的场景。
  6. 信号量(Semaphore): 类似于Java的Semaphore,用于控制同时访问某个资源的线程数量。
  7. 闭锁(CountDownLatch): 用于等待多个线程完成操作后再执行下一步操作。
  8. 过期锁(Lease Lock): 具有自动过期时间的锁,确保在一定时间内锁会被释放,避免锁长时间占用。

这些锁的类型使得Redisson适用于各种分布式场景,开发者可以根据具体的需求选择合适的锁类型来确保分布式环境下的协同和同步。

可重入锁(Reentrant Lock)

基于Redis的Redisson分布式可重入锁RLockJava对象实现了java.util.concurrent.locks.Lock接口。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。

引入 Maven 依赖

在微服务的 pom.xml 引入 redisson 的 maven 依赖

        <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.2</version> <!-- 使用最新版本 --></dependency>

自定义配置类

下面的代码是单节点 Redis 的配置。

package com.example.demo.config;import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.io.IOException;@Configuration
public class RedissonConfig {/*** 对 Redisson 的使用都是通过 RedissonClient 对象* @return* @throws IOException*/@Bean(destroyMethod="shutdown") // 服务停止后调用 shutdown 方法。public RedissonClient redisson() throws IOException {// 创建配置Config config = new Config();config.useSingleServer().setAddress("redis://xx.xx.x.x:6379").setPassword("123456");// 集群模式// config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");return Redisson.create(config);}
}

测试API

package com.example.demo.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api/redis")
public class MyController {@Autowiredprivate ReentrantLockService reentrantLockService;@GetMapping("/locked-operation")public String performLockedOperation() {// 模拟多个线程同时调用可重入锁的操作for (int i = 1; i <= 5; i++) {final int threadNumber = i;new Thread(() -> {reentrantLockService.performLockedOperation();}).start();}return "Locked operation initiated.";}
}

测试类

package com.example.demo.controller;import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
@Slf4j
public class ReentrantLockService {@Autowiredprivate RedissonClient redissonClient;public void performLockedOperation() {// 获取可重入锁RLock lock = redissonClient.getLock("myReentrantLock");String threadName = Thread.currentThread().getName();try {// 尝试加锁,最多等待10秒,锁的自动释放时间为30秒boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);if (isLocked) {// 执行需要加锁的操作log.info(threadName + " - 获取锁成功,执行加锁操作...");// 模拟业务操作Thread.sleep(5000);log.info(threadName + " - 加锁操作完成。");} else {log.info(threadName + "在指定时间内无法获取锁。");}} catch (InterruptedException e) {Thread.currentThread().interrupt();System.err.println(threadName + "尝试获取锁时发生中断。");} finally {// 释放锁if (lock.isHeldByCurrentThread()) {log.info(threadName + " - 释放锁");lock.unlock();}}}
}

输出日志

2023-11-17 15:56:13.935  INFO 12316 --- [      Thread-17] c.e.d.controller.ReentrantLockService    : Thread-17 - 获取锁成功,执行加锁操作...
2023-11-17 15:56:18.945  INFO 12316 --- [      Thread-17] c.e.d.controller.ReentrantLockService    : Thread-17 - 加锁操作完成。
2023-11-17 15:56:18.969  INFO 12316 --- [      Thread-17] c.e.d.controller.ReentrantLockService    : Thread-17 - 释放锁
2023-11-17 15:56:19.020  INFO 12316 --- [      Thread-16] c.e.d.controller.ReentrantLockService    : Thread-16 - 获取锁成功,执行加锁操作...
2023-11-17 15:56:23.901  INFO 12316 --- [      Thread-19] c.e.d.controller.ReentrantLockService    : Thread-19在指定时间内无法获取锁。
2023-11-17 15:56:23.901  INFO 12316 --- [      Thread-20] c.e.d.controller.ReentrantLockService    : Thread-20在指定时间内无法获取锁。
2023-11-17 15:56:23.909  INFO 12316 --- [      Thread-18] c.e.d.controller.ReentrantLockService    : Thread-18在指定时间内无法获取锁。
2023-11-17 15:56:24.023  INFO 12316 --- [      Thread-16] c.e.d.controller.ReentrantLockService    : Thread-16 - 加锁操作完成。
2023-11-17 15:56:24.046  INFO 12316 --- [      Thread-16] c.e.d.controller.ReentrantLockService    : Thread-16 - 释放锁

分析日志

这段日志展示了一个使用Redisson实现的分布式锁的情景。让我详细解释一下日志和代码的原理:

获取锁(Thread-17):

  • Thread-17 成功获取了名为 “myReentrantLock” 的可重入锁。
  • 执行了一段需要锁保护的业务操作,模拟了一个长时间的操作,持有锁。

释放锁(Thread-17):

- `Thread-17` 完成了业务操作,释放了锁。

获取锁(Thread-16):

  • Thread-16Thread-17 释放锁之后成功获取了相同的锁。
  • 执行了一段需要锁保护的业务操作,然后释放了锁。

获取锁失败(Thread-19, Thread-20, Thread-18):

  • Thread-19, Thread-20, 和 Thread-18 在指定的等待时间内无法获取锁,因为此时 Thread-16 持有锁。

总结原理:

  • 通过redissonClient.getLock("myReentrantLock")创建了一个可重入锁对象。
  • lock.tryLock(10, 30, TimeUnit.SECONDS)尝试获取锁,在10秒内等待,锁的自动释放时间为30秒。
  • 如果获取锁成功,执行需要加锁的操作,然后释放锁。
  • 其他线程在获取锁时,如果超过指定时间未能成功获取,会得到相应的提示。

这段代码通过Redisson实现了一个可重入的分布式锁,确保在分布式环境下对共享资源的安全访问。成功获取锁的线程执行受保护的操作,其他线程则需要等待或处理获取锁失败的情况。这有助于协调分布式系统中的并发访问,防止竞争条件和数据不一致性。

阻塞与非阻塞

阻塞方式

  • 在阻塞方式中,线程在尝试获取锁时,如果锁已被其他线程占用,那么当前线程会被阻塞,一直等到锁被释放后才能继续执行。在阻塞模式下,线程可能会等待相当长的时间,直到获取到锁。
ReentrantLock lock = new ReentrantLock();// 阻塞方式获取锁
lock.lock();
try {// 执行需要锁保护的代码
} finally {lock.unlock();
}

非阻塞方式

  • 在非阻塞方式中,线程尝试获取锁时,如果锁已被其他线程占用,当前线程不会被阻塞,而是立即返回一个结果,告知是否成功获取锁。非阻塞方式下,线程不会等待,而是可以继续执行其他操作。
ReentrantLock lock = new ReentrantLock();// 非阻塞方式尝试获取锁
if (lock.tryLock()) {try {// 执行需要锁保护的代码} finally {lock.unlock();}
} else {// 未获取到锁的处理逻辑
}

看门狗Watchdog

请在此添加图片描述

Redisson 使用看门狗(Watchdog)机制来保持分布式锁的有效性。看门狗是一种定时任务,负责定期延长锁的过期时间,确保在业务执行时间较长或者发生异常情况时,锁不会过早释放。

下面是 Redisson 看门狗的简要原理:

  1. 锁的过期时间: 当获取分布式锁时,会设置锁的过期时间(通常是锁的租期)。这个过期时间是在 Redis 中设置的,表示锁在这段时间内有效。
  2. 看门狗的作用: Redisson 的看门狗定期(比如每隔一定时间)检查当前线程持有的锁是否过期。如果锁的过期时间快到了,看门狗会尝试续租,延长锁的过期时间。
  3. 续租操作: 续租操作是通过发送一个延长锁过期时间的命令到 Redis。如果当前线程在续租时发生了异常,比如网络异常,看门狗会尽力保证在后续的定时任务中继续尝试续租。
  4. 锁的释放: 如果看门狗发现锁已经过期且无法续租,它会尝试删除锁,释放资源。这是为了防止因为业务执行时间较长或者发生异常情况导致锁一直被占用而不释放。
  5. 线程关闭时的处理: Redisson 看门狗还处理了线程关闭的情况。如果获取锁的线程关闭了,看门狗会立即释放锁,以避免死锁情况。

通过看门狗机制,Redisson 能够确保在使用分布式锁的场景下,锁不会因为持有锁的线程异常退出或者执行时间过长而导致锁被过早释放。这提高了分布式锁的可靠性和稳定性。

源码解析

请在此添加图片描述

这段代码是 Redisson 中续租锁过期时间的方法。让我们逐步解析其中的关键部分:

renewExpiration 方法: 这个方法用于执行锁的过期时间续租操作。

private void renewExpiration() {ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ee == null) {return;}// 创建定时任务,定时执行续租操作Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {// 获取续租信息ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ent == null) {return;}Long threadId = ent.getFirstThreadId();if (threadId == null) {return;}// 异步执行续租操作RFuture<Boolean> future = renewExpirationAsync(threadId);future.onComplete((res, e) -> {if (e != null) {// 续租失败,记录错误日志,移除续租信息log.error("Can't update lock " + getRawName() + " expiration", e);EXPIRATION_RENEWAL_MAP.remove(getEntryName());return;}if (res) {// 续租成功,重新调度续租任务renewExpiration();} else {// 续租失败,取消续租任务cancelExpirationRenewal(null);}});}}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); // 续租时间为租约时间的1/3// 将定时任务绑定到续租信息中ee.setTimeout(task);
}

internalLockLeaseTime 变量

请在此添加图片描述

定时任务创建: 使用 commandExecutor.getConnectionManager().newTimeout 创建一个定时任务,这个任务会在 internalLockLeaseTime / 3 毫秒后执行。

续租操作: 在定时任务执行时,异步执行 renewExpirationAsync 方法,该方法负责向 Redis 发送命令更新锁的过期时间。

回调处理: 在异步续租操作完成时,根据续租操作的结果,进行相应的处理。

  • 如果续租成功,重新调度下一次续租任务。
  • 如果续租失败,取消续租任务,并记录错误日志。

这个机制通过定时任务实现了定期的锁续租,确保分布式锁在持有期间不会因为过期而被自动释放。

相关文章:

Redission · 可重入锁(Reentrant Lock)

前言 Redisson是一个强大的分布式Java对象和服务库&#xff0c;专为简化在分布式环境中的Java开发而设计。通过Redisson&#xff0c;开发人员可以轻松地在分布式系统中共享数据、实现分布式锁、创建分布式对象&#xff0c;并处理各种分布式场景的挑战。 Redisson的设计灵感来…...

初阶C语言-指针

1.指针是什么&#xff1f; 理解指针的两个要点&#xff1a; 1.指针是内存中一个最小单元的编号&#xff0c;也就是地址 2.口头语中说的指针&#xff0c;通常是指指针变量&#xff0c;是用来存放内存地址的变量 总结&#xff1a;指针就是地址&#xff0c;口语中说的指针通常是指…...

论文笔记:微表情欺骗检测

整理了AAAI2018 Deception Detection in Videos 论文的阅读笔记 背景模型实验可视化 背景 欺骗在我们的日常生活中很常见。一些谎言是无害的&#xff0c;而另一些谎言可能会产生严重的后果。例如&#xff0c;在法庭上撒谎可能会影响司法公正&#xff0c;让有罪的被告逍遥法外。…...

智能家居有哪些产品?生活中常见的人工智能有哪些?

智能家居有哪些产品? 1、智能照明设备类&#xff1a;智能开关、智能插座、灯控模块、智能空开、智能灯、无线开关。 2、家庭安防类&#xff1a;智能门锁、智能摄像机、智能猫眼、智能门铃。 3、智能传感器类&#xff1a;烟雾传感器、可燃气体传感器、水浸传感器、声光报警器…...

洗车行软件系统有哪些 佳易王洗车店会员管理系统操作教程#洗车店会员软件试用版下载

一、前言 【试用版软件下载可点击本文章最下方官网卡片】 洗车行软件系统有哪些 佳易王洗车店会员管理系统操作教程#洗车店会员软件试用版下载 洗车管理软件应用是洗车业务的得力助手&#xff0c;实现会员管理及数据统计一体化&#xff0c;助力店铺高效、有序运营。 洗车项…...

【Java】springboot 项目中出现中文乱码

在刚创建的springboot项目中&#xff0c;出现乱码&#xff0c;跟走着解决一下 1、Ctrl Shift S 打开idea设置&#xff0c;根据图片来&#xff0c;将③④这三个地方都修改为UTF-8 2、返回配置查看&#xff0c;解决...

开放式耳机是什么意思?漏音吗?开放式的运动蓝牙耳机推荐

目前运动耳机市场主要分为入耳式、骨传导和开放式三类。入耳式耳机占比30%-40%&#xff0c;虽目前占比较大&#xff0c;但因在运动场景下有闷塞感、出汗不适、屏蔽外界环境音带来安全隐患等缺点&#xff0c;占比会逐渐下降。 骨传导耳机占比也为30%-40%&#xff0c;其不堵塞耳…...

如何优雅的处理NPE问题?

1.什么是NPE&#xff1f; NPE&#xff0c;即NullPointerException&#xff0c;是开发中最常见的问题之一&#xff0c;有必要知道如何正确地处理NPE。 对于 Java 开发者来说&#xff0c;null 是一个令人头疼的类型&#xff0c;一不小心就会发生 NPE &#xff08;空指针&#xf…...

k8s 中存储之 NFS 卷

目录 1 NFS 卷的介绍 2 NFS 卷的实践操作 2.1 部署一台 NFS 共享主机 2.2 在所有k8s节点中安装nfs-utils 2.3 部署nfs卷 2.3.1 生成 pod 清单文件 2.3.2 修改 pod 清单文件增加 实现 NFS卷 挂载的 参数 2.3.3 声明签单文件并查看是否创建成功 2.3.4 在 NFS 服务器 创建默认发布…...

Redis中BitMap实现签到与统计连续签到功能

服务层代码 //签到Overridepublic Result sign() {//1.获取当前登录的用户Long userId UserHolder.getUser().getId();//获取日期LocalDateTime now LocalDateTime.now();//拼接keyString keySuffix now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String …...

【Spring】“请求“ 之传递 JSON 数据

文章目录 JSON 概念JSON 语法JSON 的语法JSON 的两种结构 JSON 字符串和 Java 对象互转JSON 优点传递 JSON 对象 JSON 概念 JSON&#xff1a;JavaScript Object Notation【JavaScript 对象表示法】 JSON 就是一种数据格式&#xff0c;有自己的格式和语法&#xff0c;使用文本…...

文心一言 VS 讯飞星火 VS chatgpt (359)-- 算法导论24.3 1题

一、在图 24-2上运行Dijkstra算法&#xff0c;第一次使用结点 s s s作为源结点&#xff0c;第二次使用结点 z z z作为源结点。以类似于图 24-6 的风格&#xff0c;给出每次while循环后的 d d d值和 π π π值&#xff0c;以及集合 S S S中的所有结点。如果要写代码&#xff0c…...

Redis-预热雪崩击穿穿透

预热雪崩穿透击穿 缓存预热 缓存雪崩 有这两种原因 redis key 永不过期or过期时间错开redis 缓存集群实现高可用 主从哨兵Redis Cluster开启redis持久化aof&#xff0c;rdb&#xff0c;尽快恢复集群 多缓存结合预防雪崩&#xff1a;本地缓存 ehcache redis 缓存服务降级&…...

jvisualvm学习

系列文章目录 JavaSE基础知识、数据类型学习万年历项目代码逻辑训练习题代码逻辑训练习题方法、数组学习图书管理系统项目面向对象编程&#xff1a;封装、继承、多态学习封装继承多态习题常用类、包装类、异常处理机制学习集合学习IO流、多线程学习仓库管理系统JavaSE项目员工…...

Gazebo环境下开源UAV与USV联合仿真平台

推荐一个ROS2下基于Gazebo环境的开源UAV与USV联合仿真平台。平台是由两个开源项目共同搭建的。首先是UAV仿真平台&#xff0c;是基于PX4官方仿真平台&#xff08;https://docs.px4.io/main/en/sim_gazebo_gz&#xff09;&#xff1b;其次是USV仿真平台&#xff0c;是基于VRX仿真…...

Linux进程调度和进程切换

并行&#xff08;Parallel&#xff09; 含义&#xff1a;并行是指多个任务在同一时刻同时执行。 硬件要求&#xff1a;需要多个处理器&#xff08;如多核CPU&#xff09;或者多台计算设备来实现&#xff0c;这些执行单元能够真正地同时处理不同的任务。例如&#xff0c;一个具…...

机器学习基本上就是特征工程——《特征工程训练营》

作为机器学习流程的一部分&#xff0c;特征工程是对数据进行转化以提高机器学习性能的艺术。 当前有关机器学习的讨论主要以模型为中心。更应该关注以数据为中心的机器学习方法。 本书旨在介绍流行的特征工程技术&#xff0c;讨论何时以及如何运用这些技术的框架。我发现&…...

Android Framework AMS(01)AMS启动及相关初始化1-4

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要涉及systemserver启动AMS及初始化AMS相关操作。同时由于该部分内容分析过多&#xff0c;因此拆成2个章节&#xff0c;本章节是第一章节&…...

基于基于微信小程序的社区订餐系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…...

[单master节点k8s部署]29.Istio流量管理(五)

测试istio熔断管理。 采用httpbin镜像和fortio镜像&#xff0c;其中httpbin作为服务端&#xff0c;fortio是请求端。这两个的配置yaml文件都在istio的samples/httpbin目录下&#xff0c;fortio的配置文件在samples-client目录下。 [rootmaster httpbin]# ls gateway-api ht…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析&#xff0c;分为​​已启动​​和​​未启动​​两种场景&#xff1a; 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​&#xff1a;当其他组件&#xff08;如Activity、Service&#xff09;通过ContentR…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...