Spring Cloud 代码篇

一、服务中心Eureka

搭建Eureka单机

  • 目录结构
    在这里插入图片描述
  • pom文件

pom.xml(cloudDemo)

	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.4.RELEASE</version>
        <relativePath/>
    </parent>
    <properties>
        <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
    </properties>
    <!--只声明依赖的版本信息,不引入,子项目再次引入此依赖时无需显示的列出版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

pom.xml(eureka)

	<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

application.yml(eureka)

spring:
  application:
    name: eureka-server
server:
  port: 8008
eureka:
  client:
  	# 表示是否从Eureka Server获取注册信息
    fetch-registry: false # 不从其他节点获取服务信息
    # 是否注册到Eureka,如果是单机版不需要注册到其他Eureka
    register-with-eureka: false # 单机 不注册
    service-url:
      # 对外提供服务的url
      defaultZone: http://127.0.0.1:${server.port}/eureka/

EurekaApplication.java

@SpringBootApplication
@EnableEurekaServer // 标识该项目是EurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

运行启动类,访问localhost:8008/出现eureka界面启动成功。

搭建Eureka集群

  • 配置三个yml:

application-8001

spring:
  application:
    name: eureka-server # 集群节点name必须相同
server:
  port: 8001
eureka:
  instance:
    hostname: eureka8001 # 域名
  client:
    fetch-registry: true # 从其他节点获取服务信息
    register-with-eureka: true # 集群 true 注册
    service-url:
      defaultZone: http://eureka8002:8002/eureka/,http://eureka8003:8003/eureka/ # 对外提供服务的地址

其余用类似配置修改instance hostname defaultZone即可。

  • 修改本地域名:
127.0.0.1 eureka8001
127.0.0.1 eureka8002
127.0.0.1 eureka8003
  • 配置启动参数:
--spring.profiles.active=8001

二、服务提供者Provider

创建两个maven模块:commonDao,commonPojo
在这里插入图片描述
在commonPojo中创建com.ttc.pojo的包和User.java

并在commonDao的pom.xml中引入commonPojo等依赖

    <dependencies>
        <dependency>
            <groupId>com.ttc</groupId>
            <artifactId>commonPojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.11</version>
        </dependency>
    </dependencies>

provider模块:
在这里插入图片描述

在provider的pom文件中添加commonDao等依赖

    <dependencies>
        <dependency>
            <groupId>com.ttc</groupId>
            <artifactId>commonDao</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

配置application.yml

spring:
  application:
    name: cld-customer-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.1.213:3306/HLJS?useUnicode=true&characterEncoding=utf8&useSSL=false&ServerTimezone=Asia/Shanghai
    username: root
    password: password01!
server:
  port: 9000
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8008/eureka/ # 单机模式测试

ProviderApplication.java

@SpringBootApplication
@EnableEurekaClient // 到Eureka注册中心中注册服务
//@EnableDiscoveryClient // 到任何注册中心中注册服务
@MapperScan("com.ttc.dao")
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class,args);
    }
}

启动运行Ok!注册中心中也注册成功

三、消费者

  1. 首先在provider中提供一个controller以供消费者调用
    UserController.java
	@RequestMapping("/getUser/{id}")
    public User getUserById(@PathVariable String id) {
        return userService.selectUserById(id);
    }
  1. 在consumer中添加依赖
    pom.xml(consumer)
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--用来进行服务间通讯,通过provider Controller中的RequestMapping进行匹配-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 配置文件
    application.yml
spring:
  application:
    name: cld-customer-service
server:
  port: 9001
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8008/eureka/
  1. 创建包
    目录结构:
    在这里插入图片描述
  2. CustomerClient.java
    CustomerClient.java
/**
 * 配置调用远程服务的接口
 */
@FeignClient(name="CLD-PROVIDER-SERVICE") // 代表要调用远程访问,name属性就是远程服务spring.application.name属性
public interface CustomerClient {
        /**
         * 对应服务端的Controller定义的方法,其中路径要一致
         * 如果从路径中取参数,@PathVariable注解中必须填写name属性
         * @param id
         * @return
         */
        @RequestMapping("/getUser/{id}")
        public String getUserById(@PathVariable(name = "id") String id);
}
  1. CustomerController.java
    CustomerController.java
@RestController
public class CustomerController {
    @Autowired
    private CustomerClient customerClient;

