java学习day57(Spring Cloud)Spring Cloud 微服务

news2024/11/16 11:43:10
主要课程内容
第⼀部分:微服务架构
        互联网应用架构演进
        微服务架构的体现思想及优缺点
        微服务架构的核心概念
第⼆部分: SpringCloud 概述
        Sping Cloud 是什么
        Sping Cloud 解决什么问题
        Sping Cloud 架构
第三部分:案例准备
第四部分:第⼀代 Spring Cloud 核⼼组件 ( Spring Cloud Netflix
        Eureka服务注册中心
        Ribbon负载均衡
        Hystrix熔断器
        Feign远程调用组件
        GateWay网关组件
        Config 分布式配置中心
第五部分:第⼆代 Spring Cloud 核⼼组件( Spring Cloud Alibaba
        Nacos 服务注册和配置中心
        Sentinel 分布式系统的流量防卫兵
第一部分 微服务架构
1 节 互联网应用架构演进
随着互联⽹的发展,⽤户群体逐渐扩大,⽹站的流量成倍增⻓,常规的单体架构已⽆法满⾜请求压⼒和业务的快速迭代,架构的变化势在必⾏。下⾯我们就以拉勾网的架构演进为例,从最开始的单体架构分析,⼀步步的到现在的微服务架构。
淘宝: LAMP Linux Apache MySQL PHP
1 )单体应用架构
在诞⽣之初,拉勾的⽤户量、数据量规模都⽐较⼩,项目所有的功能模块都放在一个工程中编码、
编译、打包并且部署在一个 Tomcat 容器中的架构模式就是单体应用架构,这样的架构既简单实 ⽤、便于维护,成本⼜低,成为了那个时代的主流架构⽅式。

优点:
高效开发 :项⽬前期开发节奏快,团队成员少的时候能够快速迭代
架构简单 MVC 架构,只需要借助 IDE 开发、调试即可
易于测试 :只需要通过单元测试或者浏览器完成
易于部署 :打包成单⼀可执⾏的 jar 或者打成 war 包放到容器内启动
单体架构的应用比较容易部署、测试, 在项目的初期,单体应用可以很好地运行。然而,随着需求的不断增加, 越来越多的人加入开发团队,代码库也在飞速地膨胀。慢慢地,单体应用变得越来越臃肿,可维护性、灵活性逐渐降低,维护成本越来越高。
缺点:
可靠性差 : 某个应用 Bug ,例如死循环、内存溢出等, 可能会导致整个应用的崩溃
复杂性高 : 以一个百万行级别的单体应用为例,整个项目包含的模块多、模块的边界模糊、 依赖
关系不清晰、 代码质量参差不齐、 混乱地堆砌在一起。使得整个项目非常复杂。
扩展能力受限 : 单体应用只能作为一个整体进行扩展,无法根据业务模块的需要进行伸缩。例如,应用中有的模块是计算密集型的,它需要强劲的CPU ; 有的模块则是 IO 密集型的,需要更大的内存。 由于这些模块部署在一起,不得不在硬件的选择上做出妥协。
业务量上涨之后,单体应用架构进一步丰富变化,比如应用集群部署、使用 Nginx 进行负载均衡、增加缓存服务器、增加文件服务器、数据库集群并做读写分离等,通过以上措施增强应对高并发的能力、应对一定的复杂业务场景,但依然属于单体应用架构。

2 )垂直应用架构
为了避免上⾯提到的那些问题,开始做模块的垂直划分,做垂直划分的原则是基于拉勾现有的业务
特性来做,核心目标标第⼀个是为了业务之间互不影响,第⼆个是在研发团队的壮⼤后为了提⾼效率,减少组件之间的依赖。

优点
系统拆分实现了流量分担,解决了并发问题
可以针对不同模块进⾏优化
⽅便⽔平扩展,负载均衡,容错率提⾼
系统间相互独⽴,互不影响,新的业务迭代时更加⾼效
缺点
服务之间相互调⽤,如果某个服务的端⼝或者 ip 地址发⽣改变,调⽤的系统得⼿动改变
搭建集群之后,实现负载均衡⽐较复杂,如:内⽹负载,在迁移机器时会影响调⽤⽅的路 由,导致线上故障服务之间调⽤⽅式不统⼀,基于 httpclient webservice ,接⼝协议不统⼀
服务监控不到位:除了依靠端⼝、进程的监控,调⽤的成功率、失败率、总耗时等等这些监 控指标是没有的
3 SOA 应用架构
在做了垂直划分以后,模块随之增多,维护的成本在也变⾼,⼀些通⽤的业务和模块重复的越来越
多,为了解决上⾯提到的接⼝协议不统⼀、服务⽆法监控、服务的负载均衡,引⼊了阿⾥巴巴开源的Dubbo ,⼀款⾼性能、轻量级的开源 Java RPC 框架,可以和 Spring 框架无缝集成。它提供了三⼤核⼼能⼒:⾯向接⼝的远程⽅法调⽤,智能容错和负载均衡,以及服务⾃动注册和发现。
SOA (Service-Oriented Architecture) ,即面向服务的架构。根据实际业务,把系统拆分成合适
的、独立部署的模块,模块之间相互独立(通过 Webservice/Dubbo 等技术进行通信)。
优点:分布式、松耦合、扩展灵活、可重用。
缺点:服务抽取粒度较大、服务调用方和提供方耦合度较高(接口耦合度)

4 )微服务应用架构
微服务架构可以说是 SOA 架构的一种拓展,这种架构模式下它 拆分粒度更小 、服务更独立。把应用
拆分成为一个个微小的服务,不同的服务可以使用不同的开发语言和存储,服务之间往往通过 Restful
轻量级通信。微服务架构关键在于 微小、独立、轻量级通信
微服务是在 SOA 上做的升华粒度更加细致,微服务架构强调的⼀个重点是 业务需要彻底的组件化和 服务化

微服务架构和 SOA 架构相似又不同
微服务架构和 SOA 架构很明显的一个区别就是 服务拆分粒度的不同 ,但是对于拉勾的架构发展来
说,我们所看到的 SOA 阶段其实服务拆分粒度相对来说已经比较细了(超前哦!),所以上述拉勾 SOA到拉勾微服务,从服务拆分上来说变化并不大,只是引入了相对完整的新一代Spring Cloud 微服务技术。自然,上述我们看到的都是拉勾架构演变的阶段结果,每一个阶段其实都经历了很多变化,拉勾的服务拆分其实也是走过了从粗到细,并非绝对的一步到位。
举个拉勾案例来说明 SOA 和微服务拆分粒度不同
我们在 SOA 架构的初期, 简历投递模块 人才搜索模块 都有简历内容展示的需求,只不过说可能略有区别,一开始在两个模块中各维护了一套简历查询和展示的代码;后期我们将服务更细粒度拆分,拆分出简历基础服务,那么不同模块调用这个基础服务即可。
2 节 微服务架构体现的思想及优缺点
微服务架构设计的核心思想就是 ,拆分的粒度相对比较小,这样的话单一职责、开发的耦合度
就会降低、微小的功能可以独立部署扩展、灵活性强,升级改造影响范围小。
微服务架构的优点 : 微服务架构和微服务
微服务很小,便于特定业务功能的聚焦
微服务很小,每个微服务都可以被一个小团队单独实施(开发、测试、部署上线、运维),团队合
作一定程度解耦,便于实施敏捷开发
微服务很小,便于重用和模块之间的组装
微服务很独立,那么不同的微服务可以使用不同的语言开发,松耦合
微服务架构下,我们更容易引入新技术
微服务架构的缺点
微服务架构下,分布式复杂难以管理,当服务数量增加,管理将越加复杂;
微服务架构下,分布式链路跟踪难等;
3 节 微服务架构中的核心概念
服务注册与服务发现
例如:职位搜索 -> 简历服务
服务提供者:简历服务
服务消费者:职位搜索
服务注册: 服务提供者将所提供服务的信息(服务器 IP 和端口、服务访问协议等)注册 / 登记到注册中心
服务发现: 服务消费者能够从注册中心获取到较为实时的服务列表,然后根究一定的策略选择一个
服务访问

负载均衡
负载均衡即将请求压力分配到多个服务器(应用服务器、数据库服务器等),以此来提高服务的性能、可靠性

熔断
熔断即断路保护。微服务架构中,如果下游服务因访问压力过大而响应变慢或失败,上游服务为了
保护系统整体可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。

 

链路追踪
微服务架构越发流行,一个项目往往拆分成很多个服务,那么一次请求就需要涉及到很多个服务。不同的微服务可能是由不同的团队开发、可能使用不同的编程语言实现、整个项目也有可能部署在了很多服务器上(甚至百台、千台)横跨多个不同的数据中心。所谓链路追踪,就是对一次请求涉及的很多个服务链路进行日志记录、性能监控
API 网关
微服务架构下,不同的微服务往往会有不同的访问地址,客户端可能需要调用多个服务的接口才能
完成一个业务需求,如果让客户端直接与各个微服务通信可能出现:
1 )客户端需要调用不同的 url 地址,增加了维护调用难度
2 )在一定的场景下,也存在跨域请求的问题(前后端分离就会碰到跨域问题,原本我们在后端采
Cors 就能解决,现在利用网关,那么就放在网关这层做好了)
3 )每个微服务都需要进行单独的身份认证
那么, API 网关就可以较好的统一处理上述问题, API 请求调用统一接入 API 网关层,由网关转发请
求。 API 网关更专注在安全、路由、流量等问题的处理上(微服务团队专注于处理业务逻辑即可),它的功能比如
1 )统一接入(路由)
2 )安全防护(统一鉴权,负责网关访问身份认证验证,与 访问认证中心 通信,实际认证业务逻辑交移“ 访问认证中心 处理)
3 )黑白名单(实现通过 IP 地址控制禁止访问网关功能,控制访问)
3 )协议适配(实现通信协议校验、适配转换的功能)
4 )流量管控(限流)
5 )长短链接支持
6 )容错能力(负载均衡)

 

第二部分 Spring Cloud 综述
1 Spring Cloud 是什么
[ 百度百科 ] Spring Cloud 是一系列框架的有序集合。它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 Spring Boot 的开发风格做到一键启动和部署。 Spring Cloud 并没有重复制造轮子,它只是将 目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过 Spring Boot 风格进行再封 装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统 开发工具包
Spring Cloud 是一系列框架的有序集合( Spring Cloud 是一个规范)
开发服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等
利用 Spring Boot 的开发便利性简化了微服务架构的开发(自动装配)

 

 

这里,我们需要注意, Spring Cloud 其实是一套规范,是一套用于构建微服务架构的规范,而不是一个可以拿来即用的框架(所谓规范就是应该有哪些功能组件,然后组件之间怎么配合,共同完成什么事情)。在这个规范之下第三方的Netflix 公司开发了一些组件、 Spring 官方开发了一些框架 / 组件,包括第三方的阿里巴巴开发了一套框架/ 组件集合 Spring Cloud Alibaba ,这些才是 Spring Cloud 规范的实现。
Netflix 搞了一套 ,简称 SCN
Spring Cloud 吸收了 Netflix 公司的产品基础之上自己也搞了几个组件
阿里巴巴在之前的基础上搞出了一堆微服务组件 ,Spring Cloud Alibaba SCA
3 Spring Cloud 架构
如前所述, Spring Cloud 是一个微服务相关规范,这个规范意图为搭建微服务架构提供一站式服
务, 采用组件(框架)化机制 定义一系列组件,各类组件针对性的处理微服务中的特定问题,这些组件共同来构成Spring Cloud 微服务技术栈
3.1 Spring Cloud 核心组件
Spring Cloud 生态圈中的组件,按照发展可以分为第一代 Spring Cloud 组件和第二代 Spring
Cloud 组件。
第一代 Spring Cloud Netflix
SCN
第二代 Spring Cloud (主要就是 Spring
Cloud Alibaba SCA
注册中心
Netflix Eureka
阿里巴巴 Nacos
客户端负
载均衡
Netflix Ribbon
阿里巴巴 Dubbo LB Spring Cloud
Loadbalancer
熔断器
Netflix Hystrix
阿里巴巴 Sentinel
网关
Netflix Zuul :性能一般,未来将退出
Spring Cloud 生态圈
官方 Spring Cloud Gateway
配置中心
官方 Spring Cloud Config
阿里巴巴 Nacos 、携程 Apollo
服务调用
Netflix Feign
阿里巴巴 Dubbo RPC
消息驱动
官方 Spring Cloud Stream
链路追踪
官方 Spring Cloud Sleuth/Zipkin
阿里巴巴 seata 分布式事务方案
3.2 Spring Cloud 体系结构(组件协同工作机制)

 

Spring Cloud 中的各组件协同工作,才能够支持一个完整的微服务架构。比如
注册中心负责服务的注册与发现,很好将各服务连接起来
API 网关负责转发所有外来的请求
断路器负责监控服务之间的调用情况,连续多次失败进行熔断保护。
配置中心提供了统一的配置信息管理服务 , 可以实时的通知各个服务获取最新的配置信息
4 Spring Cloud Dubbo 对比
Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,基于 RPC 调用,对于目前使用率较高的
Spring Cloud Netflix 来说,它是基于 HTTP 的,所以效率上没有 Dubbo 高,但问题在于 Dubbo 体系的组件不全,不能够提供一站式解决方案,比如服务注册与发现需要借助于Zookeeper 等实现,而 Spring Cloud Netflix则是真正的提供了一站式服务化解决方案,且有 Spring 大家族背景。
前些年, Dubbo 使用率高于 SpringCloud ,但目前 Spring Cloud 在服务化 / 微服务解决方案中已经有
了非常好的发展趋势。

 

5 Spring Cloud Spring Boot 的关系
Spring Cloud 只是利用了 Spring Boot 的特点,让我们能够快速的实现微服务组件开发,否则不使
Spring Boot 的话,我们在使用 Spring Cloud 时,每一个组件的相关 Jar 包都需要我们自己导入配置以及需要开发人员考虑兼容性等各种情况。所以Spring Boot 是我们快速把 Spring Cloud 微服务技术应用起来的一种方式。
第三部分 案例准备
1 节 案例说明
本部分我们按照普通方式模拟一个微服务之间的调用,后续我们将一步步使用 Spring Cloud 的组件对案例进行改造。
需求:

 完整业务流程图:

 

2 节 案例数据库环境准备
本次课程数据库使用 Mysql 5.7.x
商品信息表:
CREATE TABLE products(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(50), #商品名称
price DOUBLE,
flag VARCHAR(2), #上架状态
goods_desc VARCHAR(100), #商品描述
images VARCHAR(400), #商品图片
goods_stock INT, #商品库存
goods_type VARCHAR(20) #商品类型
);
3 节 案例工程
我们基于 SpringBoot 来构造工程环境,我们的工程模块关系如下所示:

 

3.1 父工程 lagou-parent
Idea 中新建 module ,命名为 lagou-parent
pom.xml
<!--父工程打包方式-->
<packaging>pom</packaging>
<!--spring boot 父启动器依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<dependencies>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--日志依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!--测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--lombok工具-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<!-- Actuator可以帮助你监控和管理Spring Boot应用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<!--打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
3.2 公共组件微服务
1) 在公共组件微服务中引入数据库驱动及 mybatis-plus

 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lagou-parent</artifactId>
