网关简介
有的服务不需要登录,如商品服务,不需要登录也可以浏览,有的服务需要登录,如下单服务;前端请求来了之后,被网关拦截,网关判断这些地址需不需要登录,不需要的话放行,需要的话跳转到登录服务
zuul组件的基本使用
网关是单独的一个项目,所以需要新建api-gateway网关项目
上边新建项目的时候已经把zuul需要的依赖加进去了,不需要手动添加依赖了:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
配置文件:
server:
port: 9000
#服务名称
spring:
application:
name: api-gateway
#指定注册中心地址
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
在启动类里新增注解@EnableZuulProxy
@SpringBootApplication
//代理服务器---网关
@EnableZuulProxy
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
访问规则:
http://gateway:port/service-id/***
例如之前的下单路径:
http://localhost:8781/api/v1/order/save?product_id=6&user_id=4
使用网关后的访问路径:
http://localhost:9000/order-service/api/v1/order/save?product_id=6&user_id=4
也可以在配置文件里自定义上边的"order-service":
#自定义服务名字(自定义路由映射)
zuul:
routes:
order-service: /apigateway/**
此时访问路径可以是下边这种的(两种都可以使用):
http://localhost:9000/apigateway/api/v1/order/save?product_id=6&user_id=4
环境隔离配置——将第一种访问方式禁用掉:
zuul:
routes:
order-service: /apigateway/**
#禁用order-service,使其不能对外提供服务
ignored-services: order-service
此时,下边这种方式就访问不了了:
http://localhost:9000/order-service/api/v1/order/save?product_id=6&user_id=4
对外禁用所有以"-service"结尾的服务:
zuul:
routes:
order-service: /apigateway/**
#ignored-services: order-service
#对外禁用-service结尾的服务
ignored-patterns: /*-service/**
即最后配置文件:
#自定义服务名字(自定义路由映射)
zuul:
routes:
#以后order-service与product-service这种方式不能访问了,统一入口为apigateway,下边这两个地址一样,这样是不可以的,后边讲
order-service: /apigateway/**
product-service: /apigateway/**
#ignored-services: order-service
#对外禁用-service结尾的服务
ignored-patterns: /*-service/**
这样起到了内外网隔离的作用,原理图如下:
zuul常用问题分析
1、多个服务的网关地址不能完全一样
#自定义服务名字(自定义路由映射)
zuul:
routes:
#注意下边的多个网关地址不能完全一样,不然后边的会覆盖前边的
order-service: /apigateway/order/**
product-service: /apigateway/product/**
#ignored-services: order-service
#对外禁用-service结尾的服务
ignored-patterns: /*-service/**
2、http请求头过滤问题
下单服务里模拟获取tooken与cookie
postMan发送请求(在请求头加上tooken与cookie):
后台没有获取到cookie:
源码:这三项请求头都被过滤了
这是因为cookie被zuul网关过滤掉了,解决:sensitive-headers置空即可
#自定义服务名字(自定义路由映射)
zuul:
routes:
#以后order-service与product-service这种方式不能访问了,统一入口为apigateway,
order-service: /apigateway/order/**
product-service: /apigateway/product/**
#ignored-services: order-service
#对外禁用-service结尾的服务
ignored-patterns: /*-service/**
#解决获取不到cookie的问题(这里置空,表明不过滤任何请求头)
sensitive-headers:
zuul流程:
自定义zuul过滤器实现登录鉴权实战
1、新建filter包
2、新建类继承ZuulFilter,重写方法
3、在类顶部加注解@Comment让spring扫描
package com.maltose.apigateway.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
/**
* @Author: sgw
* @Date 2019/4/7
* @Description:登录过滤器
**/
@Component
public class LoginFilter extends ZuulFilter {
/**
* 过滤器类型:前置过滤器
*
* @return
*/
@Override
public String filterType() {
//PRE:代表前置类型的过滤器
return PRE_TYPE;
}
/**
* 过滤器执行顺序,值越小,该过滤器越先执行
*
* @return
*/
@Override
public int filterOrder() {
return 4;
}
/**
* 过滤器是否生效:true--生效
*
* @return
*/
@Override
public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
// /apigateway/product/api/v1/order/save
System.out.println("URI:" + request.getRequestURI());
//http://localhost:9000/apigateway/product/api/v1/order/save
//System.out.println("URL:" + request.getRequestURL());
//只拦截这个下单接口(需要登录),常量放前边的话不会报空指针异常,request.getRequestURI()放前边的话容易报空指针
/**
* 如果服务多的话,可以搜索ACL,把服务地址放redis缓存里,这里就不用把地址写死了
* 服务少的话可以像下边这种使用多个if进行判断
*/
if("/apigateway/order/api/v1/order/save".equalsIgnoreCase(request.getRequestURI())){
//返回true说明拦截了,则下一步进入run方法
return true;
}
//返回false1的话直接放行,不拦截
return false;
}
/**
* 过滤逻辑
*
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
System.out.println("拦截了");
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getHeader("token");
if(StringUtils.isBlank(token)){
//有时候在headr里找不到,可以在参数里获取到
token=request.getParameter("token");
}
//登录校验逻辑,根据公司情况自定义,
// 通过加密算法把token加密后返回给用户,用户来来的时候拿着加密后的token过来,我们在这里判断这个token是否有效--JWT技术
if(StringUtils.isBlank(token)){
//没有token值,说明没登录,不继续向下走
requestContext.setSendZuulResponse(false);
//设置返回状态码,这里的值是401(未授权)
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
高并发下接口限流技术gauva(谷歌的框架)
MySql最大连接数3000;
原理:框架每秒向桶里放100个令牌,接口请求来了先去拿令牌,拿到令牌后才能继续向后走,否则不允许向后执行;当接口请求太频繁的话就会拿不上令牌,此时就起到了限流的作用;
我们在网关层做一个限流:
package com.maltose.apigateway.filter;
import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
/**
* @Author: sgw
* @Date 2019/4/7 20:25
* @Description: 订单接口限流
**/
public class OrderRateLimiterFilter extends ZuulFilter {
//使用google框架gauva,每秒产生1000个令牌
private static final RateLimiter RATE_LIMITER = RateLimiter.create(1000);
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return -4;
}
@Override
public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//只对订单接口限流
if ("/apigateway/order/api/v1/order/save".equalsIgnoreCase(request.getRequestURI())) {
//返回true说明拦截了,则下一步进入run方法
return true;
}
return false;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
//RATE_LIMITER.tryAcquire()表示立马去拿令牌,只要拿不到就拦截;也可以设置一段时间内拿不到再拦截
if (!RATE_LIMITER.tryAcquire()) {
requestContext.setSendZuulResponse(false);
//状态码429:太多请求的错误
requestContext.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
}
return null;
}
}
zuul微服务网关集群搭建
目前网关只有一个,局域网内的服务可能是集群搭建,但是网关挂了就都废了
所以网关也要做集群部署
IDEA模拟集群搭建
分布式链路追踪系统