1. Overview
1.概述
RSocket is an application protocol providing Reactive Streams semantics – it functions, for example, as an alternative to HTTP.
RSocket是一个提供Reactive Streams语义的应用协议–它的功能是,例如,作为HTTP的替代。
In this tutorial, we’re going to look at RSocket using Spring Boot, and specifically how it helps abstract away the lower-level RSocket API.
在本教程中,我们将使用RSocket看一看Spring Boot,特别是它如何帮助抽象出低级别的 RSocket API。
2. Dependencies
2.依赖性
Let’s start with adding the spring-boot-starter-rsocket dependency:
让我们开始添加spring-boot-starter-rsocket依赖性。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>
This will transitively pull in RSocket related dependencies such as rsocket-core and rsocket-transport-netty.
这将过渡性地拉入RSocket相关的依赖,如rsocket-core和rsocket-transport-netty。
3. Sample Application
3.申请书样本
Now we’ll continue with our sample application. To highlight the interaction models RSocket provides, we’re going to create a trader application. Our trader application will consist of a client and a server.
现在,我们将继续进行我们的示例应用程序。为了突出RSocket提供的交互模型,我们将创建一个交易员应用程序。我们的交易员应用程序将由一个客户端和一个服务器组成。
3.1. Server Setup
3.1.服务器设置
First, let’s set up the server, which will be a Spring Boot application bootstrapping an RSocket server.
首先,我们来设置服务器,它将是一个引导RSocket服务器的Spring Boot应用程序。
Since we have the spring-boot-starter-rsocket dependency, Spring Boot autoconfigures an RSocket server for us. As usual with Spring Boot, we can change default configuration values for the RSocket server in a property-driven fashion.
由于我们有spring-boot-starter-rsocket依赖,Spring Boot为我们自动配置了一个RSocket服务器。与Spring Boot一样,我们可以以属性驱动的方式改变RSocket服务器的默认配置值。
For example, let’s change the port of our RSocket server by adding the following line to our application.properties file:
例如,让我们通过在application.properties文件中添加以下一行来改变我们RSocket服务器的端口。
spring.rsocket.server.port=7000
We can also change other properties to further modify our server according to our needs.
我们还可以改变其他属性以根据我们的需要进一步修改我们的服务器。
3.2. Client Setup
3.2 客户端设置
Next, let’s set up the client which will also be a Spring Boot application.
接下来,我们来设置客户端,它也将是一个Spring Boot应用程序。
Although Spring Boot auto-configures most of the RSocket related components, we should also define some beans to complete the setup:
虽然Spring Boot自动配置了大部分与RSocket相关的组件,但我们还应该定义一些Bean来完成设置。
@Configuration
public class ClientConfiguration {
@Bean
public RSocketRequester getRSocketRequester(){
RSocketRequester.Builder builder = RSocketRequester.builder();
return builder
.rsocketConnector(
rSocketConnector ->
rSocketConnector.reconnect(Retry.fixedDelay(2, Duration.ofSeconds(2)))
)
.dataMimeType(MimeTypeUtils.APPLICATION_JSON)
.tcp("localhost", 7000);
}
}
Here we’re creating the RSocket client and configuring it to use TCP transport on port 7000. Note that this is the server port we’ve configured previously.
这里我们要创建RSocket客户端,并将其配置为使用7000端口的TCP传输。注意,这是我们之前配置的服务器端口。
After defining this bean configuration, we have a bare-bones structure.
在定义了这个bean配置之后,我们就有了一个基本的结构。
Next, we’ll explore different interaction models and see how Spring Boot helps us there.
接下来,我们将探索不同的交互模型,看看Spring Boot如何帮助我们实现。
4. Request/Response with RSocket and Spring Boot
4.使用RSocket和Spring Boot的请求/响应
Let’s start with Request/Response. This is probably the most common and familiar interaction model since HTTP also employs this type of communication.
让我们从请求/响应开始。这可能是最常见和最熟悉的交互模型,因为HTTP也采用了这种通信类型。
In this interaction model, the client initiates the communication and sends a request. Afterward, the server performs the operation and returns a response to the client – thus the communication completes.
在这种交互模型中,客户端发起通信并发送一个请求。之后,服务器执行操作并向客户端返回响应–这样通信就完成了。
In our trader application, a client will ask for the current market data of a given stock. In return, the server will pass the requested data.
在我们的交易员应用程序中,客户将要求提供某一特定股票的当前市场数据。作为回报,服务器将传递所请求的数据。
4.1. Server
4.1.服务器
On the server side, we should first create a controller to hold our handler methods. But instead of @RequestMapping or @GetMapping annotations like in Spring MVC, we will use the @MessageMapping annotation:
在服务器端,我们应该首先创建一个控制器来保存我们的处理方法。但不是像Spring MVC中的@RequestMapping或@GetMapping注释,我们将使用@MessageMapping注释。
@Controller
public class MarketDataRSocketController {
private final MarketDataRepository marketDataRepository;
public MarketDataRSocketController(MarketDataRepository marketDataRepository) {
this.marketDataRepository = marketDataRepository;
}
@MessageMapping("currentMarketData")
public Mono<MarketData> currentMarketData(MarketDataRequest marketDataRequest) {
return marketDataRepository.getOne(marketDataRequest.getStock());
}
}
So let’s investigate our controller.
因此,让我们调查一下我们的控制器。
We’re using the @Controller annotation to define a handler which should process incoming RSocket requests. Additionally, the @MessageMapping annotation lets us define which route we’re interested in and how to react upon a request.
我们使用@Controller注解来定义一个处理程序,它应该处理传入的RSocket请求。此外,@MessageMapping注解让我们定义我们感兴趣的路由,以及如何对请求做出反应。
In this case, the server listens for the currentMarketData route, which returns a single result to the client as a Mono<MarketData>.
在这种情况下,服务器监听currentMarketData路由,将一个单一的结果作为Mono<MarketData>返回给客户端。
4.2. Client
4.2 客户
Next, our RSocket client should ask for the current price of a stock and get a single response.
接下来,我们的RSocket客户端应该询问一只股票的当前价格,并获得一个单一的响应。
To initiate the request, we should use the RSocketRequester class:
为了发起请求,我们应该使用RSocketRequester类。
@RestController
public class MarketDataRestController {
private final RSocketRequester rSocketRequester;
public MarketDataRestController(RSocketRequester rSocketRequester) {
this.rSocketRequester = rSocketRequester;
}
@GetMapping(value = "/current/{stock}")
public Publisher<MarketData> current(@PathVariable("stock") String stock) {
return rSocketRequester
.route("currentMarketData")
.data(new MarketDataRequest(stock))
.retrieveMono(MarketData.class);
}
}
Note that in our case, the RSocket client is also a REST controller from which we call our RSocket server. So, we’re using @RestController and @GetMapping to define our request/response endpoint.
请注意,在我们的案例中,RSocket客户端也是一个REST控制器,我们从这里调用我们的RSocket服务器。因此,我们使用@RestController和@GetMapping来定义我们的请求/响应端点。
In the endpoint method, we’re using RSocketRequester and specifying the route. In fact, this is the route which the RSocket server expects. Then we’re passing the request data. And lastly, when we call the retrieveMono() method, Spring Boot initiates a request/response interaction.
在端点方法中,我们使用RSocketRequester并指定了路由。事实上,这就是RSocket服务器所期望的路由。然后,我们要传递请求数据。最后,当我们调用retrieveMono()方法时,Spring Boot启动了一个请求/响应互动。
5. Fire and Forget With RSocket and Spring Boot
5.使用RSocket和Spring Boot的火灾和遗忘
Next, we’ll look at the fire-and-forget interaction model. As the name implies, the client sends a request to the server but doesn’t expect a response back.
接下来,我们将看一下 “发射-遗忘 “交互模型。顾名思义,客户端向服务器发送一个请求,但并不期望得到回应。
In our trader application, some clients will serve as a data source and will push market data to the server.
在我们的交易员应用程序中,一些客户端将作为数据源,并将市场数据推送到服务器。
5.1. Server
5.1.服务器
Let’s create another endpoint in our server application:
让我们在我们的服务器应用程序中创建另一个端点。
@MessageMapping("collectMarketData")
public Mono<Void> collectMarketData(MarketData marketData) {
marketDataRepository.add(marketData);
return Mono.empty();
}
Again, we’re defining a new @MessageMapping with the route value of collectMarketData. Furthermore, Spring Boot automatically converts the incoming payload to a MarketData instance.
同样,我们定义了一个新的@MessageMapping,路由值为collectMarketData。此外,Spring Boot自动将传入的有效载荷转换为MarketData实例。
The big difference here, though, is that we return a Mono<Void> since the client doesn’t need a response from us.
不过,这里最大的区别是,我们返回一个Mono<Void> ,因为客户端不需要我们的响应。。
5.2. Client
5.2.客户
Let’s see how we can initiate our fire-and-forget request.
让我们看看如何启动我们的 “防火 “请求。
We’ll create another REST endpoint:
我们将创建另一个REST端点。
@GetMapping(value = "/collect")
public Publisher<Void> collect() {
return rSocketRequester
.route("collectMarketData")
.data(getMarketData())
.send();
}
Here we’re specifying our route and our payload will be a MarketData instance. Since we’re using the send() method to initiate the request instead of retrieveMono(), the interaction model becomes fire-and-forget.
这里我们指定了我们的路线,我们的有效载荷将是一个MarketData实例。由于我们使用send()方法来发起请求,而不是retrieveMono(),交互模型变成了fire-and-forget。
6. Request Stream with RSocket and Spring Boot
6.使用RSocket和Spring Boot的请求流
Request streaming is a more involved interaction model, where the client sends a request but gets multiple responses over the course of time from the server.
请求流是一种更复杂的交互模型,客户发送一个请求,但在一段时间内从服务器获得多个响应。
To simulate this interaction model, a client will ask for all market data of a given stock.
为了模拟这种互动模式,客户将要求提供某只股票的所有市场数据。
6.1. Server
6.1.服务器
Let’s start with our server. We’ll add another message mapping method:
让我们从我们的服务器开始。我们将添加另一个消息映射方法。
@MessageMapping("feedMarketData")
public Flux<MarketData> feedMarketData(MarketDataRequest marketDataRequest) {
return marketDataRepository.getAll(marketDataRequest.getStock());
}
As we can see, this handler method is very similar to the other ones. The different part is that we returning a Flux<MarketData> instead of a Mono<MarketData>. In the end, our RSocket server will send multiple responses to the client.
我们可以看到,这个处理方法与其他方法非常相似。不同的部分是,我们返回一个Flux<MarketData>,而不是Mono<MarketData>。最后,我们的RSocket服务器将向客户发送多个响应。
6.2. Client
6.2.客户
On the client side, we should create an endpoint to initiate our request/stream communication:
在客户端,我们应该创建一个端点来启动我们的请求/流通信。
@GetMapping(value = "/feed/{stock}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Publisher<MarketData> feed(@PathVariable("stock") String stock) {
return rSocketRequester
.route("feedMarketData")
.data(new MarketDataRequest(stock))
.retrieveFlux(MarketData.class);
}
Let’s investigate our RSocket request.
让我们调查一下我们的RSocket请求。
First, we’re defining the route and request payload. Then, we’re defining our response expectation with the retrieveFlux() method call. This is the part which determines the interaction model.
首先,我们要定义路由和请求有效载荷。然后,我们用retrieveFlux()方法调用来定义我们的响应期望。这是决定交互模型的部分。
Also note that, since our client is also a REST server, it defines response media type as MediaType.TEXT_EVENT_STREAM_VALUE.
还要注意的是,由于我们的客户端也是一个REST服务器,它将响应媒体类型定义为MediaType.TEXT_EVENT_STREAM_VALUE.。
7. Exception Handling
7.异常处理
Now let’s see how we can handle exceptions in our server application in a declarative way.
现在让我们看看如何在我们的服务器应用程序中以声明的方式处理异常。
When doing request/response, we can simply use the @MessageExceptionHandler annotation:
当做请求/响应时,我们可以简单地使用@MessageExceptionHandler注解。
@MessageExceptionHandler
public Mono<MarketData> handleException(Exception e) {
return Mono.just(MarketData.fromException(e));
}
Here we’ve annotated our exception handler method with @MessageExceptionHandler. As a result, it will handle all types of exceptions since the Exception class is the superclass of all others.
这里我们用@MessageExceptionHandler注释了我们的异常处理方法。因此,它将处理所有类型的异常,因为Exception类是所有其他类的超类。
We can be more specific and create different exception handler methods for different exception types.
我们可以更具体一些,为不同的异常类型创建不同的异常处理方法。
This is of course for the request/response model, and so we’re returning a Mono<MarketData>. We want our return type here to match the return type of our interaction model.
这当然是针对请求/响应模型的,所以我们要返回一个Mono<MarketData>。我们希望这里的返回类型与我们的交互模型的返回类型一致。
8. Summary
8.摘要
In this tutorial, we’ve covered Spring Boot’s RSocket support and detailed different interaction models RSocket provides.
在本教程中,我们已经介绍了Spring Boot的RSocket支持,并详细介绍了RSocket提供的不同交互模型。
As always, you can check out all the code samples over on GitHub.
一如既往,你可以在GitHub上查看所有的代码样本,。