学习笔记:本系列为学习过程中练习的简单使用场景和简单的hello world代码,如有问题或详细学习请参考官方文档。
目录
1、RBucket对象桶
2、话题Topic
3、布隆过滤器Bloom
4、分布式限流器RateLimiter
5、分布式集合
1.Rmap
2.LocalCachedMap
3.mapcache
4.mapCache监听
5.Multimap-多值映射
6.set
6、Queue-基于redis的队列
7、Redisson实现分布式服务调度
服务端
客户端
1、RBucket对象桶
首先redisson是基于redis的操作,通过名字也能看出来,是redis的son级别的。
redisson对redis的操作基本都进行了封装,并对其进行了扩展,使操作redis更加的简单、方便。
再一个需要提到的是redisson是基于NIO的netty框架,而rediss是基于io的多路复用。
本章为redis中的对象操作,RBucket的操作和使用redis将对象序列化成string再存储有点类似;
hello world
本文使用springboot框架练习
1、依赖引入
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.20.0</version>
</dependency>
2、添加客户端操作的bean(此处添加的为单例redis,集群模式参考下官网)
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
3、对象实体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TestZiShu implements Serializable {
private Integer id;
private String name;
private String age;
}
4、service
@Service
public class TestUpdateService {
private static final String USER_KEY = "user:key:";
@Resource
private RedissonClient redisson;
@Resource
TestYuMapper testYuMapper;
/**
* 插入库并存入redis
* @param testZiShu
* @return
*/
public Integer add(TestZiShu testZiShu){
// 先插入,然后将对象放入redis RBucket可以放入任意类型的数据,记得最好支持序列化
int add = testYuMapper.add(testZiShu);
if(add > 0){
RBucket<TestZiShu> bucket = redisson.getBucket(USER_KEY + testZiShu.getId());
// 如果值存在则是更新
bucket.set(testZiShu);
}
return testZiShu.getId();
}
/**
* 获取redis中key对应的对象
* @param id
* @return
*/
public TestZiShu info(Integer id){
// 根据key取出对象 简化了像放入序列化的string一样,少了序列化和反序列化的步骤
RBucket<TestZiShu> bucket = redisson.getBucket(USER_KEY + id);
return bucket.get();
}
public Integer update(TestZiShu testZiShu){
// 先插入,然后将对象放入redis RBucket可以放入任意类型的数据,记得最好支持序列化
int update = testYuMapper.update(testZiShu);
if(update > 0){
RBucket<TestZiShu> bucket = redisson.getBucket(USER_KEY + testZiShu.getId());
// 如果值存在则是更新
bucket.set(testZiShu);
}
return update;
}
public Integer delete(Integer id){
// 先插入,然后将对象放入redis RBucket可以放入任意类型的数据,记得最好支持序列化
int delete = testYuMapper.delete(id);
if(delete>0){
RBucket<TestZiShu> bucket = redisson.getBucket(USER_KEY + id);
// 删除key
bucket.delete();
}
return delete;
}
/**
* 获取所有的key
* @return
*/
public String test(){
RKeys keys = redisson.getKeys();
Iterable<String> keys1 = keys.getKeys();
Set<String> set = new HashSet<>();
keys1.forEach(set::add);
return set.toString();
}
}
5、测试结果
关于redisson的学习将以redis的操作来更新,这样搜索、查看起来更清晰!
2、话题Topic
Redisson也可以实现类似RabbitMQ的消息通信,依靠的就是发布订阅主题这个组件,当生产者需要发布消息时,会将消息数据发布到Topic, 而消费者只要订阅了相关的channel就可以监听消费消息 ;如果服务中有多个监听着,都会被监听到;
hello world
本文使用springboot框架练习
1、producter
@Service
public class TopicService {
// 主题
public static final String TOPIC = "redisson:topic";
@Resource
private RedissonClient redisson;
/**
* 生产一条消息
* @param msg
* @return
*/
public String producer(String msg){
// 消息的生产端
RTopic<String> topic = redisson.getTopic(TOPIC);
topic.publish(msg);
return msg;
}
}
2、consumer
Redisson的基于发布-订阅的主题本身不具备自动监听的功能,需要让消费者继承Spring的ApplicationRunner和Ordered接口,使得消费者可以在项目启动后不断监听业务逻辑。
@Slf4j
@Service
public class TopicListenerService implements ApplicationRunner, Ordered {
@Resource
private RedissonClient redisson;
/**
* 被监听的逻辑
* @param args
* @throws Exception
*/
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("1服务开始监听");
this.listenerTopic();
}
/**
* 如果服务中有多个topic监听着,1、返回的数字越小,优先级越高;2、多个监听器都会被监听到
* @return
*/
@Override
public int getOrder() {
return 1;
}
/**
* topic
*/
private void listenerTopic(){
RTopic<String> topic = redisson.getTopic(TopicService.TOPIC);
topic.addListener(new MessageListener<String>() {
@Override
public void onMessage(CharSequence charSequence, String s) {
log.info("监听服务1监听到的消息为{}",s);
}
});
}
}
todo: 分布式部署的场景下,怎样保证多个消费者同一个消息只消费一次?
3、布隆过滤器Bloom
本质上布隆过滤器是一种数据结构,是一种比较巧妙的概率性数据结构,特点是可以高效的插入和查询,可以用来判断:“某个值一定不存在或可能存在”。
优点:相比于传统的list、set、map等数据结构,更高效、占用空间更小(不需要存储数据本身,只需要存储数据对应hash比特位);
缺点:返回的结果是概率性的,而不是确切的;不支持删除元素
HashMap 的问题
讲述布隆过滤器的原理之前,我们先思考一下,通常你判断某个元素是否存在用的是什么? 应该蛮多人回答 HashMap 吧,确实可以将值映射到 HashMap 的 Key,然后可以在 0(1)的时间复杂度内返回结果,效率奇高。但是 HashMap 的实现也有缺点,例如存储容量占比高,考虑到负载因子的存在,通常空间是不能被用满的,而一旦你的值很多例如上亿的时候,那 HashMap 占据的内存大小就变得很可观了。
还比如说你的数据集存储在远程服务器上,本地服务接受输入,而数据集非常大不可能一次性读进内存构建 HashMap 的时候,也会存在问题。
布隆过滤器数据结构
布隆过滤器是一个 bit 向量或者说 bit 数组
详解布隆过滤器的原理、使用场景和注意事项
如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值,并对每个生成的哈希值指向的 bit 位置 1,例如针对值和三个不同的哈希函数分别生成了哈"baidu"希值 14、7,则上图转变为:
测试代码:
@Service
public class BloomService {
private static final String BLOOM_KEY = "bloom:key:";
@Resource
private RedissonClient redisson;
/**
* 测试布隆过滤器
* @param id
* @return
*/
public Integer test(Integer id){
RBloomFilter<Integer> bloomFilter = redisson.getBloomFilter(BLOOM_KEY);
// 初始化布隆过滤器,第一个参数:预计存放的数据量(实际存放过程中如果不够,会自动扩充,最好初始化够);第二个参:误差
bloomFilter.tryInit(1000000L,0.01);
if(bloomFilter.contains(id)){
System.out.println(id+"存在");
}else {
System.out.println(id+"bu存在");
// 入布隆过滤器
bloomFilter.add(id);
}
return 1;
}
}
问题:在大数据集合场景下,如果使用该场景,怎样保证返回数据的准确性呢,如果该数据类型不能保证数据的准确定,用在哪种实际的场景下比较合适呢?
Bloom Filter不适合那些“零错误”的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter通过极少的错误换取了存储空间的极大节省。
4、分布式限流器RateLimiter
基于Redis的分布式限流器 (RateLimiter) 可以用来在分布式环境下现在请求方的调用频率。既适用于不同Redisson实例下的多线程限流(分布式),也适用于相同Redisson实例下的多线程限流(但实例)。
该算法不保证公平性。除了同步接口外,还提供了异步 (Async) 、反射式 (Reactive) 和RxJava2标准的接口
代码实例:
@Service
public class RateLimiterService {
private static final String RATE_LIMITER_KEY = "rateLimiter:key:";
@Resource
private RedissonClient redisson;
/**
* 测试分布式限流器
* @param str
* @return
*/
public void test(String str){
RRateLimiter rateLimiter = redisson.getRateLimiter(RATE_LIMITER_KEY+str);
// 初始化最大流 = 每 xX 秒钟产生1个(机号 X 秒内只能发一次)~ 每1秒钟产生1个牌
// 真实应用中还得借助缓存/数据库记录发送的次数、i 限制、白名单、黑名单限制
rateLimiter.trySetRate(RateType.OVERALL, 1, 10, RateIntervalUnit.SECONDS);
// 获取一个令牌
if( rateLimiter.tryAcquire(1)){
System.out.println("执行");
}
}
}
---------------------------------------------------------------------------------------------------------------------------------
单实例服务
如果是单实例的服务,可以使用Guava的RateLimiter,可以实现相同的限流功效,更加轻量级!
<!--核心代码片段-->
//400表示每秒允许处理的量是400
private RateLimiter rateLimiter = RateLimiter.create(400);
if(rateLimiter.tryAcquire()){
// 逻辑
}
5、分布式集合
1.Rmap
1: 基于Redis的Redisson分布式映射结构RMap既实现了Java中的ConcurrentHashMap接口和Map接口,同时也融合进了Redis的缓存特性 ~ 其实跟Redis的Hash数据结构类似
2: 说白了,其实你把它当做JavaSE的Map即可,只不过 在代码操作时要谨记其具有“缓存”的功效 就可以了!
3:通用对象通Bucket”而言,它可以减少Key的存储
测试:用户对象简单的CRUD操作
@Service
public class CollectService {
private static final String R_MAP_KEY = "collect:key:map";
@Resource
private RedissonClient redisson;
/**
* 分布式集合测试
* @param str
* @return
*/
public void testRMapAdd(String str){
RMap<String, TestZiShu> map = redisson.getMap(R_MAP_KEY);
// 添加相同的key会被覆盖,相当于更新
map.put(str,TestZiShu.builder().name("map2").age("18").build());
// 效率更快
// map.fastPut(str,TestZiShu.builder().name("map").age("17").build());
}
/**
* 分布式集合测试
* @param str
* @return
*/
public void testRMapInfo(String str){
RMap<String, TestZiShu> map = redisson.getMap(R_MAP_KEY);
System.out.println(map.get(str));
}
/**
* 分布式集合测试
* @param str
* @return
*/
public void testRMapDelete(String str){
RMap<String, TestZiShu> map = redisson.getMap(R_MAP_KEY);
map.remove(str);
}
}
---------------------------------------------------------------------------------------------------------------------------------
2.LocalCachedMap
1: 映射Map的一种~ 在某些特定场景下,如果我们需要对映射Map进行频繁的读取操作,这个时候网络通信很有可能会成为瓶颈!
2: 而由于Redisson在与Redis通信的同时,会将部分数据保存在本地内存里(这样的设计的好处是它能将读取速度提高最多 45倍)~即传说中的 LocalCached
测试:
@Service
public class LocalCacheMapService {
private static final String L_C_MAP_KEY = "collect:local:cache:map:key";
@Resource
private RedissonClient redisson;
private LocalCachedMapOptions options;
@PostConstruct
public void init(){
options = LocalCachedMapOptions.defaults()
// 用于淘汰清除本地缓存内的元素
// 共有以下几种选择:
// LFU - 统计元素的使用频率,淘汰用得最少(最不常用)的
// LRU - 按元素使用时间排序比较,淘汰最早(最久远)的。
// SOFT - 元素用Java的weakReference来保存,缓存元素通过GC过程清除
// WEAK - 元素用Java的softReference来保存,缓存元素通过GC过程清除
// NONE - 永不淘汰清除缓在元素。
.evictionPolicy(LocalCachedMapOptions.EvictionPolicy.NONE)
// 如果缓存容量值为9表示不限制本地缓存容量大小
.cacheSize(1000)
// 以下选项适用于断线原因造成了未收到本地缓在更新消息的情况
// 断线重连的策略有以下几种:
// CLEAR - 如断线一段时间以后则在重新建立连接以后清空本地缓存
// LOAD - 在服务端保在一份10分钟的作废日志
// 如果10分钟内重新建立连接,则按照作废日志内的记录清空本地缓在的元素
// 如果断线时间超过了这个时间,则将清空本地缓存中所有的内容
// NONE -默认值。断线重连时不做处理
.reconnectionStrategy(LocalCachedMapOptions.ReconnectionStrategy.NONE)
// 以下选项适用于不同本地缓存之间相互保持同步的情况
//缓存同步策略有以下几种:
// NVALIDATE 默认值。当本地缓存映射的某条元素发生变动时,同时驱逐所有相同本地缓存映射内的该元素
// UPDATE 当本地缓在映射的某条元素发生变动时,同时更新所有相同本地缓存映射内的该元素
// NONE - 不做任何同步处理
.syncStrategy(LocalCachedMapOptions.SyncStrategy.INVALIDATE)
// 每个Map本地缓存里元素的有效时间,默认毫秒为单位
.timeToLive(1000)
.timeToLive(10,TimeUnit.SECONDS)
// 每个Map本地缓存里元素的最长闲置时间,默认毫秒为单位
.maxIdle(1000)
.maxIdle(10,TimeUnit.SECONDS);
}
/**
* 分布式集合测试
* @param str
* @return
*/
public void testLocalCacheMapAdd(String str){
RLocalCachedMap<String, TestZiShu> map = redisson.getLocalCachedMap(L_C_MAP_KEY, options);
// 添加相同的key会被覆盖,相当于更新
map.put(str,TestZiShu.builder().name("map2").age("18").build());
// 效率更快
// map.fastPut(str,TestZiShu.builder().name("map").age("17").build());
}
/**
* 分布式集合测试
* @param str
* @return
*/
public void testLocalCacheMapInfo(String str){
RLocalCachedMap<String, TestZiShu> map = redisson.getLocalCachedMap(L_C_MAP_KEY, options);
System.out.println(map.get(str));
}
/**
* 分布式集合测试
* @param str
* @return
*/
public void testLocalCacheMapDelete(String str){
RLocalCachedMap<String, TestZiShu> map = redisson.getLocalCachedMap(L_C_MAP_KEY, options);
map.remove(str);
}
}
3.mapcache
元素淘汰 (Eviction) 类 -- 带有元素淘汰 (Eviction) 机制的映射类允许针对一个映射中每个元素单独设定 有效时间 和 最长闲置时间。--对map中元素设置过期时间(ttl)
使用场景:
定时发送邮件;验证码;
本地缓存 (LocalCache) 类 -- 本地缓存 (Local Cache) 也叫就近缓存 (Near Cache) 。这类映射的使用主要用于在特定的场景下,映射缓存 (MapCache) 上的高度频繁的读取操作,使网络通信都被视为瓶颈的情况。Redisson与Redis通信的同时,还将部分数据保存在本地内存里。这样的设计的好处是它能将读取速度提高最多 45倍。 所有同名的本地缓存共用一个订阅发布话题所有更新和过期消息都将通过该话题共享。
数据分片 (Sharding) 类 -- 数据分片 (Sharding) 类仅适用于Redis集群环境下,因此带有数据分片 (Sharding)功能的映射也叫集群分布式映射。它利用分库的原理,将单一一个映射结构切分为若干个小的映射,并均匀的分布在集群中的各个槽里。这样的设计能使一个单一映射结构突破Redis自身的容量限制,让其容量随集群的扩大而增长。在扩容的同时,还能够使读写性能和元素淘汰处理能力随之成线性增长。
测试:
@Service
public class MapCacheService {
private static final String MAP_CACHE_KEY = "collect:map:cache:key";
@Resource
private RedissonClient redisson;
/**
* 分布式集合测试
* @param str
* @return
*/
public void testMapCacheAdd(String str){
RMapCache<String, TestZiShu> map = redisson.getMapCache(MAP_CACHE_KEY);
// 添加相同的key会被覆盖,相当于更新
map.put(str,TestZiShu.builder().name("map2").age("18").build(),100,SECONDS);
// 效率更快
// map.fastPut(str,TestZiShu.builder().name("map").age("17").build());
}
/**
* 分布式集合测试
* @param str
* @return
*/
public void testMapCacheInfo(String str){
RMapCache<String, TestZiShu> map = redisson.getMapCache(MAP_CACHE_KEY);
System.out.println(map.get(str));
}
/**
* 分布式集合测试
* @param str
* @return
*/
public void testMapCacheDelete(String str){
RMapCache<String, TestZiShu> map = redisson.getMapCache(MAP_CACHE_KEY);
map.remove(str);
}
}
4.mapCache监听
Redisson为所有实现了 RMapCache或 RLocalcachedMapcache 接口的对象提供了监听以下事件的监听器:
事件|监听器元素添加事件 org.redisson.api.map.event.EntrycreatedListener
元素过期事件 org.redisson.api.map.event.EntryExpiredListener
元素删除事件org.redisson.api.map.event.EntryRemovedListener
元素更新事件org.redisson.api.map.event.EntryUpdatedListener
/**
* mapCache监听
*/
@Slf4j
@Service
public class MapCacheListenerService implements ApplicationRunner, Ordered {
private static final String MAP_CACHE_KEY = "collect:map:cache:key";
@Resource
private RedissonClient redisson;
/**
* 被监听的逻辑
* @param args
* @throws Exception
*/
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("开始监听-mapCache:"+MAP_CACHE_KEY);
this.listenerTopic();
}
/**
* 如果服务中有多个topic监听着,1、返回的数字越小,优先级越高;2、多个监听器都会被监听到
* @return
*/
@Override
public int getOrder() {
return 1;
}
/**
* topic
*/
private void listenerTopic() {
RMapCache<String, TestZiShu> mapCache = redisson.getMapCache(MAP_CACHE_KEY);
// 添加元素监听
mapCache.addListener(new EntryCreatedListener<String, TestZiShu>() {
@Override
public void onCreated(EntryEvent<String, TestZiShu> entryEvent) {
System.out.println("新增元素:key:"+entryEvent.getKey()+"value:"+entryEvent.getValue());
}
});
// 过期监听
mapCache.addListener((EntryExpiredListener<String, TestZiShu>) entryEvent ->
System.out.println("key已过期:"+entryEvent.getKey()));
// 删除元素监听
mapCache.addListener((EntryRemovedListener<String, TestZiShu>) entryEvent -> {
System.out.println("被删除的元素key为"+entryEvent.getKey());
});
// 更新元素监听
mapCache.addListener((EntryUpdatedListener<String, TestZiShu>) entryEvent -> {
System.out.println("更新的key为"+entryEvent.getKey());
});
// ...
}
}
5.Multimap-多值映射
基于Redis的Redisson的分布式 RMultimap Java对象允许Map中的一个字段值包含多个元素。字段总数受Redis限制,每个Multimap最多允许有 4,294,967,295 个不同字段。
1、基于集 (Set) 的多值映射 (Multimap)
基于Set的Multimap不允许一个字段值包含有重复的元素
2、基于列表 (List) 的多值映射 (Multimap)
基于List的Multimap在保持插入顺序的同时允许一个字段下包含重复的元素
测试:
@Data
public class CodeBook {
private String type;
private String name;
private String code;
private String value;
private String orderBy;
}
@Service
public class MultiMapService {
private static final String MULTI_MAP_KEY = "collect:multi:map:key";
@Resource
private RedissonClient redisson;
/**
*
* @param codeBook
* @return
*/
public void multimapAdd(CodeBook codeBook){
RSetMultimap<Object, Object> map = redisson.getSetMultimap(MULTI_MAP_KEY);
map.put(codeBook.getType(),codeBook);
}
/**
*
* @param str
* @return
*/
public void multimapInfo(String str){
RSetMultimap<Object, Object> map = redisson.getSetMultimap(MULTI_MAP_KEY);
System.out.println(map.get(str));
}
/**
*
* @param str
* @return
*/
public void multimapDelete(String str){
RSetMultimap<Object, Object> map = redisson.getSetMultimap(MULTI_MAP_KEY);
// 删除map中的某个key
map.fastRemove(str);
}
/**
*
* @param codeBook
* @return
*/
public void multimapDelete2(CodeBook codeBook){
RSetMultimap<Object, Object> map = redisson.getSetMultimap(MULTI_MAP_KEY);
// 删除某个key中的某个元素
map.remove(codeBook.getType(),codeBook);
}
}
6.set
测试:
@Service
public class SetService {
private static final String SET_KEY = "set:key";
@Resource
private RedissonClient redisson;
/**
*
* @param str
* @return
*/
public void setAdd(String str){
RSet<Object> set = redisson.getSet(SET_KEY);
set.add(str);
// set.addAll()
}
/**
*
* @return
*/
public void setInfo(){
RSet<Object> set = redisson.getSet(SET_KEY);
List<Object> objects = Arrays.asList(set.readAll().toArray());
// 打算顺序
Collections.shuffle(objects);
System.out.println(objects);
}
/**
*
* @param str
* @return
*/
public void setDelete(String str){
RSet<Object> set = redisson.getSet(SET_KEY);
// 清空
// set.clear();
// 删除某个元素
set.remove(str);
}
}
6、Queue-基于redis的队列
(Queue) 结构的 RQueue Java对象实现了 java.util.queue 接口。尽管 Roueue 对象无初始大小 (边界)限制,但对象的最大容量受Redis限制,最大元素数量是 4,294,967,295 个;
顾名思义,其实就是咱们在“数据结构与算法”一书中提到的拥有“FIFO特性~先进先出”的队列 ~ 异步解耦
双端队列 (Deque)
基于Redis的Redisson分布式无界双端队列 (Deque) 结构的 RDeque Java对象实现了java.uti1.Deque 接口。尽管 RDeque 对象无初始大小 (边界) 限制,但对象的最大容量受Redis限制,最大元素数量是 4 294 967 295 ;
阻塞队列 (Blocking Queue)--并发安全
基于Redis的Redisson分布式无界阻塞队列 (Blocking Queue) 结构的 RBockingQueue Java对象实现了 java,util.concurrent,BlockingQueue 接口。尽管 RBlockingQueue 对象无初始大小 (边界) 限制但对象的最大容量受Redis限制,最大元素数量是 4 294 967 295 个;
有界阻塞队列 (Bounded Blocking Queue)
基于Redis的Redisson分布式有界阻塞队列 (Bounded Blocking Queue) 结构的RBoundedBlockingQueue Java对象实现了 java.util.concurrent.BlockingQueue 接口。该对象的最大容量受Redis限制,最大元素数量是 4 294 967 295 个。队列的初始容量 (边界)必须在使用前设定好
阻塞双端队列 (Blocking Deque)
基于Redis的Redisson分布式无界阻塞双端队列 (Blocking Deque) 结构的 RBlockingDeque Java对象实现了 java.util.concurrent.BlockingDeque 接口。尽管 RBlockingDeque 对象无初始大小(边界) 限制,但对象的最大容量受Redis限制,最大元素数量是 4 294 967 295 个
阻塞公平队列 (Blocking Fair Queue)
基于Redis的Redisson分布式无界阻塞公平队列 (Blocking Fair Queue) 结构的 BlockingFairQueueJava对象在实现Redisson分布式无界阻塞队列 (Blocking Queue) 结构 RBockingoueue 接口的基础上,解决了多个队列消息的处理者在复杂的网络环境下,网络延时的影响使”较远”的客户端最终收到消息数量低于“较近”的客户端的问题。从而解决了这种现象引发的个别处理节点过载的情况。
以分布式无界阻塞队列为基础,采用公平获取消息的机制,不仅保证了 po11、 polFromAnypollLastAndofferFirstTo 和 take 方法获取消息的先入顺序,还能让队列里的消息被均匀的发布到处在复杂分布式环境中的各个处理节点里;
阻塞公平双端队列 (Blocking Fair Deque)
基于Redis的Redisson分布式无界阻塞公平双端队列 (Blocking Fair Deque)结构的aRBlockingFairDeque Java对象在实现Redisson分布式无界阳塞双端队列 (Blocking Deque) 结构RBlockingPegue 接口的基础上,解决了多个队列消息的处理者在复杂的网络环境下,网络延时的影响使”较远”的客户端最终收到消息数量低于”较近”的客户端的问题。从而解决了这种现象引发的个别处理节点过载的情况
以分布式无界阻塞双端队列为基础,采用公平获取消息的机制,不仅保证了 po11 、 takepollFirst 、 takeFirst 、 pollLast 和 takeLast 方法获取消息的先入顺序,还能让队列里的消息被均匀的发布到处在复杂分布式环境中的各个处理节点里。
测试:
@Slf4j
@Service
public class QueueService {
private static final String QUEUE_KEY = "queue:key";
private static final String BLOCKING_QUEUE_KEY = "blocking:queue:key";
@Resource
private RedissonClient redisson;
/**
*
* @param str
* @return
*/
public void queueAdd(String str){
// 无界普通队列
// RQueue<Object> queue = redisson.getQueue(QUEUE_KEY);
// queue.offer(str);
// 阻塞队列
RBlockingDeque<Object> blockingDeque = redisson.getBlockingDeque(BLOCKING_QUEUE_KEY);
blockingDeque.offer(str);
}
/**
*
* @return
*/
public void queueConsume(){
// RQueue<Object> queue = redisson.getQueue(QUEUE_KEY);
// System.out.println( queue.poll());
RBlockingDeque<Object> blockingDeque = redisson.getBlockingDeque(BLOCKING_QUEUE_KEY);
System.out.println(blockingDeque.poll());
}
}
todo:
队列和topic的区别?
7、Redisson实现分布式服务调度
测试:
服务端
接口-在api模块中创建接口(该接口会暴露给远程调用端),所以实现类要写到其他模块中;
public interface TestService {
String test();
}
实现类
@Service
public class TestServiceImpl implements TestService {
@Override
public String test() {
System.out.println("哇哦");
return "666";
}
}
将接口服务注册到redisson--服务端
@Component
public class RemoteServiceInit implements CommandLineRunner {
@Resource
private RedissonClient redisson;
@Resource
private TestServiceImpl testServiceImpl;
public RemoteServiceInit() {
}
@Override
public void run(String... args) throws Exception {
RRemoteService remoteService = redisson.getRemoteService();
//在调用远程方法以前,应该首先注册远程服务/只注册了一个服务端工作者实例,只能同时执行一个并发调用
remoteService.register(TestService.class,testServiceImpl);
//注册了12个服务端工作者实例,可以同时执行12个并发调用
// remoteService.register(TestService.class,testServiceImpl,12);
}
}
客户端
@Slf4j
@RestController
@RequestMapping("remote")
public class RemoteClient {
@Resource
private RedissonClient redisson;
@GetMapping("test")
public void test(){
//分布式远程服务调用
RRemoteService remoteService=redisson.getRemoteService();
//应签回执超时1秒钟,远积执行超时30秒钟
TestService testService = remoteService.get(TestService.class, RemoteInvocationOptions.defaults());
String result = testService.test();
System.out.println(result);
}
}
注意事项:
生产者和消费者配置的redis服务必须一致;
消费者需要引入生产者提供服务的api的jar依赖(需要上传仓库或者本地引入);