服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

Spring Cloud 与响应式微服务

日期: 来源:ImportNew收集编辑:

(给ImportNew加星标,提高Java技能)


众所周知,Spring Cloud 服务间的调用方式是使用的 RESTful API,我们平时都是R estTemplate 或 Feign 来调用的,这两种方式其实说到底都是同步的方式。


Spring 支持响应式编程。那么我们能不能在 Spring Cloud 的服务间调用的时候用这种异步非阻塞的方式呢?随着 Spring Cloud Finchley 的发布,这一切均可以实现。


本文我们就用 WebFlux、Spring Data Reactive 从头到脚构建一个响应式的微服务。


准备


为了完成这个示例,我们需要:


  • 一个服务注册中心

  • 两个微服务

  • 一个数据库


服务注册中心:我们要用到服务发现和服务注册,这里用一个单节点的 Eureka Server来做。


两个微服务:帐户服务和客户服务。每个微服务都有自己的数据库,且对外暴露简单的响应式 API,用于检索和存储数据。另外,客户服务与帐户服务可以相互通信,以获取客户的所有帐户,并通过客户服务 API 方法返回。


数据库:因为现在还没几个数据库有实现了反应式数据访问的可用驱动,Spring Data Reactive 目前仅支持 MangoDB、Redis 和 Cassandra,简单起见我们就用 MangoDB。MangoDB 我这里使用 Docker 来创建,一切均用默认配置(主要是懒 ,这样就不用去改 Spring Boot 的配置文件了)


docker run -d --name mongo -p 27017:27017 mongo


实战


服务注册中心


新建一个基本的 Spring Boot 工程,命名为eureka-server。


pom.xml 中依赖如下:


<dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>


配置文件 application.yml 配置如下:


spring:  application:    name: eureka-servereureka:  client:    register-with-eureka: false    fetch-registry: false    service-url:      defaultZone: http://localhost:8000/eureka/server:  port: 8000


在启动类上加上 @EnableEurekaServer 注解:


@EnableEurekaServer@SpringBootApplicationpublic class EurekaServerApplication {
public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); }}


账户服务


新建一个基本的 Spring Boot 工程,命名为 cloud-account。


如果是使用 Spring Initializr 话,引入 Lombok、Reactive Web、Reactive MongoDB 和 Eureka Discovery 这四个依赖。


最终 pom.xml 中应有以下依赖:


<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency>    <groupId>org.projectlombok</groupId>    <artifactId>lombok</artifactId>    <optional>true</optional></dependency><dependency>    <groupId>io.projectreactor</groupId>    <artifactId>reactor-test</artifactId>    <scope>test</scope></dependency>


配置文件 application.yml


spring:  application:    name: cloud-accountserver:  port: 8100eureka:  client:    service-url:      defaultZone: http://localhost:8000/eureka/


创建账户的实体类,其中 @AllArgsConstructor、@NoArgsConstructor 和 @Data 都是 Lombok 提供注解,不了解的可以自行学习,这里不多说了。


@AllArgsConstructor@NoArgsConstructor@Data@Document(collection = "accounts")public class Account {    @Id    private String id;    private String customerId;    private Double amount;}


我们使用 Spring Data Reactive。与非响应式 Spring Data 的 CrudReposity 对应的,响应式的 Spring Data 也提供了相应的 Repository 库:ReactiveCrudReposity,我们也可以使用它的子接口 ReactiveMongoRepository。


public interface AccountMongoReactiveRepository extends ReactiveCrudRepository<Account, String> {    Flux<Account> findByCustomerId(String customerId);}


为账户服务创建对应的 Controller,这里只简单提供一个查询客户的所有账户的接口。为了在后面测试负载均衡,这里加上了调用时间戳的打印。


