什么是响应式编程,Java 如何实现

我们这里用通过唯一 id 获取知乎的某个回答作为例子,首先我们先明确下,一次HTTP请求到服务器上处理完之后,将响应写回这次请求的连接,就是完成这次请求了,如下:

public void request(Connection connection, HttpRequest request) {

//处理request,省略代码

connection.write(response);//完成响应

}

假设获取回答需要调用两个接口,获取评论数量还有获取回答信息,传统的代码可能会这么去写:

//获取评论数量

public void getCommentCount(Connection connection, HttpRequest request) {

Integer commentCount = null;

try {

//从缓存获取评论数量,阻塞IO

commentCount = getCommnetCountFromCache(id);

} catch(Exception e) {

try {

//缓存获取失败就从数据库中获取,阻塞IO

commentCount = getVoteCountFromDB(id);

} catch(Exception ex) {

}

}

connection.write(commentCount);

}

//获取回答

public void getAnswer(Connection connection, HttpRequest request) {

//获取点赞数量

Integer voteCount = null;

try {

//从缓存获取点赞数量,阻塞IO

voteCount = getVoteCountFromCache(id);

} catch(Exception e) {

try {

//缓存获取失败就从数据库中获取,阻塞IO

voteCount = getVoteCountFromDB(id);

} catch(Exception ex) {

}

}

//从数据库获取回答信息,阻塞IO

Answer answer = getAnswerFromDB(id);

//拼装Response

ResultVO response = new ResultVO();

if (voteCount != null) {

response.setVoteCount(voteCount);

}

if (answer != null) {

response.setAnswer(answer);

}

connection.write(response);//完成响应

}

在这种实现下,你的进程只需要一个线程池,承载了所有请求。这种实现下,有两个弊端:

线程池 IO 阻塞,导致某个存储变慢或者缓存击穿的话,所有服务都堵住了。假设现在评论缓存突然挂了,全都访问数据库,导致请求变慢。由于线程需要等待 IO 响应,导致唯一一个线程池被堆满,无法处理获取回答的请求。

对于获取回答信息,获取点赞数量其实和获取回答信息是可以并发进行的。不用非得先获取点赞数量之后再获取回答信息。

现在,NIO 非阻塞 IO 很普及了,有了非阻塞 IO,我们可以通过响应式编程,来让我们的线程不会阻塞,而是一直在处理请求。这是如何实现的呢

传统的 BIO,是线程将数据写入 Connection 之后,当前线程进入 Block 状态,直到响应返回,之后接着做响应返回后的动作。NIO 则是线程将数据写入 Connection 之后,将响应返回后需要做的事情以及参数缓存到一个地方之后,直接返回。在有响应返回后,NIO 的 Selector 的 Read 事件会是 Ready 状态,扫描 Selector 事件的线程,会告诉你的线程池数据好了,然后线程池中的某个线程,拿出刚刚缓存的要做的事情还有参数,继续处理。

那么,怎样实现缓存响应返回后需要做的事情以及参数的呢Java 本身提供了两种接口,一个是基于回调的 Callback 接口(Java 8 引入的各种Functional Interface),一种是 Future 框架。

基于 Callback 的实现:

//获取回答

public void getAnswer(Connection connection, HttpRequest request) {

ResultVO resultVO = new ResultVO();

getVoteCountFromCache(id, (count, throwable) - {

//异常不为null则为获取失败

if (throwable != null) {

//读取缓存失败就从数据库获取

getVoteCountFromDB(id, (count2, throwable2) - {

if (throwable2 == null) {

resultVO.setVoteCount(voteCount);

}

//从数据库读取回答信息

getAnswerFromDB(id, (answer, throwable3) - {

if (throwable3 == null) {

resultVO.setAnswer(answer);

connection.write(resultVO);

} else {

connection.write(throwable3);

}

});

});

} else {

//获取成功,设置voteCount

resultVO.setVoteCount(voteCount);

//从数据库读取回答信息

getAnswerFromDB(id, (answer, throwable2) - {

if (throwable2 == null) {

resultVO.setAnswer(answer);

//返回响应

connection.write(resultVO);

} else {

//返回错误响应

connection.write(throwable2);

}

});

}

});

}

可以看出,随着调用层级的加深,callback 层级越来越深,越来越难写,而且啰嗦的代码很多。并且,基于 CallBack 想实现获取点赞数量其实和获取回答信息并发是很难写的,这里还是先获取点赞数量之后再获取回答信息。

