Spring Boot 2.x实战97 - 事件驱动6 - RSocket

5.RSocket

RSocket是一个使用在字节流传输(TCP/Websocket)之上的二进制点对点通讯协议;它主要用在分布式应用上,用来替换如HTTP这种通讯协议。

RSocket提供四种交互模型:

  • request/response:请求返回一条流数据(Mono);
  • request/stream:请求返回多条流数据(Flux);
  • fire-and-forget:请求不返回数据(Mono);
  • channel:双向流数据通讯(Flux method(Publisher input))。

5.1 新建应用

5.1.1 Server

新建应用,信息如下:

Group:top.wisely

Artifact:rsocket-server

Dependencies:RSocketSpring Data Reactive MongoDBLombok

build.gradle文件中的依赖如下:

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-rsocket'
	implementation 'org.springframework.boot:spring-boot-starter-data-mongodb-reactive'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
  //...
	}
}
5.1.2 Client

新建应用,信息如下:

Group:top.wisely

Artifact:rsocket-client

Dependencies:RSocketSpring Reactive WebLombok

build.gradle文件中的依赖如下:

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-webflux'
	implementation 'org.springframework.boot:spring-boot-starter-rsocket'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
  //...
	}
}

5.2 Spring Boot的自动配置

服务端的配置有:

  • RSocketServerAutoConfiguration:通过RSocketProperties使用spring.rsocket.server.*配置RSocket Server;
  • RSocketStrategiesAutoConfiguration:使用CBOR和Jackson配置数据交互的编码和解码;
  • RSocketMessagingAutoConfiguration:在Spring Messaging中配置Spring RSocket的支持;

客户端配置:

  • RSocketRequesterAutoConfiguration:配置一个RSocketRequester.Builder的Bean用来定义RSocketRequester

5.3 示例

5.3.1 Server
  • 配置RSocket Server和连接MongoDB

    spring:
      rsocket:
        server:
          address: localhost
          port: 9898
          transport: tcp #使用tcp协议监听localhost的9898端口
      data:
        mongodb:
          host: localhost
          port: 27017
          username: wisely
          password: zzzzzz
          database: first_db
    
  • 领域模型和Repository

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Document(collection = "people")
    public class Person {
        @Id
        private String id;
        private String name;
        private Integer age;
        public Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
    }
    
    public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
    }
    
  • RSocket Server

    RSocket是Spring Messaging集成的,同样也使用@MessageMapping定义消息的终点。

    @Controller
    public class PersonController {
    
        PersonRepository personRepository;
    
        public PersonController(PersonRepository personRepository) {
            this.personRepository = personRepository;
        }
    
    
        @MessageMapping("people.findById") 
        Mono<Person> getOne(Person person){ //1
            return personRepository.findById(person.getId());
        }
    
        @MessageMapping("people.findAll")
        Flux<Person> all(Person person){ //2
            return personRepository.findAll();
        }
    
        @MessageMapping("people.deleteById")
        Mono<Void> delete(Person person){ //3
            return personRepository.deleteById(person.getId());
        }
    
        @MessageMapping("people.save")
        Flux<Person> save(Publisher<Person> people){ //4
            return personRepository.saveAll(people);
        }
    
    }
    
    1. 演示request/response;
    2. 演示request/stream;
    3. 演示fire-and-forget;
    4. 演示channel,双向流数据。
  • 添加演示数据

    @Bean
    CommandLineRunner initPersonData(PersonRepository personRepository){
       return args -> {
          personRepository.deleteAll().subscribe();
          personRepository.save(new Person("wyf", 35)).subscribe();
          personRepository.save(new Person("foo", 34)).subscribe();
          personRepository.save(new Person("bar", 36)).subscribe();
       };
    }
    
5.3.2 Client

