微服务:7-1-1 服务总线-SpringCloud Bus

1、SpringCloud Bus概述

SpringCloud Bus是对SpringCloud Config的加深和扩充,实现分布式自动刷新配置功能,SpringCloud Bus配合SpringCloud config使用可以实现配置的动态刷新。

1、SpringCloud Bus是什么?

bus3
Bus支持两种消息代理:RabbitMQ和Kafka

2、SpringCloud Bus能干什么?

bus1
bus2

2、SpringCloud Bus动态刷新全局广播

这里需要先安装RabbitMQ
bus4

测试
bus5

3、SpringCloud Bus动态刷新定点通知

bus6

总结:
bus7

微服务:6-1-1 服务配置-SpringCloud Config

1、SpringCloud Config概述

分布式系统面临的配置问题
config1

1、SpringCloud Config是什么?

config2

2、SpringCloud Config能干什么?

config3

3、SpringCloud Config与GitHub整合配置

SpringCloud Config默认使用Git来存储配置文件(也有其他方法,如支持SVN和本地文件),也最推荐Git,而且使用http/https访问的形式

2、SpringCloud Config服务端配置与测试

SpringCloud Config也是一个微服务
config4

配置文件的几种格式:
config5

成功读取到github上的中心外部配置文件
config6

3、SpringCloud Config客户端配置与测试

1、SpringCloud Config客户端与服务端交互图
config7

2、bootstrap.yml配置文件说明
(1)是什么
config8

(2)内容
config9

3、搭建SpringCloud Config客户端3355
config10

4、SpringCloud Config客户端之动态刷新

为了避免每次更新配置都要重启客户端微服务3355,我们可以给其配置动态刷新

1、动态刷新步骤

config11

2、问题

上面的实现,每次在github上修改配置后,需要运维人员手动发送post请求更新客户端配置,这样存在问题
config12

由于该动太刷新满足不了需求,引入下一章消息总线Spring Cloud Bus。

微服务:5-1-1 服务网关-Gateway

1、Gateway概述

1、Gateway是什么?

SpringCloud Gateway使用的Webflux中的reactor-netty响应式变编程组件,底层使用了Netty组件
gateway1

2、Gateway能干什么?

1、反向代理

2、鉴权

3、流量控制

4、熔断

5、日志监控等等

企业一般架构:
gateway2

3、选择Gateway的原因?

1、
gateway3

2、SpringCloud Gateway具有如下特性
gateway4

3、SpringCloud Gateway与Zuul的区别
gateway5

4、Zuul模型
gateway6

5、Gateway模型
gateway7

2、Gateway三大核心概念

1、Route(路由)

路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由

2、Predicate(断言)

参考Java8的Predicate,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由

3、Filter(过滤)

Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或路由后对请求修改

4、总结

gateway8

3、Gateway工作流程

gateway9

核心逻辑:路由转发+执行过滤链

4、入门配置

Gateway自己本身也是一个微服务,需要搭建

1、新建module

新建module:cloud-gateway-gateway9627

2、改POM

gateway10
gateway网关微服务不需要引入web依赖

3、写YML

gateway11

4、业务类

无业务逻辑,可看作看门的

5、主启动类

gateway12

6、gateway9527如何做路由映射?

前面的项目8001提供服务,如果我们不想暴露8001端口,可以在8001外面套一层gateway9527

1、在gateway9527中新增配置
gateway13
为8001中的接口配置一层网关

2、访问说明
gateway14

gateway15

6、gateway网关两种配置

1、在配置文件yml中配置
如上面

2、代码中注入RouteLocator的Bean
改造上面的项目,新建config包,包下新建配置类gatewayConfig
gateway16

5、通过微服务名实现动态路由

1、在客户端80和服务提供端8001(或服务端集群)之间加了网关Gateway之后,网关Gateway需要做负载均衡。

2、默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由 进行转发,从而实现动态路由功能

