SpringCloud-02

  1. 第三天:Hystrix断路器+Zuul实现API网关+Sidecar异构系统调用NodeJS

    1. 断路器Hystrix

      1. 微服务设计引发新的问题

SpringCloud-02

微服务的设计,服务分散在多个服务器上,服务之间互相调用,要调用的服务由于跨网络跨服务器调用,响应速度明显比传统项目单机调用慢很多,甚至由于网络涌动的不稳定的现象发生导致调用超时;还有类似级联失败、雪崩效应(依赖的基础服务宕机,关联的服务导致失败甚至宕机,就像滚雪球一样层层失败。)

如何解决这类新的问题呢?传统的机制就是超时机制。

2.超时机制

良好的设计,在通过网络请求其他服务时,都必须设置超时时间。正常情况下,一个远程调用几十毫秒内返回。当要调用的服务不可用时或者网络问题,响应时间要等超时,如HttpClient几十秒才超时返回。通常,一次远程调用对应一个线程/进程,如果大量的线程/进程得不到释放,并且越积越多,服务资源就会被耗尽,从而导致资深服务不可用。所以必须为每个请求设置超时时间。

例如:我们熟悉的tomcat就有超时设计

<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" acceptCount="500" maxThreads="400" />

但我们发现传统的超时设计时间都比较久,面对今天互联网时代,用户对性能的极致要求时间显得太久了?20秒,用户要求网页秒级响应,等20秒,用户只能跟你说byebye了,扭头去找别的提供商了。

特别像微服务这样基于多个服务,服务之间都是远程调用,如果一个服务长时间等待,用户体验会极差的,那怎么办呢?断路器模式应运而生。

3.熔断机制

家里电表都有个断路器(俗称电闸),当使用的电器很多,用电巨大(例如功率过大、短路等),当电流过载时,电路就会升温,甚至烧断电路,引起火灾。有了这个断路器,我们及时拉闸,就不会造成严重后果了。

断路器可以实现快速失败,如果它在一段时间内检测到许多失败,如超时,就会强迫其以后的多个调用快速失败,不再请求所依赖的服务,从而防止应用程序不断地尝试执行可能会失败的操作,这样应用程序可以继续执行而不用等待修正错误,或者浪费CPU时间去等待长时间的超时。断路器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。

断路器模式像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。

4.状态

SpringCloud-02

断路器有三种状态:

  1. 关闭:当访问没有问题时,断路器处于关闭未使用。

  2. 打开:当访问开始出现异常,错误次数增多,达到阀值时就会打开断路器,这样服务直接访问断路器,进行快速失败返回。

  3. 半开:那服务一直走断路器,系统就没法用了,万一被调用的服务以及稳定了呢。断路器的优势就来了,过一定时间窗口后(若干秒)它就会自动分流一部分服务再去尝试访问之前失败的服务。如果继续失败,那就不再转发,如果成功了,成功率高了,那会关闭断路器。

5.结构图

SpringCloud-02

