【转】 SpringCloudAlibaba--06——geteway

【转】 SpringCloudAlibaba--06——geteway

参考地址:蚂蚁课堂

* SpringCloud-GateWay

什么是微服务网关

微服务网关是整个微服务API请求的入口,可以实现日志拦截、权限控制、解决跨域问题、

限流、熔断、负载均衡、黑名单与白名单拦截、授权等。

微服务中的架构模式都是采用前后端分离,但是前端调用的接口都可以通过F12控制台给抓包分析到,有被攻击到的风险。所以要加个网关,所有的请求都必须经过网关,然后反向代理到真实的请求路径

这时候有一个问题,前端调用后端接口存在一个跨域问题,浏览器会有个自己的安全检验,这时候只需要添加一个注解@CrossOrigin 让接口允许被访问就ok了

 

 这种做法非常冗余,如果接口非常多,几千几万个呢?所以这时候在网关上做个处理就好了。

过滤器网关的区别

过滤器用于拦截单个服务

网关拦截整个的微服务

ZuulGateway有那些区别

 

Zuul网关属于netfix公司开源的产品属于第一代微服务网关

Gateway属于SpringCloud自研发的第二代微服务网关

相比来说SpringCloudGateway性能Zuul性能要好:

注意Zuul基于Servlet实现的,阻塞式的Api, 支持长连接。

SpringCloudGateway基于Spring5构建,能够实现响应式非阻塞式的Api,支持长连接,能够更好的整合Spring体系的产品。

Gateway环境快速搭建

GateWay 是一个单独的项目,单独创建一个项目。

创建一个SpringBoot 项目,添加依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
        <version>2.0.0.RELEASE</version>
    </dependency>
</dependencies>

application配置

server:
  port: 80
