《后端程序猿 · Caffeine 本地缓存》

news2024/12/28 3:25:08

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻一周,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,如需交流,欢迎留言评论。👍

文章目录

    • 写在前面的话
    • 技术统括
      • 技术简介
      • 选型比较
    • 实战运用
      • 快速入门
      • Bean和Util
      • SpringCache 模式
    • 技术拓展
      • 配置属性
      • 清除/驱逐策略
      • 过期/更新策略
      • 填充策略
      • 移除监听
      • 写入/删除监听
      • 统计信息
    • 总结陈词

写在前面的话

笔者所在公司的框架采用 Redis 作为缓存中间件,在部分场景下,可以借用 Redis 实现增强接口性能、减轻数据库压力、充当持久存储等功能。 但程序访问 Redis 毕竟需要消耗网络带宽,此外,经常由于各种因素导致 Redis 的性能降低,诸如编码不当、键过多、 网络异常等。 鉴于此,公司框架基于 Google 开发的高性能的 Java 缓存库 Caffeine,封装了本地缓存工具,便于业务项目使用。由于目前 Redis 已广泛使用,框架层面并未将本地缓存与 @Cacheable 等注解绑定,而是基于按需使用的原则,以注入 Bean 的形式封装工具方法


技术统括

技术简介

【Caffeine 技术简介】
Caffeine 是一个高性能的 Java 本地缓存库,提供了强大的缓存功能和灵活的配置选项,你可以使用 Caffeine 来实现本地缓存,并根据具体的需求进行配置和使用。
Caffeine 是基于 Java8 的高性能本地缓存库,并且在 Spring5 (SpringBoot 2.x) 后,Spring 官方放弃了 Guava,而使用了性能更优秀的 Caffeine 作为默认缓存组件。
Caffeine 是在 Guava Cache 的基础上做一层封装,性能有明显提高,二者同属于内存级本地缓存,从并发的角度来讲,Caffeine明显优于Guava,原因是使用了Java 8最新的StampedLock锁技术。

据说,比 Guava Cache 优秀,那既然 Guava 还没开始用,那就直接它吧。
Maven依赖都不需要单独引入,SB2.x自动引入了,爽。

【Caffeine 优势特性】
Caffeine提供灵活的结构来创建缓存,并且有以下特性:
自动加载条目到缓存中,可选异步方式
可以基于大小剔除
可以设置过期时间,时间可以从上次访问或上次写入开始计算
异步刷新
keys自动包装在弱引用中
values自动包装在弱引用或软引用中
条目剔除通知
缓存访问统计

【Caffeine 使用场景】
Caffeine 是一个高性能的 Java 本地缓存库,适用于各种日常开发场景。
以下是一些 Caffeine 在日常开发中适合做的事情:
缓存常用数据:使用 Caffeine 缓存常用的数据,例如配置信息、用户信息、系统参数等,可以提高数据的访问速度和系统的性能。
减轻数据库压力:将频繁访问的数据库查询结果缓存到 Caffeine 中,可以减轻数据库的压力,提高系统的并发处理能力。
缓存计算结果:使用 Caffeine 缓存计算结果,例如复杂查询结果、数据聚合结果等,可以避免重复计算,提高系统的响应速度。
实现数据预热:在系统启动时将一些常用的数据预先加载到 Caffeine 缓存中,可以提高系统的初始化速度和响应速度。
实现请求限流:使用 Caffeine 缓存来记录请求次数和频率,实现请求的限流和流量控制,保护系统的稳定性和可用性。
实现本地锁:利用 Caffeine 缓存的原子性操作特性,可以实现分布式锁的简单版本,用于控制并发访问。
缓存控制器结果:在 Spring MVC 或 Spring Boot 应用中,可以使用 Caffeine 缓存控制器方法的返回结果,减少方法的执行次数,提高系统的性能和吞吐量。
总的来说,Caffeine 是一个功能强大、性能优越的本地缓存库,适用于各种日常开发场景,可以提高系统的性能、稳定性和可维护性。

