源码,修正一些错误:

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. [转帖]国产紫光SSD不再只是实验室展品 开始批量出货

    国产紫光SSD不再只是实验室展品 开始批量出货 https://www.cnbeta.com/articles/tech/825865.htm 没听说有做HDD的 现做了SSD 弯道超车吗 可以实现全 ...

  2. Oracle条件判断if...elsif

  3. 【Java基础】求出1-100之间偶数和

    结果:

  4. Vue 获得所选中目标的状态(checked)以及对应目标的数据,并进行相应的操作

    一.我们现在要拿取购物车中选中商品的状态和该商品的所有数据或者id <ul v-if="shopList.list.length>0"> <li class ...

  5. windos安装maven

    1.下载好maven压缩包,并解压到相应位置,本次安装在D: 2.配置环境变量 MAVEN_HOME=D:\apache-maven-3.0.5 path=%MAVEN_HOME% 3.生成maven ...

  6. myisam和innodb的区别对比

    1.MyISAM:默认表类型,它是基于传统的ISAM类型,ISAM是Indexed Sequential Access Method (有索引的顺序访问方法) 的缩写,它是存储记录和文件的标准方法.不 ...

  7. 移动APP用例设计中的关键点(转载)

    http://www.51testing.com/html/52/n-4421752.html 在测试工作中我们需要不断的总结和储备自己的知识和经验,譬如具备特定属性.环境以及场景,如:PC,手机,智 ...

  8. 解决spring多线程不共享事务的问题

    在一个事务中使用多线程操作数据库时,若同时存在对数据库的读写操作,可能出现数据读取的不准确,因为多线程将不会共享同一个事务(也就是说子线程和主线程的事务不一样),为了解决这个问题,可以使用spring ...

  9. SpringMVC 重定向到其他系统的页面的两种方式

    //测试重定向到另外的一个系统 @RequestMapping("/tttt") public void testRed(HttpServletResponse response) ...

  10. 两种从spring容器获取bean方式