    @RequestMapping("/user/{id}")
    public String getUserById(@PathVariable String id) {
        String user = customerClient.getUserById(id);
        return user;
    }
}
  1. CustomerApplication.java
    CustomerApplication.java
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients //代表要使用feign进行服务之间的通信
public class CustomerApplication {
    public static void main(String[] args) {
        SpringApplication.run(CustomerApplication.class, args);
    }
}

注意: 在使用Feign进行通讯的时候一定要用对象,不能传入多个参数。并且在provider的controller中用@RequestBody接受对象。
http发送和接受json数据,记得@RequestBody,@ResponseBody注解
在这里插入图片描述

四、负载均衡

Spring CLoud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松的将面向服务的REST模板请求自动转换成客户端负载均衡的服务调用。在Feign中已经集成了Ribbon,我们同时开启多个服务端,会自动实现负载均衡(默认为轮询策略)。
客户端可以配置负载均衡的策略:

cld-customer-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 随机模式

我们也可以指定为其他策略,包括我们自己定义的,只要把相应的包路径写到这即可。

五、熔断器Hystrix

1. 雪崩效应

在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。

2. 熔断器(CircuitBreaker)

如果熔断器在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断的尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器也可以使应用程序能够诊断错误是否已经修正,应用程序会再次尝试调用操作。
熔断器模式就像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。
在这里插入图片描述
服务降级:
熔断器给返回错误信息,就是服务降级。

3. Hystrix特性

3.1 断路器机制

断路器很好理解,当Hystrix Command请求后端服务器失败数量超过一定比例(默认50%),断路器会切换到开路状态(Open),这时所有请求会直接失败而不会发送到后端服务,断路器保持在开路状态一段时间后(默认5秒),自动切换到半开路状态(HALF-OPEN),这时会自动判断下一次请求的返回情况,如果请求成功,断路器切回闭路状态(CLOSED),否则重新切换到开路状态。
一旦后端请求不可用,断路器会直接切断请求链,避免发送大量无效请求影响系统吞吐量,并且断路器有自我检测并恢复的能力。

3.2 Fallback

Fallback相当于是将及操作,对于查询操作,我们可以实现一个fallback方法,当请求后端服务出现异常的时候,可以使用fallback方法返回的值,fallback方法的返回值一般是设置的默认值,或者来自缓存。

3.3 资源隔离

在Hystrix中,主要通过线程池来实现资源隔离。通常在使用的时候我们会根据调用的远程服务划分出多个线程池。例如调用产品服务的Command放入A线程池,调用账户服务的Command放入B线程池。这样做的主要优点是运行环境被隔离开了。这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽时,不会对系统的其他服务造成影响。但是带来的代价就是维护多个线程池会对系统带来额外的性能开销。如果是对性能有严格要求而且确信自己调用服务的客户端不会出问题的话,可以使用Hystrix的信号模式(Semaphores)来隔离资源。

4. Feign Hystrix

因为熔断只是作用在服务调用这一端,因此只需要改动消费者工程即可。

  • Consumer的配置文件中添加配置:
    application.yml(Customer)
feign:
  hystrix:
    enabled: true # 开启熔断器
  • 创建文件:
    在这里插入图片描述
    在client中创建子包impl,创建CustomerClientImpl implements CustomerClientImpl
    CustomerClientImpl.java
@Component
public class CustomerClientImpl implements CustomerClient {
    @Override
    public String getUserById(String id) {
        return "服务熔断中不可达";
    }

    @Override
    public String verifyUser(User user) {
        return "服务熔断中不可达";
    }

    @Override
    public String sayHello() {
        return "服务熔断中不可达";
    }
}
  • 配置CustomerClient.java
    在这里插入图片描述

可以通过关闭provider来模拟服务不可达。

五、集中配置组件Spring Cloud Config

随着线上项目变得日益庞大,每个项目都散落着配置文件,如果采用分布式的开发模式,需要的配置文件随着服务增加而不断增多。某一个基础服务信息变更,都会引起一系列的更新和重启,运维苦不堪言也容易出错。配置中心便是解决此类问题的灵丹妙药。
在我们了解Spring Cloud Config之前,想象一个配置中心提供的核心功能该有什么。

  • 提供服务端和客户端支持
  • 集中管理各环境的配置文件
  • 配置文件修改之后,可以快速地生效
  • 可以进行版本管理
  • 支持大的并发查询
  • 支持各种语言