【 补充:Guava LoadingCache 适合存储那些满足以下条件的数据】
频繁访问的数据:LoadingCache 提供了高性能的数据读取和写入,适合缓存那些被频繁访问的数据。这些数据可能是热点数据,经常被应用程序读取,但不经常更新。
计算密集型或者昂贵的数据:LoadingCache 可以在缓存中存储一些计算密集型或者昂贵的数据,例如数据库查询结果、API 调用结果等。这样可以避免重复计算或者昂贵的网络请求,提高系统的性能和响应速度。
临时性的数据:LoadingCache 也适合存储一些临时性的数据,例如会话数据、用户权限信息等。这些数据可能在一段时间内频繁被访问,但是不需要长期保存。
不需要持久化的数据:LoadingCache 是一个本地缓存,不具备持久化存储的能力。因此适合存储一些不需要长期保存的数据,例如缓存数据源的数据、临时计算结果等。
需要自动加载和刷新的数据:LoadingCache 支持自动加载和刷新数据,可以通过设置 refreshAfterWrite 来定期刷新缓存数据,以保持数据的新鲜性。因此适合存储那些需要定期刷新的数据,例如缓存数据源的数据、动态配置信息等。
总的来说,LoadingCache 适合存储那些被频繁访问、不需要持久化存储、临时性的数据,以及那些需要自动加载和刷新的数据。通过合理使用 LoadingCache,可以提高系统的性能、减少资源消耗,同时提供更好的用户体验。


选型比较

【Caffeine 和 GuavaCache 差异】
剔除算法方面,GuavaCache采用的是「LRU」算法,而Caffeine采用的是「Window TinyLFU」算法,这是两者之间最大,也是根本的区别。
立即失效方面,Guava会把立即失效 (例如:expireAfterAccess(0) and expireAfterWrite(0)) 转成设置最大Size为0。这就会导致剔除提醒的原因是SIZE而不是EXPIRED。Caffiene能正确识别这种剔除原因。
取代提醒方面,Guava只要数据被替换,不管什么原因,都会触发剔除监听器。而Caffiene在取代值和先前值的引用完全一样时不会触发监听器。
异步化方方面,Caffiene的很多工作都是交给线程池去做的(默认:ForkJoinPool.commonPool()),例如:剔除监听器,刷新机制,维护工作等。

【Caffeine 和 Redis 区别】
本地缓存与分布式缓存对应,缓存进程和应用进程同属于一个JVM,数据的读、写在一个进程内完成。本地缓存没有网络开销,访问速度很快。
从横向对常用的缓存进行对比,有助于加深对缓存的理解,有助于提高技术选型的合理性。下面对比三种常用缓存:Redis、EhCache、Caffeine。
image.png

通常采用 Caffeine/Guava + Redis 实现二级缓存方案,先从本地拿,拿不到再从缓存获取,减少 Redis 的连接消耗。

