1.ZUUL微服務網關
微服務架構體系中,通常一個業務系統會有很多的微服務,比如:OrderService、ProductService、UserService...,為了讓調用更簡單,一般會在這些服務前端再封裝一層,類似下面這樣:
這樣做,當然能跑起來,但是維護量大,以后各個微服務增加了新方法,都需要在網關層手動增加相應的方法封裝,而spring cloud 中的zuul很好的解決了這一問題,示意圖如下:
Zuul做為網關層,自身也是一個微服務,跟其它服務Service-1,Service-2, ... Service-N一樣,都注冊在eureka server上,可以相互發現,zuul能感知到哪些服務在線,同時通過配置路由規則(后面會給出示例),可以將請求自動轉發到指定的后端微服務上,對於一些公用的預處理(比如:權限認證,token合法性校驗,灰度驗證時部分流量引導之類),可以放在所謂的過濾器(ZuulFilter)里處理,這樣后端服務以后新增了服務,zuul層幾乎不用修改。
2.代碼實現:
代碼的機構圖如下:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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</groupId>
<artifactId>stu-zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>stu-zuul</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.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>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
啟動類:
StuZuulApplication:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
//啟動類加上注解@EnableZuulProxy
//它默認加上了@EnableCircuitBreaker和@EnableDiscoveryClient
@SpringBootApplication
@EnableZuulProxy
public class StuZuulApplication {
public static void main(String[] args) {
SpringApplication.run(StuZuulApplication.class, args);
}
}
熔斷器:
這個地方我用的springboot版本比較高,網上大部分是之前的版本的熔斷器的配置,如果產生沖突可以看下是否有哦依賴版本沖突的問題:
MyFallbackProvider
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import com.netflix.hystrix.exception.HystrixTimeoutException;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
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
public class MyFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
// 表明是為哪個微服務提供回退,*表示為所有微服務提供回退
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return null;
}
public ClientHttpResponse fallbackResponse(Throwable cause) {
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
return this.fallbackResponse();
}
}
public ClientHttpResponse fallbackResponse() {
return this.response(HttpStatus.INTERNAL_SERVER_ERROR);
}
private ClientHttpResponse response(final HttpStatus status) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return status;
}
@Override
public int getRawStatusCode() throws IOException {
return status.value();
}
@Override
public String getStatusText() throws IOException {
return status.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("服務不可用,請稍后再試。".getBytes());
}
@Override
public HttpHeaders getHeaders() {
// headers設定
HttpHeaders headers = new HttpHeaders();
MediaType mt = new MediaType("application", "json", Charset.forName("UTF-8"));
headers.setContentType(mt);
return headers;
}
};
}
}
bootstrap.yml:
server:
port: 8040
spring:
application:
name: stu-zuul
eureka:
client:
service-url:
defaultZone: http://user:password123@localhost:8761/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
hostname: localhost
ip-address: localhost
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置規則 輪詢
ConnectTimeout: 3000
ReadTimeout: 60000
zuul:
routes:
api-a:
path: /api-provider/**
service-id: stu-provide
sensitive-headers:
api-b:
path: /api-consumer/**
service-id: stu-consumer,stu-consumer-feign-hytrix
上面已經講到注解了@EnableZuulProxy后,它默認加上了@EnableCircuitBreaker和@EnableDiscoveryClient,
另外他是整合了ribbon的,不需要加額外的注解,但是可以配置相關的負載均衡的規則,下面的配置需要額外注意:
zuul:
routes:
#配置服務提供者的前綴,替代服務注冊名,訪問的時候就為http://localhost:8040/api-provider/**
api-a:
path: /api-provider/**
service-id: stu-provide
sensitive-headers:
#配置消費者的前綴,替代服務注冊名,訪問的時候就為http://localhost:8040/api-consumer/**
api-b:
path: /api-consumer/**
service-id: stu-consumer,stu-consumer-feign-hytrix
啟動后進行訪問,會得到相關提供者的信息.
附上代碼地址:https://github.com/fengcharly/springCloud-ribbon-turbine.git