14.2.1 使用 请求/响应
模型
在 Spring 中创建 RSocket 服务器与创建 控制器类一样简单,与在 web 应用程序或 REST 服务中那样。下面的 控制器是一个示例 RSocket 服务,处理来自客户端的问候语,并以另一个问候语回应:
清单 14.2 一个简单的 RSocket 请求/响应
服务器。
package rsocket;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Controller
@Slf4j
public class GreetingController{
@MessageMapping("greeting")
public Mono<String> handleGreeting(Mono<String> greetingMono) {
return greetingMono
.doOnNext(greeting ->
log.info("Received a greeting: " + greeting))
.map(greeting -> "Hello back to you!");
}
}
如您所见,web 控制器和 RSocket 控制器之间的关键区别在于:RSocket 控制器不处理给定路径的 HTTP 请求(使用@GetMapping
或 @PostMapping
注解),而是使用 @MessageMapping
注解,处理给定路由上的传入消息。在本例中,当请求被调用时,从客户端发送到名为“greeting”的路由,调用 handleGreeting()
方法。
handleGreeting()
方法以 Mono<String>
为参数。在这种情况下,问候语非常简单,一个字符串就足够了。但是如果需要的话,传入的有效载荷可以是更复杂的类型。在收到 Mono<String>
之后,它只是记录收到的问候语,然后使用 map()
函数在 Mono 上创建一个新的 Mono<String>
,以返回给客户端。
尽管 RSocket 控制器不处理 HTTP 请求,但路由名称可以具有类似路径的形式,包括路径占位符都可以传递到处理方法中。例如,考虑以下形式的 handleGreeting()
方法:
@MessageMapping("greeting/{name}")
public Mono<String> handleGreeting(
@DestinationVariable("name") String name,
Mono<String> greetingMono) {
return greetingMono
.doOnNext(greeting ->
log.info("Received a greeting from " + name + " : " + greeting))
.map(greeting -> "Hello to you, too, " + name);
}
在本例中,@MessageMapping
中指定的路由包含一个名为“name”的变量。它用大括号括起来,与 Spring MVC 中的路径变量表示方法相同。类似地,该方法接受带 @DestinationVariable
注解的字符串参数,来引用占位符变量。就像 Spring MVC 的 @PathVariable
注解一样,@DestinationVariable
用于提取指定的占位符值,并将其传递给处理方法。
一旦运行到新版本的 handleGreeting()
,将使用路由中指定的名称,向客户端返回更个性化的问候语。
在创建 RSocket 服务器时,还必须记住一件事:指定要监听的端口。默认情况下,RSocket 服务是基于 TC 的,并且侦听特定端口。spring.rsocket.server.port 配置属性可以设置 RSocket 服务器使用的端口:
spring:
rsocket:
server:
port: 7000
spring.rsocket.server.port 属性有两个用途:启用服务器和指定服务器应侦听的端口。如果没有设置,那么 Spring 将假定您的应用程序将仅作为客户端使用,不会做为服务端进行端口侦听。在这种情况下,我们启动一台服务器,设置属性 spring.rsocket.server.port,如下所示,在端口 7000 上启动侦听。
现在,让我们把注意力转向 RSocket 客户端。在 Spring 中,使用 RSocketRequester 实现了 RSocket 客户端。RSocket 的 Spring Boot 自动配置将自动在 Spring 应用程序上下文中创建 RSocketRequester.Builder 类型的 bean。您可以将这个 bean 注入任何其他bean 中。
例如,下面的 ApplicationRunner bean 注入了 RSocketRequester.Builder:
package rsocket;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.rsocket.RSocketRequester;
@Configuration
@Slf4j
public class RSocketClientConfiguration {
@Bean
public ApplicationRunner sender(RSocketRequester.Builder requesterBuilder) {
return args -> {
RSocketRequester tcp = requesterBuilder.tcp("localhost", 7000);
// ... send messages with RSocketRequester ...
};
}
}
在本例中,创建的 RSocketRequester 侦听 localhost 端口7000。返回的 RSocketRequester 可用于向服务器发送消息。
在 请求/响应
模型中,请求需要(至少)指定路由和有效数据载荷。您一定还记得,服务端控制器会处理名为“greeting”的请求,且需要字符串类型的输入。它还返回一个字符串作为输出。以下是完整的客户端代码,显示了如何向服务器发送问候语并处理响应:
清单 14.3 发送请求的客户端。
RSocketRequester tcp = requesterBuilder.tcp("localhost", 7000);
// ... send messages with RSocketRequester ...
tcp
.route("greeting")
.data("Hello RSocket!")
.retrieveMono(String.class)
.subscribe(response -> log.info("Got a response: " + response));
这将通过“greeting”路由向服务器发送问候语“Hello RSocket!”。请注意,它需要一个返回,是在调用 retrieveMono()
时指定的。这个 subscribe()
方法订阅返回的 Mono,并通过日志记录返回值。
现在,让我们假设您希望向另一个路径发送问候语,该路径在其内部接受一个变量。客户端代码基本相同,只是在 route()
中包含了占位符变量的值:
String who = "Craig";
tcp
.route("greeting/{name}", who)
.data("Hello RSocket!")
.retrieveMono(String.class)
.subscribe(response -> log.info("Got a response: " + response));
在这里,消息将被发送到名为“greeting/Craig”的路由。该路由将由控制器中的方法处理,这个方法使用了 @MessageMapping
注解,指定了路由为“greeting/{name}”。同时,您也可以在路由中硬编码名称,或使用字符串连接来创建路由名称。在客户机中使用占位符,可以很容易地避免字符串连接造成的混乱。
请求/响应
模型可能是 RSocket 最简单的通信模型。这只是个开始,让我们看看如何使用可能返回多个响应的 请求/流
模型。