SpringCloud个人笔记-05-Zuul初体验
-
sb_cloud_zuul
1.为什么需要网关呢?
1.1 我们知道我们要进入一个服务本身,很明显我们没有特别好的办法,直接输入IP地址+端口号,我们知道这样的做法很糟糕的,这样的做法大有问题,首先暴露了我们实体机器的IP地址,别人一看你的IP地址就知道服务部署在哪里,让别人很方便的进行攻击操作
1.2
我们这么多服务,我们是不是要挨个调用它呀,我们这里假设做了个权限认证,我们每一个客户访问的都是跑在不同机器上的不同的JVM上的服务程序,我们每一个服务都需要一个服务认证,这样做烦不烦呀,明显是很烦的。
那么我们这时候面临着这两个极其重要的问题,这时我们就需要一个办法解决它们。首先,我们看IP地址的暴露和IP地址写死后带来的单点问题,我是不是对这么服务本身我也要动态的维护它服务的列表呀,我需要调用这服务本身,是不是也要一个负载均衡一样的玩意,还有关于IP地址暴露的玩意,我是不是需要做一个代理呀,像Nginx的反向代理一样的东西,还有这玩意上部署公共的模块,比如所有入口的权限校验的东西。
因此我们现在需要Zuul API网关。它就解决了上面的问题,你想调用某个服务,它会给你映射,把你服务的IP地址映射成
某个路径,你输入该路径,它匹配到了,它就去替你访问这个服务,它会有个请求转发的过程,像Nginx一样,服务机器的具体实例,它不会直接去访问IP,它会去Eureka注册中心拿到服务的实例ID,即服务的名字。我再次使用客户端的负载均衡ribbon访问其中服务实例中的一台
<?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.huarui</groupId> <artifactId>sb_cloud_zuul</artifactId> <version>0.0.1-SNAPSHOT</version> <name>sb_cloud_zuul</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.19.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>Edgware.SR3</spring-cloud.version> </properties> <dependencies> <!-- eureka client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- config依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!-- zuul依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </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>
spring: application: name: sb-cloud-zuul cloud: config: discovery: enabled: true service-id: SB-CLOUD-CONFIG profile: dev #uri: http://localhost:9002/ eureka: client: serviceUrl: defaultZone: http://39.108.85.204:8761/eureka/ #instance: #注册时使用ip而不是主机名 #prefer-ip-address: true #指定ip #ip-address: ip server: port: 9005 zuul: routes: sb-product: #随意取 path: /sb-product/** # 真实访问路径 /sb-product/** -> sb-cloud-product/** serviceId: sb-cloud-product sensitiveHeaders: # ignored-patterns: /sb-cloud-order/order/getProduct,/sb-cloud-product/product/getMsg # 简洁写法 #zuul: # routes: # sb-cloud-product: sb-product management: security: enabled: false
@SpringBootApplication
@EnableZuulProxy
public class SbCloudZuulApplication {
/**
*
* http://localhost:9005/sb-cloud-product/product/getMsg
*
* http://localhost:9005/sb-cloud-order/order/getProduct
*
* localhost:9005/application/routes
* @param args
*/
public static void main(String[] args) {
SpringApplication.run(SbCloudZuulApplication.class, args);
}
}
启动sb_cloud_zuul 默认端口9005
启动sb_cloud_product 默认端口9000
直接访问 商品服务 : http://39.108.85.204:9000/product/getMsg 可以返回数据
通过zuul路由访问 : http://localhost:9005/sb-cloud-product/product/getMsg 也可以返回数据
规则就是 服务的名字/服务的请求路径
- 我想配置个性化的路径路由到我想要去的服务
zuul: routes: sb-product: #路由规则名 可随意取 path: /sb-product/** # 真实访问路径 /sb-product/** -> sb-cloud-product/** serviceId: sb-cloud-product # 要路由的服务名 sensitiveHeaders:
配置后 ,表现为 http://localhost:9005/sb-product/product/getMsg 也可以返回数据
- 我想过滤特定的路径不让别人访问
zuul: routes: sb-product: #路由规则名 可随意取 path: /sb-product/** # 真实访问路径 /sb-product/** -> sb-cloud-product/** serviceId: sb-cloud-product # 要路由的服务名 #sensitiveHeaders: #要过滤的路径(可配置多个 , 分割) ignored-patterns: /sb-cloud-order/order/getProduct,/sb-cloud-product/product/getMsg
zuul组件包含了对请求的路由和过滤两个功能
1.其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础
2.而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础
每一个进入zuul的http请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端
zuul中实现的过滤器必须包含4个基本特征:过滤类型,执行顺序,执行条件,具体操作
String filterType();
filterType:该函数需要返回一个字符串代表过滤器的类型,而这个类型就是在http请求过程中定义的各个阶段。在zuul中默认定义了4个不同的生命周期过程类型,具体如下
- pre:可以在请求被路由之前调用
- routing: 路由请求时被调用
- post:在routing和error过滤器之后被调用
- 处理请求时发生错误时被调用
int filterOrder();
通过int值来定义过滤器的执行顺序,数值越小优先级越高。
boolean shouldFilter();
返回一个boolean值来判断该过滤器是否要执行。我们可以通过此方法来指定过滤器的有效范围。
Object run();
过滤器的具体逻辑。在该函数中,我们可以实现自定义的过滤逻辑,来确定是否要拦截当前的请求,不对其进行后续的路由,或是在请求路由返回结果之后,对处理结果做一些加工等。
过滤器实现
package com.huarui.filter; import com.google.common.util.concurrent.RateLimiter; import com.huarui.exception.RateLimitException; import com.netflix.zuul.ZuulFilter; import org.springframework.stereotype.Component; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVLET_DETECTION_FILTER_ORDER; /**前置 * 限流 * 令牌桶规则 * Created by lihui on 2019/3/2. * */ @Component public class RateLimitFilter extends ZuulFilter { /** * 每秒钟10个令牌 */ private static final RateLimiter RATE_LIMITER = RateLimiter.create(10); @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return SERVLET_DETECTION_FILTER_ORDER - 2; } @Override public boolean shouldFilter() { return true; }
/** * 具体实现 * @return */ @Override public Object run() { if (!RATE_LIMITER.tryAcquire()){//如果没有取到令牌则无法访问 throw new RateLimitException(); } return null; } }
package com.huarui.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletResponse; import java.util.UUID; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.POST_TYPE; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_RESPONSE_FILTER_ORDER; /** * 后置过滤器 * Created by lihui on 2019/3/2. */ @Component public class addRespHeaderFilter extends ZuulFilter { @Override public String filterType() { return POST_TYPE; } @Override public int filterOrder() { return SEND_RESPONSE_FILTER_ORDER - 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() {//给访问的请求加上请求头信息 RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletResponse response = requestContext.getResponse(); response.setHeader("lihui", "youxiu326"+UUID.randomUUID().toString()); return null; } }
测试:请求头中携带了参数
测试:限流