@RequestMapping("/account")@RestControllerpublic class AccountController {
@Autowired private AccountMongoReactiveRepository repository;
@GetMapping("/customer/{customer}") public Flux<Account> findByCustomer(@PathVariable(name = "customer") String customer) { System.out.println("Customer => " + customer + " [ " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS")) + " ]"); return repository.findByCustomerId(customer); }
}


客户服务


新建一个基本的 Spring Boot 工程,命名为cloud-customer,POM 依赖和之前的cloud-account 的一模一样。


配置文件如下,仅是改了服务名和端口号:


spring:  application:    name: cloud-customerserver:  port: 8200eureka:  client:    service-url:      defaultZone: http://localhost:8000/eureka/


创建一个 Customer 的实体类:


@Data@AllArgsConstructor@NoArgsConstructor@Document(collection = "customers")public class Customer {    @Id    private String id;    private String name;    private String mobile;}


数据访问层直接继承 ReactiveCrudRepository,我们便有了基本的 CRUD 能力:


public interface CustomerMongoReactiveRepository extends ReactiveCrudRepository<Customer, String> {}


因为我们只是示例,不做复杂的业务逻辑,所以省略了 Service 层,在 Controller 里边直接将 CRUD 的操作代理给了 Repository。


@RestController@RequestMapping("/customer")public class CustomerController {
@Autowired private CustomerMongoReactiveRepository repository; @Autowired private WebClient.Builder webClientBuilder;
@GetMapping("") public Flux<Customer> list() { return repository.findAll(); }
@GetMapping("/{id}") public Mono<Customer> get(@PathVariable String id) { return repository.findById(id); }
@PostMapping("") public Mono<Customer> create(@RequestBody Customer customer) { return repository.save(customer); }
@PutMapping("/{id}") public Mono<Customer> update(@PathVariable("id") String id, @RequestBody Customer customer) { customer.setId(id); return repository.save(customer); }
@DeleteMapping("/{id}") public Mono<Void> delete(@PathVariable String id) { return repository.deleteById(id); }
}


到这里,我们的服务注册中心和两个微服务就都好了。但是,这两个微服务之间还是完全独立的,没有相互间的服务调用。现在我们来实现之前说的需求:客户服务与帐户服务可以相互通信,以获取客户的所有帐户,并通过客户服务 API 方法返回。


首先创建一个 Java Config,这里我们不再使用 RestTemplate 来调用服务,而是WebClient。这个配置看起来和注册 RestTemplate 时差不多,但是要注意这里注册的 Bean 是WebClient.Builder。


@Configurationpublic class WebClientConfig {
@Bean @LoadBalanced public WebClient.Builder loadBalancedWebClientBuilder() { return WebClient.builder(); }
}


除了这种写法,还有一种写法是:


public class MyClass {    @Autowired    private LoadBalancerExchangeFilterFunction lbFunction;
public Mono<String> doOtherStuff() { return WebClient.builder().baseUrl("http://cloud-account/account") .filter(lbFunction) .build() .get() .uri("") .retrieve() .bodyToMono(String.class); }}


下边的是错误的写法,会抛出异常:


@Bean@LoadBalancedpublic WebClient loadBalancedWebClient() {    return WebClient.builder().baseUrl("http://cloud-account/account").build();}



然后在 CustomerController 实现这个端点:


@GetMapping("/{id}/account")public Flux<Account> getAllAccounts(@PathVariable String id) {    return webClientBuilder.baseUrl("http://cloud-account/account/").build()        .get().uri("/customer/" + id)        .retrieve()        .bodyToFlux(Account.class);}

这里需要在 cloud-customer 里创建一个 DTO Account,因为和 cloud-account 里的完全一样,就省略了。


测试


同时启动两个 cloud-account 服务:



然后不断请求 http://localhost:8200/customer/5ae15fa640f1687f200d8941/account 接口,从下图的时间戳可以看出 WebClient 会轮流请求两个服务,达到了负载均衡的效果。



总结


我们从服务提供、服务调用、数据访问三个方面均使用了响应式编程(Reactive Programming),可以说是做到了 Full Reactive Stack Backend。相信你对响应式编程及其在 Web 应用、微服务架构中如何发挥作用有了更多的体会,本文实战是比较基础的,因为切换到响应式思维方式并非易事,希望能够通过上手编写代码体会响应式编程的感觉。


转自:Yibo,

链接:haoyizebo.com/posts/a0cb2c47/


- EOF -

推荐阅读  点击标题可跳转

1、聊聊 Spring data JPA

2、Spring Cloud Stream整合Rabbit之重复投递

3、SpringCloudGateway CORS方案看这篇就够了


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

点赞和在看就是最大的支持❤️


相关阅读

  • 套房/江景房5折起,旺季还不加价。

  • 晚上参加婚宴,看着新人幸福的样子真是感慨,于是多喝了几杯。继续来整理这几天携程春促好价,携程最近几次大促都是分批上线,好处是不断制造热度,坏处是你不知道需要的产品什么时候
  • 再搞下去,民航人挣的钱迟早被吃干抹净!

  • 昨天碰到了一件事,整得我心情到发文时还难以平静。一位老友出差顺道来看我,刚吃饱喝足他就开始叹气:“你说我现在年收入几十万,一线城市房车齐全,少说也比80%的人要强,为啥心里还
  • 那些年我们吹过的牛逼

  • 一我有个认识20年的发小彪哥,就是我们公众号第一篇里短暂出镜的那位。彪哥在大学毕业后做的是运动/电商行业,所以我们聊股票的频率不高。印象里只有在2015年短暂的牛市期间,他
  • 一代人有一代人的使命

  • 这一年世界仿佛进入了一个乱纪元模式,其中的事件和波动击碎了很多信仰幻觉,重塑了我们的认知边界。或许世界并没有出错,或许它本来就是这样,只是当它呈现出的样子与我们预期的样
  • 每月203GB通用流量,官方超值流量卡,不要错过!

  • 介绍真的超级好用!这些都不是物联卡!!都是运营商官方的手机卡,每月费用都可在营业厅App查询。读大学时,因为学校宽带限制设备使用,所以没有开宽带,用的是39月租的联通优游,直接一台
  • ​导演总爱讲道理不是个好习惯

  • 文/六神磊磊一说一点国产片,有一个小疑问:但凡电影人有了成就地位,怎么就往往爱开始拍片子讲大道理。本文纯是有感而发,作为一个观众,肯定不是否认他们对电影的贡献。只是个真诚
  • 韦小宝:最茫然的一代人

  • 文/六神磊磊看金庸《鹿鼎记》,最近经常想一件事。如果你站在韦小宝这样一个十几岁孩子的视角,去看当时的大人们,会是什么感受?他对那些成年人的印象,是什么样的?看他的师父,看天地
  • 注意,年轻人的价值判断已经变了

  • 月初我们策划了一期直播,聊爱情、婚姻等话题。中间,有用户问,“怎么看大城市里有些人不结婚的选择?”情感专家李一帆老师的回答,让我觉得颇为深刻。他是这么说的:“随着经济发展和

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • Spring Cloud 与响应式微服务

  • (给ImportNew加星标,提高Java技能)众所周知,Spring Cloud 服务间的调用方式是使用的 RESTful API,我们平时都是R estTemplate 或 Feign 来调用的,这两种方式其实说到底都是同步的
  • 云原生场景下实现编译加速

  • (给ImportNew加星标,提高Java技能)背景云原生下的流水线是通过启动容器来运行具体的功能步骤,每次运行流水线可能会被调度到不同的计算节点上。这会导致一个问题:容器运行完是不
  • Word 超神插件,用了才知道有多爽!

  • 打工人,打工魂,小伙伴们,精神状态还稳定吗?当然我没有嘲讽和幸灾乐祸的意思,毕竟我这不也在码字么,人类的悲欢有时候是可以相通的……既然逃不开干活的宿命,那就只能找个趁手的工具
  • Chatgpt 能帮我下片了!

  • 关于下载网页上的视频,相信每个小伙伴的手里都有那么三把斧,什么嗅探,什么解析等等,我们也写过很多了,比如之前咱们不还研究过 M3U8 下片的姿势,