前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【译】使用RxJava从多个数据源获取数据

【译】使用RxJava从多个数据源获取数据

作者头像
小鄧子
发布于 2018-08-20 07:09:06
发布于 2018-08-20 07:09:06
2.3K00
代码可运行
举报
运行总次数:0
代码可运行

试想,需要一些动态数据的时候,只要每次都请求网络就可以了。但是,更有效率的做法是,把联网得到的数据,缓存到磁盘或内存。

具体的说,计划如下:

  1. 偶尔的联网操作,只为获取最新数据。
  2. 尽可能快的读取到数据(通过获取之前缓存的网络数据)。

我将通过使用 RxJava,来实现这个计划。

基本模式

为每一个数据源(网络,磁盘和内存)创建Observable<Data>,使用concat()first()操作符,构造一个简单的实现方式。

concat()操作符持有多个Observable对象,并将它们按顺序串联成队列。 first()操作符只从串联队列中取出并发送第一个事件。因此,如果使用concat().first(),无论多少个数据源,只有第一个事件会被检索出并发送。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Our sources (left as an exercise for the reader)
Observable<Data> memory = ...;  
Observable<Data> disk = ...;  
Observable<Data> network = ...;

// Retrieve the first source with data
Observable<Data> source = Observable  
  .concat(memory, disk, network)
  .first();

这种模式的关键在于concat()操作符只有需要数据的时候才会订阅所有的Observable数据源。由于first()操作符会较早的停止检索队列,所以,如果存在缓存数据,就没有必要访问较慢的数据源。 也就是说,如果memory返回结果,就不必担心disknetwork会被访问。相反地,如果内存磁盘都没有数据,才执行网络请求。

注意concat()所持有的Observable数据源,是按照一个接一个的顺序被检索的。

持久化数据

很明显,下一步是缓存数据。如果不把网络请求后的结果缓存到磁盘,磁盘访问后的结果缓存到内存,那么这根本不就不叫缓存。接下来要写的代码就是,网络数据的持久化操作。

我的解决方案是,让每个数据源在发送完事件后,都保存或者缓存数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 Observable<Data> networkWithSave = network.doOnNext(new Action1<Data>() {
 @Override public void call(Data data) {
 saveToDisk(data);
 cacheInMemory(data);
 }
});

 Observable<Data> diskWithCache = disk.doOnNext(new Action1<Data>() {
 @Override public void call(Data data) {
  cacheInMemory(data);
 }
});

现在,如果你使用networkWithSavediskWithCache,数据将会在加载后自动保存。

(这個策略的另一个优势在于networkWithSavediskWithCache可以在任何地方被使用,不局限于我们的多数据模式下。)

陈旧的数据

不幸的,现在我们保存数据的那些代码,执行的有点过头了。无论数据是否过时,它总是返回相同的数据。我们希望做到,偶尔连接服务器抓取最新的数据。

解决方法在于,使用first()操作符进行过滤。就是设置它拒绝接收毫无价值的数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Observable<Data> source = Observable
    .concat(memory, diskWithCache, networkWithSave)
    .first(new Func1<Data, Boolean>() {
      @Override public Boolean call(Data data) {
        return data.isUpToDate();
      }
    });

现在,我们只需要发送被断定为最新数据的事件就OK了。因此,只要有一个数据源的数据过期,就继续检索下一个数据源,直到找到最新数据为止。

first()takeFirst()操作符的比较

对于这种设计模式,first()takeFirst()操作符可以二选其一。

两种调用方式的区别在于,如果所有数据源的数据均过期,没有任何的有效数据作为事件发送,first()会抛出NoSuchElementException异常(译者注:first()操作符均 return false),而takeFirst()操作符则直接调用完成操作,不会抛出任何异常。

使用哪个操作符,完全取决于是否需要明确处理缺失的数据。

代码示例

可以从这里检出,以上所有代码的实现示例:https://github.com/dlew/rxjava-multiple-sources-sample