3、实现
修改网关的yml文件,开启注册中心服务发现功能,将写死的服务端路由地址改成服务端在注册中心注册后动态路由的地址
gateway17

gateway18

6、Predicate的使用

1、启动网关9527后可以看到
gateway19

2、Route Predicate Factories是什么?
gateway20

3、常用的Route Predicate
gateway21

总结:Predicate就是为了实现一组匹配规则,让请求过来找对应的Route进行处理。

7、Filter的使用

1、Filter是什么

gateway22

2、Spring Cloud Gateway的Filter

1、生命周期,只有两个
(1)pre 业务逻辑之前
(2)post 业务逻辑之后

2、种类
(1)GatewayFilter 单一的
(2)GlobalFilter 全局的

3、常用的GatewayFilter

Filter非常多,这里仅示例用法,具体参考官网
示例
gateway23

4、自定义GlobalFilter

两个主要接口
implement GlobalFilter,Ordered

1、在gateway9527模块下新建包filter,包下新建MyGlobalFilter类
gateway24

2、全局GlobalFilter能干嘛
(1)全局日志记录
(2)统一网关鉴权等等

微服务:4-1-1 服务降级-Hystrix

1、Hystrix概述

1、分布式面临的问题?

hystrix1

hystrix2

2、Hystrix是什么?

hystrix3

Hystrix可用在消费端和服务端,一般用在消费端

3、Hystrix能干什么?

1、服务降级

2、服务熔断

3、接近实时的监控

4、限流、隔离等等

2、Hystrix重要概念

1、服务降级fallback

1、服务提供方发生故障时,向调用方返回一个符合预期的、可处理的备选响应,而不是长时间的等待或抛出调用方无法处理的异常。

比如:服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示

2、触发服务降级的情况
(1)程序运行异常
(2)超时
(3)服务熔断触发服务降级
(4)线程池/信号量打满

2、服务熔断break

类比保险丝,达到最大服务访问后,直接拒绝访问,断闸限电,然后调用服务降级的方法并返回友好提示

保险丝:服务降级-》进而熔断-》恢复调用链路

2、服务限流flowlimit

秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,1秒钟N个,有序进行

3、Hystrix案例

1、构建

1、新建带有Hystrix8001项目

2、改POM
hystrix4

3、写yml
hystrix5

4、主启动
hystrix6

5、业务类
(1)service
hystrix7

(2)controller
hystrix8

6、测试
接口1立马返回
接口2延迟3秒返回

2、高并发测试

hystrix9

3、新建带有Feign和Hystrix的客户端80

hystrix10

4、服务降级

1、降级配置 @HystrixCommand

1、8001fallback
先8001自身找问题
设置自身调用超时时间的峰值,峰值内可以正常运行,超过了峰值需要有兜底的方法处理,做服务降级fallback

(1)业务类启用
@HystrixCommand报异常后如何处理
一旦服务方法被调用失败并抛出了错误信息后,会自动启用@HystrixCommand标注的fallbackMethod指定的方法来处理异常(超时或其他异常都可以处理)
hystrix11

(2)主启动类激活
添加新注解@EnableCircuitBreaker
hystrix12

2、80fallback
80客户端订单微服务,也可以采用服务降级保护自身

尽管设置了热部署时,修改了@HystrixCommand内属性还是建议重启微服务。

(1)yml开启hystrix
hystrix13

(2)主启动,@EnableHystrix
hystrix14

(3)业务类
hystrix15

2、问题

hystrix16

指定统一处理服务降级的实现类
hystrix17

当客户端也配置服务降级后,当服务端宕机时,客户端会进行相应服务降级处理,不会挂起耗死服务器

5、服务熔断

1、断路器

可以看做家里的保险丝

2、熔断是什么

hystrix18
hystrix19

3、案例

1、修改8001的PaymentService
hystrix20
配置中意思是10秒钟内10次请求有6次以上(60%)失败即触发熔断,断路器打开,

这时就算后面是正常正确的请求到达,也得不到正常的返回;

