什么是响应编程,Java如何实现
在此,以唯一id知道的某个回答为例。 首先,要明确的是,一旦将HTTP请求发送到服务器并进行处理,就将响应写回到此请求的连接是完成此请求。 如下所示。
公共void请求(connection connection,http请求) )。
处理request并省略代码
connection.write(response; //完成响应
}
假设需要调用两个接口来获取答案、获取评论数和答案信息,传统代码可能会这样写。
//获取注释数
公共连接请求(connection connection,http请求) {
integer comment count=空;
try {
//从缓存中获取评论数,阻止IO
comment count=getcommnetcountfromcache (id;
}catch(exceptione ) {
try {
//如果缓存获取失败,则从数据库中获取,阻止IO
comment count=getvotecountfromdb (id;
}catch(exceptionex ) {
}
}
connection.write(commentcount;
}
//得到回答
publicvoidgetanswer (connection connection,HttpRequest request ) )。
//点赞数
Integer voteCount=null;
try {
//从缓存中获取点赞数,屏蔽IO
vote count=getvotecountfromcache (id;
}catch(exceptione ) {
try {
//如果缓存获取失败,则从数据库中获取,阻止IO
vote count=getvotecountfromdb (id;
}catch(exceptionex ) {
}
}
//从数据库获取回答信息,屏蔽IO
answer answer=getanswerfromdb (id;
组装响应
ResultVO response=new ResultVO (;
if (计数!=null ) {
response.setvotecount (vote count );
}
if (安瑟!=null ) {
response.setanswer(answer );
}
connection.write(response; //完成响应
}
在此实现中,进程只需要一个线程池来承载所有请求。 这种实现有两个弊端。
如果线程池IO被阻止,导致某个存储变慢或缓存被破坏,则所有服务器都将被阻止。 现在,假设注释缓存突然中断,全部都要访问数据库,请求变慢了。 唯一的线程池已满,无法处理获取答案的请求,因为线程必须等待I/o响应。
要取得回答信息,可以同时进行点赞数的取得和回答信息的取得。 不需要先取得点赞数再取得回答信息。
现在,NIO无阻塞IO正在普及。 有了无阻塞IO,响应编程就可以在不阻塞线程的情况下处理请求。 这是怎么实现的呢
传统的BIO在线程向Connection写入数据后,当前线程一直处于Block状态,直到返回响应,然后再执行响应后的操作。 NIO在线程将数据写入Connection后,在一个位置缓存返回响应后需要的内容和参数,然后直接返回。 如果回复响应,NIO的选择器的Read事件将处于就绪状态。 扫描Selector事件的线程将告诉您线程池中的数据已改善。 然后,线程池中的一个线程取出刚缓存的工作有参数,继续处理。
那么,在返回缓存响应后如何实现所需的内容和参数呢? Java本身提供了两种类型的接口:基于回调的Callback接口(Java 8引入的各种Functional Interface )和Future框架。
基于Callback的实现:
//得到回答
publicvoidgetanswer (connection connection,HttpRequest request ) )。
ResultVO resultVO=new ResultVO (;
getVote
CountFromCache(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 模块的话,就需要先掌握