<groupId>com.lagou</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lagou-service-common</artifactId>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!--pojo持久化使用-->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
2) 生成数据库实体类: com.lagou.common.pojo. Products
package com.lagou.common.pojo;
import lombok.Data;
import javax.persistence.Id;
import javax.persistence.Table;
@Data
@Table(name = "products")
public class Products {
@Id
private Integer id;
private String name;
private double price;
private String flag;
private String goodsDesc;
private String images;
private long goodsStock;
private String goodsType;
}
3.3 商品微服务
商品微服务是服务提供者,页面静态化微服务是服务的消费者
创建商品微服务 lagou-service-product ,继承 lagou-parent
1 )在商品微服务的 pom 文件中,引入公共组件坐标
<dependencies>
<dependency>
<groupId>com.lagou</groupId>
<artifactId>lagou-service-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
2 )在 yml 文件中配置端口、应用名、数据库连接等信息
server:
    port: 9000 # 后期该微服务多实例,9000(10个以内)
Spring:
    application:
        name: lagou-service-product
    datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/lagou?
useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
    username: root
    password: wu7787879
3) Mapper 接口开发
package com.lagou.product.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.lagou.common.pojo.Products;
/**
* 现在使用的Mybatis-plus组件,该组件是Mybatis的加强版
* 能够与SpringBoot进行非常友好的整合,对比Mybatis框架只有使用便捷的改变
* 没有具体功能的改变
* 具体使用:让具体的Mapper接口继承BaseMapper即可
*/
public interface ProductMapper extends BaseMapper<Products> {
}
4) serive 层开发
package com.lagou.product.service.impl;
import com.lagou.common.pojo.Products;
import com.lagou.product.mapper.ProductMapper;
import com.lagou.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
/**
* 根据商品ID查询商品对象
*/
@Override
public Products findById(Integer productId) {
return productMapper.selectById(productId);
}
}
5) controller 层开发
package com.lagou.product.controller;
import com.lagou.common.pojo.Products;
import com.lagou.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/query/{id}")
public Products query(@PathVariable Integer id){
return productService.findById(id);
}
}
6) 启动类
package com.lagou.product;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.lagou.product.mapper")
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class,args);
}
}
3.4 页面静态化微服务
1 ) 在 pom 文件中,引入公共组件依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lagou-parent</artifactId>
<groupId>com.lagou</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lagou-service-page</artifactId>
<dependencies>
<dependency>
<groupId>com.lagou</groupId>
<artifactId>lagou-service-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
2 )在 yml 文件中配置端口、应用名、数据库连接等信息
server:
port: 9100 # 后期该微服务多实例,端口从9100递增(10个以内)
Spring:
application:
name: lagou-service-page
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/lagou?
useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
username: root
password: wu7787879
3) 编写 PageController ,在 PageController 中调用商品微服务对应的 URL
package com.lagou.page.controller;
import com.lagou.common.pojo.Products;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/page")
public class PageController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/getData/{id}")
public Products findDataById(@PathVariable Integer id){
Products products =
restTemplate.getForObject("http://localhost:9000/product/query/"+id,
Products.class);
System.out.println("从lagou-service-product获得product对象:"+products);
return products;
}
}
4) 编写启动类,注入 RestTemplate
package com.lagou.page;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import java.awt.print.Pageable;
@SpringBootApplication
public class PageApplication {
public static void main(String[] args) {
SpringApplication.run(PageApplication.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
4 节 案例代码问题分析
我们在页面静态化微服务中使用 RestTemplate 调用商品微服务的商品状态接口时( Restful API
口)。在微服务 分布式集群环境 下会存在什么问题呢?怎么解决?
存在的问题:
1 )在服务消费者中,我们把 url 地址硬编码到代码中,不方便后期维护。
2 )服务提供者只有一个服务,即便服务提供者形成集群,服务消费者还需要自己实现负载均衡。
3 )在服务消费者中,不清楚服务提供者的状态。
4 )服务消费者调用服务提供者时候,如果出现故障能否及时发现不向用户抛出异常页面?
5 RestTemplate 这种请求调用方式是否还有优化空间?能不能类似于 Dubbo 那样玩?
6 )这么多的微服务统一认证如何实现?
7 )配置文件每次都修改好多个很麻烦!?
8 ....
上述分析出的问题,其实就是微服务架构中必然面临的一些问题:
1 )服务管理:自动注册与发现、状态监管
2 )服务负载均衡
3 )熔断
4 )远程过程调用
5 )网关拦截、路由转发
6 )统一认证
7 )集中式配置管理,配置信息实时自动更新
这些问题, Spring Cloud 体系都有解决方案,后续我们会逐个学习。
第四部分 第一代 Spring Cloud 核心组件
说明:上面提到网关组件 Zuul 性能一般,未来将退出 Spring Cloud 生态圈,所以我们直接讲解
GateWay ,在课程章节规划时,我们就把 GateWay 划分到第一代 Spring Cloud 核心组件这一部分了。
各组件整体结构如下:
从形式上来说,Feign 一个顶三, Feign = RestTemplate + Ribbon + Hystrix

 

1 Eureka 服务注册中心
常用的服务注册中心: Eureka Nacos Zookeeper Consul
1.1 关于服务注册中心
注意:服务注册中心本质上是为了解耦服务提供者和服务消费者。
服务消费者 --> 服务提供者
服务消费者 --> 服务注册中心 --> 服务提供者
对于任何一个微服务,原则上都应存在或者支持多个提供者(比如商品微服务部署多个实例),这
是由微服务的 分布式属性 决定的。
更进一步,为了支持弹性扩、缩容特性,一个微服务的提供者的数量和分布往往是动态变化的,也
是无法预先确定的。因此,原本在单体应用阶段常用的静态 LB 机制就不再适用了,需要引入额外的组件来管理微服务提供者的注册与发现,而这个组件就是服务注册中心。
1.1.1 注册中心实现原理

 

分布式微服务架构中,服务注册中心用于存储服务提供者地址信息、服务发布相关的属性信息,消
费者通过主动查询和被动通知的方式获取服务提供者的地址信息,而不再需要通过硬编码方式得到提供者的地址信息。消费者只需要知道当前系统发布了那些服务,而不需要知道服务具体存在于什么位置,这就是透明化路由。
1 )服务提供者启动
2 )服务提供者将相关服务信息主动注册到注册中心
3 )服务消费者获取服务注册信息:
pull 模式:服务消费者可以主动拉取可用的服务提供者清单
push 模式:服务消费者订阅服务(当服务提供者有变化时,注册中心也会主动推送更新后的
服务清单给消费者
4 )服务消费者直接调用服务提供者
另外,注册中心也需要完成服务提供者的健康监控,当发现服务提供者失效时需要及时剔除;
1.1.2 主流服务中心对比
Zookeeper
Dubbo + Zookeeper
Zookeeper 它是一个分布式服务框架,是 Apache Hadoop 的一个子项目,它主要是用来解决
分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布
式应用配置项的管理等。
简单来说 zookeeper 本质 = 存储 + 监听通知。
Zookeeper 用来做服务注册中心,主要是因为它具有节点变更通知功能,只要客户端监听相
关服务节点,服务节点的所有变更,都能及时的通知到监听客户端,这样作为调用方只要使用
Zookeeper 的客户端就能实现服务节点的订阅和变更通知功能了,非常方便。另外, Zookeeper
可用性也可以,因为只要半数以上的选举节点存活,整个集群就是可用的,最少节点数为 3
Eureka
Netflix 开源,并被 Pivatal 集成到 SpringCloud 体系中,它是基于 RestfulAPI 风格开发的服务
注册与发现组件。
Consul
Consul 是由 HashiCorp 基于 Go 语言开发的支持多数据中心分布式高可用的服务发布和注册服
务软件, 采用 Raft 算法保证服务的一致性,且支持健康检查。
Nacos
Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。简单来说
Nacos 就是 注册中心 + 配置中心的组合,帮助我们解决微服务开发必会涉及到的服务注册 与发
现,服务配置,服务管理等问题。 Nacos Spring Cloud Alibaba 核心组件之一,负责服务注册
与发现,还有配置。
组件名
语言
CAP
对外暴露接口
Eureka
Java
AP (自我保护机制,保证可用)
HTTP
Consul
Go
CP
HTTP/DNS
Zookeeper
Java
CP
客户端
Nacos
Java
支持 AP/CP 切换
HTTP
CAP 定理又称 CAP 原则,指的是在一个分布式系统中, Consistency (一致性)、 Availability (可用性)、Partition tolerance (分区容错性),最多只能同时三个特性中的两个,三者不可兼得。
P :分区容错性:分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性或可用性的服务(一定的要满足的)
C :数据一致性: all nodes see the same data at the same time
A :高可用: Reads and writes always succeed
CAP 不可能同时满足三个,要么是 AP ,要么是 CP
1.2 服务注册中心组件 Eureka
服务注册中心的一般原理、对比了主流的服务注册中心方案,目光聚焦 Eureka
Eureka 基础架构

 Eureka 交互流程及原理

 

Eureka 包含两个组件: Eureka Server Eureka Client Eureka Client 是一个 Java 客户端,用于简化与Eureka Server 的交互; Eureka Server 提供服务发现的能力,各个微服务启动时,会通过 Eureka Client向 Eureka Server 进行注册自己的信息(例如网络信息), Eureka Server 会存储该服务的信息;
1 )图中 us-east-1c us-east-1d us-east-1e 代表不同的区也就是不同的机房
2 )图中每一个 Eureka Server 都是一个集群。
3 )图中 Application Service 作为服务提供者向 Eureka Server 中注册服务, Eureka Server 接受到注
册事件会在集群和分区中进行数据同步, Application Client 作为消费端(服务消费者)可以从 Eureka Server中获取到服务注册信息,进行服务调用。
4 )微服务启动后,会周期性地向 Eureka Server 发送心跳( 默认周期为 30 ,默认 Eureka Server
90S 会将还没有续约的给剔除)以续约自己的信息
5 Eureka Server 在一定时间内没有接收到某个微服务节点的心跳, Eureka Server 将会注销该微
服务节点(默认 90 秒)
6 )每个 Eureka Server 同时也是 Eureka Client ,多个 Eureka Server 之间通过复制的方式完成服务
注册列表的同步
7 Eureka Client 会缓存 Eureka Server 中的信息。即使所有的 Eureka Server 节点都宕掉,服务消
费者依然可以使用缓存中的信息找到服务提供者
Eureka 通过心跳检测、健康检查和客户端缓存等机制,提高系统的灵活性、可伸缩性和高可用性。
1.3 搭建单例 Eureka Server 服务注册中心
实现过程:
1. 单实例 Eureka Server—> 访问管理界面
2. 服务提供者(商品微服务注册到集群)
3. 服务消费者(页面静态化微服务注册到 Eureka/ Eureka Server 获取服务信息)
4. 完成调用
1 、搭建 Eureka Server 服务 lagou-cloud-eureka
lagou-parent 中引入 Spring Cloud 依赖

 Spring Cloud 是一个综合的项目,下面有很多子项目,比如eureka子项目

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2 lagou-cloud-eureka 工程 pom.xml 中引入依赖
<dependencies>
<!--Eureka server依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
注意:在父工程的 pom 文件中手动引入 jaxb jar ,因为 Jdk9 之后默认没有加载该模块, Eureka
Server 使用到,所以需要手动导入,否则 EurekaServer 服务无法启动
父工程:
<!--引入Jaxb,开始-->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.2.10-b140310.1920</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!--引入Jaxb,结束-->
3 、在 yml 文件中配置 Eureka server 服务端口,服务名等信息
#Eureka server服务端口
server:
port: 9200
spring:
application:
name: lagou-cloud-eureka-server # 应用名称,会在Eureka中作为服务的id标识
(serviceId)
eureka:
instance:
hostname: localhost
client:
service-url: # 客户端与EurekaServer交互的地址,如果是集群,也需要写其它Server的地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
register-with-eureka: false # 自己就是服务不需要注册自己
fetch-registry: false #自己就是服务不需要从Eureka Server获取服务信息,默认为true,置
为false
4 、编写启动类,声明当前服务为 Eureka 注册中心
package com.lagou.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
 声明本项目是一个Eureka服务
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
5 、访问 http://127.0.0.1:9200 ,如果看到如下页面( Eureka 注册中心后台),则表明 EurekaServer 发布成功

 

 

 6、商品微服务和页面静态化微服务注册到Eureka

