微服务-实现nacos的集群和Gateway网关的实现、认证校验、解决跨域

1. nacos的集群模式

1.1 分析

nacos在企业中的使用100%都是集群模式。需要掌握nacos集群的搭建

  1. nacos的数据存放在derby本地磁盘中,nacos集群模式会导致数据库数据不一致,使用加一层思想,修改nacos的数据库,使用mysql数据库,保证数据的一致

  2. 模拟nacos在window中搭建集群。因为在2.0以后,nacos增加理论两个端口,偏移量为1000和1001,所以需要注意端口号

  3. 使用8850【9850,9851】

​ 8860【9860,9861】

​ 8870【9870,9871】

1.2 实现

1. 修改conf/application.properties文件

2.创建数据库nacos并导入sql语句

3. 修改cluster.conf.example文件

把该文件命名为cluster.conf并修改里面的内容

复制代码
#it is ip
#example 注意:把虚拟机的ip关闭
ip:端口号
ip:端口号
ip:端口号

4. 修改bin文件中的startup.cmd配置文件为集群

nacos中的startup.cmd文件中默认情况下就为集群模式,若使用单机模式的话,就需要将其改为单机模式

5. 在设置中关闭VM网络

6. 启动三台nacos服务

7. 微服务连接

若是在虚拟机中实现,还需要配置nginx的conf文件,配置相应的ip和端口号,但现在是在windows下模拟,nginx支持tcp协议,而windows不支持tcp协议,所以在此通过步骤7模拟

2. Gateway网关

2.1 概述

大家都知道在微服务架构中,一个系统会被拆分为很多个微服务。那么作为客户端【pc、androud、ios、平板】要如何去调用这么多的微服务呢?
如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去调用。
这样的架构,会存在着诸多的问题

  1. 客户端多次请求不同的微服务,增加客户端代码或配置编写的复杂性
  2. 认证复杂,每个服务都需要独立认证
  3. 存在跨域请求,在一定场景下处理相对复杂。

网关:所有微服务的入口点

作用:

  1. 路由转发
  2. 认证校验
  3. 跨域统一解决
  4. 黑白名单

2.2 常用的网关组件

  • nginx+lua

使用nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用 。lua是一种脚本语言,可以来编写一些简单的逻辑, nginx支持lua脚本

  • Kong

基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。 问题:

只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方式。

  • Zuul 1.0(慢 servlet 2.0)zuul2.0没出来

Netflix开源的网关,功能丰富,使用JAVA开发,易于二次开发 问题:缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如Nginx

  • Spring Cloud Gateway

Spring公司为了替换Zuul而开发的网关服务,将在下面具体介绍。

注意:SpringCloud alibaba 技术栈中并没有提供自己的网关,我们可以采用Spring Cloud

2.3 概述gateway网关

Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控和限流。

  • 优点
  1. 性能强劲:是第一代网关Zuul的1.6倍
  2. 功能强大:内置了很多实用的功能,例如转发、监控、限流等
  3. 设计优雅,容易扩展
  • 缺点
  1. 其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高
  2. 不能将其部署在Tomcat、Jetty等Servlet容器里,只能达成jar包执行web.jar
  3. 需要springboot 2.0以及上的版本,才支持

gateway内置了服务器netty服务器,千万不要在使用tomcat作为服务器

2.4 使用gateway网关-转发地址写死

1. 创建网关微服务

2. 添加依赖

xml 复制代码
 <dependencies>
        <!--如果引入了gateway的依赖,不能再引用spring-boot-starter-web,否则会报错。因为web内置了tomcat服务器,而gateway内置netty服务器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>

如果依赖中引入了gateway依赖,就不能再引入spring-boot-starter-web依赖,否则会报错。因为web内置了tomcat服务器,而gateway中内置了netty服务器

3. 创建yml配置文件,配置网关

yaml 复制代码
server:
  port: 88
  
