Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,那外部请求如何调用服务?如何进行路由和过滤?
在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。
Spring Cloud Zuul路由是微服务架构的不可或缺的一部分,提供动态路由,监控,弹性,安全等的边缘服务。Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器。
Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能。
一、Zuul基础功能:路由和过滤
1.创建eureka_zuul工程,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.kevin</groupId>
<artifactId>euraka_zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>euraka_zuul</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2. application.properties定义路由地址
spring.application.name=eurekaZuul
server.port=9301
eureka.client.serviceUrl.defaultZone=http://localhost:9101/eureka/
zuul.routes.ribbon.path=/ribbon/**
zuul.routes.ribbon.serviceId=eurekaRibbon
zuul.routes.feign.path=/feign/**
zuul.routes.feign.serviceId=eurekaFeign
3. Application.java开启zuul代理
package com.kevin.euraka_zuul;
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;
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class Application {
public static void main( String[] args ) {
SpringApplication.run(Application.class, args);
}
}
4. 如果需要进行过滤,可继承ZuulFilter。
Filter的生命周期有4个,分别是“PRE”、“ROUTING”、“POST”、“ERROR”;
TokenFilter.java:
package com.kevin.euraka_zuul.filter;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
@Component
public class TokenFilter extends ZuulFilter{
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
String accessToken = request.getParameter("token");
if(StringUtils.isEmpty(accessToken)) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(400);
ctx.setResponseBody("failed, no token");
}else {
ctx.setSendZuulResponse(true);
ctx.setResponseStatusCode(200);
}
return null;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
}
4. 测试:
1)过滤检查
2)负载均衡
3) 路由
二、熔断
1. 实现熔断类FallbackProvider,需要指定路由应用名和定义熔断返回内容
package com.kevin.euraka_zuul.filter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
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 FeignFallback implements FallbackProvider{
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("The service is unavailable.".getBytes());
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public void close() {
}
};
}
@Override
public String getRoute() {
return "eurekaFeign";
}
}
2. 当eurekaFeign停止服务后,通过zuul访问,返回熔断信息
三、超时与重试
1. 可以通过以下配置设置超时时长
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
ribbon.ConnectTimeout=3000
ribbon.ReadTimeout=6000
也可以指定service的超时时长
2. 重试
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
配置文件:
#开启重试
zuul.retryable=true
#最大重试次数
client.ribbon.MaxAutoRetries=3
#切换相同Server的次数
client.ribbon.MaxAutoRetriesNextServer=0
参考:
https://cloud.spring.io/spring-cloud-static/Greenwich.SR1/single/spring-cloud.html#_router_and_filter_zuul
http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html