pom 文件中添加 Eureka Client 依赖
<!--Eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yml 配置 Eureka 服务端信息
eureka:
    client:
        serviceUrl: # eureka server的路径
            defaultZone: http://localhost:9200/eureka/
    instance:
        #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip)
        prefer-ip-address: true
        #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
        instance-id:        ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
修改启动类
@SpringBootApplication
@EnableDiscoveryClient //@EnableEurekaClient
public class PageApplication {
public static void main(String[] args) {
SpringApplication.run(PageApplication.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
1.4 搭建 Eureka Server 高可用集群
在互联网应用中,服务实例很少有单个的。
如果 EurekaServer 只有一个实例,该实例挂掉,正好微服务消费者本地缓存列表中的服务实例也不
可用,那么这个时候整个系统都受影响。
在生产环境中,我们会配置 Eureka Server 集群实现高可用。 Eureka Server 集群之中的节点通过点
对点( P2P )通信的方式共享服务注册表。我们开启两台 Eureka Server 以搭建集群。
由于是在个人计算机中进行测试很难模拟多主机的情况, Eureka 配置 server 集群时需要执行 host
址。 所以需要修改个人电脑中 host 地址 :
win10 操作系统下: C:\Windows\System32\drivers\etc\host
127.0.0.1 LagouCloudEurekaServerA
127.0.0.1 LagouCloudEurekaServerB
lagou-cloud-eureka 复制一份为 lagou-cloud-eureka9201
1 、修改 lagou-cloud-eureka-server 工程中的 yml 配置文件
9200:
#Eureka server服务端口
server:
    port: 9200
spring:
    application:
        name: lagou-cloud-eureka-server # 应用名称,会在Eureka中作为服务的id标识
(serviceId)
eureka:
    instance:
        hostname: LagouCloudEurekaServerA
    client:
        register-with-eureka: true
        fetch-registry: true
        serviceUrl:
            defaultZone: http://LagouCloudEurekaServerB:9201/eureka
9201:
#Eureka server服务端口
server:
    port: 9201
spring:
    application:
        name: lagou-cloud-eureka-server # 应用名称,会在Eureka中作为服务的id标识
(serviceId)
eureka:
    instance:
        hostname: LagouCloudEurekaServerB
    client:
    register-with-eureka: true
    fetch-registry: true
    serviceUrl:
        defaultZone: http://LagouCloudEurekaServerA:9200/eureka
商品微服务:
server:
    port: 9000 # 后期该微服务多实例,9000(10个以内)
Spring:
    application:
        name: lagou-service-product
    datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/lagou?
useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
    username: root
    password: wu7787879
eureka:
    client:
        serviceUrl: # eureka server的路径
        defaultZone:
http://lagoucloudeurekaservera:9200/eureka/,http://lagoucloudeurekaserverb:9201/
eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server
可以同步注册表
    instance:
        #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip)
        prefer-ip-address: true
        #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
        instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
页面静态化微服务:
server:
    port: 9100 # 后期该微服务多实例,端口从9100递增(10个以内)
Spring:
    application:
        name: lagou-service-page
    datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/lagou?
useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
        username: root
        password: wu7787879
eureka:
    client:
        serviceUrl: # eureka server的路径
            defaultZone:
http://lagoucloudeurekaservera:9200/eureka/,http://lagoucloudeurekaserverb:9201/
eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server
可以同步注册表
    instance:
        #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip)
        prefer-ip-address: true
        #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
        instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
服务消费者调用服务提供者
改造页面静态化微服务:之前是直接通过 RestTemplate 写死 URL 进行调用,现在通过 Eureka 方式进行调用。
@RestController
@RequestMapping("/page")
public class PageController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("/getData/{id}")
public Products findDataById(@PathVariable Integer id){
//1.获得Eureka中注册的lagou-service-product实例集合
List<ServiceInstance> instances = discoveryClient.getInstances("lagouservice-product");
//2.获得实例集合中的第一个
ServiceInstance instance = instances.get(0);
//3.根据实例信息拼接IP地址
String host = instance.getHost();
int port = instance.getPort();
String url = "http://"+host+":"+port+"/product/query/"+id;
//4.调用
Products products = restTemplate.getForObject(url, Products.class);
System.out.println("从lagou-service-product获得product对象:"+products);
return products;
}
}
1.5 Eureka 细节详解
1.5.1 Eureka 元数据详解
Eureka 的元数据有两种:标准元数据和自定义元数据。
标准元数据: 主机名、 IP 地址、端口号等信息,这些信息都会被发布在服务注册表中,用于服务之
间的调用。
自定义元数据: 可以使用 eureka.instance.metadata-map 配置,符合 KEY/VALUE 的存储格式。这
些元数据可以在远程客户端中访问。
类似于
    instance:
        #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip)
        prefer-ip-address: true
        #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
        instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
        metadata-map:
            ip: 192.168.200.128
            port: 10000
            user: YuanJing
            pwd: 123456
我们可以在程序中可以使用 DiscoveryClient 获取指定微服务的所有元数据信息
package com.lagou.page.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import java.util.Set;
@RestController
@RequestMapping("/metadata")
public class MetadataController {
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("show")
public String showMetadata(){
String result = "";
List<ServiceInstance> instances = discoveryClient.getInstances("lagouservice-page");
for (ServiceInstance instance:instances) {
//获取服务元数据
Map<String, String> metadata = instance.getMetadata();
Set<Map.Entry<String, String>> entries = metadata.entrySet();
for (Map.Entry<String,String> entry : entries){
String key = entry.getKey();
String value = entry.getValue();
result+="key:"+key+",value:"+value;
}
}
return result;
}
}
debug 下查看元数据:

 

 

1.5.2 Eureka 客户端详解
服务提供者(也是 Eureka 客户端)要向 EurekaServer 注册服务,并完成服务续约等工作
服务注册详解(服务提供者)
1 )当我们导入了 eureka-client 依赖坐标,配置 Eureka 服务注册中心地址
2 )服务在启动时会向注册中心发起注册请求,携带服务元数据信息
3 Eureka 注册中心会把服务的信息保存在 Map 中。
服务续约详解(服务提供者)
服务每隔 30 秒会向注册中心续约 ( 心跳 ) 一次(也称为报活),如果没有续约,租约在 90 秒后到期,
然后服务会被失效。每隔 30 秒的续约操作我们称之为心跳检测
Eureka Client 30S 续约一次,在 Eureka Server 更新自己的状态 (Client 端进行配置 )
Eureka Server 90S 还没有进行续约,将该微服务实例从服务注册表( Map )剔除 (Client 端进行
配置 )
Eureka Client 30S 拉取服务最新的注册表并缓存到本地 (Client 端进行配置 )
往往不需要我们调整这两个配置
#向Eureka服务中心集群注册服务
eureka:
instance:
# 租约续约间隔时间,默认30秒
lease-renewal-interval-in-seconds: 30
# 租约到期,服务时效时间,默认值90秒,服务超过90秒没有发生心跳,EurekaServer会将服务从列
表移除
lease-expiration-duration-in-seconds: 90
获取服务列表(服务注册表)详解(服务消费者)
每隔 30 秒服务会从注册中心中拉取一份服务列表,这个时间可以通过配置修改。往往不需要我们调
#向Eureka服务中心集群注册服务
eureka:
client:
# 每隔多久拉取一次服务列表
registry-fetch-interval-seconds: 30
1 )服务消费者启动时,从 EurekaServer 服务列表获取只读备份,缓存到本地
2 )每隔 30 秒,会重新获取并更新数据
3 )每隔 30 秒的时间可以通过配置 eureka.client.registry-fetch-interval-seconds 修改
1.5.3 Eureka 服务端详解
服务下线
1 )当服务正常关闭操作时,会发送服务下线的 REST 请求给 EurekaServer
2 )服务中心接受到请求后,将该服务置为下线状态
失效剔除:
Eureka Server 会定时(间隔值是 eureka.server.eviction-interval-timer-in-ms ,默认 60s )进行检
查,如果发现实例在在一定时间(此值由客户端设置的 eureka.instance.lease-expiration-duration-in-seconds定义,默认值为 90s )内没有收到心跳,则会注销此实例。
自我保护机制:
自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使 Eureka 集群更加的 健壮、稳定的运行。
自我保护机制的工作机制是: 如果在 15 分钟内超过 85% 的客户端节点都没有正常的心跳,那么
Eureka 就认为客户端与注册中心出现了网络故障, Eureka Server 自动进入自我保护机制 ,此时会出现以下几种情况:
1. Eureka Server 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
2. Eureka Server 仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
3. 当网络稳定时,当前 Eureka Server 新的注册信息会被同步到其它节点中。
因此 Eureka Server 可以很好的应对因网络故障导致部分节点失联的情况,而不会像 ZK 那样如果有一半不可用的情况会导致整个集群不可用而变成瘫痪。
为什么会有自我保护机制?
默认情况下,如果 Eureka Server 在一定时间内(默认 90 秒)没有接收到某个微服务实例的心跳,
Eureka Server 将会移除该实例。但是当网络分区故障发生时,微服务与 Eureka Server 之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。
服务中心页面会显示如下提示信息

 

我们在单机测试的时候很容易满足心跳失败比例在 15 分钟之内低于 85% ,这个时候就会触发 Eureka的保护机制,一旦开启了保护机制(默认开启 ),则服务注册中心维护的服务实例就不是那么准确了,此时我们通过修改Eureka Server 的配置文件来关闭保护机制,这样可以确保注册中心中不可用的实例被及时的剔除(不推荐 )。
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
经验:建议生产环境打开自我保护机制
2 Ribbon 负载均衡
2.1 关于负载均衡
负载均衡一般分为 服务器端负载均衡 客户端负载均衡
所谓 服务器端负载均衡 ,比如 Nginx F5 这些,请求到达服务器之后由这些负载均衡器根据一定的
算法将请求路由到目标服务器处理。
所谓 客户端负载均衡 ,比如我们要说的 Ribbon ,服务消费者客户端会有一个服务器地址列表,调用方在请求前通过一定的负载均衡算法选择一个服务器进行访问,负载均衡算法的执行是在请求客户端进行。
Ribbon Netflix 发布的负载均衡器。 Eureka 一般配合 Ribbon 进行使用, Ribbon 利用从 Eureka 中读
取到服务信息,在调用服务提供者提供的服务时,会根据一定的算法进行负载。
2.2 Ribbon 高级应用
需求:
复制商品微服务 9001 ,在 9000 9001 编写 Controller ,返回服务实例端口。
Page 微服务中通过负载均衡策略调用 lagou-service-product controller

 