【Caffeine 有什么用,为什么不直接用Map】
**解答1:**Caffeine 使用了 ConcurrentHashMap 作为其缓存数据结构的基础,但对其进行了一些优化和改进,以提高并发性能、减少内存消耗,并添加了一些额外的功能和特性,使其更适合于高性能的缓存应用场景。
ConcurrentHashMap 是 Java 标准库中提供的线程安全的哈希表实现,它使用了一种分段锁的方式来实现并发访问控制,以提高并发读写性能。Caffeine 利用了 ConcurrentHashMap 的并发性能和线程安全性,并在此基础上进行了优化和改进,以提供更高性能的缓存服务。
总的来说,尽管 Caffeine 的底层实现基于 ConcurrentHashMap,但它对 ConcurrentHashMap 进行了一些改进和扩展,使得它更适合于高性能、低延迟的缓存应用场景。
**解答2:**Caffeine 是一个用于构建内存缓存的 Java 库,它提供了一些高效、可配置的缓存功能,可以帮助开发人员在应用程序中轻松地实现缓存机制。
与直接使用 Java 中的 Map 不同,Caffeine 提供了更多的特性和优势,使得它在某些场景下更为适用:
自动加载和刷新机制:Caffeine 支持自动加载和刷新缓存中的条目,无需手动编写加载逻辑。通过 LoadingCache 接口,可以在缓存中不存在指定键的条目时,自动调用加载逻辑来获取新的值,并将其放入缓存中。
缓存的自动清理和过期处理:Caffeine 提供了可配置的过期策略,可以根据时间、大小或其他条件来自动清理缓存中的条目。这样可以确保缓存不会占用过多的内存,并及时清理过期的条目,保持缓存的有效性和性能。
高性能和低内存占用:Caffeine 的实现经过优化,具有很高的性能和低的内存占用。它使用了一些高效的数据结构和算法,以及基于并发的设计模式,可以在多线程环境下高效地处理并发访问和更新操作。
可扩展性和灵活性:Caffeine 提供了丰富的配置选项和扩展点,可以根据具体的需求和场景来定制缓存的行为。开发人员可以灵活地配置缓存的大小、过期时间、刷新策略等参数,以满足不同应用场景的需求。
总的来说,尽管 Java 中的 Map 提供了基本的键值对存储功能,但在需要更多高级特性和性能优化的场景下,使用 Caffeine 可能会更加合适和方便。Caffeine 提供了更丰富的功能和更高效的实现,可以帮助开发人员构建出更可靠、更高性能的缓存系统。


实战运用

快速入门

Step1、添加Maven依赖

注意,高版本Spring或SB以及依赖了caffeine,直接使用提供的版本即可,不要再额外引入了。

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.1.8</version>
</dependency>

Step2、编写工具Bean

@Component
public class CaffeineCache {

    private final Cache<String, Object> cache;

    public CaffeineCache() {
        // 创建一个基于 Caffeine 的本地缓存,设置最大缓存条目数为 10000,过期时间为 10 分钟
        cache = Caffeine.newBuilder()
                .maximumSize(10000)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build();
    }

    // 添加缓存项
    public void put(String key, Object value) {
        cache.put(key, value);
    }

    // 获取缓存项
    public Object get(String key) {
        return cache.getIfPresent(key);
    }

    // 移除缓存项
    public void remove(String key) {
        cache.invalidate(key);
    }

    // 清空缓存
    public void clear() {
        cache.invalidateAll();
    }
}

Step3、使用测试

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @Autowired
    private CaffeineCache caffeineCache;

    public void doSomething() {
        // 添加缓存项
        caffeineCache.put("key", "value");

        // 获取缓存项
        Object value = caffeineCache.get("key");

        // 移除缓存项
        caffeineCache.remove("key");

        // 清空缓存
        caffeineCache.clear();
    }
}

通过以上步骤,你就可以在 Spring Boot 应用中使用 Caffeine 工具类来创建本地缓存,并在需要时进行缓存操作。你可以根据具体的需求调整缓存的配置参数,例如最大缓存条目数、过期时间等。

Bean和Util

将 Caffeine 缓存工具类设计为 Bean 是比较常见的做法,这样可以更好地与 Spring Boot 集成,并通过依赖注入的方式在需要的地方直接使用,同时也方便在配置类中对其进行配置和管理。
将 Caffeine 缓存工具类设计为 Bean 的优势包括:
便于管理和配置:作为 Spring Bean,可以利用 Spring 的依赖注入和配置功能,更方便地管理和配置 Caffeine 缓存的属性,如最大缓存条目数、过期时间等。
与 Spring Boot 集成更紧密:作为 Spring Bean,与 Spring Boot 的集成更加紧密,可以直接在其他 Bean 中通过自动装配进行使用,而不需要额外的配置。
易于测试:将 Caffeine 缓存工具类设计为 Bean 后,可以更方便地在单元测试中进行模拟和替换,提高测试的可维护性和灵活性。
因此,建议将 Caffeine 缓存工具类设计为 Spring Bean,以便更好地利用 Spring Boot 的特性和功能,并方便在应用程序中使用和管理。


