Spring Cloud Netflix

Spring Cloud Netflix

Spring Cloud Netflix

Spring Cloud Netflix项目为SPringBootApp提供了NetflixOSS对象存储服务集成通过自动配置和绑定SpringEnvironment和其他Spring编程模型。只需要几个简单的注解就可以快速的启用和配置常用的模式在应用中。还可以用Netflix组件构建大型分布式系统。这些模式提供了包括Service Discovery(Eureka)服务发现,Circuit Breaker(Hystrix)"断路器"(断路器是一种模式,后文详解)1 2,Intelligent Routing(Zuul)智能路由还有Client Side Load Balancing(Ribbon)客户端的负载均衡。

Service Discovery: Eureka Clients 服务发现

服务发现是基于微服务架构的关键原则之一。处理配置每个客户端或者一些惯例的形式会非常困难。Eureka是NetflixService Discovery服务端和客户端。该服务可以被配置部署到高可用性,The server can be configured and deployed to be highly available, with each server replicating state about the registered services to the others.

How to Include Eureka Client 导入 Eureka Client

group id org.springframework.cloud & artifact id spring-cloud-starter-eureka spring-cloud

Registering with Eureka 用Eureka注册服务

一个client用Eureka注册服务,client提供了自己的meta-data,比如,host port, health指示器url,home page等等。 Eureka从提供一个service的多个实例 多个client上接受心跳信息。如果心跳失败超过配置timeout,这个实例将会被从注册实例中移除。

示例代码

@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableEurekaClient
@RestController
public class Application {