那么基于 Future 呢我们用 Java 8 之后引入的 CompletableFuture 来试着实现下。

//获取回答

public void getAnswer(Connection connection, HttpRequest request) {

ResultVO resultVO = new ResultVO();

//所有的异步任务都执行完之后要做的事情

CompletableFuture.allOf(

getVoteCountFromCache(id)

//发生异常,从数据库读取

.exceptionallyComposeAsync(throwable - getVoteCountFromDB(id))

//读取完之后,设置VoteCount

.thenAccept(voteCount - {

resultVO.setVoteCount(voteCount);

}),

getAnswerFromDB(id).thenAccept(answer - {

resultVO.setAnswer(answer);

})

).exceptionallyAsync(throwable - {

connection.write(throwable);

}).thenRun(() - {

connection.write(resultVO);

});

}

这种实现就看上去简单多了,并且读取点赞数量还有读取回答内容是同时进行的。

Project Reactor 在 Completableuture 这种实现的基础上,增加了更多的组合方式以及更完善的异常处理机制,以及面对背压时候的处理机制,还有重试机制。

每日一刷,轻松提升技术,斩获各种offer:

什么是响应式编程,Java 如何实现 相关文章

什么原因才导致 select * 效率低下的

面试官:“小陈,说一下你常用的SQL优化方式吧。”陈小哈:“那很多啊,比如不要用SELECT *,查询效率低。巴拉巴拉...”面试官:“为什么不要用SELECT * 它在哪些情况下效率低呢”陈小哈:“SELECT * 它好像比写指定列名多一次全表查询吧,还多查了一些无用

linux配置DNS主从配置

DNS主从搭建 什么是DNS DNS( Domain Name System)是“域名系统”的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,使用的是UDP协议的53号端口,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工作。DNS就是这样的一位

Java(4) | 匿名对象

# 什么是匿名对象 匿名对象没有名字和赋值运算 new 类名称(); 创建对象的标准格式: 类名称 对象名 = new 类名称(); # 注意事项 匿名对象只能使用一次 所以如果确定某个对象只是用一次,则可以使用匿名对象 # 代码演示 package com.leerep.javabase.anonymou

MQ学习1

MQ(bilibili) MQ和RabbitMQ基础 1 什么是MQ ?举例:用户注册 ?原来: 用户--短信服务--邮件服务--数据库 ?多线程:用户--短信(1秒) ?用户--邮件(1秒) ?用户--数据库(1秒) ?消息队列:用户--数据库(1秒) ?用户--消息队列(MQ)(0.5秒) ?消息队列(MQ)--短信 ?消

Day8

1什么是构造函数 构造函数是bai一种特殊的成员函数du,它主要用于为对象分配存zhi储空间,对数据成dao员进zhuan行初始化.构造函数shu具有一些特殊的性质: (1)构造函数的名字必须与类同名; (2)构造函数没有返回类型,它可以带参数,也可以不带参数; (3)声明类对象

关于Java注解(annotation)的简单理解