SpringCache 模式

与SB整合通常体现在@Cacheable,有需要再扩展。
公司框架Redis使用已久,为减少影响,还是封装工具Bean,按需使用。


技术拓展

配置属性

initialCapacity 初始的缓存空间大小
maximumSize 缓存的最大条数
maximumWeight 缓存的最大权重
expireAfterAccess 最后一次写入或访问后,经过固定时间过期
expireAfterWrite 最后一次写入后,经过固定时间过期
refreshAfterWrite 写入后,经过固定时间过期,下次访问返回旧值并触发刷新
weakKeys 打开 key 的弱引用
weakValues 打开 value 的弱引用
softValues 打开 value 的软引用
recordStats 缓存使用统计
expireAfterWrite 和 expireAfterAccess 同时存在时,以 expireAfterWrite 为准。
weakValues 和 softValues 不可以同时使用。
maximumSize 和 maximumWeight 不可以同时使用。

清除/驱逐策略

缓存的驱逐策略是为了预测哪些数据在短期内最可能被再次用到,从而提升缓存的命中率。LRU策略或许是最流行的驱逐策略。但LRU通过历史数据来预测未来是局限的,它会认为最后到来的数据是最可能被再次访问的,从而给予它最高的优先级。
Caffeine提供三类驱逐策略:基于大小(size-based),基于时间(time-based)和基于引用(reference-based)

1、基于大小(size-based)
基于大小驱逐,有两种方式:一种是基于缓存大小,一种是基于权重。

// 根据缓存的计数进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .build(key -> createExpensiveGraph(key));

// 根据缓存的权重来进行驱逐(权重只是用于确定缓存大小,不会用于决定该缓存是否被驱逐)
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumWeight(10_000)
    .weigher((Key key, Graph graph) -> graph.vertices().size())
    .build(key -> createExpensiveGraph(key));

2、基于时间:设置缓存的有效时间

// 设置缓存有效期为 10 秒,从最后一次写入开始计时 
Cache<String, String> cache = Caffeine.newBuilder()
    .expireAfterWrite(Duration.ofSeconds(10)) 
    .build();

3、基于引用:设置缓存为软引用或弱引用,利用GC来回收缓存数据。

// 性能较差,不建议使用。
Cache<String, String> cache = Caffeine.newBuilder()
        .weakKeys().weakValues().build();

【弱引用相关补充】

Caffeine.weakKeys() 使用弱引用存储key。如果没有强引用这个key,则GC时允许回收该条目
Caffeine.weakValues() 使用弱引用存储value。如果没有强引用这个value,则GC时允许回收该条目
Caffeine.softValues() 使用软引用存储value, 如果没有强引用这个value,则GC内存不足时允许回收该条目

image.png


过期/更新策略

# expireAfterAccess:设置条目在最后一次访问后的过期时间。默认值为不过期。
# expireAfterWrite:设置条目在被创建或最后一次写入后的过期时间。默认值为不过期。
# refreshAfterWrite:这个其实算更新策略,设置条目在被创建或最后一次写入后的自动刷新时间。默认值为不自动刷新。
# expireAfter:
# 在expireAfter中需要自己实现Expiry接口,这个接口支持create,update,access了之后多久过期,
# 这里和前面两个API不同的是,需要你告诉缓存框架,他应该在具体的某个时间过期
# 也就是通过前面的重写create,update,access的方法,获取具体的过期时间。
Cache<String, String> cacheHandle = Caffeine.newBuilder()
        .expireAfter(new Expiry<String, String>() {
            // 创建后多久过期
            @Override
            public long expireAfterCreate(@NonNull String key, @NonNull String value, long currentTime) {
                return TimeUnit.SECONDS.toNanos(3); //3秒后过期
            }
            // 更新后多久过期
            @Override
            public long expireAfterUpdate(@NonNull String key, @NonNull String value, long currentTime, @NonNegative long currentDuration) {
                return currentDuration; // 保持不变,即不改变过期时间
            }
            // 读取后多久过期
            @Override
            public long expireAfterRead(@NonNull String key, @NonNull String value, long currentTime, @NonNegative long currentDuration) {
                return currentDuration; // 保持不变,即不改变过期时间
            }
        })
        .build();
