目录
新建项目引入依赖
- <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>
配置文件
- eureka.client.service-url.defaultZone=http://euk1.com:7001/eureka/
- spring.application.name=zuulserver
- server.port=80
启动类
@EnableZuulProxy
测试访问
网关会将服务名转换成具体服务的ip和端口,实际进行访问
http://localhost/consumer/alive
启动两个Consumer
轮询访问上面地址,会看到返回结果中,端口一直轮询在变。说明负载均衡生效了,默认是轮询
consumer.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
调试的时候,看网关请求的地址,以及 映射是否正确。网关请求有误时,可以通过此处排查错误。
配置
- management.endpoints.web.exposure.include=*
- management.endpoint.health.show-details=always
- management.endpoint.health.enabled=true
- management.endpoint.routes.enabled=true
1. 通过服务名配置(虚拟主机名)
zuul.routes.consumer=/xxoo/**
配置前先访问,然后做对比。
2.自定义映射
zuul.routes.xx.path=/xx/**
zuul.routes.xx.url=http://mashibing.com
3. .自定义下的负载均衡
zuul.routes.xx.path=/xx/**
zuul.routes.xx.service-id=cuid
cuid.ribbon.listOfServers=localhost:82,localhost:83
ribbon.eureka.enabled=false
配置
zuul.ignored-services=user-provider
zuul.prefix=/api/v1
带上前缀请求
zuul.strip-prefix=false
zuul服务器的
pom.xml:需要引入eureka客户端、zuul、ribbon-discovery-filter-spring-cloud-starter这三个。
ribbon-discovery-filter-spring-cloud-starter主要用于网关选择集群中的某一个节点。主要方法是RibbonFilterContextHolder.getCurrentContext().add("version","v2");(详情见java代码)
- <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.3.10.RELEASE</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
- <groupId>com.kingdee</groupId>
- <artifactId>zuul</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>zuul</name>
- <description>Demo project for Spring Boot</description>
- <properties>
- <java.version>1.8</java.version>
- <spring-cloud.version>Hoxton.SR11</spring-cloud.version>
- </properties>
- <dependencies>
- <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>
-
- <dependency>
- <groupId>io.jmnarloch</groupId>
- <artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
- <version>2.1.0</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>org.junit.vintage</groupId>
- <artifactId>junit-vintage-engine</artifactId>
- </exclusion>
- </exclusions>
- </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>
java代码:
- package com.kingdee.zuul.filter;
-
- import com.netflix.zuul.ZuulFilter;
- import com.netflix.zuul.context.RequestContext;
- import com.netflix.zuul.exception.ZuulException;
- import io.jmnarloch.spring.cloud.ribbon.support.RibbonFilterContextHolder;
- import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
- import org.springframework.stereotype.Component;
-
- import javax.servlet.http.HttpServletRequest;
-
- /**
- * @author :zhouhaitao
- * @date :Created in 2021/5/20 23:39
- * @description:
- * @modified By:
- * @version: v1.0
- */
- @Component
- public class GrayFilter extends ZuulFilter {
-
- @Override
- public String filterType() {
- return FilterConstants.ROUTE_TYPE;
- }
-
- @Override
- public int filterOrder() {
- return 0;
- }
-
- @Override
- public boolean shouldFilter() {
- return true;
- }
-
- @Override
- public Object run() throws ZuulException {
- RequestContext currentContent = RequestContext.getCurrentContext();
- HttpServletRequest request = currentContent.getRequest();
- String userId = request.getHeader("userId");
- //根据用户Id查规则
- if("1".equals(userId)){
- RibbonFilterContextHolder.getCurrentContext().add("version","v2");
- }else{
- RibbonFilterContextHolder.getCurrentContext().add("version","v1");
- }
-
- return null;
- }
- }

这个主要是设置cookie里哪些值会被传到后面!!