Spring Cloud Config可以完美的支持以上所有的需求。
Spring Cloud Config项目是一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。Spring Cloud使用git或svn存放配置文件,默认情况下使用git。

  1. gitee上新建仓库并新建一个文件夹SpringCloudConfig,将provider和consumer的配置文件上传到该目录下,改名为provider-dev.yml和customer.yml。
  2. 新建一个模块作为SpringCloudConfig的Server端。
    在这里插入图片描述

pom.xml(config)

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
</dependencies>

application.yml

server:
  port: 9900
spring:
  application:
    name: cld-config-center
  cloud:
    config:
      server:
        git:
          # 配置git仓库的地址
          uri: https://gitee.com/xxxxx/spring-cloud-config.git
          # git仓库地址下的相对地址
          search-paths: [SpringCloudConfig]
          # git仓库的帐号
          #username:
          # git仓库的密码
          # password:

ConfigApplication.java

@SpringBootApplication
@EnableConfigServer // 开启配置中心服务
public class ConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class, args);
    }
}
项目启动成功后,浏览器访问的方式:
/{application}/{profile}/{label}   // 文件/版本/分支
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{apllication}-{profile}.properties
  1. 配置客户端
  • 在provider和customer的pom文件中添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
  • 将原有的application.yml删掉并添加新的bootstrap.yml配置文件,引导其在启动时去找真正的配置文件
    bootstrap.yml
spring:
  cloud:
    config:
      name: provider
      profile: dev
      label: master
      uri: http://localhost:9900/
  1. 配置中心服务话
    将config模块也服务化,避免修改端口后都要修改配置文件的问题

服务端改造

  • 首先在config的pom文件中添加eureka的依赖
    pom.xml(config)
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  • 修改application.yml配置文件,添加eureka信息
    application.yml(config)
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8008/eureka/
  • config启动类ConfigApplication.java添加@EnableEurekaClient注解

客户端改造
在provider和consumer的配置文件bootstrap.yml中将uri改为:

spring:
  cloud:
    config:
      name: customer
      profile: dev
      label: master
      # uri: http://localhost:9900/
      discovery:
        enabled: true
        service-id: CLD-CONFIG-CENTER
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8008/eureka/
  1. 配置中心高可用
    模拟实际生产集群环境,在config中新增一个新的配置文件applicaiton-002.xml改动server的端口为9901,再启动一个server端来做服务的负载,提供高可用的server端支持。

六、消息总线组件Spring Cloud Bus

1. 单个工程更新

spring boot actuator执行器刷新操作
spring boot actuator是spring boot项目运行时的一个监视服务,启动项目的endpoints就是由spring boot actuator输出的,包含了对spring boot的bean的监视,健康状况的管理,可以通过/acuator查看各种项目运行的信息。

  1. 在provider工程的pom文件中加入spring boot actuator的引用
    pom.xml(provider)
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 修改bootstrap.yml配置文件,增加:
# 可以监控所有服务
management:
  endpoints:
    web:
      exposure:
        include: '*'

通过访问 localhost:9000/actuator/ 访问到当前provider actuator监听的服务。
在这里插入图片描述

  1. 在controller中,加上注解 @RefreshScope 注解,表示配置文件更新后,更新此类中的配置信息。
  2. 更改码云上的provider.yml中client.name属性的值后,通过postman工具模拟post请求,发送 http://localhost:9000/actuator/refresh ,出现version和client.name表示已经更新,重新访问可以发现已经刷新了配置文件

2. Spring Cloud Bus

Spring Cloud Bus通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其他的消息指令。Spring Cloud Bus的一个核心思想是通过分布式的启动器Spring Boot应用进行扩展,也可以用来建立一个多个应用之间的通信频道。目前唯一实现的方式使用AMQP消息代理作为通道,同样特性的设置(有些取决于通道的设置)在更多通道的文档中。

Spring Cloud Bus被国内很多都翻译为消息总线,可以将它理解为管理和传播所有分布式项目中的消息即可,其实本质是利用了MQ的广播机制在分布式的系统中传播消息,目前常用的有Kafka和RabbitMQ。利用bus的机制可以做很多的事情,其中配置中心客户端刷新就是典型的应用场景之一,我们用一张图来描述bus在配置中心使用的机制。
在这里插入图片描述
根据此图可以看出利用Spring Cloud Bus做配置更新的步骤:

  1. 提交代码触发post给客户端A发送bus/refresh
  2. 客户端A接收到请求从Server端更新配置并且发送给Spring Cloud Bus
  3. Spring Cloud Bus接收到消息并通知给其他客户端
  4. 其他客户端接收到通知,请求Server端获取最新配置
  5. 全部客户端均获取到最新的配置