在微服务中使用 Ribbon 不需要额外导入依赖坐标,微服务中引入过 eureka-client 相关依赖,会自动引入Ribbon相关依赖坐标。
代码中使用如下,在 RestTemplate 上添加对应注解即可
@SpringBootApplication
@EnableDiscoveryClient //@EnableEurekaClient
public class PageApplication {
public static void main(String[] args) {
SpringApplication.run(PageApplication.class,args);
}
@Bean
//Ribbon负载均衡
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
创建 lagou-serivce-product-9001 微服务,创建 ServerConfigController ,定义方法返回当前微服务 所使用的容器端口号
修改服务提供者 api 返回值,返回当前实例的端口号,便于观察负载情况
@RestController
@RequestMapping("/server")
public class ServerConfigController {
@Value("${server.port}")
private String serverPort;
@requestMapping("query")
public String findServerPort(){
return serverPort;
}
}
在页面静态化微服务中调用 lagou-server-product 下的资源路径: http://lagou-server-product/ser
ver/query
@RequestMapping("/getPort")
public String getProductServerPort(){
String url = "http://lagou-service-product/server/query";
return restTemplate.getForObject(url,String.class);
}
2.3 Ribbon 负载均衡策略
Ribbon 内置了多种负载均衡策略,内部负责复杂均衡的顶级接口为 com.netflix.loadbalancer.IRule
接口简介:
package com.netflix.loadbalancer;
/**
* Interface that defines a "Rule" for a LoadBalancer. A Rule can be thought of
* as a Strategy for loadbalacing. Well known loadbalancing strategies include
* Round Robin, Response Time based etc.
*
* @author stonse
*
*/
public interface IRule{
/*
* choose one alive server from lb.allServers or
* lb.upServers according to key
*
* @return choosen Server object. NULL is returned if none
* server is available
*/
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}

 

负载均衡策略
描述
RoundRobinRule :轮询策
默认超过 10 次获取到的 server 都不可用,会返回一个空的 server
RandomRule :随机策略
如果随机到的 server null 或者不可用的话,会 while 不停的循环选
RetryRule :重试策略
一定时限内循环重试。默认继承 RoundRobinRule ,也支持自定义
注入, RetryRule 会在每次选取之后,对选举的 server 进行判断,是
否为 null ,是否 alive ,并且在 500ms 内会不停的选取判断。而
RoundRobinRule 失效的策略是超过 10 次, RandomRule 是没有失
效时间的概念,只要 serverList 没都挂。
BestAvailableRule :最小
连接数策略
遍历 serverList ,选取出可用的且连接数最小的一个 server 。该算法
里面有一个 LoadBalancerStats 的成员变量,会存储所有 server
运行状况和连接数。如果选取到的 server null ,那么会调用
RoundRobinRule 重新选取。
AvailabilityFilteringRule
可用过滤策略
扩展了轮询策略,会先通过默认的轮询选取一个 server ,再去判断
server 是否超时可用,当前连接数是否超限,都成功再返回。
ZoneAvoidanceRule :区
域权衡策略 (默认策略)
扩展了轮询策略,继承了 2 个过滤器: ZoneAvoidancePredicate
AvailabilityPredicate ,除了过滤超时和链接数过多的 server ,还会
过滤掉不符合要求的 zone 区域里面的所有节点, 在一个区域 / 机房
内的服务实例中轮询。 先过滤再轮询
修改负载均衡策略:
#针对的被调用方微服务名称,不加就是全局生效
lagou-service-product:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随机策略
lagou-service-product:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #轮询策略
2.4 Ribbon 核心源码剖析
Ribbon 工作原理:

老规矩:SpringCloud充分利用了SpringBoot的自动装配特点,找spring.factories配置文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration

 

 

LoadBalancerAutoConfiguration 类中配置
装配验证:

 自动注入:

 

注入 restTemplate 定制器:
retTemplate 对象设置 loadBalancerInterceptor

 

到这里,我们明白,添加了注解的 RestTemplate 对象会被添加一个拦截器, LoadBalancerInterceptor , 该拦截器就是后续拦截请求进行负载处理的。
3 Hystrix 熔断器
属于一种容错机制
3.1 微服务中的雪崩效应
当山坡积雪内部的内聚力抗拒不了它所受到的重力拉引时,便向下滑动,引起大量雪体崩塌,人们
把这种自然现象称作雪崩。
微服务中,一个请求可能需要多个微服务接口才能实现,会形成复杂的调用链路。
服务雪崩效应: 是一种因 服务提供者的不可用 (原因)导致 服务调用者不可用 (结果),并将不可用逐渐放大的现象。

 

 

 

扇入:代表着该微服务被调用的次数,扇入大,说明该模块复用性好
扇出:该微服务调用其他微服务的个数,扇出大,说明业务逻辑复杂
扇入大是一个好事,扇出大不一定是好事
在微服务架构中,一个应用可能会有多个微服务组成,微服务之间的数据交互通过远程过程调用完
成。这就带来一个问题,假设微服务 A 调用微服务 B 和微服务 C ,微服务 B 和微服务 C 又调用其它的微服务,这就是所谓的“ 扇出 。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务 A 的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“ 雪崩效应
如图中所示,最下游 商品微服务 响应时间过长,大量请求阻塞,大量线程不会释放,会导致服务器
资源耗尽,最终导致上游服务甚至整个系统瘫痪。
形成原因:
服务雪崩的过程可以分为三个阶段:
1. 服务提供者不可用
2. 重试加大请求流量
3. 服务调用者不可用
服务雪崩的每个阶段都可能由不同的原因造成:

 

3.2 雪崩效应解决方案
从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段;
下面,我们介绍三种技术手段应对微服务中的雪崩效应,这三种手段都是从系统可用性、可靠性角
度出发,尽量防止系统整体缓慢甚至瘫痪。
服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制。我们在各种场景下都会接触到熔断这两个
字。高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。股票交易中,如果股票指数过高,也会采用熔断机制,暂停股票的交易。同样,在微服务架构中,熔断机制也是起着类似的作用。当扇出链路的某个微服务不可用或者响应时间太长时,熔断该节点微服务的调用,进行服务的降级,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
注意:
1 )服务熔断重点在 ,切断对下游服务的调用
2 )服务熔断和服务降级往往是一起使用的, Hystrix 就是这样。
服务降级
通俗讲就是整体资源不够用了,先将一些不关紧的服务停掉(调用我的时候,给你返回一个预留的
值,也叫做 兜底数据 ),待渡过难关高峰过去,再把那些服务打开。
服务降级一般是从整体考虑,就是当某个服务熔断之后,服务器将不再被调用,此刻客户端可以自
己准备一个本地的 fallback 回调,返回一个缺省值,这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强。
服务限流 服务降级是当服务出问题或者影响到核心流程的性能时,暂时将服务屏蔽掉,待高峰或者问题解决后再打开;但是有些场景并不能用服务降级来解决,比如秒杀业务这样的核心功能,这个时候可以结合服务限流来限制这些场景的并发/ 请求量
限流措施也很多,比如
        限制总并发数(比如数据库连接池、线程池)
        限制瞬时并发数(如nginx 限制瞬时并发连接数)
        限制时间窗口内的平均速率(如Guava RateLimiter nginx limit_req 模块,限制每秒的平均速率)
        限制远程接口调用速率、限制MQ 的消费速率等
3.3 Hystrix 简介
[ 来自官网 ] Hystrix( 豪猪 ) ,宣言 “defend your application” 是由 Net flfl ix 开源的一个延迟和容错库,用
于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。 Hystrix 主要通过以下几点实现延迟和容错。
包裹请求:使用 HystrixCommand 包裹对依赖的调用逻辑。 页面静态化微服务方法
@HystrixCommand 添加 Hystrix 控制)
跳闸机制:当某服务的错误率超过一定的阈值时, Hystrix 可以跳闸,停止请求该服务一段时间。
资源隔离: Hystrix 为每个依赖都维护了一个小型的线程池 ( 舱壁模式 ) 。如果该线程池已满, 发往该
依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
监控: Hystrix 可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝
的请求等。
回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员
自行提供,例如返回一个缺省值。
自我修复:断路器打开一段时间后,会自动进入 半开 状态(探测服务是否可用,如还是不可用,
再次退回打开状态)。
3.4 Hystrix 应用
3.4.1. 熔断处理
目的: 商品微服务长时间没有响应,服务消费者 —> 页面静态化微服务 快速失败给用户提示
引入依赖: 服务消费者工程(静态化微服务)中引入 Hystrix 依赖坐标(也可以添加在父工程中)
<!--熔断器Hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
开启熔断: 服务消费者工程(静态化微服务)的启动类中添加熔断器开启注解
@EnableCircuitBreaker
/**
* 注解简化写法
* @SpringCloudApplication =
@SpringBootApplication+@EnableDiscoveryClient+@EnableCircuitBreaker
*/
@SpringBootApplication
@EnableDiscoveryClient //@EnableEurekaClient
@EnableCircuitBreaker // 开启熔断
public class PageApplication {
public static void main(String[] args) {
SpringApplication.run(PageApplication.class,args);
}
@Bean
@LoadBalanced//Ribbon负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
定义服务降级处理方法: 业务方法上使用 @HystrixCommand fallbackMethod 属性关联到服务降
级处理方法
/**
* 提供者模拟处理超时,调用方法添加Hystrix控制
*/
// 使用@HystrixCommand注解进行熔断控制
@HystrixCommand(
// 线程池标识,要保持唯一,不唯一的话就共用了
threadPoolKey = "getProductServerPort2",
// 线程池细节属性配置
threadPoolProperties = {
@HystrixProperty(name="coreSize",value = "1"), // 线程数
@HystrixProperty(name="maxQueueSize",value="20") // 等待队列长
度
},
// commandProperties熔断的一些细节属性配置
commandProperties = {
// 每一个属性都是一个HystrixProperty
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value=
"2000")
}
)
@RequestMapping("/getPort2")
public String getProductServerPort2(){
String url = "http://lagou-service-product/server/query";
return restTemplate.getForObject(url,String.class);
}
商品微服务模拟超时操作
@RestController
@RequestMapping("/server")
public class ServerConfigController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/query")
public String findServerPort(){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return serverPort;
}
}
3.4.2 降级处理
配置 @HystrixCommand 注解,定义降级处理方法
@HystrixCommand(
// 线程池标识,要保持唯一,不唯一的话就共用了
threadPoolKey = "getProductServerPort3TimeoutFallback",
// 线程池细节属性配置
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "2"), // 线程数
@HystrixProperty(name = "maxQueueSize", value = "20") // 等待
队列长度
},
// commandProperties熔断的一些细节属性配置
commandProperties = {
// 每一个属性都是一个HystrixProperty
@HystrixProperty(name =
"execution.isolation.thread.timeoutInMilliseconds", value = "2000"),
// hystrix高级配置,定制工作过程细节
// 统计时间窗口定义
@HystrixProperty(name =
"metrics.rollingStats.timeInMilliseconds", value = "8000"),
// 统计时间窗口内的最小请求数
@HystrixProperty(name =
"circuitBreaker.requestVolumeThreshold", value = "2"),
// 统计时间窗口内的错误数量百分比阈值
@HystrixProperty(name =
"circuitBreaker.errorThresholdPercentage", value = "50"),
// 自我修复时的活动窗口长度
@HystrixProperty(name =
"circuitBreaker.sleepWindowInMilliseconds", value = "3000")
},
fallbackMethod = "myFallBack" // 回退方法
)
@RequestMapping("/getPort3")
public String getProductServerPort3() {
String url = "http://lagou-service-product/server/query";
return restTemplate.getForObject(url, String.class);
}
/**
* 定义回退方法,返回预设默认值
* 注意:该方法形参和返回值与原始方法保持一致
*/
public String myFallBack() {
return "-1"; // 兜底数据
}
3.5 Hystrix 舱壁模式
即:线程池隔离策略
如果不进行任何设置,所有熔断方法使用一个 Hystrix 线程池( 10 个线程),那么这样的话会导致问题,这个问题并不是扇出链路微服务不可用导致的,而是我们的线程机制导致的,如果方法A 的请求把 10 个线程都用了,方法2 请求处理的时候压根都没法去访问 B ,因为没有线程可用,并不是 B 服务不可用。

 

为了避免问题服务请求过多导致正常服务无法访问, Hystrix 不是采用增加线程数,而是单独的为每一个控制方法创建一个线程池的方式,这种模式叫做“ 舱壁模式 " ,也是线程隔离的手段。
3.6 Hystrix 工作流程与高级应用

 

1 )当调用出现问题时,开启一个时间窗( 10s
2 )在这个时间窗内,统计调用次数是否达到最小请求数?
如果没有达到,则重置统计信息,回到第 1
如果达到了,则统计失败的请求数占所有请求数的百分比,是否达到阈值?
        如果达到,则跳闸(不再请求对应服务)
        如果没有达到,则重置统计信息,回到第1
3 )如果跳闸,则会开启一个活动窗口(默认 5s ),每隔 5s Hystrix 会让一个请求通过 , 到达那个问题服务,看是否调用成功,如果成功,重置断路器回到第1 步,如果失败,回到第 3
/**
* 8秒钟内,请求次数达到2个,并且失败率在50%以上,就跳闸
* 跳闸后活动窗口设置为3s
*/
@HystrixCommand(
commandProperties = {
//统计窗口时间的设置
@HystrixProperty(name =
"metrics.rollingStats.timeInMilliseconds",value = "8000"),
//统计窗口内的最小请求数
@HystrixProperty(name =
"circuitBreaker.requestVolumeThreshold",value = "2"),
//统计窗口内错误请求阈值的设置 50%
@HystrixProperty(name =
"circuitBreaker.errorThresholdPercentage",value = "50"),
//自我修复的活动窗口时间
@HystrixProperty(name =
"circuitBreaker.sleepWindowInMilliseconds",value = "3000")
}
)
我们上述通过注解进行的配置也可以配置在配置文件中 :
# 配置熔断策略:
hystrix:
command:
default:
circuitBreaker:
# 强制打开熔断器,如果该属性设置为true,强制断路器进入打开状态,将会拒绝所有的请求。
默认false关闭的
forceOpen: false
# 触发熔断错误比例阈值,默认值50%
errorThresholdPercentage: 50
# 熔断后休眠时长,默认值5秒
sleepWindowInMilliseconds: 3000
# 熔断触发最小请求次数,默认值是20
requestVolumeThreshold: 2
execution:
isolation:
thread:
# 熔断超时设置,默认为1秒
timeoutInMilliseconds: 2000
基于 springboot 的健康检查观察跳闸状态(自动投递微服务暴露健康检查细节)

 