如果需要一个真实示例,检出 Gfycat App,它在获取数据的时候使用了这种模式。项目并没有使用以上展示的所有功能(因为不需要),但是,示范了concat().first()的基本用法。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015.08.21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
如何实现一个定时器?
定时器在各种场景都需要用到,比如游戏的Buff实现,Redis中的过期任务,Linux中的定时任务等等。顾名思义,定时器的主要用途是执行定时任务。
范蠡
2021/04/08
1.6K0
如何实现一个定时器?
深入Linux C/C++ Timer定时器的实现核心原理
我曾以为像定时器这样基础的功能,操作系统会有一个完备的实现。当需要开启一个定时任务的时候,会有一个优雅的、如下形式的接口:
sunsky
2020/12/21
11.5K1
深入Linux C/C++ Timer定时器的实现核心原理
【项目日记】仿mudou的高并发服务器 --- 整体框架搭建 ,实现时间轮模块
项目地址在这里: https://gitee.com/penggli_2_0/TcpServer
叫我龙翔
2024/11/15
1600
【项目日记】仿mudou的高并发服务器 --- 整体框架搭建 ,实现时间轮模块
时间驱动:探索计时器方案和革命性的时间轮技术
(1)心跳检测 (2)游戏中的技能冷却 (3)倒计时 (4)其他需要延迟处理的功能 定时器,就是指定某个时间处理某个事情。
Lion 莱恩呀
2024/09/21
1670
时间驱动:探索计时器方案和革命性的时间轮技术
掌握C++定时器:构建自己的定时器的分步指南
在c++中,set、map、multiset、multimap使用的是红黑树管理数据。可以利用这几个类实现定时器方案,以set为例,使用C++ 14特性。
Lion 莱恩呀
2024/09/22
3280
掌握C++定时器:构建自己的定时器的分步指南
基于Asio库的定时器,封装实现好用的定时任务
a cross-platform C++ library for network。
杨永贞
2022/04/13
2.3K0
基于Asio库的定时器,封装实现好用的定时任务
Swoole 源码分析之 Timer 定时器模块
Swoole 中的毫秒精度的定时器。底层基于 epoll_wait 和 setitimer 实现,数据结构使用最小堆,可支持添加大量定时器。
码农先森
2024/07/01
820
Go中定时器实现原理及源码解析
我在春节期间写了一篇文章有关时间轮的:https://www.luozhiyun.com/archives/444。后来有同学建议我去看看 1.14版本之后的 timer 优化。然后我就自己就时间轮和 timer 也做了个 benchmark:
luozhiyun
2021/03/07
1.4K0
C++项目:在线五子棋对战网页版--session管理模块开发
在WEB开发中,HTTP协议是⼀种⽆状态短链接的协议,这就导致⼀个客⼾端连接到服务器上之后,服务器不知道当前的连接对应的是哪个用户,也不知道客⼾端是否登录成功,这时候为客⼾端提所有服务是不合理的。因此,服务器为每个用户浏览器创建⼀个会话对象(session对象),注意:⼀个浏览器独占⼀个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使⽤浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,识别该连接对应的用户,并为用户提供服务。
二肥是只大懒蓝猫
2023/10/13
3130
C++项目:在线五子棋对战网页版--session管理模块开发
Go 并发编程与定时器
在最近的日常后台开发中经常遇到定时任务的需求,如定时通知、定时检查等重要的需求,绝对时间一定不会是完全准确的,它对于一个运行中的分布式系统其实没有太多指导意义,但是由于相对时间的计算不依赖于外部的系统,所以它的计算可以做的比较准确,这里简单总结一下定时任务在Go中的实现
Kevinello
2022/08/19
6660
从零手写操作系统之RVOS软件定时器实现-08
本系列参考: 学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春 整理而来,主要作为xv6操作系统学习的一个前置基础。
大忽悠爱学习
2023/10/11
2620
从零手写操作系统之RVOS软件定时器实现-08
libuv的定时器原理源码解析
执行定时器的时候首先会先移除该定时器,然后如果设置了repeat的话,再次加入到最小堆里,最后执行超时回调。这里有个需要注意的是设置了repeat的定时器,意思是timeout时间后触发第一次超时,后面每隔repeat的时间,触发一次超时。
theanarkh
2019/03/15
1.5K0
libuv的定时器原理源码解析
GO的定时器Timer 和定时任务cron
要是对GO 中 swaggo 的应用还有点兴趣的话,可以查看文章 工作中后端是如何将API提供出去的?swaggo很不错
阿兵云原生
2023/02/16
1.2K0
基于腾讯云AI代码助手辅助实现一个C++定时器类的功能实现
最近注意到了腾讯云AI代码助手这款辅助编码工具,正好自己又有一个项目上的小需求,故决定将其求助于AI实现。
晨星成焰
2024/08/09
1560
基于腾讯云AI代码助手辅助实现一个C++定时器类的功能实现
一种定时器的实现
注册一个时间间隔为 Interval 后执行 ExpiryAction 的定时器实例,其中,返回 TimerId 以区分在定时器系统中的其他定时器实例。
changan
2020/11/04
5660
一种定时器的实现
【项目日记】仿mudou的高并发服务器 --- 实现基础高并发服务器基础模块
实现高并发服务器的基础是实现基于事件触发的Reactor模型,通过Reactor模型对事件进行统一管理。对此我们需要设计:
叫我龙翔
2024/11/24
1530
【项目日记】仿mudou的高并发服务器 --- 实现基础高并发服务器基础模块
关于muduo网络库的注解
http://blog.csdn.net/liuxuejiang158blog/article/details/17056537#comments
bear_fish
2018/09/19
7920
4步实现MQTT客户端与OneNet高效连接
  开源项目Sparrow 的基础框架搭建已接近完成,中间件的基础功能大多已经具备。为了验证该框架的实用性,在工程中引入了业务模块OneNetMqtt。从模块命名可以推断其主要功能是通过MQTT 协议连接OneNet 平台。   最初接触OneNet 还是在大学期间,当时的毕业设计基于OneNet 实现了环境数据采集系统。由于当时的个人水平限制,并未采用MQTT协议实现,功能上体现的效果也不尽预期。现在重新构建此功能,弥补了旧时自身能力的不足,新的实现过程更为高效,连接和数据传输都相当稳定。本篇大致介绍一下功能和主要模块,后续根据需要补充。
开源519
2025/02/27
3380
4步实现MQTT客户端与OneNet高效连接
OpenServer是一款超轻量、超迷你、Actor模式、组件设计的高性能、高并发的跨全平台服务器框架
OpenServer是一款超轻量、超迷你、Actor模式、组件设计的高性能、高并发的跨全平台服务器框架。
linyouhappy
2023/04/05
1.6K0
OpenServer是一款超轻量、超迷你、Actor模式、组件设计的高性能、高并发的跨全平台服务器框架
nodejs事件循环阶段之定时器
上一篇分析了prepare阶段,check和idle阶段是一样的,所以就不分析了。今天分析定时器阶段。nodejs中setTimeout和setInterval就是使用libuv的定时器阶段实现的。libuv中,定时器是以最小堆实现的。即最快过期的节点是根节点。我看看定时器的数据结构。
theanarkh
2020/03/12
1.2K0
推荐阅读
相关推荐
如何实现一个定时器?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档