过一段时间后,失败率降低,慢慢恢复链路,断路器关闭,可以正常处理请求

2、修改8001的PaymentController
hystrix21

3、总结
(1)熔断类型
hystrix22

(2)断路器在什么情况下起作用
hystrix23

(3)断路器开启或关闭的条件
hystrix24

(3)断路器打开之后
hystrix25

官网断路器流程图
hystrix26

6、Hystrix DashBoard

1、概述
hystrix27

2、开启
hystrix28

微服务:3-1-2 服务调用-OpenFeign

1、OpenFeign概述

1、OpenFeign是什么?

1、OpenFeign是什么?
openfeign1

OpenFeign整合了Ribbon

2、OpenFeign能干什么?
openfeign2

3、Feign和OpenFeign的区别
openfeign3

2、OpenFeign使用步骤

注册中心、服务提供者、消费者三者交互图:
openfeign4

1、接口+注解

微服务调用接口+@FeignClient
feign在消费端使用

2、使用步骤

1、建module
新建cloud-consumer-feign-order80

2、改POM
openfeign5

3、写YML
openfeign是一个客户端,不用整合进微服务,不用在yml中配置
openfeign6

4、主启动
在主启动类使用注解@EnableFeignClients激活并开启Feign
openfeign7

5、业务类
(1)业务逻辑接口+@FeignClient配置调用provider的服务
主启动类使用注解@EnableFeignClients激活开启Feign功能,
@FeignClient标注接口使用Feign功能

(2)新建PaymentFeignService接口,并新增注解@FeignClient,并指定微服务
openfeign8

(3)控制层Controller
openfeign9

6、测试
(1)启动2个Eureka集群7001和7002

(2)启动2个微服务提供者8001和8002

(3)启动OpenFeign80

(4)测试接口
openfeign10
微服务提供者8001和8002轮询方式提供服务

Feign自带负载均衡配置项,因为集合了Ribbon

7、小总结
openfeign11

3、OpenFeign超时控制

Feign客户端请求时默认等待1秒钟,但是服务端响应处理超过1秒钟的话,Feign客户端就会超时报错。

为了避免这种情况,我们可以设置Feign客户端的超时控制

OpenFeign集成了Ribbon,由配置Ribbon来实现
openfeign12

4、OpenFeign日志打印功能

Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中HTTP请求的细节,即对Feign接口的调用用情况进行监控和输出。

1、日志级别

1、None:默认的,不显示任何日志

2、Basic:仅记录请求方法、URL、响应状态码及执行时间

3、Headers:除了Basic中定义的信息外,还有请求还有响应头信息

4、Full:除了Headers中定义的信息外,还有请求和响应的正文及元数据

2、配置日志Bean

新建package,写一个配置类,设置日志级别为FULL
openfeign13

3、yml文件开启日志的Feign客户端

openfeign14

调用接口后,可在控制台看到详细日志

微服务:3-1-1 服务调用-Ribbon

1、Ribbon概述

1、Ribbon是什么?

ribbon1

总结:Ribbon其实就是一个软负载均衡的客户端组件

2、负载均衡

1、负载均衡是什么?
ribbon2

2、两种负载均衡:集中式LB和进程内LB
ribbon3

2、负载均衡演示

1、架构说明

Ribbon其实就是一个软负载均衡的客户端组件,可以和其他所需请求的客户端结合使用,比如和Eureka结合
(最新的Eureka依赖中,已经整合了Ribbon,不用额外引入Ribbon依赖,所以在Eureka篇没有显示引入Ribbon也可以实现负载均衡)

ribbon4

Ribbon在工作时分成两步:
第一步 先选择EurekaServer,它优先选择同一个区域内负载较少的server
第二步 根据用户指定的策略,在server中取到的服务注册列表中选择一个地址

其中Ribbon提供了多种策略:如轮询、随机和根据响应时间加权

3、Ribbon核心组件IRule

1、IRule接口

