jax-rs jax-ws_迟来总比没有好:SSE或服务器发送的事件现在已在JAX-RS中

jax-rs jax-ws

服务器发送的事件 (或简称为SSE )是非常有用的协议,它允许服务器通过HTTP将数据推送到客户端。 这是我们的Web浏览器支持的年龄,但是令人惊讶的是, JAX-RS规范在很长一段时间内都忽略了这一点。 尽管Jersey提供了适用于SSE媒体类型的扩展名,但该API尚未正式化,因此不能移植到其他JAX-RS实现中。

幸运的是, JAX-RS 2.1 (也称为JSR-370)通过将SSE支持(客户端和服务器端)作为正式规范的一部分,已经改变了这一点。 在今天的帖子中,我们将研究如何使用最近发布的出色的Apache CXF框架3.2.0版将SSE支持集成到现有的Java REST(ful) Web服务中。 实际上,除了自举之外,实际上没有CXF特定的东西,所有示例都应在实现JAX-RS 2.1规范的任何其他框架中工作。

事不宜迟,让我们开始吧。 由于这些天大量的Java项目都建立在出色的Spring Framework之上,因此我们的示例应用程序将使用Spring Boot和Apache CXF Spring Boot Integration使我们Swift起步。 老好伙伴Apache Maven也可以通过管理项目依赖项来帮助我们。

org.springframework.bootspring-boot-starter1.5.8.RELEASEorg.apache.cxfcxf-rt-frontend-jaxrs3.2.0org.apache.cxfcxf-spring-boot-starter-jaxrs3.2.0org.apache.cxfcxf-rt-rs-client3.2.0org.apache.cxfcxf-rt-rs-sse3.2.0

在后台, Apache CXF正在使用Atmosphere框架来实现SSE传输,因此这是我们必须包含的另一个依赖项。

org.atmosphereatmosphere-runtime2.4.14

有关依赖于Atmosphere框架的详细信息导致需要提供其他配置设置,即transportId ,以便确保在运行时拾取支持SSE的传输。 相关详细信息可以添加到application.yml文件中:

cxf:servlet:init:transportId: http://cxf.apache.org/transports/http/sse

太好了,所以基础就在那里,继续前进。 我们将要构建的REST(ful) Web服务将在SSE流中公开虚构的CPU平均负载(为简单起见,随机生成)。 Stats类将构成我们的数据模型。

public class Stats {private long timestamp;private int load;public Stats() {}public Stats(long timestamp, int load) {this.timestamp = timestamp;this.load = load;}// Getters and setters are omitted...
}

说到流, Reactive Streams规范已进入Java 9 ,希望我们能看到Java社区加速采用React式编程模型。 此外,在具有Reactive Streams支持的情况下,开发支持SSE的 REST(ful) Web服务将变得更加容易和直接。 为此,让我们将RxJava 2集成到示例应用程序中。

io.reactivex.rxjava2rxjava2.1.6

这是开始使用我们的StatsRestService类(典型的JAX-RS资源实现)的好时机。 JAX-RS 2.1中的关键SSE功能以Sse上下文对象为中心,可以像这样注入。

