第二代微服务方案-SpringCloudAlibaba-DAY01

一.服务网关SpringCloudGateway

1.基本概念

1.1.Zuul与Gateway

Zuul是Netflix的开源项目,Spring Cloud将其收纳成为自己的一个子组件。zuul用的是多线程阻塞模型,它本质上就是一个同步 Servlet,这样的模型比较简单,他都问题是多线程之间上下文切换是有开销的,线程越多开销就越大。线程池数量固定意味着能力接受的请求数固定,当后台请求变慢,面对大量的请求,线程池中的线程容易被耗尽,后续的请求会被拒绝。

在Zuul 2.0中它采用了 Netty 实现异步非阻塞编程模型,异步非阻塞模式对线程的消耗比较少,对线程上线文切换的消耗也比较小,并且可以接受更多的请求。它的问题就是线程模型比较复杂,要求深究底层原理需要花一些功夫。

Spring Cloud Gateway是Spring Cloud自己的产物,基于Spring 5 和Spring Boot 2.0 开发,Spring Cloud Gateway的出现是为了代替zuul,在Spring Cloud 高版本中没有对zuul 2.0进行集成,SpringCloud Gateway使用了高性能的Reactor模式通信框架Netty。

Spring Cloud Gateway 的目标,不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流

所以说其实Gateway和zuul 2.0差别不是特别大,都是采用Netty高性能通信框架,性能都挺不错。

1.2.Spring Cloud Gataway的特点

  • 基于 Spring 5,Project Reactor , Spring Boot 2.0
  • 默认集成 Hystrix 断路器
  • 默认集成 Spring Cloud DiscoveryClient (EurekaClient)
  • Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters
  • 支持动态路由、限流、路径重写

1.3.Spring Cloud Gataway的核心概念

Spring Cloud Gataway有几个核心组成:

  • Filter(过滤器):

Spring Cloud Gateway的Filter和Zuul的过滤器类似,可以在请求发出前后进行一些业务上的处理 ,这里分为两种类型的Filter,分别是Gateway Filter网关filter和Global Filter全局Filter, 他们的区别在后续会讲到。

  • Route(路由):

网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。说白了就是把url请求路由到对应的资源(服务),或者说是一个请求过来Gateway应该怎么把这个请求转发给下游的微服务,转发给谁。

  • Predicate(断言/url判断):

这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的输入类型是一个 ServerWebExchange。简单理解就是处理HTTP请求的匹配规则,在什么样的请情况下才能命中资源继续访问。

1.4.Spring Cloud Gateway的工作方式

Spring Cloud Gateway 的工作原理跟 Zuul 的差不多,最大的区别就是 Gateway 的 Filter 只有 pre 和 post 两种,下面是官方的执行流程图:
在这里插入图片描述

2. Spring Cloud Gataway入门

  1. 依赖
<!--服务注册与发现-->
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
 </dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
  1. application.yml配置:放在nacos中

server:
  port: 10030 # 端口