当服务B不可用时,开发人员需要写一个Fallback快速失败响应。可以设置为一个固定的值或者一个空值。

Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码。而首次请求往往会比较慢(因为Spring的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了。

6.小结

  • 断路器快速失效,调用者快速返回失败信息毫不犹豫

  • 不是立即宣布死亡,而是设置阀值,超越阀值才宣布死亡

  • 传统方式死亡了就不管了,可断路器超级厉害,“死了也不放过”。自带心跳机制,自动测试路径是否可用,如发现又“活了”,会自动恢复调用关系。这一切开发者都无需编写代码

这样的设计太牛了,考虑非常细致全面。不可用时立即响应,可用时自动恢复。不是网络抖动下,就永远宣布其死亡。这样的设计思想非常适合网络这种不稳定的应用场景。

2.消费者实现Hystrix

  1. 创建Maven项目

SpringCloud-02

2.pom.xml

增加hystrix依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>consumer-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Hystrix,Feign是基于Hystrix的 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<!-- Eureka依赖,连接注册中心的都需要有这个依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- Feign依赖,声明式开发 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

3.导入hystrix支持

SpringCloud-02

Spring cloud没有直接使用NetFlix开源的Hystrix,而使用的是javanica,javanica在Hystrix基础上利用反射和注解技术增强和简化了Hystrix的开发。

4.application.yml

server:
port: 9001
spring:
application:
name: consumer-feign-hystrix
eureka:
client:
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka

5.EurekaServiceFeign.java

package cn.tedu.client;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
// 这个接口相当于把原来的服务提供者项目当成一个Service类
@FeignClient(value="provider-user")
public interface EurekaServiceFeign {
/*
 * Feign中没有原生的@GetMapping/@PostMapping/@DeleteMapping/@PutMapping,要指定需要用method进行
 */
@RequestMapping(value="/hello/{name}",method=RequestMethod.GET)
public String hello(@PathVariable("name") String name);
}

6.HelloController.java

package cn.tedu.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import cn.tedu.client.EurekaServiceFeign;
@RestController
public class HelloController {
@Autowired
private EurekaServiceFeign eurekaServiceFeign;

@GetMapping("/hello/{name}")
@ResponseBody
@HystrixCommand(fallbackMethod = "helloFallback")
public String hello(@PathVariable String name){
return eurekaServiceFeign.hello(name);
}

//对应上面的方法,参数必须一致,当访问失败时,hystrix直接回调用此方法
public String helloFallback(String name){
return "tony";    //失败调用时,返回默认值
}
}

7.HystrixRunApp.java

package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringCloudApplication
@EnableFeignClients         //开启feign
@EnableCircuitBreaker           //开启hystrix
public class HystrixRunApp {
public static void main(String[] args) {
SpringApplication.run(HystrixRunApp.class, args);
}
}

8.测试

执行顺序:

先启动服务端    8761       eureka-server            EurekaServerApp

在启动提供者1   7900       provider-user            ProviderRunApp

最后启动消费者  9001       consumer-feign-hystrix   HystrixRunApp

访问Eureka控制台:     http://localhost:8761/

SpringCloud-02

访问请求:            http://localhost:9001/hello/hi

SpringCloud-02

注意:可能访问太快时第一次就出现断路器实现的”tony”,这时访问太快,服务还未生效,多刷新几次,业务正常时应该出现上面的提示。

9.测试断路器

关闭provider-user,模拟服务失败宕机场景。

SpringCloud-02

访问请求:     http://localhost:9001/hello/hi

SpringCloud-02

可以看到已经走了fallback方法。

SpringCloud-02

重启provider-user服务,模拟服务修复,可以看到立即返回正确结果,说明服务正常时不会走断路器方法。

SpringCloud-02

设置断点,和我们预想的相同,会发现只有异常时才走断路器的回调方法。

3.拓展:HystrixDashBoard

  1. 需要依赖jar包支持

只针对当前实例,actuator是SpringBoot提供的一些监控的扩展支持

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

2.pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>consumer-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Hystrix,Feign是基于Hystrix的 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Eureka依赖,连接注册中心的都需要有这个依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- Feign依赖,声明式开发 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

3.HystrixRunApp.java

package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringCloudApplication
@EnableFeignClients         //开启feign
@EnableCircuitBreaker           //开启hystrix
@EnableHystrixDashboard
public class HystrixRunApp {
public static void main(String[] args) {
SpringApplication.run(HystrixRunApp.class, args);
}
}

4.查看监控日志

访问链接:http://localhost:9001/hystrix.stream

SpringCloud-02

访问9001,消费者链接,展示ping:一直打印,转个不停。

访问一次业务 http://localhost:9001/hello/t

停止提供者,刷新消费者,开始打印日志信息

SpringCloud-02

可以基于控制台访问

SpringCloud-02

输入上面的链接:http://localhost:9001/hystrix.stream,点击monitor按钮

SpringCloud-02

具体指标的含义为:

SpringCloud-02

在监控的界面有两个重要的图形信息:一个实心圆和一条曲线。

  • 实心圆:1、通过颜色的变化代表了实例的健康程度,健康程度从绿色、黄色、橙色、红色递减。2、通过大小表示请求流量发生变化,流量越大该实心圆就越大。所以可以在大量的实例中快速发现故障实例和高压实例。

  • 曲线:用来记录2分钟内流浪的相对变化,可以通过它来观察流量的上升和下降趋势。

4.API网关

  1. 概念

Amazon Andriod 应用的商品详情页显示内容

SpringCloud-02

  • 购物车中的商品数量

  • 历史订单

  • 客户评论

  • 低库存预警

  • 送货选项

  • 各种推荐,包括经常与该商品一起购买的其它商品、购买该商品的客户购买的其它商品、购买该商品的客户看过的其它商品

  • 其它的购物选择

使用单体应用程序架构时,移动客户端通过向应用程序发起一次REST请求,来获取这些数据,负载均衡器将请求路由给N个相同的应用程序实例中的其中之一,然后应用程序会查询各种数据库表,并将响应返回给客户端。

相反,若是采用微服务架构,显示在产品页上的数据会分布在不同的微服务上,下面列举了可能与产品详情页数据有关的一些微服务:

  • 购物车服务~购物车中的件数

  • 订单服务~历史订单

  • 目录服务~商品基本信息、如名称、图片和价格

  • 评论服务~客户的评论

  • 库存服务~低库存预警

  • 送货服务~送货选项、期限和费用,这些信息单独从送货方API获取

  • 推荐服务~推荐商品

2.客户端与微服务直接通信的问题

  1. 客户端需求和每个微服务暴露的细粒度 API 不匹配。在这个例子中,客户端需要发送 7 个独立请求。在更复杂的应用程序中,可能要发送更多的请求;按照 Amazon 的说法,他们在显示他们的产品页面时就调用了数百个服务。然而,客户端通过 LAN 发送许多请求,这在公网上可能会很低效,在移动网络上就根本不可行。这种方法还使得客户端代码非常复杂。

  2. 客户端直接调用微服务的另一个问题是,部分服务使用的协议对 web 并不友好。一个服务可能使用 Thrift 二进制 RPC,而另一个服务可能使用 AMQP 消息传递协议。不管哪种协议对于浏览器或防火墙都不够友好,最好是内部使用。在防火墙之外,应用程序应该使用诸如 HTTP 和 WebSocket 之类的协议。

  3. 另一个缺点是,它会使得微服务难以重构。随着时间推移,我们可能想要更改系统拆分服务的方式。例如,我们可能合并两个服务,或者将一个服务拆分成两个或更多服务。然而,如果客户端与微服务直接通信,那么执行这类重构就非常困难了。

3.使用 API 网关构建微服务

通常来说,使用 API 网关是更好的解决方式。API 网关是一个服务器,也可以说是进入系统的唯一节点。API 网关封装内部系统的架构,并且提供 API 给各个客户端。它还可能还具备授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等功能。下图展示了一个适应当前架构的 API 网关。

SpringCloud-02

API 网关负责服务请求路由、组合及协议转换。客户端的所有请求都首先经过 API 网关,然后由它将请求路由到合适的微服务。API 网关经常会通过调用多个微服务并合并结果来处理一个请求。它可以在 web 协议(如 HTTP 与 WebSocket)与内部使用的非 web 友好协议之间转换。

API 网关还能为每个客户端提供一个定制的 API。通常,它会向移动客户端暴露一个粗粒度的 API。以产品详情的场景为例,API 网关可以提供一个端点(/productdetails?productid=xxx),使移动客户端可以通过一个请求获取所有的产品详情。API 网关通过调用各个服务(产品信息、推荐、评论等等)并合并结果来处理请求。

Netflix API 网关是一个很好的 API 网关实例。Netflix 流媒体服务提供给成百上千种类型的设备使用,包括电视、机顶盒、智能手机、游戏系统、平板电脑等等。

最初,Netflix 试图为他们的流媒体服务提供一个通用的 API。然而他们发现,由于各种各样的设备都有自己独特的需求,这种方式并不能很好地工作。如今,他们使用一个 API 网关,通过运行与针对特定设备的适配器代码,来为每种设备提供定制的 API。通常,一个适配器通过调用平均 6 到 7 个后端服务来处理每个请求。Netflix API 网关每天处理数十亿请求。

4.API 网关的优点和缺点

如你所料,使用 API 网关有优点也有不足。使用 API 网关的最大优点是,它封装了应用程序的内部结构。客户端只需要同网关交互,而不必调用特定的服务。API 网关为每一类客户端提供了特定的 API,这减少了客户端与应用程序间的交互次数,还简化了客户端代码。

API 网关也有一些不足。它增加了一个我们必须开发、部署和维护的高可用组件。还有一个风险是,API 网关变成了开发瓶颈。为了暴露每个微服务的端点,开发人员必须更新 API 网关。API网关的更新过程要尽可能地简单,这很重要;否则,为了更新网关,开发人员将不得不排队等待。不过,虽然有这些不足,但对于大多数现实世界的应用程序而言,使用 API 网关是合理的。

5.Zuul

SpringCloud-02

Zuul提供了一个框架,可以对过滤器进行动态的加载,编译,运行。

Zuul可以通过加载动态过滤机制,从而实现以下各项功能:

  • 验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。

  • 审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。

  • 动态路由: 以动态方式根据需要将请求路由至不同后端集群处。

  • 压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。

  • 负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。

  • 静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。

  • 多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。

除此之外,Netflix公司还利用Zuul的功能通过金丝雀版本实现精确路由与压力测试。

6.拓展:金丝雀测试(灰度测试)

SpringCloud-02

现今我们的服务集群数量都非常大,系统操作的人非常多,此时更新代码也不能停机来更新,必须逐步更新。例如:金丝雀版本发布方式。

金丝雀发布:一般先发 1 台,或者一个小比例,例如 2% 的服务器,主要做流量验证用,也称为金丝雀 (Canary) 测试(国内常称灰度测试)。以前旷工开矿下矿洞前,先会放一只金丝雀进去探是否有有毒气体,看金丝雀能否活下来,金丝雀发布由此得名。简单的金丝雀测试一般通过手工测试验证,复杂的金丝雀测试需要比较完善的监控基础设施配合,通过监控指标反馈,观察金丝雀的健康状况,作为后续发布或回退的依据。

7.烂接口的特征

  • 没有接口文档

  • 出入参数风格不一致

  • 异常提示不友好

  • 模型接口混乱,粗暴升级

  • 稳定性差,还找不到人

8.Zuul生命周期

SpringCloud-02

5.Zuul实现网关

SpringCloud-02

1.创建Maven项目

SpringCloud-02

2.pom.xml

注意zuul也要注册到注册中心Eureka上

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>consumer-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

3.application.yml

zuul的配置:app-a路由名称随意,path映射路径,访问路径就无需敲入serviceId那么长,可以利用path映射路径简写。

旧的方式:http://localhost:8050/provider-user/hello/tony

新的方式:http://localhost:8050/user/hello/tony

server:
port: 8050
spring:
application:
name: gateway-zuul
eureka:
client:
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka
zuul:
routes:
app-a:
path: /user/**
serviceId: provider-user

默认会把所有注册在eureka上的微服务都反向代理

server:
port: 8050
spring:
application:
name: gateway-zuul
eureka:
client:
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka
zuul:
ignoredServices: '*'
routes:
app-a:
path: /user/**
serviceId: provider-user

这样配置后,只有provider-user的被映射到user,其它的服务访问形式不变,如果有多个之间用逗号隔开

直接访问:            http://localhost:7900/hello/tony

未映射访问(无法访问): http://localhost:8050/provider-user/hello/tony

ZUUL访问:            http://localhost:8050/user/hello/tony

4.ZuulRunApp.java

package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient      //zuul服务要注册到Eureka上
public class ZuulAppRun {
public static void main(String[] args) {
SpringApplication.run(ZuulAppRun.class, args);
}
}

5.测试

启动EurekaServerApp
启动ProviderRunApp
启动ZuulAppRun

SpringCloud-02

直接访问:     http://localhost:7900/hello/tony
未映射访问:    http://localhost:8050/provider-user/hello/tony
ZUUL访问:     http://localhost:8050/user/hello/tony

SpringCloud-02 

6.日志

SpringCloud-02

6.拓展:Zuul中实现断路器

  1. HelloFallback.java

package cn.tedu.fallback;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
@Component //Zuul实现熔断机制
public class HelloFallback implements ZuulFallbackProvider{
@Override
public String getRoute() {
return "provider-user";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {

@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return headers;
}

@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream(("fallback "+HelloFallback.this.getRoute()).getBytes());
}

@Override
public String getStatusText() throws IOException {
return HttpStatus.BAD_REQUEST.getReasonPhrase();
}

@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.BAD_REQUEST;
}

@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.BAD_REQUEST.value();
}

@Override
public void close() {

}
};
}
}

2.测试

执行业务正常,停掉provider-user服务,断路器生效

SpringCloud-02

但这样实现的断路器刷新时间比较长,一旦服务正常,刷新正常。

7.NodeJS

  1. 简介

SpringCloud-02SpringCloud-02

谷歌Chrome的V8引擎执行JavaScript的速度非常快,性能非常好。 Node.js是一个基于Chrome JavaScript运行时建立的平台,用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动,非阻塞I/O 模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。

2.安装

Windows下非常好安装,一路NEXT即可。

node-v8.11.3-x64.msi

SpringCloud-02

3.NodeJs代码

var http = require('http');
var url = require('url');
var path = require('path');
//创建server
var server = http.createServer(function(req, res){
//获得请求路径
var pathname = url.parse(req.url).pathname;
res.writeHead(200, {'Content-Type':'application/json; charset=utf-8'});
if(pathname === '/'){
res.end(JSON.stringify({ "index":"欢迎" }));
}else if(pathname === '/health.json'){
res.end(JSON.stringify({ "status":"UP" }));
}else{
res.end("404");
}
});
//创建监听,并打印日志
server.listen(8060, function(){
console.log('listening on localhost:8060');
});

注意:开发的Node.js应用,必须去实现一个健康检查接口,来让Sidecar可以把这个服务实例的健康状况报告给Eureka。

4.中文乱码

注意:文件编码必须是UTF-8,否则中文乱码

SpringCloud-02

5.启动nodeServer

安装完成nodejs,就可以在dos窗口中执行node命令,后面跟编写的node-service.js文件。

SpringCloud-02

6.测试

访问首页

SpringCloud-02

访问监控检查

SpringCloud-02

访问不存在页面

SpringCloud-02

8.Sidecar异构系统

通过sidecar来调用nodejs的服务。

1.创建Maven项目

SpringCloud-02

2.pom.xml

注意引入spring-cloud-netflix-sidecar,不是start开头的

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>consumer-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-sidecar</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

3.application.yml

server:
port: 8070
spring:
application:
name: sidecar
eureka:
client:
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka
sidecar:
port: 8060
health-uri: http://localhost:8060/health.json

4.SidecarRunApp.java

package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.sidecar.EnableSidecar;
@SpringBootApplication
@EnableSidecar
public class SidecarRunApp {
public static void main(String[] args) {
SpringApplication.run(SidecarRunApp.class, args);
}
}

注意:

To enable the Sidecar, create a Spring Boot application with @EnableSidecar. This annotation includes @EnableCircuitBreaker,
@EnableDiscoveryClient, and @EnableZuulProxy. Run the resulting application on the same host as then non-jvm application.

官网上写了异构程序必须在一个host下部署,否则要配置${eureka.instance.hostName}

5.Zuul的application.yml

增加app-sidecar的映射。#为备注,yml文件中不能使用/tab键,只能空格

server:
port: 8050
spring:
application:
name: gateway-zuul
eureka:
client:
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka
zuul:
ignoredServices: '*'
routes:
app-provider-user:            #名称随意,唯一就好
path: /user/**
serviceId: provider-user
app-sidecar:                  #通过sidecar调用nodejs的服务
path: /sidecar/**
serviceId: sidecar

6.测试

启动EurekaServerApp
启动ZuulAppRun
启动SidecarRunApp

SpringCloud-02

访问sidecar首页:http://localhost:8070

SpringCloud-02

SpringCloud-02

SpringCloud-02

第一次访问为{}空,访问业务链接后,再访问如下内容:

SpringCloud-02

7.通过Zuul访问

访问健康链接:     http://localhost:8050/sidecar/health.json

SpringCloud-02

访问首页:         http://localhost:8050/sidecar/

SpringCloud-02


作者:Darren

QQ :603026148

以上内容归Darren所有,如果有什么错误或者不足的地方请联系我,希望我们共同进步。

上一篇:「整合系列」配置隔离


下一篇:Springcloud笔记(二)