spring:
  application:
    name: zmq-gateway
    #配置路由转发
  cloud:
    gateway:
      routes:
        - id: zmq-product  #路由id,没有实际意义。如果自己设置,就会通过UUID随机生成
          uri: http://localhost:8001 #表示路由真实转发的微服务的地址
          predicates:
            - Path=/product/**

        - id: zmq-order
          uri: http://localhost:9001
          predicates:
            - Path=/order/**
  1. 配置端口号 88
  2. 微服务名称
  3. 配置路由转发

4. 在com.zmq包下创建主启动类

java 复制代码
@SpringBootApplication
public class Gateway {
    public static void main(String[] args) {
        SpringApplication.run(Gateway.class,args);
    }
}

5. 测试

  • 若资源路径中的断言路径写错,报错如下图所示
  • 若资源路径中的后面路径写错,报错如下图所示

2.5 增强版---转发地址解耦

现在在配置文件中写死了转发路径的地址,前面我们已经分析过地址写死带来的问题,不符合前闭后开原则,接下来我们从注册中心获取此地址
思考:gateway网关它也是一个微服务,那么它也可以从注册中心拉取服务器清单列表

实现步骤

1. 引入nacos依赖

xml 复制代码
<!--nacos依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

2. 修改配置文件

yaml 复制代码
server:
  port: 88

spring:
  application:
    name: zmq-gateway
    #配置路由转发
  cloud:
    gateway:
      routes:
        - id: zmq-product  #路由id,没有实际意义。如果自己设置,就会通过UUID随机生成
          uri: lb://zmq-product #表示路由真实转发的微服务的地址
          predicates:
            - Path=/product/**

        - id: zmq-order
          uri: lb://zmq-order
          predicates:
            - Path=/order/**
  #nacos的配置
    nacos:
      discovery:
        server-addr: localhost:8848
        register-enabled: false #是否注册到nacos上

将转发路径的地址进行解耦,使gateway微服务可以通过nacos组件从注册中心拉取【gateway可以只做拉取,而不注册到nacos上,在配置文件中设置即可】

register-enabled: false #是否注册到nacos上,默认为true,注册到nacos上,改为false后就可以实现只做拉取

lb:loadblance

2.6 简洁版-自动定位模式

增强版虽然实现了转发地址的解耦,但是需要手动配置转发地址相关参数,若微服务过多,可以使用简洁版实现自动定位模式,减少工作量

实现步骤

1. 修改配置文件

  • 删除关于转发路径的配置
  • 添加开启gateway的定位功能的配置
yaml 复制代码
server:
  port: 88

spring:
  application:
    name: zmq-gateway

  #nacos的配置
    nacos:
      discovery:
        server-addr: localhost:8848
       # register-enabled: false #是否注册到nacos上
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启gateway的定位功能

2. 在访问资源时,需要在原本的路径上加上微服务的名称

3. Gateway用于认证校验

通过过滤器filter实现

实现步骤

1. 创建登录过滤器类

java 复制代码
//在网关中定义的过滤器,只能在网关中使用
@Component
public class LoginFilter implements GlobalFilter, Ordered {
    @Autowired
    private UrlVo urlVo;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取request对象
        ServerHttpRequest request = exchange.getRequest();
        //获取response对象
        ServerHttpResponse response = exchange.getResponse();
        //获取上述两个对象是为了便于获取path和token
        //1.获取请求路径
        String path = request.getPath().toString();
        System.out.println(path);
        //对获取到的请求路径字符串进行截取,获取符合的地址路径【断言+资源路径】,便于后面比较判断是否放行
        path = path.substring(1);
        System.out.println(path);
        path=path.substring(path.indexOf("/"));//从第一个/的下标开始截取,indexOf,获取指定字符的索引
        System.out.println(path);
        //2.判断该路径是否属于放行路径------类似于白名单
        if(urlVo.getWhite().contains(path)){
            //放行
            return  chain.filter(exchange);
        }
        //3. 判断用户是否登录
        String token = request.getHeaders().getFirst("token");
        //4. 校验token是否为空,以及是否合法
        if(StringUtils.hasText(token)&&"admin".equals(token)){
            //放行
            return chain.filter(exchange);
        }
        //4.2 封装返回数据
        Map<String,Object> map=new HashMap<>();
        map.put("msg","未登录");
        map.put("code",501);
        //4.3 JSON转换
        byte[] bytes = JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8);
        // 4.4 调用bufferFactory方法,生成DataBuffer对象
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        //5. 调用Mono中的just方法,返回要写给前端的JSON数据
        return response.writeWith(Mono.just(wrap));
    }
    //优先级,值越小优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}
  1. 该过滤器类需要实现两个接口:GlobalFilter, Ordered,并重写接口的方法
  2. 重写fifter方法时
    • 首先通过exchange获取request对象和response对象,便于后面调用
    • 然后通过request的getPath方法,拦截获取请求路径,并将其转化为字符串,便于后面比较。
    • 对获取的路径字符串进行截取,获取用于判断的字符串,如下图
  1. 判断通过截取后的字符串是否在放行名单中,此时,需要在配置文件中添加需要放行的路径,并创建一个vo类,便于获取配置文件中的集合数据
  • 配置文件

    yaml 复制代码
    url:
      white:
        - /login
        - /register
        - /sendMsg
  • UrlVo类

    java 复制代码
    @Component
    @ConfigurationProperties(prefix="url")
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class UrlVo {
     private List<String> white;
    }
    1. 因为需要用到@Data等相关的注解,所以添加lombok依赖
    xml 复制代码
     <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    1. @ConfigurationProperties注解:是SpringBoot提供的,用于将外部配置文件中的属性映射到Java类中,绑定到Java类的字段上。可以方便地将配置文件中的属性注入到应用程序中,从而实现配置的集中管理和解耦

    prefix="XXX":意味着该类的字段会绑定到配置文件中以XXX为前缀的属性


  1. 通过request获取头文件中的token,校验token是否为空,是否合法

  2. 如果合法,就放行。

  3. 封装返回的数据,需要转换为JSON数据返回给前端,所以需要添加fastjson依赖

xml 复制代码
 <!--fastjosn-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.22</version>
        </dependency>
  • 测试

    在postman中进行测试,此时测试时需要带上token

    此时,该程序并没有连接关于用户登录的数据库,仅将token写死来测试

    • token不正确
    • token正确

测试时需要在原有的路径上面加上微服务的名称,因为使用的是自动定位模式

4. Gateway解决跨域问题【2种】

对之前的实现前后端分离项目中的前端项目做如下修改,便于测试

  1. main.js文件中,修改端口号为网关的端口号,并注释前置路由守卫,便于测试

    js 复制代码
    //a2.设置axios的基础路径
    axios.defaults.baseURL = 'http://localhost:88'
    //a3.将axios挂载到vue对象中
    Vue.prototype.$axios=axios;

    设置请求拦截器中将token写死------admin

    js 复制代码
    //设置请求拦截器------携带token令牌
    axios.interceptors.request.use(config=>{
      var token = sessionStorage.getItem("token");
      if(token){
        config.headers.token = "admin";
      }
      return config;
    })
  2. Home.vue文件中添加按钮,用于访问后端getById的路径


    实现getById方法

在不做跨域解决时,点击按钮,会报错,出现跨域问题

两种解决方法都是在gateway下解决

4.1 通过配置类

创建配置类,来解决跨域问题------在gateway微服务下

java 复制代码
package com.zmq.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class CorsConfig {
 
    @Bean
    public CorsWebFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true); // 允许认证
        config.addAllowedOrigin("*"); // 允许任何源
        config.addAllowedHeader("*"); // 允许任何头
        config.addAllowedMethod("*"); // 允许任何方法
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}
  • 测试时,需要手动加上token------token值为admin

4.2 通过配置文件

配置类和配置文件只能择其一

在配置文件中添加相关配置

yaml 复制代码
cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启gateway的定位功能
      globalcors: #全局的跨域处理
        add-to-simple-url-handler-mapping: true #解决浏览器向服务器发options请求被拦截问题,这样网关就不拦截这个请求了
        cors-configurations:
          '[/**]': #拦截一切请求
            allowedOrigins: "*" #允许任意域的跨域请求
            allowedMethods: "*" #允许的跨域ajax的请求方式
            allowedHeaders: "*" #允许在请求中携带的头信息,这里是允许所有的请求头
            allowCredentials: true #是否允许携带cookie
            maxAge: 360000 #这次跨域检测的有效期
相关推荐
刘一说3 小时前
Java 中实现多租户架构:数据隔离策略与实践指南
java·oracle·架构
喵叔哟3 小时前
67.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--分摊功能总体设计与业务流程
数据库·微服务·架构
tryCbest3 小时前
Oracle查看存储过程
数据库·oracle
IT邦德4 小时前
OEL9.7 安装 Oracle 26ai RAC
数据库·oracle
研究司马懿5 小时前
【云原生】Gateway API高级功能
云原生·go·gateway·k8s·gateway api
瑶山5 小时前
Spring Cloud微服务搭建五、集成负载均衡,远程调用,熔断降级
spring cloud·微服务·负载均衡·远程调用·熔断降级
KYGALYX14 小时前
服务异步通信
开发语言·后端·微服务·ruby
Goat恶霸詹姆斯17 小时前
mysql常用语句
数据库·mysql·oracle
xiaowu08018 小时前
C# 拆解 “显式接口实现 + 子类强类型扩展” 的设计思想
数据库·oracle