Spring Cloud Alibaba系列-第11节-GateWay网关,路由,nacos注册,动态路由

目录

1. 网关的介绍

1.1 API网关的作用

2. gateway实现路由

2.1 创建网关服务

2.2 测试网关路由

3. 动态路由

3.1 增加Nacos监听

3.2 修改application.yml

3.3 在nacos创建动态路由配置

3.4 测试动态路由 

3.4.1 首先将nacos中的service-gateway中的json文件删除

3.4.2 测试请求状态

3.4.3 在Nacos增加消费者路由测试

3.4.4 增加生产者服务路由并测试

4. 出现的问题与修改

 


观博有逻辑,导航来相助《Spring Cloud Alibaba系列-目录导航 

1. 网关的介绍

1.1 API网关的作用

修改前

以如上图所示:模拟客户端请求到微服务会出现如下问题:

  • 客户端发送不同的请求到微服务,需要知道不同的微服务的服务地址
  • 多次发送请求,会增加网络传输
  • 鉴权等会分布在每个微服务中处理,重复操作
  • 后端微服务中,可能采用不同的协议,客户端需要按协议适配

如上图所示:在客户端与微服务中增加网关层,作用如下:

  • API网关可以把后端的多个服务进行整合,提供唯一的业务接口(请求转发),客户端只需要调用这个接口即可完成数据获取与展示。
  • 针对所有的请求进行统一鉴权,限流,熔断,日志。
  • 协议转化。针对后端不同的协议,在网关层统一处理后以http对外提供服务。
  • 统一错误码处理
  • 请求转发,并且可以基于网关实现内、外网隔离。

 

2. gateway实现路由

前提条件:以前面章节中的生产者与消费者两个微服务为基础微服务创建网关服务

本次创建:创建网关服务

实现的目标:请求使用网关服务讲请求转发到生产者与消费者中

2.1 创建网关服务

在service-cloud父项目中,创建一个名字为service-gateway的模块,如果不会创建参照之前博客

在service-gateway的application.yml中增加如下内容

server:
  #设置服务端口
  port: 8090