在客户端我们使用RSocketRequester作为客户端调用RSocket Server,类似于Spring WebFlux的WebClient或Spring MVC的RestTemplate

  • 配置RSocketRequester的Bean

    @Bean
    RSocketRequester rSocketRequester(RSocketRequester.Builder builder){
       return builder
             .connectTcp("localhost", 9898) //连接服务端地址和端口
             .block();
    }
    
  • 和服务端共享数据模型

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Person {
        private String id;
        private String name;
        private Integer age;
    
        public Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public Person(String id) {
            this.id = id;
        }
    }
    
  • 使用RSocketRequester

    我们使用Spring WebFlux利用RSocketRequester调用RSocket Server。

    @RestController
    @RequestMapping("/people")
    public class ClientPersonController {
        RSocketRequester rSocketRequester;
    
        public ClientPersonController(RSocketRequester rSocketRequester) {
            this.rSocketRequester = rSocketRequester;
        }
        @GetMapping("/{id}")
        public Mono<Person> getOne(@PathVariable String id){
            return this.rSocketRequester
                    .route("people.findById") //1
                    .data(new Person(id)) //2
                    .retrieveMono(Person.class); //3
        }
        @GetMapping
        public Flux<Person> getAll(){
            return this.rSocketRequester
                    .route("people.findAll")
                    .data(new Person())
                    .retrieveFlux(Person.class);//4
        }
    
    
        @DeleteMapping("/{id}")
        public Mono<Void> delete(@PathVariable String id){
            return this.rSocketRequester
                    .route("people.deleteById")
                    .data(new Person(id))
                    .send(); //5
        }
    
        @PostMapping
        public Flux<Person> save(@RequestBody Flux<Person> personFlux){
            return this.rSocketRequester
                    .route("people.save")
                    .data(personFlux, Person.class)
                    .retrieveFlux(Person.class);
        }
    }
    
    1. 通过route()方法指定服务器端的终点;
    2. 通过data()方法向服务端传递数据;
    3. 通过retrieveMono()方法获取返回值是Mono
    4. 通过retrieveFlux方法获取返回值是Flux
    5. 通过send()方法发送数据而不关心返回。
  • 使用WebClient调用WebFlux

    @Component
    public class ControllerClient {
        WebClient webClient;
    
        public ControllerClient(WebClient.Builder builder) {
            this.webClient = builder.build();
        }
    
        public void getOne(){
            System.out.println("查询一条数据");
            Mono<Person> mono = webClient
                    .get()
              			//此处id由查询MongoDB得到
                    .uri("http://localhost:8080/people/{id}", "5d03608320802b1b10458227")
                    .retrieve() 
                    .bodyToMono(Person.class);
            mono.subscribe(System.out::println);
        }
    
        public void getAll(){
            System.out.println("查询所有");
            Flux<Person> flux = webClient
                    .get()
                    .uri("http://localhost:8080/people")//8
                    .retrieve()
                    .bodyToFlux(Person.class);
            flux.subscribe(System.out::println);
        }
    
        public void delete(){
            System.out.println("删除一条数据");
            Mono<Void> mono = webClient
                    .delete()
                    .uri("http://localhost:8080/people/{id}", "5d03608320802b1b10458227")
                    .retrieve()
                    .bodyToMono(Void.class);
            mono.subscribe();
        }
    
        public void save(){
            System.out.println("新增多个");
            List<Person> people = Arrays.asList(new Person("aaa", 36),
                                                new Person("bbb", 37),
                                                new Person("ccc", 38));
            Flux<Person> flux = webClient
                    .post()
                    .uri("http://localhost:8080/people")
                    .body(Flux.fromIterable(people), Person.class)
                    .retrieve()
                    .bodyToFlux(Person.class);
            flux.subscribe(System.out::println);
        }
    
    }
    
  • 启动调用

    @Bean
    CommandLineRunner webclient(ControllerClient client){
       return args -> {
          client.getOne();
          Thread.sleep(1000);
          client.getAll();
          Thread.sleep(1000);
          client.delete();
          Thread.sleep(1000);
          client.save();
          Thread.sleep(1000);
          client.getAll();
       };
    }
    
5.3.3 验证

启动rsocket-server,自动初始化三条Person数据到MongoDB。我们再启动rsocket-client

Spring Boot 2.x实战97 - 事件驱动6 - RSocket_第1张图片

新书推荐:

我的新书《从企业级开发到云原生微服务:Spring Boot 实战》已出版,内容涵盖了丰富Spring Boot开发的相关知识
购买地址:https://item.jd.com/12760084.html
在这里插入图片描述

主要包含目录有:

第一章 初识Spring Boot(快速领略Spring Boot的美丽)
第二章 开发必备工具(对常用开发工具进行介绍:包含IntelliJ IDEA、Gradle、Lombok、Docker等)
第三章 函数式编程
第四章 Spring 5.x基础(以Spring 5.2.x为基础)
第五章 深入Spring Boot(以Spring Boot 2.2.x为基础)
第六章 Spring Web MVC
第七章 数据访问(包含Spring Data JPA、Spring Data Elasticsearch和数据缓存)
第八章 安全控制(包含Spring Security和OAuth2)
第九章 响应式编程(包含Project Reactor、Spring WebFlux、Reactive NoSQL、R2DBC、Reactive Spring Security)
第十章 事件驱动(包含JMS、RabbitMQ、Kafka、Websocket、RSocket)
第11章 系统集成和批处理(包含Spring Integration和Spring Batch)
第12章 Spring Cloud与微服务
第13章 Kubernetes与微服务(包含Kubernetes、Helm、Jenkins、Istio)
多谢大家支持。

你可能感兴趣的:(Spring,Boot2.x实战全集,Spring,Boot2.x实战,-,事件驱动,Spring,Boot2.x实战,-,响应式编程)