IRule:根据特定算法从服务列表中选取一个要访问的服务
Ribbon自带的7中负载均衡方法:
ribbon5
ribbon6

2、替换负载均衡算法

比如,当前项目的负载均衡算法是默认的轮询,想要换成随机,该如何操作?
1、修改cloud-consumer-order80

2、注意配置细节
将负载均衡算配置成随机的配置类,不能放在注解@ComponentScan所扫描的当前包以及子包下,
ribbon7

但是当前项目的入口主启动类,由注解@SpringBootApplication标注,@SpringBootApplication中包含注解@ComponentScan,所以当前整个项目都属@ComponentScan的扫描范围,
ribbon8

则我们需要新建一个配置包和@SpringBootApplication所在主启动类处于同级位置

3、新建package:com.atguigu.myrule
ribbon9

4、com.atguigu.myrule包下新建MySelfRule规则类,将负载均衡算法换成随机
ribbon10

5、主启动类添加RibbonClient
ribbon11
configuration指向我们自定义的配置类MySelfRule

6、测试
ribbon12
测试后发现消费者端随机访问服务提供端8001和8002

4、负载均衡算法

1、原理

负载均衡轮询原理:
ribbon13

微服务:2-1-2 服务注册与发现三个组件对比

1、Eureka、Zookeeper和Consul对比

cap1

2、CAP理论

cap2

CAP经典图:
cap3

1、AP(Eureka):
cap4

2、CP(Zookeeper/Consul):
cap5
网络分区出现后,为了保证一致性,就必须拒绝客户端请求,否则无法保证一致性

结论:违背了A(可用性)的要求,只满足一致性和分区容错性,即CP。

微服务:2-1-1 服务注册与发现-Eureka

1、Eureka基础知识

1、什么是服务治理

Spring Cloud 封装了Netflix公司开发的Eureka模块来实现服务治理。

在传统的RPC远程调用框架中,管理每个服务与服务之家依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务与服务之间的依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

2、什么是服务注册与发现

eureka1
eureka2

3、Eureka两组件

eureka3

2、单机Eureka构建步骤

eureka4
eureka18

1、IDEA生成eurekaServer端服务注册中心(类似物业中心)

1、建Module
eureka5_module
2、改POM
eureka6_pom
3、写YML
eureka7_yml
4、主启动
eureka8_application
5、测试
eureka9_localhost

2、EurekaClient端cloud-provider-payment8001注册进EurekaServer成为服务提供者provider(类似于入驻该物业中心的奶茶店)

1、建Module

2、改POM
eureka10_provider_pom
3、写YML
eureka11_provider_yml
4、主启动
eureka12_provider_application
5、测试
eureka13_provider_localhost

6、yml配置文件中,spring的application的name就是Eureka启动后的application的name
eureka14_provider

3、EurekaClient端cloud-consumer-order80注册进EurekaServer成为服务消费者consumer(类似于奶茶店消费者)

1、建Module

2、改POM
与上面一致

3、写YML
eureka15_consumer_yml
4、主启动
eureka16_consumer_application
5、测试
eureka17_consumer_localhost

3、集群Eureka构建步骤

1、Eureka集群原理说明

eureka20_cluster_theory
eureka19_cluster

2、EurekaServer集群环境构建步骤

1、参考cloud-eureka-server7001,新建cloud-eureka-server7002
eureka21_server7002_module

2、改POM
eureka22_server7002_pom

总工程的pom文件
eureka23_server7002_parent_pom

3、修改映射配置
eureka24_server7002_host

4、写YML(之前是单机,现在改成集群)
eureka25_server7001&7002_pom

5、主启动
eureka26_server7001&7002_application

3、将支付服务8001微服务发布到7001和7002两台Eureka集群配置中

1、8001配置7001和7002两台Eureka集群
eureka27_server8001_yml

4、将订单服务服务800微服务发布到7001和7002两台Eureka集群配置中

1、80配置7001和7002两台Eureka集群
eureka28_server80_yml

