springboot gateway网关+jwt+ribbon+zookeeper实现动态路由和负载均衡
需求
采用jwt的方式,前端携带token进行鉴权,后端采用微服务的方式提供服务,我们需要采用网关的方式对前端的访问进行限流、鉴权、负载均衡、监控、路由和灰度发布。 后端其他人员不再关注与权限与安全相关需求,只需要专注业务开发。
1. 什么是网关
API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。
2. ribbon的作用
因为gateway的特殊性LoadBalancerClientFilter
过滤器主要解析lb:// 为前缀的路由规则,在通过LoadBalancerClient方法获取到需要的服务实例,从而实现负载均衡。在这里我们要写自己的负载均衡就需要重新需要重写LoadBalancerClientFilter 过滤器LoadBalancerClientFilter 介绍:次过滤器作用在url以lb开头的路由,然后利用loadBalancer来获取服务实例,构造目标requestUrl,设置到GATEWAYREQUESTURL_ATTR属性中,供NettyRoutingFilter使用。
GatewayLoadBalancerClientAutoConfiguration
在初始化会检测@ConditionalOnBean(LoadBalancerClient.class) 是否存在,如果存在就会加载LoadBalancerClientFilter
负载过滤器
3. 代码实现
代码实现github:
网关中常用的Filter实现有两种方式,一种是GatewayFilter
,实现该过滤器需要根据url来指定需要的过滤器,并且需要实现Ordered
(数字小的优先);第二种是GlobalFilter,实现该过滤器则所有的请求都会经过该过滤器。
3.1 动态路由
常规的路由的实现方式主要是在yml里面进行配置,这样的话需要每次都重启服务来配置路由,非常麻烦。因此通过某种方式实现动态修改路由就很重要。直接上代码:
在springboot启动的时候就加载路由规则,同时在修改路由规则之后也会调用GatewayServiceHandler
刷新一遍路由规则到内存中。
@Service
public class GatewayServiceHandler implements ApplicationEventPublisherAware, CommandLineRunner{
private final static Logger log = LoggerFactory.getLogger(GatewayServiceHandler.class);
@Autowired
private RedisRouteDefinitionRepository routeDefinitionWriter;
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
// 获取数据dao
@Autowired
private GatewayRouteDefinitionMapper gatewayRouteMapper;
// springboot启动后执行
@Override
public void run(String... args){
this.loadRouteConfig();
}
/**
* 更新路由
* @return
*/
public String loadRouteConfig() {
//从数据库拿到路由配置
List<GatewayRouteDefinition> gatewayRouteList = gatewayRouteMapper.queryAllRoutes();
log.info("网关配置信息:=====>"+ JSON.toJSONString(gatewayRouteList));
gatewayRouteList.forEach(gatewayRoute -> {
//todo 增加断言/路由规则校验
// 创建路由对象
RouteDefinition definition = new RouteDefinition();
definition.setId(gatewayRoute.getId());
// 设置路由执行顺序
definition.setOrder(gatewayRoute.getOrder());
// 设置路由规则转发的目标uri
URI uri = URI.create(gatewayRoute.getUri());
definition.setUri(uri);
// 设置路由断言
String predicatesJson = gatewayRoute.getPredicatesJson();
List<PredicateDefinition> predicates = new ArrayList<