    @RequestMapping("/")
    public String home() {
        return "Hello world";
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

}

完全普通的SpringBootApp。在这个例子中,显式使用@EnableEurekaClient,但是如果只想要Eureka可用,还可以使用 @EnableDiscoveryClient。指明Eureka server的地址,地址配置是必须的。 application.yml

eureka:  
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

defaultZone是一个字符串,对任何client提供了服务的URL,默认不会对任何client区别对待。

默认应用名称(service id),virtual host non-secure port,从环境获取,分别是${spring.application.name}, ${spring.application.name} and ${server.port}。

@EnableEurekaClient使App成为一个Eureka实例(它可以自己注册自己),还成为了一个client(它可以查询“注册表”,定位其他服务)。Eureka实例的行为驱动是eureka.instance.* * ,以这些为前缀的配置keys,但是如果你确认你的application有配置spring.application.name* (this is the default for the Eureka service ID, or VIP),Eureka默认配置就可以。

更多配置可选项详情看,EurekaInstanceConfigBean和EurekaClientConfigBean

Authenticating with the Eureka Server 通过Eureka Server认证

如果你的eureka.client.serviceUrl.defaultZone配置Url的格式是http://user:password@localhost:8761/eureka,这种样式(curl样式),HTTP basic 认证会自动加入到eureka client。再复杂一点,要创建一个DiscoveryClientOptionalArgs的Bean并注入ClientFilter。所有这些都会被用到client对server的调用上。

注意,由于Eureka的限制,支持每一个server的basic认证证书是不可能的,所以,发现的第一个证书将会被用上。(我也不明白)

Status Page and Health Indicator 状态页面和健康指示器

状态页面默认 "/info" 健康指示器默认 "/health",这些是默认的 Spring Boot Actuator应用的有用的endpoint的默认地址。你需要修改这些,即使是对 Actuator 应用,如果你使用的不是默认的context path或者servlet path (比如,server.servletPath=/foo) 或者管理endpoint路径(比如,management.contextPath=/admin)

application.yml

eureka:  
  instance:
    statusPageUrlPath: ${management.context-path}/info
    healthCheckUrlPath: ${management.context-path}/health

这几个链接展示了client消费的metadata,并且被用在一些决定是不是要发送请求到你的应用的情景下,所以如果这几个url是精确的会非常有帮助。

Registering a Secure Application 注册一个安全的应用。

如果你的应用想要通过https交流,你可以设置两个flags 在EurekaInstanceConfig里边。也就是,eureka.instance.[nonSecurePortEnabled,securePortEnabled]=[false,true] 分别的。 这些将会使Eureka 发布展示安全交流的明确参数示例信息publish instance information showing an explicit preference for secure communication。这时,Spring Cloud DiscoveryClient将会对一个按这种方式配置的service一直返回https://... 的URI。并且Eureka(原生)实例信息将会有一个安全URL检查。

由于Eureka的内部工作方式,它将会仍然发布一个不安全的URL,对状态status和主页homepage,除非你也将这些先是覆盖。你可以使用placeholders来配置eureka示例urls。比如,

application.yml

eureka:  
  instance:
    statusPageUrl: https://${eureka.hostname}/info
    healthCheckUrl: https://${eureka.hostname}/health
    homePageUrl: https://${eureka.hostname}/

注意,${eureka.hostname} 是一个原生placeholder,只在Eureka的之后版本可用。你也可以实现相同的功能通过Spring placeholder。比如,使用,${eureka.instance.hostName}

注意,如果你的应用运行在一个代理的后边,and the SSL termination is in the proxy 并且SSL termination 结束 在这个代理。(比如,如果你运行在Cloud Foundry或者是其他平台作为服务),那么你需要确保代理的“forwarded”headers 被应用拦截处理。一个Spring Boot app中的嵌入式的Tomcat容器会自动做这些,如果显式配置了“X-Forwarded-\” headers.错误的标志是,渲染的url会是错误的,(错误的host,port,protocol)。(具体我也没弄明白)。

Eureka's Health Checks Eureka的健康检测

默认情况下,Eureka使用client的心跳来确定是否这个client是工作状态。除非特别指定了,否则Discover Client将不会传播Spring Boot Actuator应用的当前的健康检查状态。这意味着,注册成功之后,Eureka 将会一直声明这个应用是工作状态。这个行为可以通过开启Eureka health checks修改,开启之后的结果是,发送应用状态信息到Eureka。如果当前应用的状态不是工作状态“UP”,其他的应用将不会跟故障应用交互。

application.yml

eureka:  
  client:
    healthcheck:
      enabled: true

警告,eureka.client.healthcheck.enabled=true 只能在application.yml中设置。如果配置在 bootstrap.yml中,将会产生不良副作用,比如在eureka注册的时候显示UNKNOWN状态。

如果需要更多的控制通过health checks,可以考虑实现自己的 com.netflix.appinfo.HealthCheckHandler

Eureka Metadata for Instances and Clients

值得花点时间来理解Eureka的metadata是如何工作的,才能在自己的应用中有意义的使用它。这里有标准的meatdata,比如,hostname,IP,port,status page,health check。这些在服务注册中被发布,被用在client和service直接沟通中。附加的metadata会被添加到实例注册中,在eureka.instance.metadataMap,并且这些将会被远程客户端访问,但是通常不会改变客户端的行为,除非客户端知道了metadata的具体含义。这里有几个特殊的案例,Spring Cloud 已经为metadata分配了含义。

Using Eureka on Cloudfoundry

Cloudfoundry有一个全局的路由,所以,相同应用的所有实例都有相同的hostname(在其他相似的PaaS解决方案中也是相同)。这不是使用Eureka的必须屏障,但是如果使用了路由(推荐的,或者甚至是强制的,取决于设置平台platform的方式),你需要显式的设置hostname还有port(安全 非安全都可以),这样(所有的实例)才能使用路由。你或许也想使用实例metadata,以此来区分客户端(比如,自定义的负责均衡)上的不同实例。默认情况下,eureka.instance.instanceIdvcap.application.instance_id。例如,

application.yml

eureka:  
  instance:
    hostname: ${vcap.application.uris[0]}
    nonSecurePort: 80

取决于在Cloudfoundry实例中设置的安全规则,你可能能够注册并且使用虚拟主机的IP,来直接service to service 调用。这个功能在Pivotal Web Services (PWS)还没有实现。

Using Eureka on AWS

Changing the Eureka Instance ID 修改Eureka实例ID

一个Eureka实例注册的时候带着ID,这个ID和它的hostname是相同的(只在一台主机一个服务的情况下)。
Spring Cloud Eureka 提供了一个合理的默认值,${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}},例如, myhost:myappname:8080.

