REST微服务的分布式事务实现-使用Spring Cloud的fallback模式

栏目: 服务器 · 发布时间: 8年前

内容简介:REST微服务的分布式事务实现-使用Spring Cloud的fallback模式

Fallback是Spring Cloud Netflix框架套件中的Hystrix使用的,用于在出错时候进行的应急措施,我们可以用它来实现在出错的时候来进行回退操作。如果大家对Spring Cloud Netflix不太了解,可以查阅另一个篇文章 Spring Cloud netflix概览和架构设计 ,来对Spring Cloud Netflix框架的各个组件做一个了解。在这篇文章中,我们将介绍如何使用Hyxtrix的Fallback来实现分布式事务,并提供一个完整的实例来展示这种方法。

Hystrix

首先来说一下Hystrix,Hystrix是Spring Cloud Netflix套件中的一个功能组件,我们可以在现有的基于Spring Cloud的微服务应用中使用Hystrix来提供额外的功能。它提供的功能有:

  • 运行时间统计。我们只需要在我们的某一个方法上加上 @HystrixCommand 的标签,这个方法的执行就能够被统计,包括运行测试、时间等。
  • 可视化显示运行统计信息的web应用。Hystrix提供了一个Hystrix Dashboard,它嵌入了一个web应用,可以直接查看被监控的方法和服务的执行情况。
  • 断路器功能。在Spring Cloud的微服务框架中,会有很多的服务间调用,包括代理转发请求到服务,服务间的调用等,由于网络等的原因,这些调用有很多不可控因素。Hystrix的断路器功能就是在某个服务发生错误的时候,避免由于一直等待等问题,而造成整个系统的瘫痪。
  • 出错时的Fallback退回操作。我们在用 @HystrixCommand 监控一个方法的时候,除了能够监控这个方法的执行,还能够设置一个 fallback 方法,用于在这个方法出错的时候来调用。这一般用于执行出错时的回退操作,特别是在服务间调用的时候。

下面就是Hystrix提供的Dashboard页面:

REST微服务的分布式事务实现-使用Spring Cloud的fallback模式

Hystrix dashboard为我们提供了每个Command的调用次数及其历史,还有调用时间等的统计值。

我们在基于Spring Cloud的微服务中实现分布式事务的时候,就可以使用Hystrix的fallback方法来实现出错时的回退功能。

Hystrix fallback

我们在用 @HystrixCommand 的时候,可以加fallback方法,这样,Hystrix断路器会监控这个方法的执行,在超时、发生异常等情况下就会调用相应的 fallback 设置的方法。例如:

@RestController
@RequestMapping("/api/order")
public class OrderResource{

  @HystrixCommand(fallbackMethod = "createFallback")
  @PostMapping(value = "/")
  publicOrderbuyTicket(@RequestBody Order order){
    return orderService.create(order);
  }

  privateOrdercreateFallback(Order order){
    return orderService.createFallback(order);
  }
}

在这个类中, create 方法上加了 @HystrixCommand 标签,当这个方法在执行的时候发生任何异常, createFallback 方法就会被调用,而且,调用的时候,会使用相同的对象作为参数。

下面的流程图就描述了使用 @Hystrix 执行一个Service方法的大致流程:

REST微服务的分布式事务实现-使用Spring Cloud的fallback模式

Feign Client

在基于Spring Cloud的微服务系统中,服务之间需要调用的时候,一种常用的方式是使用Feign客户端。

首先,定义一个接口,并使用 @FeignClient 标签。例如下面这个用于访问用户服务的接口:

@FeignClient(value = "user", path = "/api/user/comp")
public interface UserCompositeService{

    @PostMapping(value = "/pay")
    void payForOrder(@RequestBody UserPayDTO pay);
}

在这个接口中用 @FeignClient 标签标记它是一个Feign客户端,这个客户端对应的是 user 模块,URL路径的前缀是 /api/user/comp 。它里面定义了一个方法 payForOrder ,也就是支付订单的方法,这个方法是接受POST请求,相对路径是’/pay’。