cacheHandle.put("abc", "123");
System.out.println("abc的值:" + cacheHandle.getIfPresent("abc"));
ThreadUtil.sleep(5000);
System.out.println("abc的值:" + cacheHandle.getIfPresent("abc"));

填充策略

Caffeine 提供了四种缓存添加策略:手动加载,自动加载,手动异步加载和自动异步加载。
很好理解,其实就是build的几个重载模式。
参考:Caffeine Cache 进程缓存利器


移除监听

RemovalListener:如果我们需要在缓存被移除的时候,得到通知产生回调,并做一些额外处理工作。

Cache<String, String> cache = Caffeine.newBuilder()
        .maximumSize(2)
        .removalListener(((key, value, cause) -> {
            System.out.println("Removed key: " + key + ", value: " + value + ", cause: " + cause);
        }))
        .build();
cache.put("key1", "value1");
cache.put("key2", "value22");
cache.put("key3", "value333");
ThreadUtil.sleep(1000);
System.out.println(cache.asMap());

//下方是输出信息:
Removed key: key1, value: value1, cause: SIZE
{key2=value22, key3=value333}

写入/删除监听

@Autowired
private OnelinkCaffeineCacheHandle caffeineCache;

Caffeine<Object, Object> customBuild = caffeineCache.getCustomBuild();
customBuild.writer(new CacheWriter<String, String>() {
    @Override
    public void write(String key, String value) {
        System.out.println("Cache entry written for key: " + key + ", value: " + value);
    }
    @Override
    public void delete(String key, String value, RemovalCause cause) {
        System.out.println("Cache entry removed for key: " + key + ", value: " + value + ", removal cause: " + cause);
    }
});

统计信息

【关于 CacheStats】
Guava 的 CacheStats 类是用来表示缓存的统计信息的。它提供了一组方法来获取缓存的命中率、加载次数、加载失败次数等信息。通过这些统计信息,可以帮助你了解缓存的使用情况,优化缓存的配置和性能。
以下是 CacheStats 类的一些常用方法:
hitCount():返回缓存命中的次数。
missCount():返回缓存未命中的次数。
loadSuccessCount():返回缓存加载成功的次数。
loadExceptionCount():返回缓存加载失败的次数。
totalLoadTime():返回缓存加载的总时间,单位为纳秒。
evictionCount():返回缓存中条目被回收的次数。
使用 CacheStats 类可以帮助你监控缓存的性能,并且根据统计信息来调整缓存的配置,以提高系统的性能和稳定性。

【示例代码】

Tips:for循环中大量使用才有统计的意义,正常生产环境也没必要记录。

public static void main(String[] args) {
    Cache<String, String> cache = Caffeine.newBuilder()
            .maximumSize(2)
            .recordStats()
            .removalListener(((key, value, cause) -> {
                System.out.println("Removed key: " + key + ", value: " + value + ", cause: " + cause);
            }))
            .build();
    
    // 添加一些条目
    cache.put("key1", "value1");
    cache.put("key2", "value22");
    cache.put("key3", "value333");
    ThreadUtil.sleep(1000);
    
    // 此时缓存大小超过最大权重,将会驱逐一些条目
    System.out.println(cache.asMap());
    System.out.println(cache.stats());
    
    //输出信息如下:
    // Removed key: key1, value: value1, cause: SIZE
    // {key2=value22, key3=value333}
    // CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadFailureCount=0, totalLoadTime=0, evictionCount=1, evictionWeight=1}
    // hitCount(): 返回命中缓存的总数
    // evictionCount():缓存逐出的数量
}