一、什么是注解 从 JDK5 开始,Java增加对元数据的支持,也就是注解。简单理解就是代码里的特殊标志,这些标志可以在编译,类加载,运行时被读取,并执行相应的处理,以便于其他工具补充信息或者进行部署。 二、为什么要使用注解 注解可以被其他程序(比如:

RestHighLevelClient的使用

是什么 es官网推荐的es客户端组件RestHighLevelClient, 其封装了操作es的crud方法,底层原理就是模拟各种es需要的请求,如put,post,delete,get等方式 使用步骤 1.引入pom依赖 2.application.yml配置ES 3.查询 // 构建查询参数SearchSourceBuilder search

makefile终极奥义

什么是makefile? 或许很多 Winodws 的程序员都不知道这个东西,因为那些 Windows 的 IDE 都为你做了这个工作,但是一个好的和 professional 的程序员, makefile 还是要懂。这就好像现在有这么多的 HTML 的编辑器,但如果你想成为一个专业人士,你还是要了

HTTP请求响应过程

HTTP请求 HTTP是超文本传输协议,其定义了客户端与服务器端之间文本传输的规范。HTTP默认使用80端口,这个端口指的是服务端的端口,而客户端使用的端口是动态分配的。当我们没有指定端口访问时,浏览器会默认帮我们添加80端口。我们也可以自己指定访问端口如

你不知道的 Proxy

在从观察者模式到响应式的设计原理这篇文章中,阿宝哥介绍了 observer-util 这个库如何使用 Proxy 来实现响应式。而对于 vue-next 项目中的 @vue/reactivity 模块,也是利用 Proxy 来实现响应式。因此,如果你要学习 @vue/reactivity 模块的话,就需要先掌握

java响应式编程有几种方式_什么是响应式编程,Java 如何实现相关推荐

  1. Java反射之创建对象的四种方式

    Java反射之创建对象的四种方式 1.使用new关键字 2.使用Java反射机制,反射构造器 3.使用克隆方式创建对象Cloneable 4.使用序列化Serializable 1.使用Java反射机 ...

  2. Java异步非阻塞编程的几种方式

    简介: Java异步非阻塞编程的几种方式 一. 从一个同步的Http调用说起 一个很简单的业务逻辑,其他后端服务提供了一个接口,我们需要通过接口调用,获取到响应的数据. 逆地理接口:通过经纬度获取这个 ...

  3. 返回ajax有几种方式,java ajax返回 Json 的 几种方式

    方式 1. : 自写代码转 Json 需要  HttpHttpServletRequest request  HttpServletResponse response 后台 : @RequestMap ...

  4. 【转】java调用http接口的几种方式总结

    java调用http接口的几种方式总结 本文参考: https://blog.csdn.net/fightingXia/article/details/71775516 https://www.cnb ...

  5. java读取XML文件的四种方式

    java读取XML文件的四种方式 Xml代码 <?xml version="1.0" encoding="GB2312"?> <RESULT& ...

  6. java直接调用复制文件,java中文件复制的4种方式,java文件的复制

    java中文件复制的4种方式,java文件的复制 今天一个同事问我文件复制的问题,他一个100M的文件复制的指定目录下竟然成了1G多,吓我一跳,后来看了他的代码发现是自己通过字节流复制的,定义的字节数 ...

  7. Java中HashMap遍历的两种方式

    第一种: Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Ma ...

  8. Java转JSON串的几种方式

    以下总结一下java转JSON串的几种方式: 1.将java对象转成json串 2.通过JSONObject生成json串 3.通过json字符串生成json串 代码通过阿里的fastjson包实现. ...

  9. JAVA中集合输出的四种方式

    在JAVA中Collection输出有四种方式,分别如下: 一) Iterator输出. 该方式适用于Collection的所有子类. public class Hello {public stati ...

最新文章

  1. 网络推广营销浅析网站在优化中流量突然减少了是为什么?
  2. sas university edition在ubuntu中的使用
  3. 铺地毯(矩形的交+前后缀矩形交)
  4. apache.camel_带有Spring Boot 2支持的Apache Camel 2.22发布
  5. linux CentOS7最小化安装环境静默安装Oracle11GR2数据库(安装依赖包_03)
  6. 微软为何选择在 Github 上开源 .NET 核心?
  7. 渗透测试入门2之进入内网
  8. ai里怎样取消扩展外观_扩展AI:困难的5个原因
  9. linux环境编程apue和unp,《APUE》和《UNP》文件的编译和使用(转载)
  10. 汽车销售管理系统 c语言版 课程设计,《汽车销售管理系统 C语言版》.doc
  11. Networkx参考手册
  12. 2021-1016-复盘 强势股-风口综合实战
  13. Edge浏览器运行卡顿怎么办 怎样让Edge浏览器速度更快
  14. 计算机网络介质图片,存储介质是什么
  15. Oracle数据库----------------索引
  16. 有未经处理的异常 0xC00000FD Stack overflow (参数: 0x00000000, 0x00842000)
  17. java cloud五大神兽_SpringCloud五大神兽之Eureka
  18. 善用 GOOGLE -- 从入门到精通
  19. python分析股票MACD指标
  20. 功能测试中遇到不可重现软件缺陷的解决策略

热门文章

  1. Unity中基于三角剖分 实现三维城市实时构建
  2. 【初识Java】Java与C语言对比学习
  3. windows7下安装vs2013
  4. SEO搜索引擎如何进行优化处理
  5. mysql乱码的问题_MySQL中出现乱码问题的终极解决宝典
  6. 115个Java面试题和答案——终极列表
  7. SVG path绘制百分比圆弧,给力啊
  8. java 对接快递100实时查询API接口
  9. Pico 4一体机游戏下载教程(附游戏资源包)Pico4串流第三方VR游戏教程讲解
  10. openGauss简单查询SQL的执行流程解析