5、测试

1、先启动EurekaServer,7001和7002服务
eureka29_server7001

2、再启动服务提供者provider,8001
eureka30_server7002
3、再启动服务消费者,80

4、访问
eureka31_server80
eureka32

6、支付服务提供者集群环境构建

服务提供者是一个集群:
eureka39

1、参考cloud-provider-payment8001,新建cloud-provider-payment8002
eureka33_server8002_module

2、改POM
复制8001的pom

3、写yml
复制8001的yml,将端口改成8002即可

5、主启动
复制8001

5、业务类
复制8001

5、修改8001和8002的Controller
简单标注该provider的端口号
eureka34_server_controller

6、在80消费者controller中,修改访问服务提供者的路径,把写死的路径改成消费提供者集群对外暴露的名称:CLOUDER-PATMENT-SERVER
eureka36_server80

7、负载均衡
在80消费者controller中,修改访问服务提供者的路径,把写死的路径改成消费提供者集群对外暴露的名称:CLOUDER-PATMENT-SERVER之后,当访问https://CLOUDER-PATMENT-SERVER路径时,服务器不知道你到底要访问CLOUDER-PATMENT-SERVER下的哪一台机器,所以要使用**@LoadBalance**注解赋予RestTemplate负载均衡的能力

修改ApplicationContextConfig
eureka37_server80_config

8、启动7001和7002
eureka35_server70017002

9、启动8001和8002

10、启动80
启动80端口的消费者,消费者访问服务提供者8001和8002时,服务提供者集群采用轮询机制给消费者提供服务。
eureka38_80

4、服务发现discovery

1、对于注册进eureka里面的微服务,可以通过服务发现来获得该服务信息

以8001为例:
2、修改cloud-provider-payment8001的controller
eureka40_discovery_controller

3、8001主启动类
添加注解@EnableDisCoveryClient
eureka41_discovery_application

4、自测
eureka42_discovery_run
eureka43_discovery_run2

5、Eureka自我保护

1、故障现象

eureka44

2、导致原因

1、某时刻,某一个微服务不可用了,Eureka不会立刻清理,依旧会对该服务的信息进行保存

2、属于CAP中的AP分支

eureka45

3、怎么禁止自我保护

某时刻,某一个微服务不可用了,Eureka立刻清理不保存该服务的信息

以7001和8001为例
1、修改7001yml
eureka46

2、修改8001yml
eureka47

GO:2.9 Go语句及其执行规则

本篇文章中讨论的go语句,这也是 Go 语言的最大特色了。它足可以代表 Go 语言最重要的编程哲学和并发编程模式。

重温一下下面这句话:


Don’t communicate by sharing memory; share memory by communicating.

从 Go 语言编程的角度解释,这句话的意思就是:不要通过共享数据来通讯,恰恰相反,要以通讯的方式共享数据。


我们已经知道,通道(也就是 channel)类型的值,可以被用来以通讯的方式共享数据。更具体地说,它一般被用来在不同的 goroutine 之间传递数据。那么 goroutine 到底代表着什么呢?

简单来说,goroutine 代表着并发编程模型中的用户级线程。你可能已经知道,操作系统本身提供了进程和线程,这两种并发执行程序的工具。

1、进程与线程

1、进程,描述的就是程序的执行过程,是运行着的程序的代表。换句话说,一个进程其实就是某个程序运行时的一个产物。如果说静静地躺在那里的代码就是程序的话,那么奔跑着的、正在发挥着既有功能的代码就可以被称为进程。

我们的电脑为什么可以同时运行那么多应用程序?我们的手机为什么可以有那么多 App 同时在后台刷新?这都是因为在它们的操作系统之上有多个代表着不同应用程序或 App 的进程在同时运行。

2、再来说说线程。首先,线程总是在进程之内的,它可以被视为进程中运行着的控制流(或者说代码执行的流程)。

