五、Spring Cloud - Netflix(Zuul服务网关构建代码层面)
本文依然是基于http://blog.didispace.com/springcloud5/博主提供的部分代码,以及大部分的使用思路。
1)项目的结构(5个都要用到,不单单2017-Netflix_Zuul哦)
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>com.sample</groupId>
<artifactId>2017-Netflix_Zuul</artifactId>
<version>1.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<!-- 指定source和target的版本 -->
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
</project>
application.properties
spring.application.name=api-gateway
server.port=5555
# 启动2017-Netflix_ServiceA和2017-Netflix_ServiceB, 它们两提供不同的两个服务
# routes to url (第一种 url映射方式)
zuul.routes.api-a-url.path=/api-a-url/**
zuul.routes.api-a-url.url=http://localhost:8091/
# routes to serviceId (第二种 serviceId映射方式)
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=netflix-service-a
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.serviceId=netflix-service-b
# 用于验证zuul自带的负载均衡功能, 启动2017-Netflix_Service1和2017-Netflix_Service2, 它们两提供同一个服务netflix-service
zuul.routes.api-service.path=/api-service/**
zuul.routes.api-service.serviceId=netflix-service
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
# Zuul测试连接如下
#http://localhost:5555/api-a-url/add?a=1&b=2
#http://localhost:5555/api-a/add?a=1&b=2
#http://localhost:5555/api-service/add?a=1&b=2
# 下面其实还是走原有Ribbon, 已经测试过了
#http://localhost:5555/zuulRibbonCallAddService
zuul.routes.customers=/customers/**
package com.jack.ssm.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;
/**
* ClassName:GenericZuulFallbackProvider.java
*
* http://tachniki.blogspot.in/2017/02/generic-ZuulFallbackProvider.html参考这篇文章
* 将多个熔断器抽象成一个公共的熔断类,然后在spring boot application里面根据不同的route ID,实现对应的熔断器.
*
* Date: 2017年6月7日下午1:49:35
* @author Jack.Huang
* @version V1.0
* @since JDK 1.7.0_60/JDK 1.8.0_45
*/
public class GenericZuulFallbackProvider implements ZuulFallbackProvider {
private String responseBody = "{\"message\":\"Service Unavailable. Please try after sometime\"}";
private HttpHeaders headers = null;
private String route = null;
private int rawStatusCode = 503;
private HttpStatus statusCode = HttpStatus.SERVICE_UNAVAILABLE;
private String statusText = "Service Unavailable";
@Override
public String getRoute() {
if (route == null)
route = "route"; // 若为空, 则熔断默认route ID="route", 但我配置文件没配这个这只是一种实现的样例.
return route;
}
/**
* 熔断器会调用下面的fallbackResponse方法,最终模拟返回一个ClientHttpResponse.
* 里面包含HttpHeaders、rawStatusCode、statusCode和responseBody等信息,这些信息都可以自定义返回值.
* MediaType则包含多种返回信息的格式Json、Pdf、Image等等.
*
* @see org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider#fallbackResponse()
*/
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse(){
@Override
public InputStream getBody() throws IOException {
if (responseBody == null)
responseBody ="{\"message\":\"Service Unavailable. Please try after sometime\"}";
return new ByteArrayInputStream(responseBody.getBytes());
}
@Override
public HttpHeaders getHeaders() {
if (headers == null) {
headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
}
return headers;
}
@Override
public void close() {
}
@Override
public int getRawStatusCode() throws IOException {
return rawStatusCode;
}
@Override
public HttpStatus getStatusCode() throws IOException {
if (statusCode == null)
statusCode = HttpStatus.SERVICE_UNAVAILABLE;
return statusCode;
}
@Override
public String getStatusText() throws IOException {
if (statusText == null)
statusText = "Service Unavailable";
return statusText;
}
};
}
public String getResponseBody() {
return responseBody;
}
public void setResponseBody(String responseBody) {
this.responseBody = responseBody;
}
public HttpHeaders getHeaders() {
return headers;
}
public void setHeaders(HttpHeaders headers) {
this.headers = headers;
}
public int getRawStatusCode() {
return rawStatusCode;
}
public void setRawStatusCode(int rawStatusCode) {
this.rawStatusCode = rawStatusCode;
}
public HttpStatus getStatusCode() {
return statusCode;
}
public void setStatusCode(HttpStatus statusCode) {
this.statusCode = statusCode;
}
public String getStatusText() {
return statusText;
}
public void setStatusText(String statusText) {
this.statusText = statusText;
}
public void setRoute(String route) {
this.route = route;
}
}
package com.jack.ssm;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.RestTemplate;
import com.jack.ssm.fallback.GenericZuulFallbackProvider;
/**
* ClassName:RibbonApplication.java
* Date: 2017年6月5日下午5:06:00
* @author Jack.Huang
* @version V1.0
* @since JDK 1.7.0_60/JDK 1.8.0_45
*/
@EnableZuulProxy
//@EnableZuulServer
@SpringCloudApplication // 它整合了@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker
public class ZuulRibbonApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ZuulRibbonApplication.class, args);
}
@Bean
public ZuulFallbackProvider routeAPIAZuulFallbackProvider() {
GenericZuulFallbackProvider routeZuulFallback = new GenericZuulFallbackProvider();
routeZuulFallback.setRoute("netflix-service-a");
return routeZuulFallback;
}
@Bean
public ZuulFallbackProvider routeAPIbZuulFallbackProvider() {
GenericZuulFallbackProvider routeZuulFallback = new GenericZuulFallbackProvider();
routeZuulFallback.setRoute("netflix-service-b");
return routeZuulFallback;
}
@Bean
public ZuulFallbackProvider netflixServiceZuulFallbackProvider() {
GenericZuulFallbackProvider portalZullFallback = new GenericZuulFallbackProvider();
portalZullFallback.setRoute("netflix-service");
portalZullFallback.setRawStatusCode(200);
portalZullFallback.setStatusCode(HttpStatus.OK);
portalZullFallback.setResponseBody("We are little busy. Comeback After Sometime");
return portalZullFallback;
}
/*
@Bean
public ZuulFallbackProvider zuulFallbackProvider() {
return new ZuulFallbackProvider() {
@Override
public String getRoute() {
return "customers";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("fallback".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
};
}
*/
}
3) 验证Zuul路由跳转功能
启动2017-Netflix_Server
java -jar 2017_Netflix_Eureka_Server-1.0.1-SNAPSHOT.jar --spring.profiles.active=eureka
再分别启动2017-Netflix_ServiceA、2017-Netflix_ServiceB以及2017-Netflix_Zuul
他们的启动命令都是这个, spring boot嘛: G:\GIT-WORKPLACE\project\2017-Netflix_Zuul>mvn spring-boot:run
Zuul测试连接如下:
http://localhost:5555/api-a-url/add?a=1&b=2&accessToken=token
http://localhost:5555/api-a/add?a=1&b=2&accessToken=token
http://localhost:5555/api-b-url/add?a=1&b=2&accessToken=token
http://localhost:5555/api-b/add?a=1&b=2&accessToken=token
4) 验证Zuul负载均衡功能
启动2017-Netflix_Server
java -jar 2017_Netflix_Eureka_Server-1.0.1-SNAPSHOT.jar --spring.profiles.active=eureka
再分别启动2017-Netflix_Service1、2017-Netflix_Service1以及2017-Netflix_Zuul
http://localhost:5555/api-service/add?a=1&b=2&accessToken=token
Zuul负载均衡功能默认是实现的,代码不需要写任何东西。
5) 验证Zuul熔断功能
Zuul熔断功能默认也是实现的,但是需要写东西哦。
官网有例子http://cloud.spring.io/spring-cloud-static/Dalston.RELEASE/#hystrix-fallbacks-for-routes,
但是很tm坑啊,按官网的是没效果的,还需要在ZuulRibbonApplication加入如下类似代码。
@Bean
public ZuulFallbackProvider routeAPIAZuulFallbackProvider() {
GenericZuulFallbackProvider routeZuulFallback = new GenericZuulFallbackProvider();
routeZuulFallback.setRoute("netflix-service-a");
return routeZuulFallback;
}
参考歪国友人的文章:
http://tachniki.blogspot.in/2017/02/generic-ZuulFallbackProvider.html
歪果友人同时也提供了一个公共熔断的实现方案,以及不同服务路由的不同熔断规则的实现方案。
附带源码
https://Git.coding.NET/JackHuang0801/project.git