显示导航

使用 Grails 发送服务器端发送事件

本指南将指导您如何使用 Grails 和 RxJava 发送服务器端发送事件。

作者: Graeme Rocher

Grails 版本 5.0.1

1 Grails 培训

Grails 培训 - 由创建并积极维护 Grails 框架的人员开发和交付!

2 开始使用

在本指南中,您将构建一个通过 服务器端发送事件与 JavaScript 客户端通信的 Grails 应用程序。

2.1 所需物品

要完成本指南,您将需要以下物品

  • 一些时间

  • 一个合适的文本编辑器或 IDE

  • 安装了 JDK 1.8 或更高版本,并正确配置了 JAVA_HOME

2.2 如何完成指南

若要开始操作,请执行以下操作

Grails 指南存储库包含两个文件夹

  • initial 初始项目。通常是一个带有附加代码以便您能快速上手的简单 Grails 应用程序。

  • complete 完成的示例。它是按照指南提出的步骤进行操作并对 initial 文件夹应用这些更改所得出的结果。

要完成本指南,请转到 initial 文件夹

  • cd 进入 grails-guides/server-sent-events/initial

并按照下一部分中的说明进行操作。

如果您cd 进入 grails-guides/server-sent-events/complete,就可以直接进入完成的示例

3 编写应用程序

3.1 添加 RxJava 依赖

第一步是配置所需的 Gradle 依赖项。这基本上意味着在 `build.gradle` 文件中添加对 Grails and Micronaut 中的 RxJava 插件 reactivex` 的依赖

dependencies {
    ...
    implementation "io.reactivex:rxjava"
    implementation "org.grails.plugins:rxjava"
}

RxJava 用于促进反应式编程模型,以便向 JavaScript 客户端发送事件。

3.2 创建控制器

接下来创建一个名为 TickTock 的新控制器。如果您已安装 Grails,则可以通过运行 grails create-controller 命令来执行此操作,否则可以使用已包含的 grailsw 包装器

$ ./grailsw create-controller TickTock
首次运行 grails 命令时,将从互联网下载应用程序依赖项。后续调用将快得多。

该命令的输出将如下所示

| Created grails-app/controllers/example/TickTockController.groovy
| Created src/test/groovy/example/TickTockControllerSpec.groovy

正如您所看到的,在 grails-app/controllers/example 目录中创建了一个新的控制器。Grails 会使用 grails-app/conf/application.yml 文件中 grails.codegen.defaultPackage 值定义的值来确定要使用哪个包。

3.3 实现控制器

现在,您需要修改该控制器才能充分利用 RxJava 功能。为此,添加必要的导入并使控制器实现 grails.rx.web.RxController 特性

grails-app/controllers/example/TickTockController.groovy
import rx.Observer
import rx.Observable
import grails.rx.web.RxController
import java.util.concurrent.TimeUnit
import groovy.transform.CompileStatic

/**
 * Demo Server Sent Events Controller
 */
@CompileStatic
class TickTockController implements RxController {

下一步是实现 index 操作。这是控制器在未指定显式操作的情况下执行的默认操作。

RxController 特性添加了一个名为 rx 的帮助器,它允许您控制从控制器发送到客户端的响应。

为了开始发送服务器端发送的事件,我们将使用 rx.stream(..) 方法

grails-app/controllers/example/TickTockController.groovy
def index() {
    rx.stream { Observer observer ->
    }
}

stream 方法接受一个闭包,该闭包接收一个 rx.Observer 实例,当数据准备好通过 rx.Observer 接口发送时,您可以使用它向客户端发送事件。

通过使用 rx 帮助器的各种方法,您可以将值传递到 Observer 的 onNext 方法。例如

grails-app/controllers/example/TickTockController.groovy
for (i in (0 .. 5)) {
    if (i % 2 == 0) {
        observer.onNext(
            rx.render('Tick')
        )
    }
    else {
        observer.onNext(
            rx.render('Tock')
        )
    }
    sleep 1000
}

在此示例中,我们循环遍历 0 到 5 之间的值,并分别为奇数和偶数值调用 rx.render(..) 以分别输出 “Tick” 或 “Tock”。对 sleep(..) 的调用是为了模拟一个慢速请求,例如从外部 Web 服务获取值时。

完成发送事件后,您可以使用 Observer 的 onCompleted 事件完成

grails-app/controllers/example/TickTockController.groovy
observer.onCompleted()

最终完成的逻辑如下所示

grails-app/controllers/example/TickTockController.groovy
def index() {
    rx.stream { Observer observer ->

        for (i in (0 .. 5)) {
            if (i % 2 == 0) {
                observer.onNext(
                    rx.render('Tick')
                )
            }
            else {
                observer.onNext(
                    rx.render('Tock')
                )
            }
            sleep 1000
        }
        observer.onCompleted()
    }
}

请注意,如果对 sleep(1000) 的调用实际上是应用程序的具体要求,那么用 interval 方法编写的一种更惯用的方式将是

grails-app/controllers/example/TickTockController.groovy
@SuppressWarnings('DuplicateNumberLiteral')
def example() {
    int i = 0
    rx.stream(Observable
                .interval(1, TimeUnit.SECONDS)
                .map {
        i++
        if (i % 2 == 0) {
            rx.render('Tick')
        }
        else {
            rx.render('Tock')
        }
    })
}

3.4 实现客户端

要实现客户端,首先打开 grails-app/views/index.gsp 文件,并在 “Welcome to Grails” <h1> 标签下面添加一个页眉

grails-app/views/index.gsp
<h1>Welcome to Grails</h1>
<h2 style="text-align:center;font-size:50px" id="message"></h2>

我们将使用这个页眉来放置从服务器推送的事件中接收的内容。

接下来在页面的底部添加以下 <script>

grails-app/views/index.gsp
<script type="text/javascript">
    (function() {
        var eventSource = new EventSource("tickTock");
        eventSource.onmessage = function(event) {
            console.log("data: "+event.data)
            document.getElementById('message').innerHTML = event.data;
        };
    })();
</script>

上面的 JavaScript 代码利用服务器推送事件规范的 EventSource 对象,开始从控制器接收事件

grails-app/views/index.gsp
var eventSource = new EventSource("tickTock");
你需要一个 现代浏览器 才能使用 EventSource 对象

然后它注册一个监听器,将使用来自服务器的响应更新之前定义的 <h2> 页眉

grails-app/views/index.gsp
eventSource.onmessage = function(event) {
    console.log("data: "+event.data)
    document.getElementById('message').innerHTML = event.data;
};

好了!你现在可以运行应用程序了!

4 运行应用程序

要运行应用程序,请使用 ./gradlew bootRun 命令,它将在端口 8080 上启动应用程序。

然后你可以导航到 http://localhost:8080,你会看到在 "Welcome to Grails!" 消息的下面,“Tick” 和 “Tock” 消息交替显示,因为浏览器从服务器接收事件。

5 需要 Grails 帮助吗?

Object Computing, Inc. (OCI) 赞助了本指南的创建。我们提供各种咨询和支持服务。

OCI 是 Grails 的家

认识团队