3. 使用RabbitMQ

  1. 改造客户端:

在provider和consumer的pom.xml文件中都添加:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

在provider和consumer的bootstrap.yml中都配置成

spring:
  cloud:
    config:
      name: provider # 或者consumer
      profile: dev
      label: master
      # uri: http://localhost:9900/
      discovery:
        enabled: true
        service-id: CLD-CONFIG-CENTER
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8008/eureka/
# 可以监控所有服务
management:
  endpoints:
    web:
      exposure:
        include: '*'

在provider和consumer的Controller中添加注解@RefreshScop。
重启之后,通过访问 http://localhost:9000/actuator/bus-refresh刷新配置,即可刷新配置文件。

4. 改进版本

在上面的流程中,我们已经达到了利用消息总线触发一个客户端bus/refresh,而刷新所有客户端的配置的目的。但这种方式并不优雅。原因如下:

  • 打破了微服务的职责单一性。微服务本身是业务模块,他本不应该承担配置刷新的职责。
  • 破坏了微服务各个节点的对等性。
  • 有一定的局限性。例如,微服务在迁移时,他的网络地址常常会发生变化,此时如果想要做到自动刷新,那就不得不修改web Hook的配置。

因此我们将上面的架构模式稍微改变一下
在这里插入图片描述
这时Spring Cloud Bus做的配置更新步骤如下:

  1. 提交代码触发post请求给bus/refresh
  2. server端接收到请求并发送给Spring Cloud Bus
  3. Spring CLoud Bus接收到消息并通知给其他客户端
  4. 其他客户端接收到通知,请求Server端获取最新配置
  5. 全部客户端均获取到最新的配置

升级改造

  1. 配置中心工程的pom文件中增加:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 配置中心中的yml配置
server:
  port: 9900
spring:
  application:
    name: cld-config-center
  cloud:
    config:
      server:
        git:
          # 配置git仓库的地址
          uri: https://gitee.com/li_dx/spring-cloud-config.git
          # git仓库地址下的相对地址
          search-paths: [SpringCloudConfig]
          # git仓库的帐号
          #username:
          # git仓库的密码
          # password:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8008/eureka/
# 可以监控所有服务
management:
  endpoints:
    web:
      exposure:
        include: '*'
  1. 客户端工程中可以去掉对spring-boot-starter-actuator的依赖,及相关配置。
  2. 重启服务工程,通过发送 http://localhost:9900/actuator/bus-refresh刷新配置。

测试:
向配置中心工程中发送刷新命令:
在这里插入图片描述

七、微服务网关Zuul

1. 微服务网关介绍

前面介绍了Eureka用于服务的注册与发现,Feign支持服务的调用以及负载均衡,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,似乎一个微服务框架已经完成了。

但是在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API的网关根据请求的URL,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分布给后台服务端。

在微服务架构模式下后端服务的实例数一般是动态的,对于客户端而言很难发现动态改变的服务实例的访问地址信息。因此在基于微服务的项目中为了简化前端的调用逻辑,通常会引入API Gateway作为轻量级网关,同时API Gateway中也会实现相关的认证逻辑从而简化内部服务之间相互调用的复杂度。

2. Spring Cloud Zuul

在Spring Cloud体系中,Spring Cloud Zuul就是提供负载均衡、反向代理、权限认证的一个API Gateway。Spring Cloud Zuul路由是微服务架构的不可或缺的一部分,提供动态路由,监控,弹性,安全等边缘服务。Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器。

3. 简单使用

  1. 创建一个新的maven模块zuul,pom文件中添加:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
  1. 启动类
@SpringBootApplication
@EnableZuulProxy // 支持网关路由
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }
}
  1. application.yml配置文件
spring:
  application:
    name: cld-gate-way
server:
  port: 8888