# springboot中暴露健康检查等断点接口
management:
endpoints:
web:
exposure:
include: "*"
# 暴露健康接口的细节
endpoint:
health:
show-details: always
访问健康检查接口: http://localhost:9100/actuator/health
Hystrix 线程池队列配置案例:
有一次在生产环境,突然出现了很多笔还款单被挂起,后来排查原因,发现是内部系统调用时出现了Hystrix调用异常。在开发过程中,因为核心线程数设置的比较大,没有出现这种异常。放到了测试环境,偶尔有出现这种情况。
后来调整 maxQueueSize 属性,确实有所改善。可没想到在生产环境跑了一段时间后却又出现这种了情况,此时我第一想法就是去查看maxQueueSize 属性,可是 maxQueueSize 属性是设置值了。
当时就比较纳闷了,为什么 maxQueueSize 属性不起作用,后来通过查看官方文档发现 Hystrix 还有一个queueSizeRejectionThreshold 属性,这个属性是控制队列最大阈值的,而 Hystrix 默认只配置了 5 个,因此就算我们把maxQueueSize 的值设置再大,也是不起作用的。两个属性必须同时配置
hystrix:
threadpool:
default:
coreSize: 10 #并发执行的最大线程数,默认10
maxQueueSize: 1000 #BlockingQueue的最大队列数,默认值-1
queueSizeRejectionThreshold: 800 #即使maxQueueSize没有达到,达到
queueSizeRejectionThreshold该值后,请求也会被拒绝,默认值5
正确的配置案例:
将核心线程数调低,最大队列数和队列拒绝阈值的值都设置大一点:
hystrix:
threadpool:
default:
coreSize: 10
maxQueueSize: 1500
queueSizeRejectionThreshold: 1000
4 Feign 远程调用组件
在之前的案例中,服务消费者调用服务提供者的时候使用 RestTemplate 技术。
4.1 Feign 简介
Feign Net flfl ix 开发的一个轻量级 RESTful HTTP 服务客户端(用它来发起请求,远程调用的) ,是以Java 接口注解的方式调用 Http 请求,而不用像 Java 中通过封装 HTTP 请求报文的方式直接调用,Feign被广泛应用在 Spring Cloud 的解决方案中。
类似于 Dubbo ,服务消费者拿到服务提供者的接口,然后像调用本地接口方法一样去调用,实际发
出的是远程的请求。
Feign 可帮助我们更加便捷,优雅的调用 HTTP API :不需要我们去拼接 url 然后呢调用 restTemplate
api ,在 SpringCloud 中,使用 Feign 非常简单,创建一个接口(在消费者 -- 服务调用方这一端),
并在接口上添加一些注解,代码就完成了SpringCloud对 Feign 进行了增强,使 Feign 支持了 SpringMVC 注解( OpenFeign
本质:封装了 Http 调用流程,更符合 面向接口化 的编程习惯,类似于 Dubbo 的服务调用
4.2 Feign 配置应用
在服务调用者工程(消费)创建接口(添加注解)
(效果) Feign = RestTemplate+Ribbon+Hystrix
服务消费者工程(页面静态化微服务)中引入 Feign 依赖(或者父类工程)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
服务消费者工程(静态化微服务)启动类使用注解 @EnableFeignClients 添加 Feign 支持
@SpringBootApplication
@EnableDiscoveryClient // 开启服务发现
@EnableFeignClients // 开启Feign
public class PageApplication {
public static void main(String[] args) {
SpringApplication.run(PageApplication.class,args);
}
}
注意: 此时去掉 Hystrix 熔断的支持注解 @EnableCircuitBreaker 即可包括引入的依赖,因为 Feign 会自动引入在消费者微服务中创建Feign 接口

 

@FeignClient(name = "lagou-service-product")
public interface ProductFeign {
/**
* 通过id获取商品信息
* @param id
* @return
*/
@RequestMapping("/product/query/{id}")
public Products query(@PathVariable Integer id);
/**
* 获得端口号
* @return
*/
@RequestMapping("/server/query")
public String findServerPort();
}
注意:
1 @FeignClient 注解的 name 属性用于指定要调用的服务提供者名称,和服务提供者 yml 文件中
spring.application.name 保持一致
2 )接口中的接口方法,就好比是远程服务提供者 Controller 中的 Hander 方法(只不过如同本地调用了),那么在进行参数绑定的时,可以使用@PathVariable @RequestParam @RequestHeader
等,这也是 OpenFeign SpringMVC 注解的支持,但是需要注意 value 必须设置,否则会抛出异常
3) @FeignClient(name = "lagou-service-product") name 在消费者微服务中只能出现一次。(升级Spring Boot 2.1.0 Spring Cloud Greenwich.M1 版本后,在 2 Feign 接口类内定义相同的名字,
@FeignClient(name = 相同的名字 就会出现报错,在之前的版本不会提示报错),所以最好 将调用一个 微服务的信息都定义在一个 Feign 接口中
改造 PageController 中原有的调用方式
@RestController
@RequestMapping("/page")
public class PageController {
@Autowired
private ProductFeign productFeign;
@RequestMapping("/getData/{id}")
public Products findDataById(@PathVariable Integer id) {
return productFeign.query(id);
}
@RequestMapping("/getPort")
public String getProductServerPort() {
return productFeign.findServerPort();
}
}
4.3 Feign 对负载均衡的支持
Feign 本身已经集成了 Ribbon 依赖和自动配置,因此我们不需要额外引入依赖,可以通过
ribbon.xx 来进 行全局配置 , 也可以通过服务名 .ribbon.xx 来对指定服务进行细节配置配置(参考之前,此处略)
Feign 默认的请求处理超时时长 1s ,有时候我们的业务确实执行的需要一定时间,那么这个时候,我们就需要调整请求处理超时时长,Feign 自己有超时设置,如果配置 Ribbon 的超时,则会以 Ribbon 的为准
#针对的被调用方微服务名称,不加就是全局生效
lagou-service-product:
ribbon:
#请求连接超时时间
#ConnectTimeout: 2000
#请求处理超时时间
#ReadTimeout: 5000
#对所有操作都进行重试
OkToRetryOnAllOperations: true
####根据如上配置,当访问到故障请求的时候,它会再尝试访问一次当前实例(次数由
MaxAutoRetries配置),
####如果不行,就换一个实例进行访问,如果还不行,再换一次实例访问(更换次数由
MaxAutoRetriesNextServer配置),
####如果依然不行,返回失败信息。
MaxAutoRetries: 0 #对当前选中实例重试次数,不包括第一次调用
MaxAutoRetriesNextServer: 0 #切换实例的重试次数
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #负载策略
调整
4.4 Feign 对熔断器的支持
1 )在 Feign 客户端工程配置文件( application.yml )中开启 Feign 对熔断器的支持
# 开启Feign的熔断功能
feign:
hystrix:
enabled: true
Feign 的超时时长设置那其实就上面 Ribbon 的超时时长设置
Hystrix 超时设置(就按照之前 Hystrix 设置的方式就 OK 了)
注意:
1 )开启 Hystrix 之后, Feign 中的方法都会被进行一个管理了,一旦出现问题就进入对应的回退逻辑处理
2 )针对超时这一点,当前有两个超时时间设置( Feign/hystrix ), 熔断的时候是根据这两个时间的最小 值来进行的 ,即处理时长超过最短的那个超时时间了就熔断进入回退降级逻辑
hystrix:
command:
default:
circuitBreaker:
# 强制打开熔断器,如果该属性设置为true,强制断路器进入打开状态,将会拒绝所有的请求。
默认false关闭的
forceOpen: false
# 触发熔断错误比例阈值,默认值50%
errorThresholdPercentage: 50
# 熔断后休眠时长,默认值5秒
sleepWindowInMilliseconds: 3000
# 熔断触发最小请求次数,默认值是20
requestVolumeThreshold: 2
execution:
isolation:
thread:
# 熔断超时设置,默认为1秒
timeoutInMilliseconds: 5000
2 )自定义 FallBack 处理类(需要实现 FeignClient 接口)
package com.lagou.page.feign.fallback;
import com.lagou.common.pojo.Products;
import com.lagou.page.feign.ProductFeign;
import org.springframework.stereotype.Component;
@Component
public class ProductFeignFallBack implements ProductFeign {
@Override
public Products query(Integer id) {
return null;
}
@Override
public String findServerPort() {
return "-1";
}
}
@FeignClient(name = "lagou-service-product",fallback =
ProductFeignFallBack.class)
public interface ProductFeign {
/**
* 通过商品id查询商品对象
* @param id
* @return
*/
@GetMapping("/product/query/{id}")
public Products queryById(@PathVariable Integer id);
@GetMapping("/service/port")
public String getPort();
}
4.5 Feign 对请求压缩和响应压缩的支持
Feign 支持对请求和响应进行 GZIP 压缩,以减少通信过程中的性能损耗。通过下面的参数 即可开启请求与响应的压缩功能:
feign:
hystrix:
enabled: true
#开启请求和响应压缩
compression:
request:
enabled: true #默认不开启
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型,
此处也是默认值
min-request-size: 2048 # 设置触发压缩的大小下限,此处也是默认值
response:
enabled: true #默认不开启
5 GateWay 网关组件
网关:微服务架构中的重要组成部分
局域网中就有网关这个概念,局域网接收或者发送数据出去通过这个网关,比如用 Vmware 虚拟机
软件搭建虚拟机集群的时候,往往我们需要选择 IP 段中的一个 IP 作为网关地址。
我们学习的 GateWay-->Spring Cloud GateWay (它只是众多网关解决方案中的一种)
5.1 GateWay 简介
Spring Cloud GateWay Spring Cloud 的一个全新项目,目标是取代 Netflix Zuul ,它基于
Spring5.0+SpringBoot2.0+WebFlux (基于高性能的 Reactor 模式响应式通信框架 Netty ,异步非阻塞模型)等技术开发,性能高于Zuul ,官方测试, GateWay Zuul 1.6 倍,旨在为微服务架构提供一种简单有效的统一的API 路由管理方式。
Spring Cloud GateWay 不仅提供统一的路由方式(反向代理)并且基于 Filter( 定义过滤器对请求过
滤,完成一些功能 ) 链的方式提供了网关基本的功能,例如:鉴权、流量控制、熔断、路径重写、日志监控等。
网关在架构中的位置
5.2 GateWay 核心概念
Spring Cloud GateWay 天生就是异步非阻塞的,基于 Reactor 模型(同步非阻塞的 I/O 多路复用机
制)
一个请求 —> 网关根据一定的条件匹配 匹配成功之后可以将请求转发到指定的服务地址;而在这
个过程中,我们可以进行一些比较具体的控制(限流、日志、黑白名单)
路由( route ): 网关最基础的部分,也是网关比较基础的工作单元。路由由一个 ID 、一个目标
URL (最终路由到的地址)、一系列的断言(匹配条件判断)和 Filter 过滤器(精细化控制)组成。
如果断言为 true ,则匹配该路由。
断言( predicates ):参考了 Java8 中的断言 java.util.function.Predicate ,开发人员可以匹配 Http
请求中的所有内容(包括请求头、请求参数等)(类似于 nginx 中的 location 匹配一样),如果断言
与请求相匹配则路由。
过滤器(filter ):一个标准的 Spring webFilter ,使用过滤器,可以在请求之前或者之后执行业务
逻辑。
5.3 GateWay 如何工作

 

