利用redis实现缓存、发布订阅、分布式锁功能
Redis是一个内存键值存储数据库,通常用于缓存、会话管理、消息队列等场景。以下是一些常见的Redis使用场景:
1.缓存:将常用的数据缓存在Redis中,以减少对数据库的访问次数,提高应用程序的性能。
2.会话管理:使用Redis来存储用户的会话数据,以提高应用程序的并发处理能力。
3.发布/订阅系统:使用Redis的发布/订阅功能来实现实时通知、消息推送等功能。
4.分布式锁:使用Redis的分布式锁来实现分布式系统中的互斥访问控制。
5.任务队列:使用Redis的列表或队列来实现异步任务处理、延迟任务等功能。
Jedis是Java开发人员常用的Redis客户端库,它提供了简单易用的API来操作Redis数据库,Jedis支持Redis的所有功能,包括字符串、列表、集合、有序集合等数据结构,以及事务、Lua脚本等高级功能。单同时需要注意Jedis采用了单线程模型,不能充分利用多核CPU的性能,Jedis需要手动管理连接池和资源回收等问题,容易出现内存泄漏等问题,Jedis缺乏异步API的支持,不能很好地处理高并发请求,Jedis的同步调用模型容易出现阻塞问题,需要使用异步模型或者连接池等技术来解决。Jedis本质上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接。
除了Jedis,Lettuce是一个高性能的Redis客户端库,基于Netty框架,提供了异步和响应式API来操作Redis数据库,且支持单机、主从、集群等多种Redis部署方式,另外,采用了连接池和自动重连等技术,能够自动管理连接和资源回收等问题。
前面是一些概念介绍,接下来看看,在实际项目中,如何采用spring boot和redis实现缓存机制。对于Spring boot,如果程序中没有定义类型为CacheManager的Bean组件或是名为cacheResolver的CacheResolver缓存解析器,Spring Boot将按顺序选择并启用以下缓存组件1.Generic,2.JCache (JSR-107)(EhCache 3、Hazelcast、Infinispan等),3.EhCache 2.x,4.Hazelcast,5.Infinispan,6.Couchbase,7.Redis,8.Caffeine,9.Simple。实际上,Spring Boot默认缓存管理中,没有添加任何缓存管理组件能实现缓存管理。因为开启缓存管理后,Spring Boot会按照上述列表顺序查找有效的缓存组件进行缓存管理,如果没有任何缓存组件,会默认使用最后一个Simple缓存组件进行管理。Simple缓存组件是Spring Boot默认的缓存管理组件,它默认使用内存中的ConcurrentMap进行缓存存储,所以在没有添加任何第三方缓存组件的情况下,可以实现内存中的缓存管理,但是不推荐使用这种缓存管理方式。当在Spring Boot默认缓存管理的基础上引入Redis缓存组件,即在pom.xml文件中添加Spring Data Redis依赖启动器后,SpringBoot会使用RedisCacheConfigratioin当做生效的自动配置类进行缓存相关的自动装配,容器中使用的缓存管理器是RedisCacheManager, 这个缓存管理器创建的Cache为RedisCache, 进而操控redis进行数据的缓存。所以,如果要采用redis实现缓存,首先需要引入“spring-boot-starter-data-redis”的依赖,接着在application.properties中配置redis服务相关的配置,如下图所示,另外,还需要创建RedisConfig配置类,配置类中定义cacheManager和redisCacheTemplate,如下图所示。
![](https://img-blog.csdnimg.cn/img_convert/2607bf582afa143405352497cbcf4467.png)
为什么需要在配置类中定义redisCacheTemplate和cacheManager呢?因为默认情况下,缓存的对象的类要实现Serializable接口,是以JDK序列化数据存储在Redis中,如果想实现JSON格式存入缓存中,那么就需要进行序列化,这里使用GenericJackson2JsonRedisSerializer类对value就行序列化。
最后在需要进行缓存的类或者方法上添加Cache相关的注解,即可实现缓存功能。常用注解以及使用场景如下所示:
@Cacheable:可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。该注解一般用在查询方法上。
@CachePut:也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。该注解一般用在新增方法上。
@CacheEvict:是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。该注解一般用在更新和删除方法上。
@EnableCaching:开启缓存功能,一般放在启动类上或者自定义的RedisConfig配置类上。部分代码片段如下所示,所有代码细节可以查看Demo。
![](https://img-blog.csdnimg.cn/img_convert/05b7770c2bc9d6930da773a14a259a34.png)
下载Demo后,配置成本地Mysql的用户名、密码等信息,调用服务的post接口添加数据,再调用get接口获取数据,当从数据库中获取时,会打印的sql日志,具体如下所示。继续用get接口获取数据,则会从缓存中获取,日志中不再打印sql信息,当超过缓存失效时间20second后,又会从数据库中获取。
![](https://img-blog.csdnimg.cn/img_convert/b6085f78b2176f4784756dd21c9c098e.png)
上面介绍了如何使用redis实现缓存,接下来看看发布/订阅功能,采用Redis实现发布订阅有很多优点,例如:
轻量级:Redis是一个内存数据库,所以它非常适合作为轻量级消息代理使用。因为Redis不需要像传统的消息中间件那样持久化数据,所以它可以非常快速地处理消息。
简单易用:使用Redis实现发布订阅非常简单,只需要几行代码就可以实现。
可扩展性:由于Redis可以作为集群部署,所以它的扩展性非常好。它可以轻松地处理大量消息,并且可以在需要时添加更多的Redis节点。
高性能:Redis是一个非常快速的内存数据库,所以它可以处理大量消息并提供快速的响应时间。
当然,相比于专门的消息中间件来传递message,消息中间件会有下面的一些优点。
持久性:与Redis不同,大多数消息中间件都提供消息持久化功能,这意味着即使应用程序在消息被发送后崩溃,消息也不会丢失。
多样性:消息中间件通常提供多种协议,例如AMQP、MQTT和STOMP等,可以让应用程序使用不同的协议进行通信。
可靠性:与Redis不同,大多数消息中间件具有复杂的可靠性机制,例如事务处理和消息确认,以确保消息被传递并正确处理。
高可用性:消息中间件通常提供集群部署,可以在节点故障时提供高可用性。
综上所述,使用Redis实现发布订阅是一个简单、轻量级、快速的解决方案,适合处理大量非关键性消息。而使用消息中间件则适用于更复杂的应用场景,例如需要可靠性保证、持久性和多样性的场景。那么如何使用spring boot,redis实现发布订阅功能呢?非常简单,具体步骤如下所示:
一:添加“spring-boot-starter-data-redis”的依赖
二:在application.properties文件中配置redis的host/port等信息
三:添加配置类信息和创建publish和subscribe服务,具体代码如下所示:在配置类中创建MessageListenerAdapter,MessageListenerAdapter是Spring AMQP中的一个类,它用于将消息侦听器(MessageListener)适配到处理具体消息的方法上。在代码的demo代码中是适配到onReceive()方法上。为什么需要配置MessageListenerAdapter呢?因为在Spring AMQP中,消息侦听器负责处理接收到的消息,但是要正确处理消息,必须了解消息的内容和格式,这使得编写消息处理逻辑变得复杂。MessageListenerAdapter就是为了简化这个过程而设计的。使用MessageListenerAdapter,可以将一个POJO对象的方法适配成消息侦听器,这样就不需要显式地编写MessageListener了。在适配过程中,MessageListenerAdapter将负责将消息转换为方法的参数,并将方法的返回值转换为消息。
此外,还配置了RedisMessageContainer,RedisMessageListenerContainer在Spring应用中使用Redis消息监听器时是必须配置的,它可以帮助自动管理Redis连接和连接池、自动订阅和取消订阅Redis频道或模式、自动分发Redis消息给相应的监听器处理,简化了代码的编写和维护,并确保了应用的可靠性和稳定性。
另外,还配置了ReactiveRedisTemplate,redistemplate是Spring Data Redis中提供的Redis客户端,用于进行与Redis的交互。
![](https://img-blog.csdnimg.cn/img_convert/29b6683e0de16c46f17c3bfca6cc4f10.png)
更多代码细节可查看demo,启动应用程序后,调用post接口"http://localhost:8080/api/news/publish",模拟往频道上发送消息,查看日志信息,可以看到订阅端打印了发送的消息,说明发布和订阅消息成功。
![](https://img-blog.csdnimg.cn/img_convert/3b85db4d539c3591b2e54318dcfda427.png)
除了实现发布订阅功能,还可以借助redis实现分布式锁功能。分布式锁是在分布式系统中协调并发访问共享资源的一种常用机制,其主要目的是保证多个进程或线程在访问共享资源时的正确性和一致性。在分布式系统中,多个进程或线程可能会同时访问同一个共享资源,如数据库、缓存、文件等,如果不采取特殊的处理机制,可能会出现以下问题:
竞态条件:当多个进程或线程同时读写同一共享资源时,可能会出现互相干扰、顺序不确定等问题,导致程序运行不稳定或出现异常。
脏数据:当一个进程或线程正在修改某个共享资源时,如果另一个进程或线程也同时对其进行操作,可能会导致数据出现不一致的情况。
死锁:当多个进程或线程相互等待对方释放锁时,可能会出现死锁的情况,导致程序无法正常运行。
分布式锁就是解决上面这些问题的有效方法,实现的分布式锁应该具备如下的特点:
1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
2、高可用的获取锁与释放锁;
3、高性能的获取锁与释放锁;
4、具备可重入特性;
5、具备锁失效机制,防止死锁;
具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
常见的分布式锁实现方式包括基于数据库、ZooKeeper、Redis等技术实现,其中Redis是最常用的分布式锁实现方式之一。下面提供了利用redis实现分布式锁的部分代码片段。首先,创建RedisDistributedLock对象,在RedisDistributedLock中定义了tryLock()和unLock()方法,即添加锁和释放锁两个方法。对于tryLock(),实际是使用"redisTemplate.opsForValue().setIfAbsent"来实现添加锁的功能,该方法是 Spring Data Redis 提供的一个 Redis 命令封装方法,用于将 key/value 存储到 Redis 中,仅在该 key 不存在时才执行存储操作,并且该方法是线程安全的,可以用来实现分布式锁。释放锁是通过lua脚本实现,具体代码如下所示:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;import java.util.Collections;
import java.util.concurrent.TimeUnit;@Component
public class RedisDistributedLock {private static final long DEFAULT_EXPIRE_TIME = 30000L; // 默认过期时间30秒private static final long DEFAULT_TRY_LOCK_TIMEOUT = 5000L; // 默认尝试获取锁的超时时间5秒private static final String UNLOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then\n" +" return redis.call('del', KEYS[1])\n" +"else\n" +" return 0\n" +"end";@Autowiredprivate RedisTemplate<String, String> redisTemplate;/*** 尝试获取锁** @param lockKey 锁的key* @param requestId 请求id,用于标识加锁的客户端* @param expireTime 锁的过期时间,单位毫秒* @param tryTimeout 尝试获取锁的超时时间,单位毫秒* @return 是否获取到锁*/public boolean tryLock(String lockKey, String requestId, long expireTime, long tryTimeout) {long startTime = System.currentTimeMillis();while (System.currentTimeMillis() - startTime < tryTimeout) {Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.MILLISECONDS);if (locked != null && locked) {return true;}try {Thread.sleep(50);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}return false;}/*** 尝试获取锁,使用默认的过期时间和尝试获取锁的超时时间** @param lockKey 锁的key* @param requestId 请求id,用于标识加锁的客户端* @return 是否获取到锁*/public boolean tryLock(String lockKey, String requestId) {return tryLock(lockKey, requestId, DEFAULT_EXPIRE_TIME, DEFAULT_TRY_LOCK_TIMEOUT);}/*** 释放锁** @param lockKey 锁的key* @param requestId 请求id,用于标识加锁的客户端* @return 是否释放成功*/public boolean unlock(String lockKey, String requestId) {RedisScript<Long> script = new DefaultRedisScript<>(UNLOCK_LUA_SCRIPT, Long.class);Long result = redisTemplate.execute(script, Collections.singletonList(lockKey), requestId);return result != null && result == 1;}
}
创建了RedisDistributedLock对象后,在实际业务代码中就可以调用添加锁和释放锁的方法完成分布式锁业务场景。在执行数据修改操作前调用tryLock()方法添加锁,执行完操作后,再释放锁。
@Autowired
private RedisDistributedLock redisDistributedLock;public void doSomething(String key) {String token = redisDistributedLock.tryLock(key); // 尝试获取锁if (token == null) {// 获取锁失败return;}try {// 执行业务逻辑} finally {redisDistributedLock.releaseLock(key, token); // 释放锁}
}
以上就是通过redis实现分布式锁的主要代码片段,代码补充完整后,可通过性能测试工具并发访问接口,查看数据是否正常。
可以看到,采用spring boot框架,引入“spring-boot-starter-data-redis”依赖,借助已经封装好的注解或者对象,可以很方便的通过redis实现缓存、发布订阅、分布式锁功能。
相关文章:
![](https://img-blog.csdnimg.cn/img_convert/3b85db4d539c3591b2e54318dcfda427.png)
利用redis实现缓存、发布订阅、分布式锁功能
Redis是一个内存键值存储数据库,通常用于缓存、会话管理、消息队列等场景。以下是一些常见的Redis使用场景:1.缓存:将常用的数据缓存在Redis中,以减少对数据库的访问次数,提高应用程序的性能。2.会话管理:使…...
![](https://img-blog.csdnimg.cn/0819aea3def84fa29e7c448f56b9c924.png)
SVN无法连接到服务器的各种问题原因及解决办法
SVN专业使用教程详解 第一节 安装VisualSVN Server服务器 第一步 下载SVN服务器,需要链接的请私信。 点击下载的执行文档进行安装 选择组件 选择在部署 VisualSVN Server 时安装VisualSVN Server 和 Administration Tools 组件。 调整初始服务器配置 或者&…...
![](https://www.ngui.cc/images/no-images.jpg)
React 基本使用
目录 React 安装 React基本使用 React脚手架 脚手架使用React JSX基本使用 JSX列表渲染 JSX条件渲染 JSX模板精简 JSX样式控制 JSX综合案例 React 安装 npm i react react-domnpm init -y(生成基础目录文件) <!-- 引入js文件 --><sc…...
![](https://img-blog.csdnimg.cn/img_convert/8dd304cacb97d125d8517a0cb7b3c1cf.png)
单例模式设计(面试题)
1、static修饰变量规则static修饰的静态成员属于 类而不是对象,所有的对象共享一份静态成员数据,所以不占用类的空间static修饰的成员,定义类的时候,必须分配空间static修饰的静态成员数据 必须类中定义 类外初始化静态成员变量可…...
![](https://img-blog.csdnimg.cn/b5ce06f4ea134eaf895683a4fbb5c975.png)
机器学习:基于支持向量机(SVM)进行人脸识别预测
机器学习:基于支持向量机(SVM)进行人脸识别预测 文章目录机器学习:基于支持向量机(SVM)进行人脸识别预测一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.准备数据2.业务理解3.数据理解4.数…...
![](https://img-blog.csdnimg.cn/79160ed004e046079744386392c6b5b8.jpeg)
【服务器数据恢复】多块磁盘离线导致RAIDZ崩溃的数据恢复案例
服务器数据恢复环境: SUN ZFS系列某型号存储阵列; 40块磁盘组建的存储池(其中4块磁盘用作全局热备盘),池内划分出若干空间映射到服务器使用; 服务器使用Windows操作系统。 服务器故障: 服务器在…...
![](https://img-blog.csdnimg.cn/e230ac33d7dd499d9d18b237bb50a1d2.png)
iconfont 图标如何在uniapp中的tabBar使用
注意: 小程序并不支持tabBar中 设置 iconfont 1. 材料准备 首先进入字体图标网址:iconfont-阿里巴巴矢量图标库;(如果你没有登入,记得登入一下) 把图标添加入购物车 添加到购物车之后-(右上角…...
![](https://img-blog.csdnimg.cn/6713ace18463443a8cdc7c05ae6b170a.png)
第六章.卷积神经网络(CNN)—卷积层(Convolution)池化层(Pooling)
第六章.卷积神经网络(CNN) 6.1 卷积层(Convolution)&池化层(Pooling) 1.整体结构 以5层神经网络的实现为例: 1).基于全连接层(Affine)的网络 全连接层:相邻层的所有神经元之间都有连接 2).常见的CNN的网络 3).全连接层存在的问题 数据的形状容易被…...
![](https://img-blog.csdnimg.cn/1b4563ea0ed242348b47cc93a22265e4.png)
c/c++开发,无可避免的模板编程实践(篇六)
一、泛型算法 1.1 泛型算法概述 c标准库不仅包含数据结构(容器、容器适配器等),还有很多算法。数据结构可以帮助存放特定情况下需要保存的数据,而算法则会将数据结构中存储的数据进行变换。标准库没有给容器添加大量的功能函数&am…...
![](https://img-blog.csdnimg.cn/d33b4be47df346f5bd3b85cc4722060e.png)
【Java】Spring核心与设计思想
文章目录Spring核心与设计思想1. Spring是什么1.1 什么是容器1.2 什么是IOC1.2.1 传统程序开发1.2.2 控制反转式程序开发1.2.3 对比总结规律1.3 理解Spring IOC1.4 DI概念说明Spring核心与设计思想 1. Spring是什么 我们通常所说的Spring指的是Spring Framework(S…...
![](https://img-blog.csdnimg.cn/img_convert/a3af6e5536de9febdc64654bc5157782.png)
组合实现多类别分割(含实战代码)
来源:投稿 作者:AI浩 编辑:学姐 摘要 segmentation_models_pytorch是一款非常优秀的图像分割库,albumentations是一款非常优秀的图像增强库,这篇文章将这两款优秀结合起来实现多类别的图像分割算法。数据集选用CamVid…...
![](https://hnxx.oss-cn-shanghai.aliyuncs.com/official/1673487401625.png?t=0.37365235739630287)
从红队视角看AWD攻击
AWD的权限维持 攻防兼备AWD模式是一种综合考核参赛团队攻击、防御技术能力、即时策略的比赛模式。在攻防模式中,参赛队伍分别防守同样配置的虚拟靶机,并在有限的博弈时间内,找到其他战队的薄弱环节进行攻击,同时要对自己的靶机环…...
![](https://img-blog.csdnimg.cn/img_convert/60594079dd78e9b94bc8c233f2973d9a.png)
龙腾万里,福至万家——“北京龙文化促进协会第九届龙抬头传承会”在京举办
2023年2月21日(农历2月初二)上午9:00点至下午13:00,由北京龙文化促进协会主办、传世经典(北京)文化发展有限公司承办、北京华夏龙文旅联盟协办的“北京龙文化促进协会第九届二月二龙抬头传承会”在北京市丰台区顺和国际大厦A口6层会议厅隆重召开。 传承会活动内容主…...
![](https://www.ngui.cc/images/no-images.jpg)
《软件方法》强化自测题-业务建模(4)
按照业务建模、需求、分析、设计工作流考察,答案不直接给出,可访问自测链接或扫二维码自测,做到全对才能知道答案。 知识点见《软件方法》(http://www.umlchina.com/book/softmeth.html)、 “软件需求设计方法学全程…...
![](https://img-blog.csdnimg.cn/ede475fb2b3e4636b7177fede0c71bca.png)
Prometheus之pushgateway
Pushgateway简介 Pushgateway是Prometheus监控系统中的一个重要组件,它采用被动push的方式获取数据,由应用主动将数据推送到pushgateway,然后Prometheus再从Pushgateway抓取数据。使用Pushgateway的主要原因是: Prometheus和targ…...
![](https://img-blog.csdnimg.cn/img_convert/9a36fd8409df4120bbda13672e7b8c17.png)
3分钟带您快速了解HIL测试及其架构
什么是HIL测试硬件在环(HIL)仿真是一种用于测试导航系统的技术,其中测试前并不知道车辆轨迹。在这种情况下,车辆轨迹被实时馈送到GNSS模拟器。HIL可用于复杂实时系统的开发和测试,如卫星控制系统、军事战术导弹、飞机飞…...
![](https://img-blog.csdnimg.cn/img_convert/a5e6ce6eacd4c6f0ec6043db1ff906af.jpeg)
华为认证含金量如何?
一本证书是否有用,还要看它是否被市场所认可。 我们说华为认证HCIP有用,很大一部分还取决于它极高的适用性和权威性。华为是国内最大的生产销售通信设备的民营通信科技公司。 自2013年起,国家对网络安全极度重视,相继把国外的网…...
![](https://www.ngui.cc/images/no-images.jpg)
刷题记录:牛客NC54586小翔和泰拉瑞亚
传送门:牛客 题目描述: 小翔爱玩泰拉瑞亚 。 一天,他碰到了一幅地图。这幅地图可以分为n列,第i列的高度为Hi,他认为这个地图不好看,决定对它进行改造。 小翔又学会了m个魔法,实施第i个魔法可以使地图的第Li列到第Ri列…...
![](https://img-blog.csdnimg.cn/fc1a15988cea4ebf864c88b8aa7387cf.png)
面试个3年自动化测试,测试水平一言难尽。。。。
公司前段缺人,也面了不少测试,结果竟然没有一个合适的。 一开始瞄准的就是中级的水准,也没指望来大牛,提供的薪资在10-20k,面试的人很多,但平均水平很让人失望。 看简历很多都是3年工作经验,但…...
![](https://img-blog.csdnimg.cn/16c060d9a42c4913b3f059443b245439.png)
C++面向对象(下)
文章目录前言1.再谈构造函数1.初始化列表2.explicit关键字2. static成员1.概念3.友元1.概念2.友元函数3.友元类4. 内部类5.匿名对象6.编译器优化7.总结前言 本文是主要是将之前关于C面向对象中的一些没有归纳到的零星知识点进行补充,同时对C中的面向对象简单收个尾…...
![](https://img-blog.csdnimg.cn/9a633f6b132c48fc90398f64255e5c2a.png)
面试一位软件测试6年工作者:一年经验掰成六年来用....
在众多面试中,对于那个工作了6年的面试者,我印象很深刻,因为最开始拿到简历的时候,我一摸:"这简历,好厚啊!"再一看,工作6年。 于是我去找了我的领导,我说:“这人我应该没…...
![](https://www.ngui.cc/images/no-images.jpg)
Java8 新特性--Optional
Optional是什么 java.util.Optional Jdk8提供Optional,一个可以包含null值的容器对象,可以用来代替xx ! null的判断。 Optional常用方法 of public static <T> Optional<T> of(T value) {return new Optional<>(value); }为value…...
![](https://img-blog.csdnimg.cn/196303784b2c49f5be19e159fe6bb3a9.png)
Pytorch GPU版本简明下载安装教程
1.根据自己的显卡型号下载显卡驱动并安装。这一步会更新你的显卡驱动,也可忽略第1步,如果第2步出现问题,返回执行第1步。 点击这里下载英伟达显卡驱动 2.安装完成后,wincmd打开命令行,输入nvidia-smi,查看…...
![](https://img-blog.csdnimg.cn/img_convert/6f8087eca74d7ae3e93cbbef05c024d3.png)
【C++】map和set的封装
文章目录一、前情回顾二、简化源码三、仿函数四、迭代器五、set的实现六、map的实现七、红黑树代码一、前情回顾 set 参数只有 key,但是map除了key还有value。我们还是需要KV模型的红黑树的: #pragma once #include <iostream> #include <ass…...
![](https://www.ngui.cc/images/no-images.jpg)
互融云金融控股集团管理平台系统搭建
金融控股公司是指对两个或两个以上不同类型金融机构拥有实质控制权,自身仅开展股权投资管理、不直接从事商业性经营活动的有限责任公司或者股份有限公司。 金融控股公司是金融业实现综合经营的一种组织形式,也是一种追求资本投资最优化、资本利润最大化…...
![](https://img-blog.csdnimg.cn/dccedd9b8ee3400cb1e0d50fb0b58fe6.png)
Git复习
1. 引言 现在要用到Git,复习一下关于Git的指令,知识摘自《Pro Git》 2. 起步 git和其他版本控制软件最大的差别在于git是直接记录某个版本的快照,而不是逐渐地比较差异。 安装: sudo apt install git-all设置用户信息: git c…...
![](https://www.ngui.cc/images/no-images.jpg)
WebGPU学习(2)---使用VertexBuffer(顶点缓冲区)
在本文中,我们使用 VertexBuffer 绘制一个矩形。示例地址 1.准备顶点数据 首先,我们准备好顶点数据。定义顶点数据有多种方法,这次我们将在 TypeScript 代码中将其定义为 Float32Array 类型的数据。 const quadVertexSize 4 * 8; // 一个顶…...
![](https://www.ngui.cc/images/no-images.jpg)
【C++之容器篇】AVL树的底层原理和使用
目录前言一、AVL树二、AVL树的底层实现1. 结点类型的定义2. AVL树的定义3. 查找函数4. 插入函数(重难点)三、判断平衡树的方法前言 AVL树其实是在搜索树的基础上加上一些限制因素,从而使搜索树的结构保持相对平衡,通过前面我们对二叉搜索树的学习&#x…...
![](https://hnxx.oss-cn-shanghai.aliyuncs.com/official/1673487401625.png?t=0.37365235739630287)
从交换机安全配置看常见局域网攻击
前言 构建零信任网络,自然离不开网络准入(NAC),这就涉及到交换机的一些安全测试,于是有了此文《从交换机安全配置看常见局域网攻击》。 交换机安全配置 如本文标题所说从交换机安全配置看常见的局域网攻击,那么下面提到的各种攻…...
![](https://img-blog.csdnimg.cn/e28cedd476b545b2b1245a7d50e255cb.png)
工具篇3.5世界热力图
一、定义 世界热力图是一种地图形式,它使用颜色的变化来显示世界各个地区的某种指标(如 GDP、人口、气候等)的分布和密度。通常,世界热力图会使用不同的颜色来表示数据的变化,例如使用蓝色表示低值,红色表…...
![](http://static.oschina.net/uploads/img/201405/26155258_YmUH.jpg)
中国建设第一平台网站/陕西百度代理公司
WEB 前后端分离三个最大的优点在于:1:最大的好处就是前端JS可以做很大部分的数据处理工作,对服务器的压力减小到最小2:后台错误不会直接反映到前台,错误接秒较为友好3:由于后台是很难去探知前台页面的分布情…...
![](/images/no-images.jpg)
wordpress $show_date/友情链接交易购买
最近在做一个项目,其中用到了TabActivity,想截取OnKeyDown事件来提示用户退出程序在api lever 5以上有OnBackPressed重载函数,可以达到同样的效果。 但是我一直没有成功,调试程序发现,因为TabActivity压根没有获取我按…...
![](https://img-blog.csdnimg.cn/4a191b0ce9a44258b0a1e3800ec32580.png)
网站 设计报价/网站在线生成app
原文链接 Android APK和API漏洞扫描器 Windows https://www.vegabird.com/yaazhini/MacOS https://www.vegabird.com/yaazhini/https://www.vegabird.com/yaazhini/report_api.html...
![](/images/no-images.jpg)
网站建设具体流程图/整站优化seo
简书 Wwwwei转载请注明原创出处,谢谢!Java枚举7种常见用法举个例子需求假设现在有三种水果:苹果、香蕉、梨;现在随机选择一样水果,打印选择的结果信息。思路对于这种多分支结构,我们习惯性的使用编号的方式,将三种水果分别编号为1,2,3;通过编号来完成和水…...
![](https://img-blog.csdnimg.cn/img_convert/bf6f1305c71a12f88b82e547bb2a7de2.png)
做网站公司599/黄页88网络营销宝典
在进行数据解析时,先来看看excel测试数据格式,这里采用接口和测试数据分离的方式,即分为两个sheet页签分别存放接口信息,用例信息excel封装成对象步骤1、导入easypoi的坐标2、加载excel的流对象3、导入参数对象ImportParams4、工具…...
![](https://img-blog.csdnimg.cn/img_convert/5b691368bf41a9ad8284bbbf01d6d4a6.png)
徐州网站关键词排名/广州网站快速优化排名
“最遥远的距离,也抵挡不了听同一首歌。”还记得之前给大家分享的网易云音乐7.1.8内测嘛?想必大家对网易云音乐7.1.8里面的我的耳机分你一半情有独钟,那么前几天正式版也更新了,但是好像内有加入一起听的功能!近日&…...