spring:
  #设置服务名称
  application:
    name: service-gateway
  #设置nacos注册地址
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    #设置网关配置
    gateway:
      routes:
      # 添加consumer-8001服务路由
        - id: consumer-8001
          # 支持普通url和lb://注册中心服务名称
          uri: lb://consumer-8001/
          #路由条件,根据匹配结果进行路由
          predicates:
            - Path=/echo/**
      # 添加provider-7001服务路由
        - id: provider-7001
          uri: lb://provider-7001/
          predicates:
          - Path=/echop/**

 创建网关服务(service-gateway)的启动类,如下图:

在provider-7001服务中增加接口如下图:

2.2 测试网关路由

前提条件:启动nacos注册中心,启动消费者(consumer-8001),服务者(provider-7001),网关服务(service-gateway)

启动后再nacos页面如下图:证明网关服务已经注册到nacos中

请求测试:

分别请求 http://localhost:8090/echop/1111(路由到provider-7001)    http://localhost:8090/echo/feign/1111(路由到consumer-8001)  查看请求结果

请求的端口为8090,因为是使用网关进行请求的转发,因此端口为网关服务的端口。

 

 

3. 动态路由

Spring Cloud Gateway本身不支持直接从Nacos动态加载路由配置表,需要自己编写监听器监听配置变化并刷新路由表。

3.1 增加Nacos监听

增加NacosDynamicRouteService类如下

.

package com.run.ayena.config;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;


@Component
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {

    //设置路由配置的Nacos dataid
    private String dataId = "service-gateway";
    //设置路由配置的nacos group
    private String group = "gateway";
    //Nacos 配置中心地址
    @Value("${spring.cloud.nacos.config.server-addr}")
    private String serverAddr;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher applicationEventPublisher;

    private static final List<String> ROUTE_LIST = new ArrayList<>();


    @PostConstruct //服务器加载Servlet的时候运行,并且只会被服务器执行一次
    public void dynamicRouteByNacosListener() {
        try {
            //创建nacos服务
            ConfigService configService = NacosFactory.createConfigService(serverAddr);
            //根据条件获取nacos配置
            configService.getConfig(dataId, group, 5000);
            //为nacos配置添加监听
            configService.addListener(dataId, group, new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    //清空路由信息
                    clearRoute();
                    try {
                        //路由信息JSON转为RouteDefinition对象
                        List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class);
                        //添加路由信息
                        for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
                            addRoute(routeDefinition);
                        }
                        //推送路由信息
                        publish();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }

    private void clearRoute() {
        for(String id : ROUTE_LIST) {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
        }
        ROUTE_LIST.clear();
    }

    private void addRoute(RouteDefinition definition) {
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            ROUTE_LIST.add(definition.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void publish() {
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

3.2 修改application.yml

将已经配置的路由信息删除

server:
  #设置服务端口
  port: 8090
spring:
  #设置服务名称
  application:
    name: service-gateway
  #设置nacos注册地址
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
      discovery:
        server-addr: localhost:8848
    #设置网关配置
    gateway:
      routes:

3.3 在nacos创建动态路由配置

具体的json文件如下 

[
    {
        "id":"consumer-8001",
        "order": 0,
        "uri":"lb://consumer-8001/",
        "predicates":[
            {
                "name":"Path",
                "args":{
                    "pattern":"/echo/**"
                }
            }
        ]
    },
        {
        "id":"provider-7001",
        "order": 1,
        "uri":"lb://provider-7001/",
        "predicates":[
            {
                "name":"Path",
                "args":{
                    "pattern":"/echop/**"
                }
            }
        ]
    }
]

3.4 测试动态路由 

启动nacos ,启动生产者服务,消费者服务,网关服务

3.4.1 首先将nacos中的service-gateway中的json文件删除

3.4.2 测试请求状态

由于3.4.1 已经将路由信息删除,此时是没有路由的,因此请求会报错。请求与报错信息如下图:

通过网关请求生产者:

通过网关请求消费者:

3.4.3 在Nacos增加消费者路由测试

请求消费者请求结果如下(已配置路由):

请求生产者请求结果如下(未配置路由):

3.4.4 增加生产者服务路由并测试

 测试由网关到生产者请求(已配置网关):

 测试由网关到消费者请求(已配置网关):

 

4. 出现的问题与修改

问题描述:上述已经实现了路由的动态配置,但是出现的问题是,在生产者与消费者不动的情况下,如果重启网关服务,不会在nacos上加载路由信息,导致请求失败,因此对应修改如下:

在NacosDynamicRouteService类中,添加监听的方法中,手动将nacos中的路由信息添加到网关中

package com.run.ayena.config;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;


@Component
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {

    //设置路由配置的Nacos dataid
    private String dataId = "service-gateway";
    //设置路由配置的nacos group
    private String group = "gateway";
    //Nacos 配置中心地址
    @Value("${spring.cloud.nacos.config.server-addr}")
    private String serverAddr;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher applicationEventPublisher;

    private static final List<String> ROUTE_LIST = new ArrayList<>();


    @PostConstruct //服务器加载Servlet的时候运行,并且只会被服务器执行一次
    public void dynamicRouteByNacosListener() {
        try {
            //创建nacos服务
            ConfigService configService = NacosFactory.createConfigService(serverAddr);
            //根据条件获取nacos配置
            String config = configService.getConfig(dataId, group, 5000);
            stringToRouteAndAddRoure(config);

            //为nacos配置添加监听
            configService.addListener(dataId, group, new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    //清空路由信息
                    clearRoute();
                    try {
                        stringToRouteAndAddRoure(configInfo);
                        //推送路由信息
                        publish();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }

    private void stringToRouteAndAddRoure(String configInfo){
        //路由信息JSON转为RouteDefinition对象
        List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class);
        //添加路由信息
        for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
            addRoute(routeDefinition);
        }
    }

    private void clearRoute() {
        for(String id : ROUTE_LIST) {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
        }
        ROUTE_LIST.clear();
    }

    private void addRoute(RouteDefinition definition) {
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            ROUTE_LIST.add(definition.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void publish() {
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

 

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值