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 最简单的通信模型。这只是个开始,让我们看看如何使用可能返回多个响应的 请求/流模型。

results matching ""

    No results matching ""