使用Spring Cloud你可以通过提供唯一的id,在eureka.instance.instanceId,来覆盖默认值。例如,

application.yml

eureka:  
  instance:
    instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}

带有这些metadata,多个服务就可以部署在localhost,这个随机值,会使示例唯一。在Cloudfoundry,在Spring Boot 应用中,这个vcap.application.instance_id 将会被自动移植,所以,随机值将不再需要。

Using the EurekaClient 使用 EureClient

一旦你有一个@EnableDiscoveryClient(或者@EnableEurekaClient)应用,你可以使用它来从Eureka Server(上文提到)中发现服务实例。一个实现途径是,使用原生的com.netflix.discovery.EurekaClient(作为对应的是Spring Cloud 的 DiscoveryClient)。例子,

@Autowired
private EurekaClient discoveryClient;

public String serviceUrl() {  
    InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);
    return instance.getHomePageUrl();
}

小窍门,不要在@PostConstruct方法中,或者 @Scheduled 方法中,(或者,任何地方ApplicationContext可能还没有启动的地方)使用EurekaClientEurekaClient在一个* SmartLifecycle(with phase=0) 被初始化,所以早期,可以依赖其他SmartLifecycle(高phase)中的可用的EurekaClient*。

Alternatives to the native Netflix EurekaClient EurekaClient可替代品

不是不得不使用原生的EurekaClient,通常使用一定程度的包装更加方便。Spring Cloud 支持 Feign(a REST client builder),并且 Spring RestTemplate 使用逻辑Eureka服务ID(VIPs 不懂),而不是物理url.配置固定的列表的物理机的Ribbon,你可以简单地设置.ribbon.listOfServers,以分号相隔的物理地址,或者hostname,就是client的ID。

你还可以使用org.springframework.cloud.client.discovery.DiscoveryClient, 它提供了一个简单的API来发现clients,不是特定于Netflix。比如,

@Autowired
private DiscoveryClient discoveryClient;

public String serviceUrl() {  
    List<ServiceInstance> list = discoveryClient.getInstances("STORES");
    if (list != null && list.size() > 0 ) {
        return list.get(0).getUri();
    }
    return null;
}

Why is it so Slow to Register a Service?

作为一个实例也涉及到用间隔的心跳注册,默认间隔时间是30秒,一个Service直到实例,server,client有相同的metadata在他们本地的缓存中(可能需要3次心跳)才可被客户端发现。你可以修改间隔时间,通过使用eureka.instance.leaseRenewalIntervalInSeconds,并且这会加速获取连接到其他服务的clients的处理。在生产环境中,用默认的更好,因为,server内部的一些计算以租赁续租时间为假设。in the server that make assumptions about the lease renewal period。

Zones

如果,你已经将Eureka Clients 部署到多个地区,你可能想要,首选相同地区的服务,再选其他地区的服务。需要正确配置Eureka clients。

首先,你需要确保你有Eureka Server部署到每一个地区,他们之间都是平等的。

下一步,你需要告诉Eureka你的服务在哪个地区。可以通过使用metadataMap属性。例如,如果,service 1 被部署到 zone 1 和 zone 2.你将需要设置以下Eureka properties在service 1中。

Service 1 in Zone 1

eureka.instance.metadataMap.zone = zone1  
eureka.client.preferSameZoneEureka = true  

Service 1 in Zone 2

eureka.instance.metadataMap.zone = zone2  
eureka.client.preferSameZoneEureka = true  

Service Discovery: Eureka Server 服务发现

How to Include Eureka Server

group id org.springframework.cloud,artifact id spring-cloud-starter-eureka-server , 配置细节spring-cloud

How to Run a Eureka Server

Example eureka server;

@SpringBootApplication
@EnableEurekaServer
public class Application {

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

}