####服务网关名称
spring:
  application:
    name: mayikt-gateway
  cloud:
    gateway:
      discovery:
        locator:
          ####开启以服务id去注册中心上获取转发地址
          enabled: true
        ###路由策略
      routes:
        ###路由id
        - id: mayikt
          ####转发http://www.mayikt.com/  要加 /
          uri: http://www.mayikt.com/
          ###匹配规则,默认是false
          predicates:
            - Path=/mayikt/**

 这样是不行的,要去注册中心拿到服务然后转发

要添加Nacos 依赖

   <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        <version>0.2.2.RELEASE</version>
    </dependency>

配置:

server:
  port: 80
####服务网关名称
spring:
  application:
    name: mayikt-gateway
  cloud:
    gateway:
      discovery:
        locator:
          ####开启以服务id去注册中心上获取转发地址
          enabled: true
        ###路由策略
      routes:
        ###路由id
        - id: mayikt
          ####转发http://www.mayikt.com/
          uri: http://www.mayikt.com/
          ###匹配规则
          predicates:
            - Path=/mayikt/**
        ###路由id
        - id: member
          #### 基于lb负载均衡形式转发
          uri: lb://mayikt-member
          filters:
            - StripPrefix=1
          ###匹配规则
          predicates:
            - Path=/member/**
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

一定要加:

filters:
  - StripPrefix=1

* 网关 和 Nginx 区别

相同点:

都是可以实现对api接口的拦截,负载均衡、反向代理、请求过滤等,可以实现和网关一样的效果。

不同点:

Nginx采用C语言编写的

微服务都是自己语言编写的 比如Gateway就是java写的

毕竟Gateway属于Java语言编写的, 能够更好对微服务实现扩展功能,相比Nginx如果想实现扩展功能需要结合Nginx+Lua语言等,相比的话成本比较高。

Nginx实现负载均衡的原理:属于服务器端负载均衡器。

Gateway实现负载均衡原理:采用本地负载均衡器的形式。

* GateWay 全局过滤

@Component
public class TokenFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (token == null || token.isEmpty()) {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.BAD_REQUEST);
            String msg = "token not is null ";
            DataBuffer buffer = response.bufferFactory().wrap(msg.getBytes());
            return response.writeWith(Mono.just(buffer));
        }
        // 使用网关过滤
        return chain.filter(exchange);
    }
}

 正常开发肯定不会这样判断token,会去判断这个token对不对,这只是官方的一个简单demo

* 网关部署实现集群 设计思想

思考问题:如果网关宕机了,怎么办?

所以,网关也要搭建集群!那么,这么网关肯定配置也都是一样的。客户先通过nginx访问,然后轮询到网关,那么nginx宕机呢?一样的,nginx也做集群,客户通过lvs访问到nginx就好了

 所以,网关集群是基于nginx 实现的。

服务获取的是网关的请求地址:(服务获取的都是网关转发的地址,不可能是客户端请求过来的地址)

网关获取客户端的请求地址

这个是会员服务获取网关的请求地址:

 然后就可以搭建网关的集群:

找到hosts文件:

 然后配置nginx:

 然后就可以测试集群轮询效果了。

* 动态部署GetWay

意思就是:任何配置都实现可以不用重启网关服务就能即时刷新网关配置

实现思路:2种

1、分布式配置中心(不建议使用,阅读性差)定义的json 格式

 2、基于数据库表结构设计(特别建议,阅读性高)

CREATE TABLE `mayikt_gateway` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `route_id` varchar(11) DEFAULT NULL,
  `route_name` varchar(255) DEFAULT NULL,
  `route_pattern` varchar(255) DEFAULT NULL,
  `route_type` varchar(255) DEFAULT NULL,
  `route_url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

 思路:默认加载:当网关服务启动时,从数据库查询网关的配置,把数据库的内容读取到网关内存中

伪代码:1、更新数据库,2、调用网关api更新

代码:

先添加依赖:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>
<!-- mysql 依赖 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 阿里巴巴数据源 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.14</version>
</dependency>

打开数据库:

datasource:
    url: jdbc:mysql://localhost:3306/meite_gateWay?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver

扫到数据库访问层:

业务逻辑层代码:

代码

@Service
public class GatewayService implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;
    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;
    @Autowired
    private MayiktGatewayMapper mayiktGateway;

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

    public void initAllRoute() {
        // 从数据库查询配置的网关配置
        List<GateWayEntity> gateWayEntities = mayiktGateway.gateWayAll();
        for (GateWayEntity gw : gateWayEntities) {
            loadRoute(gw);
        }
    }

    public String loadRoute(GateWayEntity gateWayEntity) {
        RouteDefinition definition = new RouteDefinition();
        Map<String, String> predicateParams = new HashMap<>(8);
        PredicateDefinition predicate = new PredicateDefinition();
        FilterDefinition filterDefinition = new FilterDefinition();
        Map<String, String> filterParams = new HashMap<>(8);
        // 如果配置路由type为0的话 则从注册中心获取服务
        URI uri = null;
        if (gateWayEntity.getRouteType().equals("0")) {
            uri = uri = UriComponentsBuilder.fromUriString("lb://" + gateWayEntity.getRouteUrl() + "/").build().toUri();
        } else {
            uri = UriComponentsBuilder.fromHttpUrl(gateWayEntity.getRouteUrl()).build().toUri();
        }
        // 定义的路由唯一的id
        definition.setId(gateWayEntity.getRouteId());
        predicate.setName("Path");
        //路由转发地址
        predicateParams.put("pattern", gateWayEntity.getRoutePattern());
        predicate.setArgs(predicateParams);

        // 名称是固定的, 路径去前缀
        filterDefinition.setName("StripPrefix");
        filterParams.put("_genkey_0", "1");
        filterDefinition.setArgs(filterParams);
        definition.setPredicates(Arrays.asList(predicate));
        definition.setFilters(Arrays.asList(filterDefinition));
        definition.setUri(uri);
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return "success";
    }
}

实体类&访问层

public interface MayiktGatewayMapper {

    @Select("SELECT ID AS ID, route_id as routeid, route_name as routeName,route_pattern as routePattern\n" +
            ",route_type as routeType,route_url as routeUrl\n" +
            " FROM mayikt_gateway\n")
    public List<GateWayEntity> gateWayAll();

    @Update("update mayikt_gateway set route_url=#{routeUrl} where route_id=#{routeId};")
    public Integer updateGateWay(@Param("routeId") String routeId, @Param("routeUrl") String routeUrl);
}

controller:

导入service 类,然后调用它的 initAllRoute 方法,返回

 然后测试:

先去调用一下这个配置方法,就这个controller

 显示sucess 成功以后,再去访问服务,就好了,如果直接访问服务,是会报错的,404

我们正常都是要自己写个接口,修改数据库,页面也要自己设计写一个页面。。。a

如果有想法的朋友,可以自己写个框架,来扩展GatWay,就写一个界面嘛,来动态的管理GatWay

 

posted @ 2020-10-15 01:44  CLASSIC~W  阅读(380)  评论(0编辑  收藏  举报