@Service
@Path("/api/stats")
public class StatsRestService {@Context public void setSse(Sse sse) {// Access Sse context here}

在Sse上下文之外,我们可以访问两个非常有用的抽象: SseBroadcaster和OutboundSseEvent.Builder ,例如:

private SseBroadcaster broadcaster;
private Builder builder;@Context 
public void setSse(Sse sse) {this.broadcaster = sse.newBroadcaster();this.builder = sse.newEventBuilder();
}

您可能已经猜到了, OutboundSseEvent.Builder构造了OutboundSseEvent类的实例,这些实例可以通过电线发送,而SseBroadcaster则向所有连接的客户端广播相同的SSE流。 话虽如此,我们可以生成OutboundSseEvent的流并将其分发给感兴趣的每个人:

private static void subscribe(final SseBroadcaster broadcaster, final Builder builder) {Flowable.interval(1, TimeUnit.SECONDS).zipWith(eventsStream(builder), (id, bldr) -> createSseEvent(bldr, id)).subscribeOn(Schedulers.single()).subscribe(broadcaster::broadcast);
}private static Flowable<OutboundSseEvent.Builder> eventsStream(final Builder builder) {return Flowable.generate(emitter -> emitter.onNext(builder.name("stats")));
}

如果您不熟悉RxJava 2 ,请不用担心,这就是这里发生的情况。 eventsStream方法为stats类型的SSE事件返回有效的无限OutboundSseEvent.Builder实例流。 订阅方法稍微复杂一点。 我们首先创建一个流,该流每秒发出序号,例如fe 0、1、2、3、4、5、6等。 稍后,我们将此流与eventsStream方法返回的流组合在一起,实质上将两个流合并为一个流,该流每秒发出一个元组(number, OutboundSseEvent.Builder ) 。 公平地说,该元组对我们不是很有用,因此我们将其转换为OutboundSseEvent类的实例,将数字视为SSE事件标识符:

private static final Random RANDOM = new Random();private static OutboundSseEvent createSseEvent(OutboundSseEvent.Builder builder, long id) {return builder.id(Long.toString(id)).data(Stats.class, new Stats(new Date().getTime(), RANDOM.nextInt(100))).mediaType(MediaType.APPLICATION_JSON_TYPE).build();
}

OutboundSseEvent可以使用常规MessageBodyWriter解析策略在data属性中携带将相对于指定的mediaType进行序列化的任何有效负载。 一旦获得OutboundSseEvent实例,便使用SseBroadcaster :: broadcast方法将其发送出去。 请注意,我们使用subscribeOn运算符将控制流移交给了另一个线程,这通常是您一直在做的事情。

很好,希望现在清除了流部分,但是我们如何才能真正订阅SseBroadcaster发出的SSE事件? 这比您想像的要容易:

@GET
@Path("broadcast")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void broadcast(@Context SseEventSink sink) {broadcaster.register(sink);
}

我们都准备好了。 这里最重要的是正在生成的内容类型,应将其设置为MediaType.SERVER_SENT_EVENTS 。 在这种情况下, SseEventSink的上下文实例变为可用,并且可以向SseBroadcaster实例注册。

要查看我们的JAX-RS资源,我们需要使用例如JAXRSServerFactoryBean引导服务器实例,并在此过程中配置所有必需的提供程序。 请注意,我们也在显式指定要使用的传输,在这种情况下为SseHttpTransportFactory.TRANSPORT_ID

@Configuration
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {@Beanpublic Server rsServer(Bus bus, StatsRestService service) {JAXRSServerFactoryBean endpoint = new JAXRSServerFactoryBean();endpoint.setBus(bus);endpoint.setAddress("/");endpoint.setServiceBean(service);endpoint.setTransportId(SseHttpTransportFactory.TRANSPORT_ID);endpoint.setProvider(new JacksonJsonProvider());return endpoint.create();}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/static/**").addResourceLocations("classpath:/web-ui/"); }
}

要结束循环,我们只需要为Spring Boot应用程序提供运行器即可:

@SpringBootApplication
public class SseServerStarter {    public static void main(String[] args) {SpringApplication.run(SseServerStarter.class, args);}
}

现在,如果我们运行该应用程序并使用多个Web浏览器或同一浏览器中的不同选项卡导航到http:// localhost:8080 / static / broadcast.html ,我们将观察到所有事件内部绘制的相同事件流:

不错,广播当然是一个有效的用例,但是在每次端点调用时返回一个独立的SSE流又如何呢? 简单,只需使用SseEventSink方法(例如sendclose )即可直接操作SSE流。

@GET
@Path("sse")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void stats(@Context SseEventSink sink) {Flowable.interval(1, TimeUnit.SECONDS).zipWith(eventsStream(builder), (id, bldr) -> createSseEvent(bldr, id)).subscribeOn(Schedulers.single()).subscribe(sink::send, ex -> {}, sink::close);
}

这次,如果我们运行该应用程序并使用多个Web浏览器或同一浏览器中的不同选项卡导航到http:// localhost:8080 / static / index.html ,我们将观察到完全不同的图表:

出色的服务器端API确实非常简洁且易于使用。 但是客户端方面,我们可以使用Java应用程序中的SSE流吗? 答案是肯定的。 JAX-RS 2.1还概述了客户端API,其核心是SseEventSource 。

final WebTarget target = ClientBuilder.newClient().register(JacksonJsonProvider.class).target("http://localhost:8080/services/api/stats/sse");try (final SseEventSource eventSource =SseEventSource.target(target).reconnectingEvery(5, TimeUnit.SECONDS).build()) {eventSource.register(event -> {final Stats stats = event.readData(Stats.class, MediaType.APPLICATION_JSON_TYPE);System.out.println("name: " + event.getName());System.out.println("id: " + event.getId());System.out.println("comment: " + event.getComment());System.out.println("data: " + stats.getLoad() + ", " + stats.getTimestamp());System.out.println("---------------");});eventSource.open();// Just consume SSE events for 10 secondsThread.sleep(10000); 
}

如果运行此代码段(假设服务器也已启动并且正在运行),我们将在控制台中看到类似的内容(您可能还记得,数据是随机生成的)。

name: stats
id: 0
comment: null
data: 82, 1509376080027
---------------
name: stats
id: 1
comment: null
data: 68, 1509376081033
---------------
name: stats
id: 2
comment: null
data: 12, 1509376082028
---------------
name: stats
id: 3
comment: null
data: 5, 1509376083028
---------------...

如我们所见,服务器端的OutboundSseEvent变为客户端的InboundSseEvent 。 客户端可以使用通常的MessageBodyReader解析策略,通过指定预期的媒体类型来消耗数据属性中可以反序列化的任何有效负载。

单篇文章中压缩了很多内容。 而且,关于SSE和JAX-RS 2.1的其他事情我们在这里没有涉及,例如使用HttpHeaders.LAST_EVENT_ID_HEADER或配置重新连接延迟。 如果有兴趣学习的话,这些可能是即将发布的帖子的重要话题。

总而言之, JAX-RS对SSE的支持是我们许多人期待已久的事情。 最后,它在那里,请尝试一下!

完整的项目资源可在Github上找到 。

翻译自: https://www.javacodegeeks.com/2017/10/better-late-never-sse-server-sent-events-now-jax-rs.html

jax-rs jax-ws

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

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

相关文章

根据录入的计算公式计算_小规模纳税人增值税计算公式是什么,什么人能被称为小规模纳税人?- 理财技巧...

摘要&#xff1a; 想必大家都知道小规模纳税人在增值税的缴纳上能够享受到不少的优惠措施和政策&#xff0c;最终可以少交不少钱&#xff01;那么什么人能被称为小规模纳税人呢&#xff1f;小规模纳税人增值税计算公式又是什么呢&#xff1f;下面赢家财富网就带大家详细了解一下…

Linux 命令之 chmod -- 改变文件或目录权限

文章目录一、命令介绍二、语法格式三、常用选项四、权限表达式&#xff08;一&#xff09;采用权限字母和操作符表达式&#xff08;二&#xff09;采用权限数字表达式五、参考示例&#xff08;一&#xff09;为拥有者、所属群组以及其他人添加关于directory1的读、写和执行权限…

12章总结

一.集合类概述 java.util包中提供了一些集合类&#xff0c;这些集合类又被称为容器。 集合类与数组的不同之处&#xff1a; 数组的长度是固定的&#xff0c;集合的长度是可变的&#xff1a;数组用来存放基本类型的数据&#xff0c;集合用来存放对象的引用。 常…

java亮剑_黄金矿工3-太空版

快速搜索机型:诺基亚 N70系列(176208)7610 3230 6600 6260 6620 6630 6670 6680 6681 6682 N70 N72 ;松下: X700 X800 ;联想: P930诺基亚 N73系列(240320)N73 5320 5320XM 5320di_XM 5630XM 5700 5700XM 5710XM 5730XM 6110 6110N 6120 6120C 6120ci 6121 6122C 6124C 6210S 62…

怎么知道跟交换机互联的交换机_怎么选择POE交换机

工作的呢&#xff1f;48V供电的就是标准PoE吗&#xff1f;下面我们简单讲解一下标准PoE和MCU PoE交换机&#xff08;单片机&#xff09;及非标PoE设备是怎么工作的。标准PoE和MCU PoE交换机&#xff08;单片机&#xff09;以及非标准PoE产品定义首先我们来看一下什么是标准PoE、…

并发查询parallel_惯用并发:flatMap()与parallel()– RxJava常见问题解答

并发查询parallel简单&#xff0c;有效和安全的并发是RxJava的设计原则之一。 然而&#xff0c;具有讽刺意味的是&#xff0c;它可能是该库中最容易被误解的方面之一。 让我们举一个简单的例子&#xff1a;假设我们有一堆UUID并且对于每个UUID &#xff0c;我们必须执行一组任务…

java正则表达式 匹配()_学习Java正则表达式(匹配、替换、查找)

import java.util.ArrayList;import java.util.regex.Matcher;import java.util.regex.Pattern;public class test {public static void main(String[] args) {getStrings(); //用正则表达式获取指定字符串内容中的指定内容System.out.println("********************"…

Linux 命令之 chown -- 用来变更文件或目录的拥有者或所属群组

命令介绍 Linux/Unix 属于多用户多任务操作系统&#xff0c;所有的文件皆有拥有者。利用 chown 命令可以将指定文件的拥有者改为指定的用户或组&#xff0c;用户可以是用户名或者用户ID&#xff0c;组可以是组名或者组ID&#xff0c;文件是以空格分开的要改变权限的文件列表&a…

在excel中如何筛选重复数据_Excel中12招筛选使用大全,小白也能秒变高手

【温馨提示】亲爱的朋友&#xff0c;阅读之前请您点击【关注】&#xff0c;您的支持将是我最大的动力&#xff01;在日常使用Excel处理数据时&#xff0c;相信小伙伴们对于筛选功能已经是不陌生了&#xff0c;Excel筛选功能可以快速有效的帮助我们处理大量的数据&#xff0c;将…

静态内部类实现mysql连接_Java - 静态内部类

Java语言允许在类中再定义类&#xff0c;这种在其它类内部定义的类就叫内部类。内部类又分为&#xff1a;常规内部类、局部内部类、匿名内部类和静态嵌套类四种。1、静态内部类定义静态内部类&#xff0c;定义在类中&#xff0c;任何方法外&#xff0c;用static定义&#xff1b…

Linux 命令之 ll -- 列出当前文件或目录的详细信息

文章目录命令介绍常用选项参考示例&#xff08;一&#xff09;查看当前目录下所有文件的详细信息&#xff0c;且按创建时间排序&#xff08;二&#xff09;按时间倒序&#xff0c;分页排列文件&#xff08;三&#xff09;查看某个目录的详细信息&#xff08;四&#xff09;查看…

cli命令行界面 demo_Java命令行界面(第24部分):MarkUtils-CLI

cli命令行界面 demo本系列的第一篇 有关使用Java解析命令行参数的文章介绍了Apache Commons CLI库。 这是本系列中介绍的基于Java的命令行解析库中最古老的&#xff0c;而且可能是最常用的之一。 Apache Commons CLI确实显示了它的时代&#xff0c;特别是与一些更现代的基于Jav…

python打包成exe_【Python基础】一篇文件教你py文件打包成exe

场景:如果要将我们编写好的代码给别人使用,如果要他们直接使用我们的代码,就需要安装各种编译软件以及第三方模块,还要对软件操作,编程有一定的了解,这对使用者的要求比较高,不是很方便,为了解决这一问题,我们可以选择将我们编写的代码,编译成一个可执行文件,这样,就可以实现跨…

mongodb启用身份验证_为您的Web应用程序启用两因素身份验证

mongodb启用身份验证支持两因素身份验证&#xff08;2FA&#xff09;几乎总是一个好主意&#xff0c;尤其是对于后台系统。 2FA有许多不同的形式&#xff0c;其中一些包括SMS&#xff0c;TOTP甚至是硬件令牌 。 启用它们需要类似的流程&#xff1a; 用户转到其个人资料页面&a…

MySQL 数据库命令之 mysqlshow -- 显示 MySQL 数据库相关信息

文章目录介绍语法格式常用选项参考示例&#xff08;一&#xff09;显示指定数据库中的所有表的记录数和列数&#xff08;二&#xff09;显示指定数据库中所有的数据表的额外信息&#xff08;三&#xff09;查看指定数据库中的所有数据表&#xff08;四&#xff09;显示所有的数…

java中的gui_java gui快速入门教程

JCheckBox和JRadioButton使用示例import java.awt.*;import javax.swing.*;class Hobby extends JPanel {JCheckBox c1 new JCheckBox("写作",false);JCheckBox c2 new JCheckBox("音乐",false);JCheckBox c3 new JCheckBox("跑步",false);JR…

cad图标注释大全_CAD源泉插件快捷键使用教程(全集)

点击直达全集教程地址​www.bilibili.com此插件和海龙工具箱功能相似&#xff01;不建议同时安装&#xff0c;快捷命令冲突。插件工具箱 图文介绍平面空间布置 jj这个命令把我们常规用到的家装空间都已经用上了&#xff0c;除了切换不同空间布置格局&#xff0c;而且图块的样式…

java登录界面命令_Java命令行界面(第30部分):观察

java登录界面命令这个有关Java命令行参数解析的系列文章由四个月来发表的29篇帖子组成&#xff0c;涵盖了28个不同的开放源代码库&#xff0c;可用于解析Java命令行参数。 这篇文章收集了可以从本系列的前29篇文章中得出的一些观点&#xff0c;并提供了选择28个库中的一个或决定…

MySQL 数据库命令之 mysqladmin -- MySQL 服务器管理客户端

文章目录一、介绍二、语法格式三、命令参数&#xff08;一&#xff09;参数默认值&#xff08;二&#xff09;默认参数四、支持的管理命令五、参考示例&#xff08;一&#xff09;每隔两秒查看一次服务器的状态&#xff0c;总共重复 5 次&#xff08;二&#xff09;修改 root 密…

分支限界法 tsp java_基于分支限界法的旅行商问题(TSP)一

//分支限界法#include#include#include#includeconst int INF 100000;const int MAX_N 22;using namespacestd;//n*n的一个矩阵intn;int cost[MAX_N][MAX_N];//最少3个点&#xff0c;最多MAX_N个点structNode{bool visited[MAX_N];//标记哪些点走了int s;//第一个点int s_p;/…