server有一个home page, 每个普通Eureka功能有一个 Http Api endpont 在 /eureka/* 下边。

Eureka底层阅读,flux capacitor

小窍门,由于Gradle的依赖解决规则,缺少parent bom 功能,简单的依赖于spring-cloud-starter-eureka-server 会在应用启动时造成错误。为了补救,Spring Boot Gradle plugin 必须被添加,并且Spring cloud starter parent bom 必须按照以下方式导入。

build.gradle

buildscript {  
  dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.5.RELEASE")
  }
}

apply plugin: "spring-boot"

dependencyManagement {  
  imports {
    mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
  }
}

High Availability, Zones and Regions

Eureka Server没有backend store,但是“注册表”中service实例必须发送心跳来保持他们的注册的最新状态(这些可以在内存中实现,不是必须通过网络发送心跳)。Clients内存中也有eureka的注册缓存(所以他们不是每个request必须去Server的注册中心).

默认情况下,每一个Eureka Server也是一个Eureka Client, 并且要求至少一个服务url。如果你没有提供这个url,service也会运行工作,但是它会记录大量的日志关于不能正常注册.but it will shower your logs with a lot of noise about not being able to register with the peer.

Standalone Mode 单机模式

两个缓存的组合(client和server cache), 心跳使得一个单机的Eureka Server非常容易失败复原,只要有监视器或者弹性运行时来保持它存活(比如,Cloud Foundry)。在单机模式下,你或许更喜欢关闭client一边的行为,所以,它不会一直尝试一直失败去获取它的同级。例如,

application.yml (Standalone Eureka Server)

server:  
  port: 8761

eureka:  
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

注意,serviceUrl指向的是和本地实例相同的host。

Peer Awareness 平等(服务)觉察

Eureka 可以被设置的更加有弹性和可用性通过运行多个实例,并且让他们之间互相注册。实际上,这是默认行为,所以,为了让它工作起来,你需要做的是,给一个同等的,peer,添加一个可用的serviceUrl。比如,

application.yml (Two Peer Aware Eureka Servers)

---
spring:  
  profiles: peer1
eureka:  
  instance:
    hostname: peer1
  client:
    serviceUrl:
      defaultZone: http://peer2/eureka/

---
spring:  
  profiles: peer2
eureka:  
  instance:
    hostname: peer2
  client:
    serviceUrl:
      defaultZone: http://peer1/eureka/

在这个例子中,我们有一个YAML文件,可以被用于在两台主机上运行相同的server,通过不同的Spring Profiles.你可以使用这个配置来检测peer awareness平等服务觉察,在一个主机上(在生产环境中做没有多大价值),通过修改/etc/hosts 来解决多个host names.实际上,eureka.instance.hostname 不是必须的,如果你正在运行的机器知道自己的host name(默认情况下,采用java.net.InetAddress查找).

你可以添加多个peer到一个系统上,并且只要他们之间互相连接至少一边,他们之间就可以同步“注册表”。如果一个peer物理隔离了(在一个数据中心或者多个数据中心之间),然后这个系统理论上可以继续存活。

Prefer IP Address 更喜欢IP地址

在一些案例中,对于Eureka最好是,通知service的IP地址而不是hostname.将eureka.instance.preferIpAddress设置为true,并且当应用注册到eureka时,将会使用ip地址而不是hostname.


Circuit Breaker: Hystrix Clients 断路器: HystrixClients

Netflix创建了一个叫做Hystrix的库,实现了断路器模式circuit breaker pattern.在微服务架构中,service 调用,多层实现很常见。

HystrixGraph

一个底层的service失败会造成连锁失败,一路失败直到用户。当调用一个特定的服务达到了一定的临界状态(5秒20次失败,Hystrix的默认值),这个“电路”打开,调用不在进行。在出错误的情况中,an open circuit a fallback可以被开发者提供。

HystrixFallback

有一个电路打开,阻止了连锁失败反应,并且允许不堪重负的或者失败的服务来恢复。Fallback 可以是另外一个Hystrix 保护的调用,静态数据,或者合理的空数据。Fallback(后备)可能是一个链,第一个Fallback使得接下来的其他的业务调用fall back到静态数据。

How to Include Hystrix

group id org.springframework.cloud,artifact id spring-cloud-starter-hystrix. Spring Cloud Project page配置细节。

Example boot app:

@SpringBootApplication
@EnableCircuitBreaker
public class Application {

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

}

@Component
public class StoreIntegration {

    @HystrixCommand(fallbackMethod = "defaultStores")
    public Object getStores(Map<String, Object> parameters) {
        //do stuff that might fail
    }

    public Object defaultStores(Map<String, Object> parameters) {
        return /* something useful */;
    }
}