Spring 官方介绍:
客户端向 Spring Cloud GateWay 发出请求,然后在 GateWay Handler Mapping 中找到与请求相匹配的路由,将其发送到GateWay Web Handler Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前 (pre )或者之后( post )执行业务逻辑。
Filter “pre” 类型过滤器中可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在 “post” 类型的过滤器中可以做响应内容、响应头的修改、日志的输出、流量监控等。
5.4 GateWay 应用
使用网关对静态化微服务进行代理(添加在它的上游,相当于隐藏了具体微服务的信息,对外暴露的是网关)
创建工程 lagou-cloud-gateway-server 导入依赖
GateWay 不需要使用 web 模块,它引入的是 WebFlux (类似于 SpringMVC
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lagou</groupId>
<artifactId>lagou-cloud-gateway</artifactId>
<version>1.0-SNAPSHOT</version>
<!--spring boot 父启动器依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--GateWay 网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--引入webflux-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!--日志依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!--测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--lombok工具-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<!--引入Jaxb,开始-->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.2.10-b140310.1920</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!--引入Jaxb,结束-->
<!-- Actuator可以帮助你监控和管理Spring Boot应用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--链路追踪-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<!--spring cloud依赖版本管理-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<!--打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
注意:不要引入 starter-web 模块,需要引入 web-flux
application.yml 配置文件内容
server:
port: 9300
eureka:
client:
serviceUrl: # eureka server的路径
defaultZone:
http://LagouCloudEurekaServerA:9200/eureka,http://LagouCloudEurekaServerB:9201/e
ureka
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
spring:
application:
name: lagou-cloud-gateway
#网关的配置
cloud:
gateway:
routes: #配置路由
- id: service-page-router
uri: http://127.0.0.1:9100
predicates: #当断言成功后,交给某一个微服务处理时使用的是转发
- Path=/page/**
- id: service-product-router
uri: http://127.0.0.1:9000
predicates:
- Path=/product/**
filters:
# http://127.0.0.1:9300/product/service/port --> /service/port -->
商品微服务
- StripPrefix=1 #去掉uri中的第一部分,所以就要求我们通过网关访问的时候,把uri
的第一部分设置为product,从uri的第二部分开始才是真正的uri

 

 

 启动类

package com.lagou.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
测试
http://127.0.0.1:9501/page/getData/1
http://127.0.0.1:9501/product/product/query/1
5.5 GateWay 路由规则详解
Spring Cloud GateWay 帮我们内置了很多 Predicates 功能,实现了各种路由匹配规则(通过
Header 、请求参数等作为条件)匹配到对应的路由。

 时间点后匹配

spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
时间点前匹配
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
时间区间匹配
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]
指定 Cookie 正则匹配指定值
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
指定 Header 正则匹配指定值
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
请求 Host 匹配指定值
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
请求 Method 匹配指定请求方式
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
请求路径正则匹配
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
请求包含某参数
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
请求包含某参数并且参数值匹配正则表达式
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.
远程地址匹配
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
5.6 GateWay 动态路由详解
GateWay 支持自动从注册中心中获取服务列表并访问,即所谓的动态路由
实现步骤如下
1 pom.xml 中添加注册中心客户端依赖(因为要获取注册中心服务列表, eureka 客户端已经引入)
2 )动态路由配置
注意:动态路由设置时, uri lb: // 开头( lb 代表从注册中心获取服务),后面是需要转发到的服务名
5.7 GateWay 过滤器
57.1 GateWay 过滤器简介
从过滤器生命周期(影响时机点)的角度来说,主要有两个 pre post
生命周 期时机
作用
pre
这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选 择请求的微服务、记录调试信息等。
post
这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP
Header 、收集统计信息和指标、将响应从微服务发送给客户端等。
从过滤器类型的角度, Spring Cloud GateWay 的过滤器分为 GateWayFilter GlobalFilter 两种
过滤器类型
影响范围
GateWayFilter
应用到单个路由路由上
GlobalFilter
应用到所有的路由上
Gateway Filter 可以去掉 url 中的占位后转发路由,比如
predicates:
- Path=/product/**
filters:
- StripPrefix=1 # 可以去掉product之后转发
注意: GlobalFilter 全局过滤器是程序员使用比较多的过滤器,我们主要讲解这种类型
5.7.2 自定义全局过滤器实现 IP 访问限制(黑白名单)
请求过来时,判断发送请求的客户端的 ip ,如果在黑名单中,拒绝访问
自定义 GateWay 全局过滤器时,我们实现 Global Filter 接口即可,通过全局过滤器可以实现黑白名
单、限流等功能。
package com.lagou.gateway;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
/**
* 定义全局过滤器,会对所有路由生效
*/
@Slf4j
@Component // 让容器扫描到,等同于注册了
public class BlackListFilter implements GlobalFilter, Ordered {
// 模拟黑名单(实际可以去数据库或者redis中查询)
private static List<String> blackList = new ArrayList<>();
static {
blackList.add("0:0:0:0:0:0:0:1"); // 模拟本机地址
blackList.add("127.0.0.1");
}
/**
* 过滤器核心方法
* @param exchange 封装了request和response对象的上下文
* @param chain 网关过滤器链(包含全局过滤器和单路由过滤器)
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain
chain) {
System.out.println("....BlackListFilter....");
// 思路:获取客户端ip,判断是否在黑名单中,在的话就拒绝访问,不在的话就放行
// 从上下文中取出request和response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 从request对象中获取客户端ip
String clientIp = request.getRemoteAddress().getHostString();
// 拿着clientIp去黑名单中查询,存在的话就决绝访问
if(blackList.contains(clientIp)) {
// 决绝访问,返回
response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码
log.info("=====>IP:" + clientIp + " 在黑名单中,将被拒绝访问!");
String data = "Request be denied!";
DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
return response.writeWith(Mono.just(wrap));
}
// 合法请求,放行,执行后续的过滤器
return chain.filter(exchange);
}
/**
* 返回值表示当前过滤器的顺序(优先级),数值越小,优先级越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
5.8 GateWay 高可用
网关作为非常核心的一个部件,如果挂掉,那么所有请求都可能无法路由处理,因此我们需要做
GateWay 的高可用。
GateWay 的高可用很简单: 可以启动多个 GateWay 实例来实现高可用,在 GateWay 的上游使用
Nginx 等负载均衡设备进行负载转发以达到高可用的目的。
启动多个 GateWay 实例(假如说两个,一个端口 9002 ,一个端口 9003 ),剩下的就是使用 Nginx 等完成负载代理即可。示例如下:
#配置多个GateWay实例
upstream gateway {
server 127.0.0.1:9002;
server 127.0.0.1:9003;
}
location / {
proxy_pass http://gateway;
}
6 Spring Cloud Config 分布式配置中心
6.1 分布式配置中心应用场景
往往,我们使用配置文件管理一些配置信息,比如 application.yml
单体应用架构, 配置信息的管理、维护并不会显得特别麻烦,手动操作就可以,因为就一个工程;
微服务架构, 因为我们的分布式集群环境中可能有很多个微服务,我们不可能一个一个去修改配置
然后重启生效,在一定场景下我们还需要在运行期间动态调整配置信息,比如:根据各个微服务的负载情况,动态调整数据源连接池大小,我们希望配置内容发生变化的时候,微服务可以自动更新。
场景总结如下:
1 )集中配置管理,一个微服务架构中可能有成百上千个微服务,所以集中配置管理是很重要的
(一次修改、到处生效)
2 )不同环境不同配置,比如数据源配置在不同环境(开发 dev, 测试 test, 生产 prod )中是不同的
3 )运行期间可动态调整。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小等配
置修改后可自动更新
4 )如配置内容发生变化,微服务可以自动更新配置
那么,我们就需要对配置文件进行 集中式管理 ,这也是分布式配置中心的作用。
6.2 Spring Cloud Config
6.2.1 Config 简介
Spring Cloud Con fifi g 是一个分布式配置管理方案,包含了 Server 端和 Client 端两个部分。

 

Server 端:提供配置文件的存储、以接口的形式将配置文件的内容提供出去,通过使用
@EnableCon fifi gServer 注解在 Spring boot 应用中非常简单的嵌入
Client 端:通过接口获取配置数据并初始化自己的应用
6.2.2 Config 分布式配置应用
说明: Con fifi g Server 是集中式的配置服务,用于集中管理应用程序各个环境下的配置。 默认使用 Git 存储配置文件内容,也可以 SVN
比如,我们要对 静态化微服务或者商品微服务 application.yml 进行管理(区分开发环境
dev )、测试环境 (test) 、生产环境 (prod)
1 )登录 GitHub ,创建项目 lagou-config
2 )上传 yml 配置文件,命名规则如下:
{application}-{pro fifi le}.yml 或者 {application}-{pro fifi le}.properties
其中, application 为应用名称, pro fifi le 指的是环境(用于区分开发环境,测试环境、生产环境
等)
示例:lagou-service-page-dev.yml lagou-service-page-test.yml lagou-service-page
prod.yml
3 构建 Config Server 统一配置中心
新建 SpringBoot 工程,引入依赖坐标 (需要注册自己到 Eureka
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lagou-parent</artifactId>
<groupId>com.lagou</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lagou-cloud-config</artifactId>
<dependencies>
<!--eureka client 客户端依赖引入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--config配置中心服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
</project>
配置启动类,使用注解 @EnableConfigServer 开启配置中心服务器功能
package com.lagou.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;
import java.io.ObjectInputFilter;
@SpringBootApplication
@EnableConfigServer // 开启配置服务器功能
@EnableDiscoveryClient
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
application.yml 配置
server:
port: 9400
#注册到Eureka服务中心
eureka:
client:
service-url:
defaultZone:
http://LagouCloudEurekaServerA:9200/eureka,http://LagouCloudEurekaServerB:9201/e
ureka
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
spring:
application:
name: lagou-service-config
cloud:
config:
server:
git:
uri: https://github.com/jetwu-do/lagou-config.git #配置git服务地址
username: jetwu-do #配置git用户名
password: wu7787879 #配置git密码
search-paths:
- lagou-config
# 读取分支
label: master
测试
http://127.0.0.1:9600/master/application-dev.yml
master :分支名称
application-dev.yml :文件名称
4 构建 Client 客户端(在已有页面静态化微服务基础上)
案例实现:在 lagou-service-page 微服务中动态获取 config server 的配置信息
已有工程中添加依赖坐标
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
application.yml 修改为 bootstrap.yml 配置文件
bootstrap.yml 是系统级别的,优先级比 application.yml 高,应用启动时会检查这个配置文件,
在这个配置文件中指定配置中心的服务地址,会自动拉取所有应用配置并且启用。
(主要是把与统一配置中心连接的配置信息放到 bootstrap.yml
注意:需要统一读取的配置信息,从配置中心获取
bootstrap.yml (部分)
server:
port: 9100 # 后期该微服务多实例,端口从9100递增(10个以内)
Spring:
application:
name: lagou-service-page
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/lagou?
useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
username: root
password: wu7787879
cloud:
config:
#config客户端配置,和ConfigServer通信,并告知ConfigServer希望获取的配置信息在哪个文
件中
name: application
profile: dev #后缀名称
label: master #分支名称
uri: http://localhost:9400 #ConfigServer配置中心地址
ConfigController
package com.lagou.page.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/config")
public class ConfigClientController {
@Value("${mysql.user}")
private String mysqlUser;
@Value("${person.name}")
private String personName;
@RequestMapping("/remote")
public String findRemoteConfig() {
return mysqlUser + " " + personName;
}
}
启动日志:

 

测试:
http://127.0.0.1:9100/config/remote
6.3 Config 配置手动刷新
不用重启微服务,只需要手动的做一些其他的操作(访问一个地址 /refresh )刷新,之后再访问即
可此时,客户端取到了配置中心的值,但当我们修改GitHub 上面的值时,服务端( Con fifi g Server )能实时获取最新的值,但客户端(Con fifi g Client )读的是缓存,无法实时获取最新值。 Spring Cloud 已 经为我们解决了这个问题,那就是客户端使用post 去触发 refresh ,获取最新数据。
1 Client 客户端添加依赖 springboot-starter-actuator (已添加)
2 Client 客户端 bootstrap.yml 中添加配置(暴露通信端点)
management:
    endpoints:
        web:
            exposure:
            include: refresh
#也可以暴露所有的端口
management:
    endpoints:
        web:
            exposure:
                include: "*"
3 Client 客户端使用到配置信息的类上添加 @RefreshScope
package com.lagou.page.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/config")
@RefreshScope //手动刷新
public class ConfigClientController {
@Value("${mysql.user}")
private String mysqlUser;
@Value("${person.name}")
private String personName;
@RequestMapping("/remote")
public String findRemoteConfig() {
return mysqlUser + " " + personName;
}
}
4 )手动向 Client 客户端发起 POST 请求, http://localhost:9100/actuator/refresh ,刷新配置信息

 

注意:手动刷新方式避免了服务重启
思考:可否使用广播机制,一次通知,处处生效,方便大范围配置自动刷新?
6.4 Config 配置自动更新
实现一次通知,处处生效
在微服务架构中,我们可以结合消息总线(Bus )实现分布式配置的自动更新( Spring Cloud
Config + Spring Cloud Bus
6.4.1 消息总线 Bus
所谓消息总线 Bus ,即我们经常会使用 MQ 消息代理构建一个共用的 Topic ,通过这个 Topic 连接各个
微服务实例, MQ 广播的消息会被所有在注册中心的微服务实例监听和消费。 换言之就是通过一个主题 连接各个微服务,打通脉络
Spring Cloud Bus (基于 MQ 的,支持 RabbitMq/Kafka ) 是 Spring Cloud 中的消息总线方案,
Spring Cloud Config + Spring Cloud Bus 结合可以实现配置信息的自动更新。

 

6.4.2 Spring Cloud Config + Spring Cloud Bus 实现自动刷新
MQ 消息代理,我们还选择使用 RabbitMQ ConfigServer ConfigClient 都添加都消息总线的支持
以及与 RabbitMq 的连接信息
1 Config Server 服务端和客户端添加消息总线支持
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
2 Config Server 和客户端添加配置
spring:
rabbitmq:
host: 192.169.200.128
port: 5672
username: guest
password: guest
3 Config Server 微服务暴露端口
management:
endpoints:
web:
exposure:
include: bus-refresh
#建议暴露所有的端口
management:
endpoints:
web:
exposure:
include: "*"
4 )重启各个服务,更改配置之后,向配置中心服务端发送 post 请求,各个客户端配置即可自动刷新
http://127.0.0.1:9400/actuator/bus-refresh

 

 

5 Config Client 测试
http://localhost:9100/config/remote
在广播模式下实现了一次请求,处处更新,如果我只想定向更新呢?
在发起刷新请求的时候 http://localhost:9006/actuator/bus-refresh/lagou-service-page:9100
即为最后面跟上要定向刷新的实例的 服务名 : 端口号 即可
第五部分 第二代 Spring Cloud 核心组件( SCA
SpringCloud 是若干个框架的集合,包括 spring-cloud-config spring-cloud-bus 等近 20 个子项
目,提供了服务治理、服务网关、智能路由、负载均衡、断路器、监控跟踪、分布式消息队列、配置管理等领域的解决方案。Spring Cloud 通过 Spring Boot 风格的封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、容易部署的分布式系统开发工具包。一般来说,Spring Cloud 包含以下组件,主要以 Netflix 开源为主。
Spring Cloud 一样, Spring Cloud Alibaba 也是一套微服务解决方案,包含开发分布式应用微服
务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。依托Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。

 

阿里开源组件
Nacos :一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Sentinel :把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
RocketMQ :开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
Dubbo :这个就不用多说了,在国内应用非常广泛的一款高性能 Java RPC 框架。
Seata :阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
Arthas :开源的 Java 动态追踪工具,基于字节码增强技术,功能非常强大。
阿里商业化组件
作为一家商业公司,阿里巴巴推出 Spring Cloud Alibaba ,很大程度上市希望通过抢占开发者生态,来帮助推广自家的云产品。所以在开源社区,夹带了不少私货,阿里商业化组件,整体易用性和稳定性还是很高的。
Alibaba Cloud ACM :一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
Alibaba Cloud OSS :阿里云对象存储服务( Object Storage Service ,简称 OSS ),是阿里云提供的云存储服务。
Alibaba Cloud SchedulerX :阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准的定时 (基于 Cron 表达式)任务调度服务。
集成 Spring Cloud 组件
Spring Cloud Alibaba 作为整套的微服务解决组件,只依靠目前阿里的开源组件是不够的,更多的是集
成当前的社区组件,所以 Spring Cloud Alibaba 可以集成 Zuul GateWay 等网关组件,也可继承
Ribbon OpenFeign 等组件。
1 Nacos 服务注册和配置中心
1.1 Nacos 介绍
Nacos Dynamic Naming and Configuration Service )是阿里巴巴开源的一个针对微服务架构中
服务发现、配置管理和服务管理平台。
Nacos 就是注册中心 + 配置中心的组合( Nacos=Eureka + Config + Bus
官网: https://nacos.io 下载地址: https://github.com/alibaba/Nacos
Nacos 功能特性
服务发现与健康检查
动态配置管理
动态 DNS 服务
服务和元数据管理(管理平台的角度, nacos 也有一个 ui 页面,可以看到注册的服务及其实例信息
(元数据信息)等),动态的服务权重调整、动态服务优雅下线,都可以去做
1.2 Nacos 单例服务部署
下载解压安装包,执行命令启动(我们使用最近比较稳定的版本 nacos-server-1.2.0.tar.gz
linux/mac:sh startup.sh -m standalone
windows:cmd startup.cmd
访问 nacos 控制台: http://127.0.0.1:8848/nacos/#/login 或者 http://127.0.0.1:8848/nacos/ind
ex.html (默认端口 8848 ,账号和密码 nacos/nacos

 

1.3 微服务注册到 Nacos
1. 在父 pom 中引入 SCA 依赖
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--SCA -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2. 在商品服务提供者工程中引入 nacos 客户端依赖, 必须删除 eureka-client 依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
3. application.yml 修改,添加 nacos 配置信息
yml 文件中需要删除调用 config eureka 相关的配置,否则启动失败
cloud:
    nacos:
        discovery:
            server-addr: 127.0.0.1:8848 #nacos server 地址
4. 启动商品微服务,观察 nacos 控制台

 

 

保护阈值:可以设置为 0-1 之间的浮点数,它其实是一个比例值(当前服务健康实例数 / 当前服务总实例数)
场景:
        一般流程下,nacos 是服务注册中心,服务消费者要从 nacos 获取某一个服务的可用实例信息,对于服务实例有健康/ 不健康状态之分, nacos 在返回给消费者实例信息的时候,会返回健康实例。这个时候在一些高并发、大流量场景下会存在一定的问题
        如果服务A 100 个实例, 98 个实例都不健康了,只有 2 个实例是健康的,如果 nacos 只返回这两个健康实例的信息的话,那么后续消费者的请求将全部被分配到这两个实例,流量洪峰到来,2 个健康的实例也扛不住了,整个服务A 就扛不住,上游的微服务也会导致崩溃,产生雪崩效应。
        保护阈值的意义在于:       
        当服务A健康实例数/ 总实例数 < 保护阈值 的时候,说明健康实例真的不多了,这个时候保护阈值会被触发(状态true
        nacos将会把该服务所有的实例信息(健康的+ 不健康的)全部提供给消费者,消费者可能访问到不健康的实例,请求失败,但这样也比造成雪崩要好,牺牲了一些请求,保证了整个系统的一个可用。
注意:阿里内部在使用 nacos 的时候,也经常调整这个保护阈值参数。
1.4 负载均衡
        Nacos客户端引入的时候,会关联引入 Ribbon 的依赖包,我们使用 OpenFiegn 的时候也会引入 Ribbon的依赖, Ribbon 包括 Hystrix 都按原来方式进行配置即可
        此处,我们将商品微服务,再启动了一个9001 端口,注册到 Nacos 上,便于测试负载均衡,我们通过后台也可以看出。
启动:
lagou-server-page
lagou-server-product-9000
lagou-server-proudct-9001
测试:
http://localhost:9100/page/getPort
1.5 Nacos 数据模型(领域模型)
Namespace 命名空间、 Group 分组、集群这些都是为了进行归类管理,把 服务 配置文件 进行归
类,归类之后就可以实现一定的效果,比如隔离
比如,对于服务来说,不同命名空间中的服务不能够互相访问调用

 Namespace:命名空间,对不同的环境进行隔离,比如隔离开发环境、测试环境和生产环境

Group :分组,将若干个服务或者若干个配置集归为一组,通常习惯一个系统归为一个组(拉勾招聘、拉勾猎头、拉勾教育)
Service :某一个服务,比如商品微服务
DataId :配置集或者可以认为是一个配置文件
Namespace + Group + Service 如同 Maven 中的 GAV 坐标, GAV 坐标是为了锁定 Jar ,而这里是为 了锁定服务
Namespace + Group + DataId 如同 Maven 中的 GAV 坐标, GAV 坐标是为了锁定 Jar ,而这里是为 了锁定配置文件
最佳实践
        Nacos抽象出了 Namespace Group Service DataId 等概念,具体代表什么取决于怎么用(非常灵活),推荐用法如下
概念
描述
Namespace
代表不同的环境,如开发 dev 、测试 test 、生产环境 prod
Group
代表某项目,比如拉勾云项目
Service
某个项目中具体 xxx 服务
DataId
某个项目中具体的 xxx 配置文件
1.6 Nacos 配置中心
之前: Spring Cloud Config + Bus (配置的自动更新)
1) Github 上添加配置文件
2 )创建 Config Server 配置中心 —> Github 上去下载配置信息
3 )具体的微服务 ( 最终使用配置信息的 ) 中配置 Config Client—> ConfigServer 获取配置信息
Nacos 之后,分布式配置就简单很多
Github 不需要了(配置信息直接配置在 Nacos server 中), Bus 也不需要了 ( 依然可以完成动态刷
)
接下来
1 、去 Nacos server 中添加配置信息
2 、改造具体的微服务,使其成为 Nacos Config Client ,能够从 Nacos Server 中获取到配置信息
1.6.1 Nacos Server 添加配置

 

1.6.2 微服务中开启 Nacos 配置管理
1 )添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2 )微服务中如何锁定 Nacos Server 中的配置文件( dataId
通过 Namespace + Group + dataId 来锁定配置文件, Namespace 不指定就默认 public Group
指定就默认 DEFAULT_GROUP
dataId 的完整格式如下
${prefix}-${spring.profile.active}.${file-extension}
prefix 默认为 spring.application.name 的值,也可以通过配置项
spring.cloud.nacos.config.prefix 来配置。
spring.profile.active 即为当前环境对应的 profile 注意:当 spring.profile.active
为空时,对应的连接符 - 也将不存在, dataId 的拼接格式变成 ${prefix}.${file - extension}
file - exetension 为配置内容的数据格式,可以通过配置项
spring.cloud.nacos.config.file - extension 来配置。目前只支持 properties yaml
型。
cloud:
    nacos:
        discovery:
            server-addr: 127.0.0.1:8848
        config:
            server-addr: 127.0.0.1:8848
            namespace: 26ae1708-28de-4f63-8005-480c48ed6510 #命名空间的ID
            group: DEFAULT_GROUP #如果使用的默认分组,可以不设置
            file-extension: yaml
3 )通过 Spring Cloud 原生注解 @RefreshScope 实现配置自动更新
package com.lagou.page.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/config")
@RefreshScope //自动刷新
public class ConfigClientController {
@Value("${lagou.message}")
private String message;
@Value("${database.type}")
private String dbType;
@RequestMapping("/remote")
public String findRemoteConfig() {
return message + " " + dbType;
}
}
4 )思考:一个微服务希望从配置中心 Nacos server 中获取多个 dataId 的配置信息,可以的,扩展多个 dataId
# nacos配置
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# nacos config 配置
config:
server-addr: 127.0.0.1:8848
# 锁定server端的配置文件(读取它的配置项)
namespace: 07137f0a-bf66-424b-b910-20ece612395a # 命名空间id
group: DEFAULT_GROUP # 默认分组就是DEFAULT_GROUP,如果使用默认分组可以不配置
file-extension: yaml #默认properties
# 根据规则拼接出来的dataId效果:lagou-service-page.yaml
ext-config[0]:
data-id: abc.yaml
group: DEFAULT_GROUP
refresh: true #开启扩展dataId的动态刷新
ext-config[1]:
data-id: def.yaml
group: DEFAULT_GROUP
refresh: true #开启扩展dataId的动态刷新
2 SCA Sentinel 分布式系统的流量防卫兵
2.1 Sentinel 介绍
        Sentinel是一个面向云原生微服务的流量控制、熔断降级组件。
        替代Hystrix ,针对问题:服务雪崩、服务降级、服务熔断、服务限流
Hystrix
        服务消费者(静态化微服务)—> 调用服务提供者(商品微服务)
        在调用方引入Hystrix
1 )自己搭建监控平台 dashboard
2 )没有提供 UI 界面进行服务熔断、服务降级等配置(使用的是 @HystrixCommand 参数进行设
置,代码入侵)
Sentinel
1 )独立可部署 Dashboard/ 控制台组件(其实就是一个 jar 文件,直接运行即可)
2 )减少代码开发,通过 UI 界面配置即可完成细粒度控制
Sentinel 分为两个部分 :
核心库:( Java 客户端)不依赖任何框架 / 库,能够运行于所有 Java 运行时环境,同时对 Dubbo /
Spring Cloud 等框架也有较好的支持。
控制台:( Dashboard )基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat
应用容器。
Sentinel 具有以下特征 :
丰富的应用场景 Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即
突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用
应用等。
完备的实时监控 Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器
秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态 Sentinel 提供开箱即用的与其它开源框架 / 库的整合模块,例如与 Spring
Cloud Dubbo 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel
完善的 SPI 扩展点 Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 的主要特性:

 Sentinel 的开源生态:

2.2 Sentinel 部署
下载地址: https://github.com/alibaba/Sentinel/releases 我们使用 v1.7.1
启动: java -jar sentinel-dashboard-1.7.1.jar &
用户名 / 密码: sentinel/sentinel

 

2.3 服务改造
在我们已有的业务场景中, 静态化微服务 调用了 商品微服务 ,我们在静态化微服务进行的熔断降级等控制,那么接下来我们改造静态化微服务,引入Sentinel 核心包。
为了不污染之前的代码,复制一个页面静态化微服务 lagou-service-page-9101-sentinel
pom.xml 引入依赖
<!--sentinel 核心环境 依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
application.yml 修改(配置 sentinel dashboard ,暴露断点依然要有,删除原有 hystrix 配置,删除
原有 OpenFeign 的降级配置)
server:
port: 9101 # 后期该微服务多实例,端口从9100递增(10个以内)
Spring:
#解决bean重复注册问题
main:
allow-bean-definition-overriding: true
application:
name: lagou-service-page
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/lagou?
useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
username: root
password: wu7787879
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
namespace: 26ae1708-28de-4f63-8005-480c48ed6510 #命名空间的ID
group: DEFAULT_GROUP #如果使用的默认分组,可以不设置
file-extension: yaml
#sentinel配置
sentinel:
transport:
dashboard: 127.0.0.1:8080
port: 8719
# springboot中暴露健康检查等断点接口
management:
endpoints:
web:
exposure:
include: "*"
# 暴露健康接口的细节
endpoint:
health:
show-details: always
上述配置之后,启动静态化微服务,使用 Sentinel 监控静态化微服务
此时我们发现控制台没有任何变化,因为懒加载,我们只需要发起一次请求触发即可

 

 2.4 Sentinel 关键概念

概念描述
资源
它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用
的其它应用提供的服务,甚至可以是一段代码。 我们请求的 API 接口就是资源
规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规
则。所有规则可以动态实时调整。
2.5 Sentinel 流量规则模块
系统并发能力有限,比如系统 A QPS 支持 1 个,如果太多请求过来,那么 A 就应该进行流量控制
了,比如其他请求直接拒绝

 

资源名:默认请求路径
针对来源: Sentinel 可以针对调用者进行限流,填写微服务名称,默认 default (不区分来源)
阈值类型 / 单机阈值
QPS :(每秒钟请求数量)当调用该资源的 QPS 达到阈值时进行限流
线程数:当调用该资源的线程数达到阈值的时候进行限流(线程处理请求的时候,如果说业务逻辑
执行时间很长,流量洪峰来临时,会耗费很多线程资源,这些线程资源会堆积,最终可能造成服务不可用,进一步上游服务不可用,最终可能服务雪崩)
是否集群:是否集群限流
流控模式:
直接:资源调用达到限流条件时,直接限流
关联:关联的资源调用达到阈值时候限流自己
链路:只记录指定链路上的流量
流控效果:
快速失败:直接失败,抛出异常
Warm Up :根据冷加载因子(默认 3 )的值,从阈值 / 冷加载因子,经过预热时长,才达到设置的
QPS 阈值
排队等待:匀速排队,让请求匀速通过,阈值类型必须设置为 QPS ,否则无效
流控模式之关联限流
关联的资源调用达到阈值时候限流自己,比如用户注册接口,需要调用身份证校验接口(往往身份
证校验接口),如果身份证校验接口请求达到阈值,使用关联,可以对用户注册接口进行限流。
package com.lagou.edu.controller;
import com.lagou.edu.controller.service.ResumeServiceFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
/**
* 用户注册接口
* @return
*/
@GetMapping("/register")
public String register() {
System.out.println("Register success!");
return "Register success!";
}
/**
* 验证注册身份证接口(需要调用公安户籍资源)
* @return
*/
@GetMapping("/validateID")
public String validateID() {
System.out.println("validateID");
return "ValidateID success!";
}
}

 

模拟密集式请求 /user/validateID 验证接口,我们会发现 /user/register 接口也被限流了
流控模式之链路限流
链路指的是请求链路(调用链: A-->B--C D-->E-->C
链路模式下会控制该资源所在的调用链路入口的流量。需要在规则中配置入口资源,即该调用链路
入口的上下文名称。
一棵典型的调用树如下图所示:
                                                        machine-root
                                                                /     \
                                                              /         \
                                                  Entrance1   Entrance2
                                                           /               \
                                                         /                   \
                              DefaultNode(nodeA)  DefaultNode(nodeA)
上图中来自入口 Entrance1 Entrance2 的请求都调用到了资源 NodeA Sentinel 允许只根据某个调用入口的统计信息对资源限流。比如链路模式下设置入口资源为 Entrance1 来表示只有从入口
Entrance1 的调用才会记录到 NodeA 的限流统计当中,而不关心经 Entrance2 到来的调用。
流控效果之 Warm up
当系统长期处于空闲的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压
垮,比如电商网站的秒杀模块。
通过 Warm Up 模式(预热模式),让通过的流量缓慢增加,经过设置的预热时间以后,到达系统
处理请求速率的设定值。
Warm Up 模式默认会从设置的 QPS 阈值的 1/3 开始慢慢往上增加至 QPS 设置值。

 

流控效果之排队等待
排队等待模式下会严格控制请求通过的间隔时间,即请求会 匀速 通过,允许部分请求排队等待,通
常用于消息队列削峰填谷等场景。需设置具体的超时时间,当计算的等待时间超过超时时间时请求就会被拒绝。
很多流量过来了,并不是直接拒绝请求,而是请求进行排队,一个一个匀速通过(处理),请求能
等就等着被处理,不能等(等待时间 > 超时时间)就会被拒绝
例如, QPS 配置为 5 ,则代表请求每 200 ms 才能通过一个,多出的请求将排队等待通过。超时时
间代表最大排队时间,超出最大排队时间的请求将会直接被拒绝。排队等待模式下, QPS 设置值不要超过 1000 (请求间隔 1 ms )。
2.6 Sentinel 降级规则模块
流控是对外部来的大流量进行控制,熔断降级的视角是对内部问题进行处理。
Sentinel 降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这
个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断,这里的降级其实是 Hystrix 中的熔断
策略
Sentinel 不会像 Hystrix 那样放过一个请求尝试自我修复,就是明明确确按照时间窗口来,熔断触发
后,时间窗口内拒绝请求,时间窗口后就恢复。

 

RT (平均响应时间 )
1s 内持续进入 >=5 个请求,平均响应时间超过阈值(以 ms 为单位),那么在接下的时间
窗口(以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException )。注意
Sentinel 默认统计的 RT 上限是 4900 ms ,超出此阈值的都会算作 4900 ms ,若需要变更此上限可
以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。

 

异常比例
当资源的每秒请求量 >= 5 ,并且每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的时间窗口(以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0] ,代表 0% - 100%

 异常数

当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若
timeWindow 小于 60s ,则结束熔断状态后仍可能再进入熔断状态,时间窗口 >= 60s

 

2.7 SCA 小结
SCA 实际上发展了三条线
第一条线:开源出来一些组件
第二条线:阿里内部维护了一个分支,自己业务线使用
第三条线:阿里云平台部署一套,付费使用
从战略上来说, SCA 更是为了贴合阿里云。
目前来看,开源出来的这些组件,推广及普及率不高,社区活跃度不高,稳定性和体验度上仍需进
一步提升,根据实际使用来看 Sentinel 的稳定性和体验度要好于 Nacos

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/30535.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022华为杯研究生数学建模竞赛DS数模选题建议

2022华为杯研究生数学建模竞赛DS数模选题建议 开放性&#xff1a;F>E>AD>BC. 难度&#xff1a;AD>BC>EF &#xff08;仅C君个人看法&#xff09; A题 移动场景超分辨定位问题 此题是物理cv类题目&#xff0c;属于比较新颖的超分辨率图像检测类任务&#xff0c…

1、Java的json得到我们想要的数据结构

Java的json得到我们想要的数据结构 第一步&#xff1a;首先我们要知道json就两种数据结构。 &#xff01;&#xff01;&#xff01;第一种数据结构&#xff1a;对象用{ }表示 &#xff01;&#xff01;&#xff01;第二种数据结构&#xff1a;数组用[ ]表示 我们用这个案例来…

在智能家居领域产品中常用芯片

芯片是当前“电子科技设备的灵魂”所在&#xff0c;几乎决定了所有电子设备的综合性能&#xff0c;现如今智能家居带来了全新的使用场景与交互方式&#xff0c;从扫地机器人、智能洗碗机、智能冰箱等智能机器&#xff0c;到智能照明、智能感知、网络通讯、家庭影音等智能系统&a…

H264基础知识入门

之前视频基础&#xff0c;有讲到视频的原始数据YUV&#xff0c;相比RBG&#xff0c;数据确实减少了&#xff0c;但还是一个非常大数据量&#xff0c;会占用很大空间以及在给网络传输带来很大压力。所以必须要对视频进行压缩&#xff0c;减少占用空间。这里主要分享H264编码技术…

数字IC设计之——低功耗设计

目录 概述 背景 为什么需要低功耗设计 CMOS IC功耗分析 基本概念 功耗的分类 功耗相关构成 不同层次低功耗设计方法 芯片中的功耗分布以及对应的低功耗方案 低功耗方案 系统算法级的低功耗技术 编码阶段的低功耗技术 门控时钟 Clock Gating 物理实施的低功耗技术 操作数分离&am…

【第六部分 | JavaScript高级】1:面向对象

目录 【第一章】面向对象 | Class创建、构造函数、方法 | Class继承 | 三个注意点 | 静态成员 | 原型对象 __ _proto___ | 类的本质 【第一章】面向对象 | Class创建、构造函数、方法 创建类 class name {// class body }var xx new name() 构造函数 class Person {co…

【Godot】数据响应的方式执行功能

Godot Engine 版本&#xff1a;4.0 beta 6 下载地址&#xff1a;Index of /godotengine/4.0/beta6/ (downloads.tuxfamily.org) 在这个教程中&#xff0c;学会理解以数据为主的进行处理执行逻辑的代码编写方式&#xff0c;虽然看似简单&#xff0c;但是确是方便又好用。 以及下…

Git使用教程

Git项目的三个工作区域的概念&#xff1a; 1、Git仓库Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分&#xff0c;从其它计算机克隆仓库时&#xff0c;拷贝的就是这里的数据。 2、工作目录工作目录是对项目的某个版本独立提取出来的内容…

Ansible之 AWX 创建管理项目的一些笔记

写在前面 分享一些 AWX 创建管理项目的笔记博文内容涉及&#xff1a; 容器化 AWX 手工创建项目Demo通过 SCM 创建项目 Demo项目角色&#xff0c;更新策略介绍&#xff0c;SCM 凭据的创建 食用方式&#xff1a; 需要了解 Ansible理解不足小伙伴帮忙指正 傍晚时分&#xff0c;你坐…

ssm项目改造spring boot项目

快速创建 Spring Boot 项目 添加依赖 如果是普通 Maven 项目&#xff0c;需要手动添加。 <!-- 打包方式 jar 包 --> <packaging>jar</packaging><!-- 指定父工程 --> <parent><groupId>org.springframework.boot</groupId><ar…

操作系统学习笔记(Ⅰ):概述

目录 1 操作系统概念 1.1 定义 1.2 功能 1.系统资源的管理者 2.用户和计算机硬件间接口 3.最接近硬件的层次 2 操作系统的特征 2.1 并发 2.2 共享 2.3 虚拟 2.4 异步 3 发展和分类 3.1 手工操作阶段 3.2 批处理阶段 1.单道批处理阶段 2.多道批处理系统 3.3 分…

启明欣欣STM32开发板闪烁LED实验

最近在咸鱼上买了一块启明欣欣的STM32板子&#xff0c;准备在上面测试open62541和CANopen&#xff0c;到货后如下图&#xff0c; 找商家要了资料&#xff0c;然后运行一个LED灯的实验来简单测试下板子&#xff0c;本文记录一下这个过程。 一 准备 安装Keil 5.35&#xff0c;安…

【selection】 学习光标API并实现编辑区插入表情图片的功能

目录场景介绍selection介绍selection APIrange 介绍range API实现编辑区插入表情图片参考资料场景介绍 在写web版聊天器时&#xff0c;遇到一个需求&#xff1a; 聊天时用户可以在编辑区加入表情图片&#xff0c;并且表情图片要插入在光标位置。// *web版聊天器地址&#xff…

useMemo 使用误区

文章の目录问题背景useMemo 使用前后组件性能对比结论问题背景 在某一个h5项目中&#xff0c;使用了 useMemo 对项目中的组件进行优化&#xff0c;减少组件不必要的re-render, 优化后的结果&#xff1a; 在组件的props和状态未改变时&#xff0c;组件不再进行 re-render 表面上…

生意不好如何逆风翻盘 | 多门店经营必读技巧(1):导购管理 连锁店管理的技巧 连锁店生意经 如何做导购管理

很多连锁店老板反馈&#xff0c;为了优化门店的销售业绩&#xff0c;什么方法都试过了&#xff0c;改店铺陈列、搞优惠活动、做会员管理.......为什么分店的业绩还是看不到明显的提升&#xff1f; 方法试过了&#xff0c;结果没变化&#xff0c;那只能是执行这块出了问题&#…

量子计算(八):观测量和计算基下的测量

文章目录 观测量和计算基下的测量 一、观测量 二、计算基下的测量 三、投影测量 观测量和计算基下的测量 一、观测量 量子比特&#xff08;qubit&#xff09;不同于经典的比特&#xff08;bit&#xff09;&#xff0c;一个量子比特|>可以同时处于|0>和|1>两个状态…

Linux从入门到精通(八)——Linux磁盘管理

文章篇幅较长&#xff0c;建议先收藏&#xff0c;防止迷路 文章跳转Linux从入门到精通&#xff08;八&#xff09;——Linux磁盘管理goLinux从入门到精通&#xff08;九&#xff09;——Linux编程goLinux从入门到精通&#xff08;十&#xff09;——进程管理goLinux从入门到精…

C++ 结合mysql写一个服务端

1 libhv和mysql libhv是一个跨平台的C网络库。 mysql是一个关系型数据库。 2 下载MySQL&#xff0c; 最好不要下载高版本的&#xff0c;容易出错&#xff01;&#xff01;&#xff01; 下载地址MySQL 下载好后目录是这样的&#xff1a; 然后在环境变量里配置&#xff1a;…

Hive 3.1.3

1.下载安装包 Index of /hive/hive-3.1.3https://dlcdn.apache.org/hive/hive-3.1.3/ 2.安装&修改配置文件 2.1 安装MySQL a. 搜索centos7默认的mariadb & 卸载 [root@node1 ~]# rpm -qa | grep mariadb mariadb-libs-5.5.44-2.el7.centos.x86_64 卸载 [r…

【计算机组成原理Note】5.4.2 控制器-微程序

5.4.2 控制器-微程序 硬布线工作原理&#xff1a;微操作控制信号由组合逻辑电路根据当前的指令码、状态和时序&#xff0c;即时产生微程序工作原理&#xff1a;事先把微操作控制信号存储在一个专门的存储器(控制存储器)中&#xff0c;将每一条机器指令编写成一个微程序&#xf…