# 配置路由信息
zuul:
  routes:
    consumer:
      path: /consumer/**
      url: http://localhost:9001/user/
  1. 启动工程测试
http://localhost:8888/consumer/sayHello

可以发现,把path中的**作为请求转发给consumer,再把结果作为响应返回给zuul转发给浏览器了。

4. 网关服务化

通过URL映射的方式来实现zuul的转发有局限性,比如每增加一个服务就需要配置一条内容,另外后端的服务如果是动态来提供,是集群的话,就不能使用这种方案来配置了。

实际上在实现微服务架构时,服务名与服务实例地址的关系在eureka server中已经存在了,所以只需要将Zuul注册到eureka server上去发现其他服务,就可以实现对serviceId的映射。

还是对cld-gate-way进行改造。

  1. 添加依赖

pom.xml(zull)

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

添加对eureka的支持

  1. 修改配置文件
spring:
  application:
    name: cld-gate-way
server:
  port: 8888

# 配置路由信息
zuul:
  routes:
    baidu:
      path: /bd/**
      url: http://www.baidu.com
    customer:
      path: /customer/**
      url: CLD-CUSTOMER-SERVICE # 指定路由的服务
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8008/eureka/

在application.yml中增加Eureka的配置信息。将路由的URL改为serviceId

  1. 测试
http://localhost:8888/customer/user/sayHello

5. 默认路由规则

如果后台服务多达十几个的时候,每一个都这样配置也挺麻烦的,Spring CLoud Zuul已经帮我们做了默认配置。默认情况下,Zuul会代理所有注册到Eureka Server的微服务,并且Zuul的路由规则如下:
http://ZUUL_HOST:ZUUL_PORT/微服务在Eureka上的serviceId/**会被转发到serviceId对应的微服务:

http://localhost:8888/cld-customer-service/user/sayHello

注意:服务名在路由中用小写字母。

6. 过滤器

其实Zuul还有更多的应用场景,比如:鉴权、流量转发、请求统计等等,这些功能都可以使用Zuul来实现。
Filter是Zuul的核心,用来实现对外服务的控制。
Fileter的生命周期有4个,分别是
“PRE(前处理)”、“ROUTING(路由中)”、“POST(后处理)”、“ERROR(错误)”,整个生命周期可以用下图来表示:
在这里插入图片描述

Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对于请求的典型生命周期:

  • PRE:这种过滤器在请求被路由之前调用。我们可以利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用ApacheHttpClient或Netflix Ribbon请求微服务。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从未服务发送给客户端等。
  • ERROR:在其他阶段发生错误时执行该过滤器。除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

6.1 自定义Filter

实现自定义Filter,需要继承ZuulFilter的类,并且覆盖其中的4个方法:
MyFilter.java

package com.ttc.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;

public class MyFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return "pre"; // 定义filter的类型,有pre、routing、post、error四种
    }

    @Override
    public int filterOrder() {
        return 10; // 定义filter的顺序,数字越小表示顺序越高,越先执行
    }

    @Override
    public boolean shouldFilter() {
        return true; // 表示是否需要执行该filter,true-执行,false表示不执行
    }

    @Override
    public Object run() throws ZuulException {
        return null; // filter需要执行的具体操作
    }
}

6.2 自定义Filter示例

假设一个场景:因为服务网关应对的是外部的所有请求,为了避免产生安全隐患,我们需要对请求做一定的限制,比如请求中含有Token便让其继续往下走,如果请求不带Token就直接返回并给出提示。

首先自定义一个Filter,在run()方法中验证参数是否含有Token。

package com.ttc.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class MyFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return "pre"; // 定义filter的类型,有pre、routing、post、error四种
    }

    @Override
    public int filterOrder() {
        return 10; // 定义filter的顺序,数字越小表示顺序越高,越先执行
    }

    @Override
    public boolean shouldFilter() {
        return true; // 表示是否需要执行该filter,true-执行,false表示不执行
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        
        String token = request.getParameter("token");
        
        if(StringUtils.isNotBlank(token)) {
            ctx.setSendZuulResponse(true); // 对请求进行路由
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);
            return null;
        } else {
            ctx.setSendZuulResponse(false); // 不进行路由
            ctx.setResponseStatusCode(400);
            ctx.setResponseBody("token is empty");
            ctx.set("isSuccess", false);
            return null;
        }
    }
}

测试

http://localhost:8888/customer/user/sayHello
http://localhost:8888/customer/user/sayHello?token="888"

总结

Spring Cloud涉及到的模块非常多,但是核心组件就是那五个:

  • 注册中心
  • 通讯负载均衡
  • 服务网关
  • 断路器
  • 服务配置(以及消息总线)
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值