@HystrixCommand 被Netflix发布的javanica库提供。Spring Cloud 自动用这个注解把Spring beans包装在一个代理中,代理连接Hustrix的断路器。断路器负责计算电路的开关,还有在失败的情况下做什么。

为配置@HystrixCommand,你可以使用commandProperties属性,带着一个@HystrixProperty的list。See here for more details. See the Hystrix wiki for details on the properties available.

Propagating the Security Context or using Spring Scopes 传播Security Context,或者使用Spring Scopes(不明白)

如果你想要thread local context传播到@HystrixCommand,默认的声明将不会工作。因为,它在一个线程池(万一超时)中执行指令。你可以切换Hystrix使用相同的线程和调用者,通过一些配置,或者直接注解,让它使用一个不同的隔离策略。例如,

@HystrixCommand(fallbackMethod = "stubMyService",
    commandProperties = {
      @HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
    }
)
...

如果使用@SessionScope或者@RequestScope效果相同。当你需要做这些的时候你将会知道因为一个运行时异常说道can’t find the scoped context。

你也有可选项,设置hystrix.shareSecurityContext为true。这个配置将会自动配置一个Hystrix concurrency strategy plugin hook。这个hook将会把SecurityContext从主线程传输到Hystrix命令的执行线程。Hystrix不允许多Hystrix并发策略,所以,一个拓展机制是可用的,通过声明自己的* HystrixConcurrencyStrategy*作为一个Spring Bean.Spring Cloud将会寻找你自己的实现在Spring context中,并且包装到Spring自己的plugin中。

Health Indicator

连接的中断器的状态也会在调用应用中被暴露/health

{
    "hystrix": {
        "openCircuitBreakers": [
            "StoreIntegration::getStoresByLocationLink"
        ],
        "status": "CIRCUIT_OPEN"
    },
    "status": "UP"
}

Hystrix Metrics Stream Hystrix 度量流

开启Hystrix Metrics Stream通过添加一个依赖spring-boot-starter-actuator。这将会暴露/hystrix.stream作为一个管理endpoint。

<dependency>  
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

Circuit Breaker: Hystrix Dashboard 中断器:Hystrix仪表盘

Hystrix几大主要好处之一是,Hystrix收集每一个HystrixCommand的度量集。Hystrix仪表盘显示了每一个中断器的健康状态通过高效的方法。

Hystrix

How to Include Hystrix Dashboard

group id org.springframework.cloud,artifact id spring-cloud-starter-hystrix-dashboard. See the Spring Cloud Project page for details on setting up your build system with the current Spring Cloud Release Train.

在Spring Boot main class添加一个注解@EnableHystrixDashboard来把Hystrix Dashboard运行起来。然后就可以访问/hystrix,并且把仪表盘指向了一个单独的实例,/hystrix.stream 在一个Hystrix client application上。

Turbine

在系统整体健康方面查看单个实例数据没有太大作用。Turbine是一个应用,作用是聚合所有有关的/hystrix.stream 到一个组合中/turbine.stream,来在Hystrix Dashboard中使用。独立的实例通过Eureka定位。在main class中使用注解@EnableTurbine(还可以,使用spring-cloud-starter-turbine来设置classpath)来运行Turbine。所有文档的配置属性from the Turbine 1 wiki都会被应用。唯一的不同是,turbine.instanceUrlSuffix 不需要预先考虑port,它会自己处理,除非turbine.instanceInsertPort=false。(句子都不知道哪跟哪)

注意,默认情况下,Turbine在一个注册的实例上寻找/hystrix.stream,通过寻找它在Eureka中的homePageUrl,然后添加上/hystrix.stream。这意味着,如果 spring-boot-actuator运行在自己的端口上(默认情况),对/hystrix.stream的调用就会失败。为了能够使turbine找到Hystrix stream的正确端口,你需要添加上management.port在实例的metadata中。

eureka:  
  instance:
    metadata-map:
      management.port: ${management.port:8081}

