源码,修正一些错误:

https://github.com/bigben0123/sample-spring-cloud-webflux

原创 SpringForAll社区 2018-05-18

作者 Spring4all 社区

摘要:

如果你想用Spring的最新和最好的工具开始使用响应式微服务,那么这篇文章就是为你准备的! 我已经在一年前的 Reactive microservices with Spring 5 这篇文章中描述了Spring对反应式的支持。当时,Spring WebFlux项目一直处于积极的发展阶段,现在,在Spring 5正式发布之后,它在这个版本是非常值得关注的。 此外,我们将尝试把反应式微服务放入Spring Cloud组件中,其中包含诸如Eureka服务发现,使用Spring Cloud Commons @LoadBalanced进行负载均衡以及使用Spring Cloud Gateway(也基于WebFluxNetty)的API网关等元素,。 我们还将以Spring Data Reactive Mongo项目为例,介绍Spring对NoSQL数据库的反应性支持。 我们的体系结构示例图如下,它包含两个微服务,一个服务发现,一个网关和MongoDB数据库。 源代码sample-spring-cloud-webflux通常在GitHub上的一样可用。 

接下来,我们进一步来描述创建上述系统的步骤。

第1步:使用Spring WebFlux构建反应式应用程序

为了在项目中使用库Spring WebFlux,我们应该将 spring-boot-starter-webflux添加到依赖关系中。 它包括一些依赖库,如Reactor或Netty服务器。

  1. <dependency>

  2. <groupId>org.springframework.boot</groupId>

  3. <artifactId>spring-boot-starter-webflux</artifactId>

  4. </dependency>

REST控制器看起来与同步Web服务定义的控制器非常相似。 唯一的区别在于返回对象的类型。 如果我们返回单个对象,将使用Mono类的实例,如果我们返回的是多个对象,比如是一个列表,将返回一个Flux类的实例。 由于Spring Data Reactive Mongo,我们不需要做任何事情,只需调用相应接口上的所需方法即可。 实例代码如下:

  1. @RestController

  2. public class AccountController {

  3. private static final Logger LOGGER = LoggerFactory.getLogger(AccountController.class);

  4. @Autowired

  5. private AccountRepository repository;

  6. @GetMapping("/customer/{customer}")

  7. public Flux findByCustomer(@PathVariable("customer") String customerId) {

  8.  LOGGER.info("findByCustomer: customerId={}", customerId);

  9.  return repository.findByCustomerId(customerId);

  10. }

  11. @GetMapping

  12. public Flux findAll() {

  13.  LOGGER.info("findAll");

  14.  return repository.findAll();

  15. }

  16. @GetMapping("/{id}")

  17. public Mono findById(@PathVariable("id") String id) {

  18.  LOGGER.info("findById: id={}", id);

  19.  return repository.findById(id);

  20. }

  21. @PostMapping

  22. public Mono create(@RequestBody Account account) {

  23.  LOGGER.info("create: {}", account);

  24.  return repository.save(account);

  25. }

  26. }

第2步:使用Spring Data Reactive Mongo将应用程序与数据库集成

应用程序和数据库之间的集成实现也非常简单。 首先,我们需要在项目依赖项中添加相关数据库依赖 spring-boot-starter-data-mongodb-reactive

  1. <dependency>

  2. <groupId>org.springframework.boot</groupId>

  3. <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>

  4. </dependency>

添加依赖过后,Mongo将自动支持响应式应用。 下一步是使用ORM映射声明一个实体。 以下类也作为AccountController的响应返回。

  1. @Document

  2. public class Account {

  3. @Id

  4. private String id;

  5. private String number;

  6. private String customerId;

  7. private int amount;

  8. ...

  9. }

最后,我们可以创建一个扩展ReactiveCrudRepository的接口。 它遵循Spring Data JPA实现的模式,并提供了一些CRUD操作的基本方法。 它还允许我们自定义方法,这些名称会自动映射到查询。 与标准Spring Data JPA库相比唯一的区别在于方法签名。 这些对象将由Mono和Flux进行包装。

  1. public interface AccountRepository extends ReactiveCrudRepository {

  2. Flux findByCustomerId(String customerId);

  3. }

在这个例子中,我使用了Docker容器在本地运行MongoDB。 因为我使用Docker Toolkit在Windows上运行Docker,所以Docker机器的默认地址是192.168.99.100。 这是application.yml文件中数据源的配置。

  1. spring:

  2.  data:

  3.    mongodb:

  4.      uri: mongodb://192.168.99.100/test

步骤3:使用Eureka启用服务发现