一个进程至少会包含一个线程。如果一个进程只包含了一个线程,那么它里面的所有代码都只会被串行地执行。每个进程的第一个线程都会随着该进程的启动而被创建,它们可以被称为其所属进程的主线程

相对应的,如果一个进程中包含了多个线程,那么其中的代码就可以被并发地执行。除了进程的第一个线程之外,其他的线程都是由进程中已存在的线程创建出来的

也就是说,主线程之外的其他线程都只能由代码显式地创建和销毁。这需要我们在编写程序的时候进行手动控制,操作系统以及进程本身并不会帮我们下达这样的指令,它们只会忠实地执行我们的指令。

不过,在 Go 程序当中,Go 语言的运行时(runtime)系统会帮助我们自动地创建和销毁系统级的线程。这里的系统级线程指的就是我们刚刚说过的操作系统提供的线程。

而对应的用户级线程指的是架设在系统级线程之上的,由用户(或者说我们编写的程序)完全控制的代码执行流程。用户级线程的创建、销毁、调度、状态变更以及其中的代码和数据都完全需要我们的程序自己去实现和处理。

这带来了很多优势,比如,因为它们的创建和销毁并不用通过操作系统去做,所以速度会很快,又比如,由于不用等着操作系统去调度它们的运行,所以往往会很容易控制并且可以很灵活。

但是,劣势也是有的,最明显也最重要的一个劣势就是复杂。如果我们只使用了系统级线程,那么我们只要指明需要新线程执行的代码片段,并且下达创建或销毁线程的指令就好了,其他的一切具体实现都会由操作系统代劳。

但是,如果使用用户级线程,我们就不得不既是指令下达者,又是指令执行者。我们必须全权负责与用户级线程有关的所有具体实现。

操作系统不但不会帮忙,还会要求我们的具体实现必须与它正确地对接,否则用户级线程就无法被并发地,甚至正确地运行。毕竟我们编写的所有代码最终都需要通过操作系统才能在计算机上执行。这听起来就很麻烦,不是吗?

不过别担心,Go 语言不但有着独特的并发编程模型,以及用户级线程 goroutine,还拥有强大的用于调度 goroutine、对接系统级线程的调度器。

这个调度器是 Go 语言运行时系统的重要组成部分,它主要负责统筹调配 Go 并发编程模型中的三个主要元素,即:G(goroutine 的缩写)、P(processor 的缩写)和 M(machine 的缩写)

其中的 M 指代的就是系统级线程。而 P 指的是一种可以承载若干个 G,且能够使这些 G 适时地与 M 进行对接,并得到真正运行的中介

从宏观上说,G 和 M 由于 P 的存在可以呈现出多对多的关系。当一个正在与某个 M 对接并运行着的 G,需要因某个事件(比如等待 I/O 或锁的解除)而暂停运行的时候,调度器总会及时地发现,并把这个 G 与那个 M 分离开,以释放计算资源供那些等待运行的 G 使用。

而当一个 G 需要恢复运行的时候,调度器又会尽快地为它寻找空闲的计算资源(包括 M)并安排运行。另外,当 M 不够用时,调度器会帮我们向操作系统申请新的系统级线程,而当某个 M 已无用时,调度器又会负责把它及时地销毁掉。

正因为调度器帮助我们做了很多事,所以我们的 Go 程序才总是能高效地利用操作系统和计算机资源。程序中的所有 goroutine 也都会被充分地调度,其中的代码也都会被并发地运行,即使这样的 goroutine 有数以十万计,也仍然可以如此。
mpg1

关于 Go 语言内部的调度器和运行时系统的更多细节,我在这里就不再深入讲述了。你需要知道,Go 语言实现了一套非常完善的运行时系统,保证了我们的程序在高并发的情况下依旧能够稳定、高效地运行。

下面,从编程实践的角度出发,以go语句的用法为主线,介绍go语句的执行规则、最佳实践和使用禁忌。

2、什么是主 goroutine,它与我们启用的其他 goroutine 有什么不同?