spring:
  application:
    name: service-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # 注册地址
    gateway:
      # Gateway超时
      httpclient:
        connect-timeout: 5000
        response-timeout: 5s
      discovery:
        locator:
          enabled: false #开放服务名访问方式
          lower-case-service-id: true #服务名小写
          # - id.uri.predicates.filters...代表一个元素
      routes:
        - id: service-user #指定服务名
          uri: lb://service-user #去注册中心找这个服务名
          predicates: #断言,匹配访问的路径
            - Path=/user/**    #服务访问路径
          filters:
            - StripPrefix=1    #请求转发的时候会去掉 /user访问路径
        - id: service-order #指定服务名
          uri: lb://service-order #去注册中心找这个服务名
          predicates: #断言,匹配访问的路径
            - Path=/order/**    #服务访问路径
          filters:
            - StripPrefix=1    #请求转发的时候会去掉 /user访问路径

3.bootstrap.yml,放在项目中

spring:
  profiles:
    active: dev
  cloud:
    nacos:
      config:
        prefix: application-gateway
        file-extension: yml
        server-addr: localhost:8848 #配置中心
        group: DEFAULT_GROUP #默认分组 可以不用配置,看nacos网页中配置的什么
    sentinel:
      transport:
        dashboard: localhost:1111
# shared-configs: #公共配置
# - application-common-dev.yml
# namespace: 赋值粘贴

4.测试
启动注册中心,启动用户服务,启动网关访问:http://localhost:1110/user/user/1,请求将会打到用户服务上,并返回用户数据。

3. Predicate断言工厂

3.1.什么是断言工厂

什么是断言工程,在Spring Cloud Gateway官方文档有如下解释:

Spring Cloud Gateway将路由作为Spring WebFlux HandlerMapping基础架构的一部分进行匹配。Spring Cloud Gateway包括许多内置的路由断言工厂。所有这些断言都与HTTP请求的不同属性匹配。您可以将多个路由断言工厂与逻辑and语句结合使用。

这里不难理解,其实断言工厂就是用来判断http请求的匹配方式。比如我们再上面案例中配置的:“Path=/user/**” ,就是使用的是 “Path Route Predicate Factory” 路径匹配工厂,意思是http请求的资源地址必须是 /user 才会被匹配到对应的路由,然后继续执行对应的服务获取资源。

在Spring Cloud Gateway中,针对不同的场景内置的路由断言工厂,比如

  • Query Route Predicate Factory:根据查询参数来做路由匹配
  • RemoteAddr Route Predicate Factory:根据ip来做路由匹配
  • Header Route Predicate Factory:根据请求头中的参数来路由匹配
  • Host Route Predicate Factory:根据主机名来进行路由匹配
  • Method Route Predicate Factory :根据方法来路由匹配
  • Cookie Route Predicate Factory:根据cookie中的属性值来匹配
  • Before Route Predicate Factory:指定时间之间才能匹配
  • After Route Predicate Factory: 指定时间之前才能匹配
  • Weight Route Predicate Factory: 根据权重把流量分发到不同的主机
3.2.根据查询参数断言- Query Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green

上面配置表达的试试是如果请求参数中包含了 green,那么就会断言成功,从而执行uri后面的地址。

3.3.根据path断言-Path Route Predicate Factory(重要)
spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

请求路径如: /red/1/red/blue/blue/green 就可以断言成功

3.4.根据权重比例断言-Weight Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

大约80%的请求转发到weighthigh.org,将大约20%的流量转发weightlow.org。

3.5.根据远程ip断言 - RemoteAddr Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

如果请求的远程地址为 192.168.1.1192.168.1.24之间,则此路由匹配

3.6.指定时间之后断言-After Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

在atfer配置的时间之后才能访问

3.7.在指定时间之前断言-Before Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

在before配置的时间之前才能访问

3.8.在指定时间段之间断言-Between Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

请求时间在两个时间之内者允许访问

3.9.根据cookie断言-Cookie Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

cookies中必须有Cookie配置的属性才能匹配

3.10.根据请求头断言-Header Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

请求头必须出现 X-Request-Id 才可以访问

3.11.根据主机断言-Host Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

如果请求的主机头具有值**.somehost.org,或者**.anotherhost.org这匹配路由

3.12.根据请求方式断言-Method Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

只允许 GET和POST请求

4.Gateway 的 Filter 过滤器(重要)

Gateway的Filter的zuul的Filter有相似之处,与zuul不同的是,Gateway的filter从生命周期上可以为“pre”和“post”类型。根据作用范围可分为针对于单个路由的gateway filter,和针对于所有路由的Global Filer

4.1. 内置的Gateway filter

针对单个路由的Filter, 它允许以某种方式修改HTTP请求或HTTP响应。过滤器可以作用在某些特定的请求路径上。Gateway内置了很多的GatewayFilter工厂。如果要使用这些Filter只需要在配置文件配置GatewayFilter Factory的名称。下面拿一个内置的Gateway Filter举例:

AddRequestHeader GatewayFilter Factory
该Filter是Gateway内置的,它的作用是在请求头加上指定的属性。配置如下:

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue

spring.cloud.gateway.routes.filters配置项配置了一个AddRequestHeader ,他是“AddRequestHeader GatewayFilter Factory”的名称,意思是在请求头中添加一个“X-Request-red”的属性,值为blue

其他的Filter可以去看 AbstractGatewayFilterFactory 的实现类。

4.2.自定义Gateway Filter

在Spring Cloud Gateway自定义过滤器,过滤器需要实现GatewayFilter和Ordered这两个接口。我们下面来演示自定义filter计算请求的耗时。

4.2.自定义Gateway Filter

在Spring Cloud Gateway自定义过滤器,过滤器需要实现GatewayFilter和Ordered这两个接口。我们下面来演示自定义filter计算请求的耗时。
@Configuration
public class FilterConfig {

//配置Filter作用于那个访问规则上
@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {

    return builder.routes().route(r -> r.path("/services/user/**")
            //去掉2个前缀
                    .filters(f -> f.stripPrefix(2)
                    .filter(new RequestTimeFilter())
                    .addResponseHeader("X-Response-test", "test"))
                    .uri("lb://service-user")
                    .order(0)
                    .id("test-RequestTimeFilter")
            ).build();
	}
}

}

4.3.自定义GlobalFilter

GlobalFilter:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。
这里我们模拟了一个登陆检查的Filter.

@Component
@Slf4j
public class TimeGlobleFilter implements GlobalFilter , Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        List<String> token = exchange.getRequest().getHeaders().get("token");

        log.info("检查 TOKEN = {}" ,token);
        if(token == null || token.isEmpty()){
            //响应对象
            ServerHttpResponse response = exchange.getResponse();
            //构建错误结果
            HashMap<String,Object> data = new HashMap<>();
            data.put("code",401);
            data.put("message","未登录");

            DataBuffer buffer = null;
            try {
                byte[] bytes = JSON.toJSONString(data).getBytes("utf-8");
                buffer = response.bufferFactory().wrap(bytes);

                //设置完成相应,不会继续执行后面的filter
                //response.setComplete();
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            //把结果写给客户端
            return response.writeWith(Mono.just(buffer));
        }

        log.info("Token不为空 ,放行");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

5.Gateway跨域配置

所谓的跨域是因为浏览器的同源(同一个域)策略限制,其实就是同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互 ,在前后端分离的项目架构中就会出现跨域问题,因为Gateway 网关是微服务的访问入口,所以我们只需要在Gateway配置跨域即可

spring:
  cloud:
    globalcors: #跨域配置
            cors-configurations:
              '[/**]':
                allowedOrigins: "https://docs.spring.io" #允许的站点
                allowedMethods:	#允许的请求方式
                  - GET
                  - POST
                  - DELETE
                  - PUT
                  - HEAD
                  - CONNECT
                  - TRACE
                  - OPTIONS
                allowHeaders:	#允许的请求头
                  - Content-Type

Gateway中的全局超时设置:
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000
response-timeout: 5s
指定路由超时配置:
spring:
cloud:
gateway:
routes:
- id: per_route_timeouts
uri: https://example.org
predicates:
- name: Path
args:
pattern: /delay/{timeout}
metadata:
response-timeout: 200
connect-timeout: 200

6.Gateway超时

二.SpringCloudAlibaba介绍

1.SpringCloudAlibaba认识

下面是Spring Cloud 官方对Spring Cloud Alibaba的介绍

Spring Cloud Alibaba旨在为微服务开发提供一站式解决方案。该项目包括开发分布式应用程序和服务所需的组件,以便开发人员可以使用Spring Cloud编程模型轻松开发分布式应用程序。使用Spring Cloud Alibaba,您只需要添加一些注释和配置,就可以为您的应用程序使用Alibaba的分布式解决方案,并使用Alibaba中间件构建自己的分布式系统。

Spring Cloud Alibaba其实是阿里的微服务解决方案,是阿里巴巴结合自身微服务实践,开源的微服务全家桶,在Spring Cloud项目中孵化成为Spring Cloud的子项目。第一代的Spring Cloud标准中很多组件已经停更,如:Eureak,zuul等。所以Spring Cloud Alibaba很有可能成为Spring Cloud第二代的标准实现,所以许多组件在业界逐渐开始使用,已有很多成功案例。

值得一提的是Spring Cloud Alibaba对Dubbo做了很好的兼容,同时也提供了一些强大的功能,如 Sentinel 流控 ,Seata 分布式事务,Nacos 服务发现与注册等等。

Spring Cloud Alibaba的功能
Spring Cloud Alibaba是阿里巴巴结合自身的微服务实践开源的微服务全家桶,我个人觉得其组件比Spring Cloud 中的组件更加好用和强大。并且对的Spring Cloud组件做了很好的兼容。比如在Spirng Cloud Alibaba中依然可以使用Feign作为服务调用方式,使用Eureak做服务注册发现等等。Spring Cloud Alibaba主要的功能如下:

  • 流控制和服务降级:支持WebServlet,WebFlux,OpenFeign,RestTemplate,Dubbo访问限制和降级流的功能。它可以在运行时通过控制台实时修改限制和降级流的规则,并且还支持监视限制和降级度量标准。
  • 服务注册和发现:可以注册服务,并且客户可以使用Spring托管的bean(自动集成功能区)发现实例。
  • 分布式配置:支持分布式系统中的外部配置,配置更改时自动刷新。
  • Rpc服务:扩展Spring Cloud客户端RestTemplate和OpenFeign以支持调用Dubbo RPC服务。
  • 事件驱动:支持构建与共享消息系统连接的高度可扩展的事件驱动微服务。
  • 分布式事务:支持高性能且易于使用的分布式事务解决方案。
  • 阿里云对象存储:大规模,安全,低成本,高度可靠的云存储服务。支持随时随地在任何应用程序中存储和访问任何类型的数据。
  • 阿里云SchedulerX:准确,高度可靠,高可用性的计划作业调度服务,响应时间在几秒钟内。
  • 阿里云短信:阿里云短信服务覆盖全球,提供便捷,高效,智能的通信功能,帮助企业快速联系客户。

2.SpringCloud和SpringCloudAlibaba两代技术对比

在这里插入图片描述

三.Nacos服务注册与发现

1.Nacos认识与安装

1.1.什么是Nacos

Nacos不仅是服务发现组件,同时也是一个配置管理组件,也就是说它不仅可以用来取代Eureak作为注册中心, 也可以用来取代Spring Cloud Config 做配置统一管理。

1.2.Nacos服务安装

下载地址:https://github.com/alibaba/nacos/releases

启动Nacos:解压后,

  • windows执行bin目录下的startup命令 :startup.cmd -m standalone

  • linux 执行 :sh startup.sh -m standalone

访问Nacos,端口8848:http://127.0.0.1:8848/nacos/index.html ,用户名和密码都是:nacos
登录之后:
在这里插入图片描述

1.3.基于数据库持久化Nacos

找到nacos安装目录config/application.properties 文件,编辑数据库信息,修改内容如下

spring.datasource.platform=mysql

### Count of DB:
db.num=1

### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos-config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=123456

找到config/ nacos-mysql.sql 文件 ,创建数据库 nacos-config,然后导入该sql脚本文件

在这里插入图片描述

2.项目结构搭建

2.1.服务调用流程

我们这里要演示的案例是两个服务的通信,用户服务(user-server)作为服务提供者需要编写接口返回User实体对象,订单服务(order-server)作为消费者需要调用用户服务获取User实体对象,浏览器调用订单服务,订单服务调用用户服务或到User实体后返回给容器,用户和订单都注册到Nacos中,如下:
在这里插入图片描述

2.2.项目结构搭建

springcloudalibaba-parent
pom.xml
springcloudalibaba-user-common //公共的user实体,服务调用传输对象
springcloudalibaba-order-server-1020 //消费者服务
springcloudalibaba-user-server-1010 //提供者服务

父工程搭建:

<!--公共的一些配置-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        
    </properties>

   <!--SpringBoot-->
    <parent>
        <groupId> org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
    </parent>

    <!--SpringCloud-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

3.服务注册到Nacos(重要)

3.1.导入依赖

 <dependency>
     <groupId>com.alibaba.cloud </groupId>
     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--加入WEB依赖是为了方便后面写Controller-->
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
</dependency>

3.2.主配置类

//服务注册与发现
@SpringBootApplication
@EnableDiscoveryClient
public class UserServerApplication1010 {
    public static void main(String[] args) {
        SpringApplication.run(UserServerApplication1010.class) ;
    }
}

3.3.yml

server:
  port: 1010
spring:
  application:
    name: user-server
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848	#注册中心地址

3.4.测试在这里插入图片描述

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值