这个配置key,turbine.appConfig是一个erueka serviceId的list,trubine将会用这个来寻找实例。然后,turbine stream被用在Hystrix dashboard中,通过一个url,像这样,http://my.turbine.sever:8080/turbine.stream?cluster=;(cluster parameter可以忽略,如果名字是默认的)。cluster参数必须匹配一个turbine.aggregator.clusterConfig中的entry。 eureka返回的值是大写的,因此我们期待这个例子可以工作,如果这里有一个应用注册名为“customers”:

例子,

turbine:  
  aggregator:
    clusterConfig: CUSTOMERS
  appConfig: customers

这个clusterName可以被自定义通过SPEL表达式在turbine.clusterNameExpression,在根节点下,根root代表了什么,代表了一个InstanceInfo的实例。默认的value是appName,那意味着Eureka ServiceId 以cluster key结尾(比如,对于InstanceInfo)。


CircuitBreaker 断路器 1

在软件系统中远程调用不同进程中的“资源”很常见,远程的进程可能是一个网络中的另外一台主机。跟普通的内存“方法”调用不同的是远程调用有可能失败,或者是长时间没反应直到timeout。跟糟糕的是,如果你有很多对unresponsive supplier“无响应的提供者”的调用,then you can run out of critical resources leading to cascading failures across multiple systems。然后就会用完临界资源造成多个系统的连锁失败(我也不懂)。Michael Nygard 推广断路器模式为防止灾难式的连锁反应。

断路器背后的基础原理非常简单。你包装一个protected受保护的方法调用在一个断路器对象中,断路器对象负责监控失败情况。一旦失败达到一个设定临界值,the circuit breaker trips断路器就会“绊倒”,并且对该断路器后续的调用将返回一个error,这时受保护的方法将不会被调用。通常你也会想要这个监视器在 circuit breaker trips断路器绊倒的时候发出警报。

circuit-breaker

作者在博客中展示了一个ruby实现的示例。

例子是简单的个说明示例。实际上,断路器提供了一个更多的特性和参数化。断路器经常对于一个范围的调用错误提供保护。不是所有的错误都应该绊倒断路器,一些应该反应为普通的错误,并且作为正常逻辑的一部分处理。

大量的交互情况下,可能会有许多调用等待初始化超时问题。既然远程调用经常很慢,常用的做法是把每一次调用放到一个不同的线程中,并使用future or promise来处理结果,当返回结果时。通过从线程池拖出线程,可以安排一个“电路中断”但线程池用完的时候。

这个例子展示一个简单的trip绊倒中断器的方法,一次成功过调用之后,计数器重置。一个更复杂的方法是,查看失败的频率,比如设定一个50%的失败就trip。你还可能有不同的临界值对于不同的error。比如,10次timeout,3次连接失败。

这个例子展示的是中断器的同步调用,但是中断器的异步调用也很有用。一个常用的技术是将所有的request放入一个队列,supplier消费request按照自己的速度。一个有用的技术为避免服务器超载。在这个案例中,“电路中断”在队列满的时候。

中断器凭借自己帮助减少资源tied up被占用在可能失败操作中。对于客户端避免等待超时,并且一个中断的电路避免继续给一个已经超载的服务器继续负载。这里说到的情况是远程调用,这是个中断器的常见的应用案例,但是中断器还可以被用在任何情况下,什么情况下,你想要保护部分完好系统不要受其他故障部分系统的影响。

中断器是一个值得监视的地方。任何中断器状态的变化都应该被记录到日志,并且中断器应该对深层次的监视器显示自己的状态细节。中断器的行为通常是一个更深层故障问题的警告的源头。操作人员Operations staff ,应该可以绊倒或者重置中断器。

中断器自己是有价值的,但是客户端调用他们需要对中断器失败做出反应。正如远程调用需要考虑万一失败的情况怎么办一样。你正在执行的操作有没有失败,或者有没有什么变通的方法可以实施?一张信用卡认证可能会被放入一个队列稍后处理,获取一些数据失败,或许可以通过展示一些旧的数据来缓和这些失败,是一个很好的办法。


Related Article