总结陈词

上文介绍了本地缓存的用法,仅供参考。
本地缓存具备其独有美丽,可以搭配Redis发挥更多作用。
但切记缓存都是一个双刃剑,用的姿势如果不对,会造成更严重的后果。

💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1898462.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4、SSD主控

简述 主控是个片上系统&#xff0c;由硬件和固件组成一个功能完整的系统&#xff1b;上文所述的FTL就属于主控的固件范畴。主控闪存构成了整个SSD&#xff0c;在闪存确定的情况下&#xff0c;主控就反映了各家SSD的差异。实时上各家SSD的差异也主要反应在主控上&#xff0c;毕…

TCP和IP数据包结构

一、问题引入 一般我们在谈上网速度的时候&#xff0c;专业上用带宽来描述&#xff0c;其实无论说网速或者带宽都是不准确的&#xff0c;呵呵。比如&#xff1a;1兆&#xff0c;512K……有些在学校的学生&#xff0c;也许会有疑问&#xff0c;明明我的业务是1M&#xff0c;为…

打开浏览器控制台,点击应用,浏览器崩溃

调试的时候&#xff0c;打开控制台&#xff0c;点击 “应用” 立马浏览器奔溃&#xff0c;但是点击别的没问题 调查发现是因为manifest.json这个文件引起的 manifest.json 最主要的原因是因为没有设置这个sizes字段 Google浏览器更新大概到126之后的版本会有问题&#xff0c;之…

vuepress使用简介及个人博客搭建

目录 一、介绍二、环境准备三、安装运行vuepress四、目录结构五、配置文件六、导航栏配置七、导航栏logo八、浏览器图标九、侧边栏配置十、添加 Git 仓库和编辑链接十一、部署到GitHub十二、搭建成功 一、介绍 VuePress 是 Vuejs 官方提供的一个是Vue驱动的静态网站生成器&…

10.09面试题目记录