测试点:
停止一个api-driver。访问:yapi:网关token,看返回。
初始请求。返回值中token为msb cookie
加上下面配置
敏感的header不会传播到下游去,也就是说此处的token不会传播的其它的微服务中去。
- zuul:
- #一下配置,表示忽略下面的值向微服务传播,以下配置为空表示:所有请求头都透传到后面微服务。
- sensitive-headers: token
访问。网关token为null。
- @Component
- public class MsbFallback implements FallbackProvider{
-
- /**
- * 表明为哪个微服务提供回退
- * 服务Id ,若需要所有服务调用都支持回退,返回null 或者 * 即可
- */
- @Override
- public String getRoute() {
- // TODO Auto-generated method stub
- return "*";
- }
-
- @Override
- public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
-
- if (cause instanceof HystrixTimeoutException) {
- return response(HttpStatus.GATEWAY_TIMEOUT);
- } else {
- return response(HttpStatus.INTERNAL_SERVER_ERROR);
- }
-
-
- }
-
- private ClientHttpResponse response(final HttpStatus status) {
- return new ClientHttpResponse() {
- @Override
- public HttpStatus getStatusCode() throws IOException {
- //return status;
- return HttpStatus.BAD_REQUEST;
- }
-
- @Override
- public int getRawStatusCode() throws IOException {
- //return status.value();
- return HttpStatus.BAD_REQUEST.value();
- }
-
- @Override
- public String getStatusText() throws IOException {
- //return status.getReasonPhrase();
- //return HttpStatus.BAD_REQUEST.name();
- return HttpStatus.BAD_REQUEST.getReasonPhrase();
- }
-
- @Override
- public void close() {
- }
-
- @Override
- public InputStream getBody() throws IOException {
- String msg = "{\"msg\":\"服务故障\"}";
- return new ByteArrayInputStream(msg.getBytes());
- }
-
- @Override
- public HttpHeaders getHeaders() {
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_JSON);
- return headers;
- }
- };
- }
-
- }
选用基础yml
测试点:启动eureka,api-driver, online-taxi-zuul
正常启动,正常访问yapi 网关token。正常
停止api-driver。
则走了容错 方法。
将fallback的改成:
- @Override
- public String getRoute() {
- // TODO Auto-generated method stub
- // return "*";
- return "api-passenger";
- }
在访问上面 yapi 中 zuul,中网关token。则报500。
再改成:
- @Override
- public String getRoute() {
- // TODO Auto-generated method stub
- // return "*";
- // return "api-passenger";
- return "api-driver";
- }
重写访问,熔断生效。
最后改回去*。
保护自己,用ratelimit。
令牌桶
假设进入高速公路的车辆需要在入口处领取到通行卡才能进入高速公路。为了节约人力成本,入口处放置自动出卡机。按照国家高速公路交通安全法的规定,在高速公路上行驶的车辆,车速超过100km/h时,应与同车道前车保持100米以上距离。为了保持最小安全行车距离100米,按车速100km/h计算,需要间隔至少3.6秒才能放行一辆车,因此出卡机每隔3.6秒出一张通行卡。在自动出卡机下放置一个盒子,自动出卡机按照3.6秒的间隔向盒子中投放通行卡。每辆进入高速公路的车辆,从盒子中领取通行卡之后才可以进入高速公路。
令牌桶可以看作是一个存放一定数量令牌的容器。系统按设定的速度向桶中放置令牌。当桶中令牌满时,多出的令牌溢出,桶中令牌不再增加。在使用令牌桶对流量规格进行评估时,是以令牌桶中的令牌数量是否足够满足报文的转发为依据的。每个需要被转发的报文,都要从令牌桶中领取一定数量的令牌(具体数量视报文大小而定),才可以被正常转发。如果桶中存在足够的令牌可以用来转发报文,称流量遵守或符合约定值,否则称为不符合或超标。
1. 启动jmeter,双击:jmeter.bat
2. 右击TestPlan,add ,Threads,Thread Group
3. 右击测试令牌桶线程组,add,sampler, http request。
4. 在线程组:
1、Number of Threads(users):用户个数
2、Ramp-up Period(in seconds):在多长时间内,加载指定的用户个数,单位为s。假如需加载100个用户,在5s中之内加载完成,那么平均每秒钟加载20个用户。
3、Loop Count(循环次数):用户执行操作的循环次数,如果选择forever,则永远循环下去。
测试点:启动eureka,api-driver,online-taxi-zuul。
令牌桶设置成2,jemter 用10个并发。可以看到控制台输出结果。
例如老项目中,原来的url,微服务改造后变成别的url了,通过网关进行转发
例如
app : /url/xxx/xxx将url中的test31转成服务提供的test3
方法1
- zuul.
- route.
- 随便写自定义名
- path:=/account/**
- serviceId=account
-
- /account/a
- 没有/a
问题:原来的url不动1,而服务没有提供相应的接口
这个方法实际上是修改服务名,而不是修改对应的具体接口
方法2:
使用灰度的方式,使用filter进行转发
方法3:
使用nginx进行转发
方法4:
使用Zuul的自定义route过滤器进行转发
1.过滤器,route。
2.获取 请求过来的url(请求)
3. url (请求)= url (目的地)映射。
4. 设置RequestContext 中的 serviceld , url
开发之前,想清楚步骤。
技术和业务做选择和取舍。
- @Component
- public class CommonServicePathFilter extends ZuulFilter {
-
- private final static String GETWAY_FOWARD_PREFIX="getway_forward_";
-
- private final static String GETWAY_COMPAY_CONFIG_KEY = "getway_company";
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- @Override
- public String filterType() {
- //这里很重要,必须是route
- return "route";
- }
-
- @Override
- public int filterOrder() {
- return 1;
- }
-
-
- @Override
- public Object run() throws ZuulException {
- RequestContext ctx = RequestContext.getCurrentContext();
- String url = ctx.getRequest().getRequestURI();
- Map<String,String> forwardMap = getForwardMap(url);
- if(forwardMap != null){
- String fowardUrl = forwardMap.get(url);
- String serviceId = getServiceId(fowardUrl);
- String requestUrl = getRequestUrl(fowardUrl,serviceId);
- //1.设置目标service的Controller的路径
- ctx.put(FilterConstants.REQUEST_URI_KEY,requestUrl);
- //2.设置目标service的serviceId
- ctx.put(FilterConstants.SERVICE_ID_KEY,serviceId);
- }
- return null;
- }
- private String getServiceId(String url){
- if(url.startsWith("/")){
- String temp = url.substring(1);
- return temp.split("/")[0];
- }else{
- return null;
- }
- }
-
- private String getRequestUrl(String url,String serviceId){
- return url.substring(serviceId.length() +1);
- }
-
- @Override
- public boolean shouldFilter() {
- return true;
- }
-
-
-
- private Map<String,String> getForwardMap(String originalUrl){
- //todo:这里是返回一个map,传入一个originUrl,返回一个要转发的url
- }
-
- }
路由过滤器
RibbonXXXFilter :将url 路由到服务
SimpleHostRoutingFilter :将url路由到 url 地址
SendForwardFilter : 转发(转向zuul自己)
RibbonRoutingFilter order 10
将url路由到服务
- zuul:
- routes:
- quality: /请求路径/**
其中 quality 为服务的名称
SimpleHostRoutingFilter order 100
将url路由到url地址
yml 配置:
- zuul:
- routes:
- #自己转发到自己 名字任意起
- xxx: 如果找不到会报404的错误(类似于将让eureka找到自己的服务,这个是zuul里面的)
- path: /前端请求的地址/**
- url: http://…… 接口的地址(全路径)
SendForwardFilter order 500
将url路由到自己的controller
yml 配置:
- zuul:
- routes:
- #自己转发到自己 名字任意起
- xxx:
- path: /前端请求的地址/**
- url: forward:/接口的地址
例如
访问 localhost:9090/forword1/
设置

实际上访问zuul的

路由规则的查看。http://localhost:9100/actuator/routes
-
- pom:
-
- <dependency>
- <groupld>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-actuator</artifactId>
- </dependency>
-
- yml:
- management:
- endpoints:
- web:
- exposure:
- include: "*"
- endpoint:
- health:
- ##默认是never
- show-details: ALWAYS
- enabled: true
- routes:
- enabled: true
过滤器的查看: http://localhost:9100/actuator/filters

ip过滤。次数限制,设备号过滤。
pre过滤器中设置。sendzuulresponse (false ),只控制不向 route过滤器 执行(即其他的pre还是执行的)
因为ribbonroutefilter的shouldFilter判断了sendZuulResponse条件。