然后,我们需要在User模块提供这个路径的响应。一般,我会把服务间调用的url定义成 /api/user/comp/** 这样,对应的web接口所在的类名叫 UserCompositeResource 这样的名称,这样就便于对服务间调用进行统一的权限、日志等控制。下面就是 UserCompositeResource 类的内容:

@RestController
@RequestMapping("/api/user/comp")
public class UserCompositeResourceimplements UserCompositeService{

    @Override
    @PostMapping(value = "/pay")
    @Transactional
    public void payForOrder(@RequestBody UserPayDTO payDTO){
        // do your business here
    }
}

我们一般是把所有的服务间调用的 Feign 接口文件放在一个单独的项目里,打包方式是 jar ,然后其它需要使用这个接口的项目就依赖这个接口项目。然后就像上面这样实现这个接口。这样能较好的控制接口和实现的统一。但是,这样就需要设计好各个项目之间的依赖问题。

然后,在调用的时候,直接在需要的地方注入 UserCompositeService 使用即可:

@Service
public class TicketOrderService{
    @Inject
    UserCompositeService userCompositeService;

    publicOrderbuy(OrderDTO dto){
        // create Order
        userCompositeService.payForOrder(payDTO);
        // do other...
    }
}

当Spring在容器中初始化这个 TicketOrderService 类的实例的时候,对成员变量 userCompositeService ,它知道它是一个 Feign Client 的接口,Spring就会自动创建一个类,实现这个接口,并且根据接口的定义,和接口中方法的定义,实现接口的方法。实现出来的方法,实际上就是通过RestTemplate调用相应的Rest接口,将返回的结果转换成相应的类型。

所以,我们使用 Feign Client 来实现服务间调用,就跟调用一般的方法一样简单。而且,在服务调用者和服务提供者之间使用同一个接口,也能很容易控制服务间接口。

Ribbon与Load balance

我们使用Feign Client作为服务间调用的接口,那么,这个接口下面又是如何找到相应的服务和该服务的实例进行调用的呢?在Spring Cloud Netflix中,由Ribbon提供负载均衡功能,而负载均衡的服务器列表,是从Eureka服务器获得。当我们使用Feign Client的时候,也是使用Ribbon提供的负载均衡服务。

?????

????

???

??

实例

该实例包括完整的微服务组件,包括代理、服务注册中心、和3个微服务,服务之间使用 Feign Client 通讯,其组件图如下。

REST微服务的分布式事务实现-使用Spring Cloud的fallback模式

然后,我们的业务场景,是一个用户购票的流程,流程图如下:

REST微服务的分布式事务实现-使用Spring Cloud的fallback模式
  1. 用户发起购票请求,网关将该请求转发给order服务
  2. order服务在一个事务里面创建订单等信息、然后调用user服务进行扣费、然后调用ticket服务转移票。
  3. 方法完成,给用户返回。

创建项目

Spring提供了一个 在线工具 ,可以用来创建spring boot项目。我们用这个 工具 来创建实例中的几个微服务的项目:

  • proxy
    Proxy代理服务器需要使用ZUUL, Eureka Discovery。
  • registry
    然后是服务注册服务,使用Eureka Server。
  • order/user/ticket service
    然后创建3个服务项目,都需要使用Web, JPA, H2, Eureka Discovery, Hystrix。

使用spring boot,可以让我们免去很多配置的烦恼,很多情况下,只要将需要的库加到pom里(只要是spring提供了集成),剩下的基本上就是自动配置。 例如我们要使用数据库,在开发环境,如果不想在本地使用数据库,就使用H2的内存数据库,将H2的库加到依赖里,然后再使用JPA框架,如Spring-Data,就能够自动配置DataSource,自动创建数据库。再比如MQ,我们如果要使用JMS,只需要添加ActiveMQ的库,就会有一个基于内存的MQ共我们使用。然后我们就可以在需要的时候配置外部的数据库或MQ,在正式环境使用。

注意

HystrixCommand和Transactional公用

一般情况下,在Spring中,一个方法使用 @Transactional 标签后,方法内出现任何错误,都会数据库的操作都会回退,但是,如果把它和 @HystrixCommand 公用就会不一样了。

我们知道,Spring使用代理模式实现添加了事务标签的方法,也就是在这个方法调用的前后添加事务控制代码,通过try/catch实现出错的时候回退操作。但是,如果你同时使用了 @HystrixCommand 标签,它也会通过代理把方法内容封装一下,来实现监控运行情况,和实现断路器功能等。而且默认会在独立的线程里面执行方法,这样,就跟外面的启用的事务不在一个线程里,所以事务就不会起作用。

对于这个,一个简单的办法就是把这两个标签拆开,写到2个service类里,由于 @HystrixCommand 是在事务里面执行,那就先调用hystrix的:

@Service
public class ServiceA{
    @Inject ServiceAWithTransaction serviceTrans;

    @HystrixCommand
    public void callMyMethod(){
        serviceTrans.callMyMethod();
    }
}

然后在另一个service里面用事务

@Service
public class ServiceAWithTransaction{

    @Transactional
    public void callMyMethod(){
        // ....
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Amazon Web Services

Programming Amazon Web Services

James Murty / O'Reilly Media / 2008-3-25 / USD 49.99

Building on the success of its storefront and fulfillment services, Amazon now allows businesses to "rent" computing power, data storage and bandwidth on its vast network platform. This book demonstra......一起来看看 《Programming Amazon Web Services》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换