艾融软件 - 线上面试题 排序算法的时间复杂度 O(n^2&#xff09;&#xff1a;冒泡&#xff0c;选择&#xff0c;插入 O(logn&#xff09;&#xff1a;折半插入排序 O(nlogn)&#xff1a;希尔&#xff0c;归并&#xff0c;快速&#xff0c;堆 O(nk)&#xff1a;桶&#xff0c;…

12款超良心好用APP推荐,每一款都值得下载!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/分享是奉献的果实&#xff0c;分享是快乐的前提。每天给小伙伴们分享自己认可的软件&#xff0c;也是莫大的幸福&#xff0c;今天获得12款好用的软…

【qt】如何获取本机的IP地址?

需要用到这个类QHostInfo和pro里面添加network模块 用这个类的静态函数forName()来获取该主机名的信息 返回的就是这个类 这个QHostInfo类就包括主机的IP地址信息 用静态函数addresses()来获取 返回的是一个QHostAddress的容器 QList<QHostAddress>addrList hostIn…

基于React和TypeScript的开源白板项目(Github项目分享)

在学习前端开发的过程中&#xff0c;有时候我们需要一些有趣的项目来提升我们的技能。今天我要给大家介绍的是一个非常酷的项目——NinjaSketch&#xff0c;这是一个用React和TypeScript构建的简易白板工具。这个项目使用了Rough.js来实现手绘风格的效果。尽管这个应用不是响应…

知识图谱和 LLM:多跳问答

检索增强生成&#xff08;RAG&#xff09;应用程序通过将外部来源的数据集成到 LLM 中&#xff0c;擅长回答简单的问题。但他们很难回答涉及将相关信息之间的点连接起来的多部分问题。这是因为 RAG 应用程序需要一个数据库&#xff0c;该数据库旨在存储数据&#xff0c;以便轻松…

html+js+css在线倒计时

代码在图片后面 点赞加关注 谢谢大佬照顾&#x1f61c; 图例 时间到前 时间到后 源代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width,…

Docker:三、安装nginx与tomcat

&#x1f341;安装常见服务 &#x1f332;安装nginx &#x1f9ca;1、搜索镜像 Ⅰ.hub docker上查询&#xff1a;https://hub.docker.com/_/nginx Ⅱ. 命令查询&#xff1a;docker search nginx &#x1f9ca;2、下载镜像 命令&#xff1a;docker pull nginx &#x1f9c…

google 邮件信息收集

主要介绍通过google和fofax对目标进行邮件信息收集 chrome插件 email-whatsapp-extractor link-klipper-extract-all bulk-url-opener-extension email-whatsapp-extractor 使用正则表达式&#xff0c;获取访问页面内所有的email邮箱和whatsapp号码&#xff0c;以表格的形式导…

vulnhub--IMF

环境 攻击机&#xff1a;192.168.96.4 靶机&#xff1a;ip未知 主机探测 确定靶机ip为32的主机 端口扫描 访问80端口 外围打点 在contact.php页面源码中找到了flag1 之后没啥突破 但查看网络后发现contact.php页面请求的三个js文件的文件名很有特点&#xff0c;猜测是base64编码…

奥比中光astra_pro相机使用记录

一、信息获取 1、官网 用于了解产品信息 http://www.orbbec.com.cn/sys/37.html 2、开发者社区 咨询问题下载开发部https://developer.orbbec.com.cn/ 二 、windowvs19 1、相机型号 orbbec_astro_pro 根据对应的型号找到需要的包工具 踩坑1&#xff0c;因为这个相机型号…

C++ UTF-8编解码

icu 编解码数据&#xff1a; extern const UConverterSharedData _MBCSData, _Latin1Data, _UTF8Data, _UTF16BEData, _UTF16LEData, _UTF32BEData, _UTF32LEData, _ISO2022Data, _LMBCSData1,_LMBCSData2, _LMBCSData3, _LMBCSData4, _LMBCSData5, _LMBCSDat…

数字信号处理及MATLAB仿真(2)——离散系统

上回书说到如何来编写一些简单的离散时间序列&#xff0c;今天咱们就来谈谈一些关于常系数差分方程的操作吧。 说到这里咱们对于常系数差分方程可能最关心的就是怎么去求解了。 其中最关键的部分就是filter函数&#xff0c;可以用来计算系统在输入信号为x的输出信号y。大家学过…

Hilbert编码 思路和scala 代码

需求&#xff1a; 使用Hilbert 曲线对遥感影像瓦片数据进行编码&#xff0c;获取某个区域的编码值即可 Hilbert 曲线编码方式 思路 大致可以对四个方向的数据进行归类 左下左上右上右下 这个也对应着编码的顺序 思考在不同Hilbert深度&#xff08;阶&#xff09;情况下的…

【重磅】万能模型-直接能换迪丽热巴的模型

万能模型&#xff0c;顾名思义&#xff0c;不用重新训练src&#xff0c;直接可以用的模型&#xff0c;适应大部分原视频脸 模型用法和正常模型一样&#xff0c;但可以跳过训练阶段&#xff01;直接到合成阶段使用该模型 本模型没有做Xseg&#xff0c;对遮挡过多的画面不会自动适…

掌握 Postman 脚本:入门指南

在探索 API 测试自动化环墁下&#xff0c;Postman 脚本显现其强大功能和灵活性&#xff0c;它不仅仅是 API 测试的工具&#xff0c;更是一个综合性的自动化平台。 Postman 脚本简介 Postman 允许用户在 API 请求生命周期中运行 JavaScript 脚本&#xff0c;这些脚本分为以下三…

FineBI在线学习资源-数据处理

FineBI在线学习资源汇总&#xff1a; 学习资源 视频课程 帮助文档 问答 数据处理学习文档&#xff1a; 相关资料&#xff1a; 故事背景概述-https://help.fanruan.com/finebi6.0/doc-view-1789.html 基础表处理-https://help.fanruan.com/finebi6.0/doc-view-1791.html …