与 SpringCloudEureka的集成非常类似于传统的REST微服务。 要启用发现客户端功能,我们应该首先将启动器 spring-cloud-starter-netflix-eureka-client添加到项目依赖项中。

  1. <dependency>

  2. <groupId>org.springframework.cloud</groupId>

  3. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

  4. </dependency>

然后我们必须使用 @EnableDiscoveryClient这个注解来启用它的功能。

  1. @SpringBootApplication

  2. @EnableDiscoveryClient

  3. public class AccountApplication {

  4. public static void main(String[] args) {

  5.  SpringApplication.run(AccountApplication.class, args);

  6. }

  7. }

微服务将自动在Eureka注册中心进行注册。 当然,我们可能会运行每个服务的多个实例。 以下是运行 account-service实例和 customer-service服务实例后的Eureka Dashboard 仪表板界面(http:// localhost:8761)。 这里将不详细讲解使用嵌入式Eureka服务器运行应用程序的细节。 有关详细信息,请参阅我之前的文章,Spring Boot 2.0的微服务快速指南,Eureka和Spring Cloud。 Eureka服务器可作为 adiscovery-service模块使用。 

第4步:使用WebClient进行反应性微服务之间的服务间通信

Spring WebFlux项目中的WebClient实现了一个服务间通信。 与RestTemplate相同,您应该使用Spring Cloud Commons @LoadBalanced对其进行注解。 它支持使用Netflix OSS Ribbon客户端与服务发现和负载均衡进行集成。 所以,第一步是使用 @LoadBalanced注解声明一个客户端构建器。

  1. @Bean

  2. @LoadBalanced

  3. public WebClient.Builder loadBalancedWebClientBuilder() {

  4. return WebClient.builder();

  5. }

然后我们可以将WebClientBuilder注入到REST控制器中。 通过GET / {id} / with-accounts实现与 account-service通信,首先我们使用一个基于响应式的Spring Data repository来搜索客户实体。 它返回Mono对象,而WebClient返回Flux。 现在,我们的主要目的是将这些内容合并到订阅者,并从Flux中返回一个包含帐户列表的Mono对象。 下面的代码片段说明了我如何使用WebClient与另一个微服务进行通信,然后将响应和结果合并到单个Mono对象。 这种合并可以用更“优雅”的方式完成,所以你可以随意创建一个推送请求。

  1. @Autowired

  2. private WebClient.Builder webClientBuilder;

  3. @GetMapping("/{id}/with-accounts")

  4. public Mono findByIdWithAccounts(@PathVariable("id") String id) {

  5. LOGGER.info("findByIdWithAccounts: id={}", id);

  6. Flux accounts = webClientBuilder.build().get().uri("http://account-service/customer/{customer}", id).retrieve().bodyToFlux(Account.class);

  7. return accounts

  8.  .collectList()

  9.  .map(a -> new Customer(a))

  10.  .mergeWith(repository.findById(id))

  11.  .collectList()

  12.  .map(CustomerMapper::map);

  13. }

第5步:使用Spring Cloud Gateway构建API网关

Spring Cloud Gateway是最新的Spring Cloud项目之一。 它建立在Spring WebFlux的基础之上,并且由于这一点,我们可以将它用作基于反应式微服务的入口。 与Spring WebFlux应用程序类似,它在嵌入式Netty服务器上运行。 要使用Spring Boot应用程序启用它,只需在您的项目中包含以下依赖项。

  1. <dependency>

  2.    <groupId>org.springframework.cloud</groupId>

  3.    <artifactId>spring-cloud-starter-gateway</artifactId>

  4. </dependency>

我们还应该启用发现客户端,以便网关能够获取已注册的微服务列表。 但是,不需要在Eureka中注册网关的应用程序。 要禁用注册,可以在application.yml文件中将属性 eureka.client.registerWithEureka设置为 false

  1. @SpringBootApplication

  2. @EnableDiscoveryClient

  3. public class GatewayApplication {

  4. public static void main(String[] args) {

  5.  SpringApplication.run(GatewayApplication.class, args);

  6. }

  7. }

默认情况下,Spring Cloud Gateway不支持与服务发现的集成。要启用它,我们应该将属性 spring.cloud.gateway.discovery.locator.enabled设置为true。现在,应该完成的最后一件事情就是路由器的配置。 Spring Cloud Gateway提供了两种可以在路由中配置的组件:filters(过滤器)和predicates(谓词)。 Predicates用于将HTTP请求与路由进行匹配,而过滤器可用于在发送请求之前或之后修改请求和响应。这是网关的完整配置。它启用服务发现位置,并根据服务注册表中的条目定义两种路由。我们使用Path Route Predicate工厂来匹配传入的请求,并使用RewritePath GatewayFilter工厂来修改请求的路径,以使其匹配相应的服务格式(端点显示在路径/下,而网关将它们暴露在路径下/account 和/customer下)。

  1. spring:

  2.  cloud:

  3.    gateway:

  4.      discovery:

  5.        locator:

  6.          enabled: true

  7.      routes:

  8.      - id: account-service

  9.        uri: lb://account-service

  10.        predicates:

  11.        - Path=/account/**

  12.        filters:

  13.        - RewritePath=/account/(?.*), /$\{path}

  14.      - id: customer-service

  15.        uri: lb://customer-service

  16.        predicates:

  17.        - Path=/customer/**

  18.        filters:

  19.        - RewritePath=/customer/(?.*), /$\{path}

第6步:测试样本系统

在做一些测试之前,让我们回顾一下我们的示例系统。 我们有两个微服务 - account-servicecustomer-service - 使用MongoDB作为数据库。 微服务customer-service调用account-service暴露的端点GET / customer / {customer}。 account-service的URL来自Eureka。 整个系统隐藏在网关后面,该网关位于localhost:8090的地址下。 现在,第一步是在Docker容器上运行MongoDB。 执行以下命令后,Mongo在地址192.168.99.100:27017下可用。

  1. $ docker run -d --name mongo -p 27017:27017 mongo

然后我们可以继续运行 discovery-service。 Eureka在其默认地址localhost:8761下可用。 您可以使用IDE运行它,或者执行命令 java-jar target/discovery-service-1.0-SNAPHOT.jar。 同样的适用于我们的示例微服务。 但是,account-service需要在两个实例中进行通信,所以当使用-Dserver.port VM参数运行第二个实例时,您需要覆盖默认的HTTP端口,例如 java-jar-Dserver.port=2223target/account-service-1.0-SNAPSHOT.jar。 最后,在运行网关服务之后,我们可以添加一些测试数据。

  1. $ curl --header "Content-Type: application/json" --request POST --data '{"firstName": "John","lastName": "Scott","age": 30}' http://localhost:8090/customer

  2. {"id": "5aec1debfa656c0b38b952b4","firstName": "John","lastName": "Scott","age": 30,"accounts": null}

  3. $ curl --header "Content-Type: application/json" --request POST --data '{"number": "1234567890","amount": 5000,"customerId": "5aec1debfa656c0b38b952b4"}' http://localhost:8090/account

  4. {"id": "5aec1e86fa656c11d4c655fb","number": "1234567892","customerId": "5aec1debfa656c0b38b952b4","amount": 5000}

  5. $ curl --header "Content-Type: application/json" --request POST --data '{"number": "1234567891","amount": 12000,"customerId": "5aec1debfa656c0b38b952b4"}' http://localhost:8090/account

  6. {"id": "5aec1e91fa656c11d4c655fc","number": "1234567892","customerId": "5aec1debfa656c0b38b952b4","amount": 12000}

  7. $ curl --header "Content-Type: application/json" --request POST --data '{"number": "1234567892","amount": 2000,"customerId": "5aec1debfa656c0b38b952b4"}' http://localhost:8090/account

  8. {"id": "5aec1e99fa656c11d4c655fd","number": "1234567892","customerId": "5aec1debfa656c0b38b952b4","amount": 2000}

要测试服务间通信,只需在网关服务上调用端点 GET/customer/{id}/with-accounts。 它将请求转发给customer-service,然后customer-service使用响应式WebClient调用由account-service 暴露的端点。 结果如下所示。 

结论

Spring 5和Spring Boot 2.0的出现,带来了许多可用的方法来构建基于微服务的体系结构。我们可以使用与Spring Cloud Netflix项目的一对一通信,基于消息代理的消息传递微服务,以及与Spring Cloud Stream的发布/订阅通信模型构建标准同步系统,最后使用Spring实现异步,反应式微服务WebFlux。本文的主要目标是向您展示如何将Spring WebFlux与Spring Cloud项目一起使用,以便为构建在Spring Boot之上的响应式微服务提供服务发现,负载均衡或API网关等机制。在Spring 5之前,缺乏对响应式微服务的支持是Spring框架的缺点之一,但现在,Spring WebFlux已不再是这种情况。不仅如此,我们还可以利用Spring对最受欢迎的NoSQL数据库(如MongoDB或Cassandra)的反应式支持,并轻松地将我们的反应式微服务与同步REST微服务一起放入一个系统中。

【SFA官方翻译】Spring WebFlux和Spring Cloud进行响应式微服务开发的更多相关文章

  1. 【SFA官方翻译】使用 Kubernetes、Spring Boot 2.0 和 Docker 的微服务快速指南

    [SFA官方翻译]使用 Kubernetes.Spring Boot 2.0 和 Docker 的微服务快速指南 原创: Darren Luo SpringForAll社区 今天 原文链接:https ...

  2. Spring Cloud分布式微服务云架构集成项目

    Spring Cloud集成项目有很多,下面我们列举一下和Spring Cloud相关的优秀项目,我们的企业架构中用到了很多的优秀项目,说白了,也是站在巨人的肩膀上去整合的.在学习Spring Clo ...

  3. Spring Cloud分布式微服务云架构

    分布式.微服务.云架构 JAVA语言开发.跨平台.高性能.高可用.安全.服务化.模块化.組件化.驱动式开发模式 commonservice eurekaNetflix 云端服务发现,一个基于 REST ...

  4. Spring Cloud分布式微服务系统中利用redssion实现分布式锁

    在非分布式系统中要实现锁的机制很简单,利用java.util.concurrent.locks包下的Lock和关键字synchronized都可以实现.但是在分布式系统中,如何实现各个单独的微服务需要 ...

  5. Spring boot 基于Spring MVC的Web应用和REST服务开发

    Spring Boot利用JavaConfig配置模式以及"约定优于配置"理念,极大简化了基于Spring MVC的Web应用和REST服务开发. Servlet: package ...

  6. Spring Boot2.0之Admin-UI分布式微服务监控中心

    前面https://www.cnblogs.com/toov5/p/9823353.html  说的很不好用哈哈 还需要json格式化 我们可以用Admin-UI 比较爽歪歪 原理: 将所有服务的监控 ...

  7. Spring WebFlux 01 (原理及使用场景)

    一.什么是 Spring WebFlux 好多人以为Spring WebFlux就是Spring MVC的升级版,其实不然,那到底什么是Spring WebFlux呢,首先就要搞清楚Spring We ...

  8. Spring Webflux: Kotlin DSL [片断]

    原文链接:https://dzone.com/articles/spring-webflux-kotlin-dsl-snippets 作者:Biju Kunjummen 译者:Jackie Tang ...

  9. 140、spring webflux 高并发的spring组件

    最近公司可谓是风云变幻,年前说要拆开卖,后来说要整体卖,表示像我这种渣渣,始终逃脱不掉被卖的命运 下面进入正题 spring webflux 是spring 支持的高并发web框架,将每个http请求 ...

随机推荐

  1. [转帖]再次提醒Google Chrome用户应尽快升级浏览器到72.0.3626.121

    再次提醒Google Chrome用户应尽快升级浏览器到72.0.3626.121 转帖地址: https://www.cnbeta.com/articles/tech/825591.htm 国内离线 ...

  2. 微信小程序错误码参考大全

    开发过程中,会遇到很多微信返回的状态码,鬼知道代表什么意思,现在好了,整理总结了一份状态码,方便大家. 转载:http://www.yiyongtong.com/archives/view-1856- ...

  3. MQ4入门篇(一)

    写一个下单功能,和一个平仓功能: 下单: 1:下单使用到的函 int OrderSend(string symbol, int cmd, double volume, double price, in ...

  4. JS 类型检测

    typeof 适合函数对象和基本类型的判断 typeof 100instanceof 适合判断对象类型 obj instanceof Object 基于原型链判断操作符,若做操作符不是对象,则会直接返 ...

  5. 工作效率提升之Eclipse篇(1):干掉烦人的xml文件的validation

    每次启动maven项目,都会有一堆烦人的xml文件的validation,一旦网络较慢,项目重新启动的时候,这些多余的验证纯属浪费时间. Eclipse上取消validation的方法: 1.菜单[W ...

  6. jmeter元素

    1 test plan functional test mode 选择项:如果勾选 jmeter 会记录从服务器返回的响应数据,如果监视器-选择了文件-则会保存到对应文件 测试jmeter是否配置正确 ...

  7. SpringBoot之文件下载

    package org.springboot.controller; import org.springboot.constant.Constant; import org.springframewo ...

  8. Running ASP.NET Core applications on Windows Subsystem for Linux

    Setting up Linux on Windows 10 First thing is to enable Windows Subsystem for Linux. It doesn’t inst ...

  9. poj-3080(kmp+暴力枚举)

    题意:给你多个字符串,问你这几个字符串的最长公共子串是哪个,如果有多个,输出字典序最大的那个,如果最长的公共子串长度小于3,输出一个奇怪的东西: 解题思路:首先看数据,数据不大,开始简单快乐的暴力之路 ...

  10. PKU2018校赛 H题 Safe Upper Bound

    http://poj.openjudge.cn/practice/C18H 题目 算平均数用到公式\[\bar{x}=\frac{x_1+x_2+x_3+\cdots+x_n}{n}\] 但如果用in ...