-
第三天:Hystrix断路器+Zuul实现API网关+Sidecar异构系统调用NodeJS
-
断路器Hystrix
-
微服务设计引发新的问题
-
-
微服务的设计,服务分散在多个服务器上,服务之间互相调用,要调用的服务由于跨网络跨服务器调用,响应速度明显比传统项目单机调用慢很多,甚至由于网络涌动的不稳定的现象发生导致调用超时;还有类似级联失败、雪崩效应(依赖的基础服务宕机,关联的服务导致失败甚至宕机,就像滚雪球一样层层失败。)
如何解决这类新的问题呢?传统的机制就是超时机制。
2.超时机制
良好的设计,在通过网络请求其他服务时,都必须设置超时时间。正常情况下,一个远程调用几十毫秒内返回。当要调用的服务不可用时或者网络问题,响应时间要等超时,如HttpClient几十秒才超时返回。通常,一次远程调用对应一个线程/进程,如果大量的线程/进程得不到释放,并且越积越多,服务资源就会被耗尽,从而导致资深服务不可用。所以必须为每个请求设置超时时间。
例如:我们熟悉的tomcat就有超时设计
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" acceptCount="500" maxThreads="400" />
但我们发现传统的超时设计时间都比较久,面对今天互联网时代,用户对性能的极致要求时间显得太久了?20秒,用户要求网页秒级响应,等20秒,用户只能跟你说byebye了,扭头去找别的提供商了。
特别像微服务这样基于多个服务,服务之间都是远程调用,如果一个服务长时间等待,用户体验会极差的,那怎么办呢?断路器模式应运而生。
3.熔断机制
家里电表都有个断路器(俗称电闸),当使用的电器很多,用电巨大(例如功率过大、短路等),当电流过载时,电路就会升温,甚至烧断电路,引起火灾。有了这个断路器,我们及时拉闸,就不会造成严重后果了。
断路器可以实现快速失败,如果它在一段时间内检测到许多失败,如超时,就会强迫其以后的多个调用快速失败,不再请求所依赖的服务,从而防止应用程序不断地尝试执行可能会失败的操作,这样应用程序可以继续执行而不用等待修正错误,或者浪费CPU时间去等待长时间的超时。断路器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。
断路器模式像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。
4.状态
断路器有三种状态:
-
关闭:当访问没有问题时,断路器处于关闭未使用。
-
打开:当访问开始出现异常,错误次数增多,达到阀值时就会打开断路器,这样服务直接访问断路器,进行快速失败返回。
-
半开:那服务一直走断路器,系统就没法用了,万一被调用的服务以及稳定了呢。断路器的优势就来了,过一定时间窗口后(若干秒)它就会自动分流一部分服务再去尝试访问之前失败的服务。如果继续失败,那就不再转发,如果成功了,成功率高了,那会关闭断路器。
5.结构图
当服务B不可用时,开发人员需要写一个Fallback快速失败响应。可以设置为一个固定的值或者一个空值。
Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码。而首次请求往往会比较慢(因为Spring的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了。
6.小结
-
断路器快速失效,调用者快速返回失败信息毫不犹豫
-
不是立即宣布死亡,而是设置阀值,超越阀值才宣布死亡
-
传统方式死亡了就不管了,可断路器超级厉害,“死了也不放过”。自带心跳机制,自动测试路径是否可用,如发现又“活了”,会自动恢复调用关系。这一切开发者都无需编写代码
这样的设计太牛了,考虑非常细致全面。不可用时立即响应,可用时自动恢复。不是网络抖动下,就永远宣布其死亡。这样的设计思想非常适合网络这种不稳定的应用场景。
2.消费者实现Hystrix
-
创建Maven项目
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支持
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/
访问请求: http://localhost:9001/hello/hi
注意:可能访问太快时第一次就出现断路器实现的”tony”,这时访问太快,服务还未生效,多刷新几次,业务正常时应该出现上面的提示。
9.测试断路器
关闭provider-user,模拟服务失败宕机场景。
访问请求: http://localhost:9001/hello/hi
可以看到已经走了fallback方法。
重启provider-user服务,模拟服务修复,可以看到立即返回正确结果,说明服务正常时不会走断路器方法。
设置断点,和我们预想的相同,会发现只有异常时才走断路器的回调方法。
3.拓展:HystrixDashBoard
-
需要依赖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
访问9001,消费者链接,展示ping:一直打印,转个不停。
访问一次业务 http://localhost:9001/hello/t
停止提供者,刷新消费者,开始打印日志信息
可以基于控制台访问
输入上面的链接:http://localhost:9001/hystrix.stream,点击monitor按钮
具体指标的含义为:
在监控的界面有两个重要的图形信息:一个实心圆和一条曲线。
-
实心圆:1、通过颜色的变化代表了实例的健康程度,健康程度从绿色、黄色、橙色、红色递减。2、通过大小表示请求流量发生变化,流量越大该实心圆就越大。所以可以在大量的实例中快速发现故障实例和高压实例。
-
曲线:用来记录2分钟内流浪的相对变化,可以通过它来观察流量的上升和下降趋势。
4.API网关
-
概念
Amazon Andriod 应用的商品详情页显示内容
-
购物车中的商品数量
-
历史订单
-
客户评论
-
低库存预警
-
送货选项
-
各种推荐,包括经常与该商品一起购买的其它商品、购买该商品的客户购买的其它商品、购买该商品的客户看过的其它商品
-
其它的购物选择
使用单体应用程序架构时,移动客户端通过向应用程序发起一次REST请求,来获取这些数据,负载均衡器将请求路由给N个相同的应用程序实例中的其中之一,然后应用程序会查询各种数据库表,并将响应返回给客户端。
相反,若是采用微服务架构,显示在产品页上的数据会分布在不同的微服务上,下面列举了可能与产品详情页数据有关的一些微服务:
-
购物车服务~购物车中的件数
-
订单服务~历史订单
-
目录服务~商品基本信息、如名称、图片和价格
-
评论服务~客户的评论
-
库存服务~低库存预警
-
送货服务~送货选项、期限和费用,这些信息单独从送货方API获取
-
推荐服务~推荐商品
2.客户端与微服务直接通信的问题
-
客户端需求和每个微服务暴露的细粒度 API 不匹配。在这个例子中,客户端需要发送 7 个独立请求。在更复杂的应用程序中,可能要发送更多的请求;按照 Amazon 的说法,他们在显示他们的产品页面时就调用了数百个服务。然而,客户端通过 LAN 发送许多请求,这在公网上可能会很低效,在移动网络上就根本不可行。这种方法还使得客户端代码非常复杂。
-
客户端直接调用微服务的另一个问题是,部分服务使用的协议对 web 并不友好。一个服务可能使用 Thrift 二进制 RPC,而另一个服务可能使用 AMQP 消息传递协议。不管哪种协议对于浏览器或防火墙都不够友好,最好是内部使用。在防火墙之外,应用程序应该使用诸如 HTTP 和 WebSocket 之类的协议。
-
另一个缺点是,它会使得微服务难以重构。随着时间推移,我们可能想要更改系统拆分服务的方式。例如,我们可能合并两个服务,或者将一个服务拆分成两个或更多服务。然而,如果客户端与微服务直接通信,那么执行这类重构就非常困难了。
3.使用 API 网关构建微服务
通常来说,使用 API 网关是更好的解决方式。API 网关是一个服务器,也可以说是进入系统的唯一节点。API 网关封装内部系统的架构,并且提供 API 给各个客户端。它还可能还具备授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等功能。下图展示了一个适应当前架构的 API 网关。
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
Zuul提供了一个框架,可以对过滤器进行动态的加载,编译,运行。
Zuul可以通过加载动态过滤机制,从而实现以下各项功能:
-
验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
-
审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
-
动态路由: 以动态方式根据需要将请求路由至不同后端集群处。
-
压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
-
负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
-
静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
-
多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。
除此之外,Netflix公司还利用Zuul的功能通过金丝雀版本实现精确路由与压力测试。
6.拓展:金丝雀测试(灰度测试)
现今我们的服务集群数量都非常大,系统操作的人非常多,此时更新代码也不能停机来更新,必须逐步更新。例如:金丝雀版本发布方式。
金丝雀发布:一般先发 1 台,或者一个小比例,例如 2% 的服务器,主要做流量验证用,也称为金丝雀 (Canary) 测试(国内常称灰度测试)。以前旷工开矿下矿洞前,先会放一只金丝雀进去探是否有有毒气体,看金丝雀能否活下来,金丝雀发布由此得名。简单的金丝雀测试一般通过手工测试验证,复杂的金丝雀测试需要比较完善的监控基础设施配合,通过监控指标反馈,观察金丝雀的健康状况,作为后续发布或回退的依据。
7.烂接口的特征
-
没有接口文档
-
出入参数风格不一致
-
异常提示不友好
-
模型接口混乱,粗暴升级
-
稳定性差,还找不到人
8.Zuul生命周期
5.Zuul实现网关
1.创建Maven项目
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
直接访问: http://localhost:7900/hello/tony
未映射访问: http://localhost:8050/provider-user/hello/tony
ZUUL访问: http://localhost:8050/user/hello/tony
6.日志
6.拓展:Zuul中实现断路器
-
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服务,断路器生效
但这样实现的断路器刷新时间比较长,一旦服务正常,刷新正常。
7.NodeJS
-
简介
谷歌Chrome的V8引擎执行JavaScript的速度非常快,性能非常好。 Node.js是一个基于Chrome JavaScript运行时建立的平台,用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动,非阻塞I/O 模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。
2.安装
Windows下非常好安装,一路NEXT即可。
node-v8.11.3-x64.msi
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,否则中文乱码
5.启动nodeServer
安装完成nodejs,就可以在dos窗口中执行node命令,后面跟编写的node-service.js文件。
6.测试
访问首页
访问监控检查
访问不存在页面
8.Sidecar异构系统
通过sidecar来调用nodejs的服务。
1.创建Maven项目
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
访问sidecar首页:http://localhost:8070
第一次访问为{}空,访问业务链接后,再访问如下内容:
7.通过Zuul访问
访问健康链接: http://localhost:8050/sidecar/health.json
访问首页: http://localhost:8050/sidecar/