微服务
一、微服务
1.单体架构
(1)单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。
(2)优点:
- 架构简单
- 部署成本低
(3)缺点:
- 耦合度高
2.分布式架构
(1)分布式架构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,成为一个服务。
(2)优点:
- 降低服务耦合
- 有利于服务升级拓展
3.微服务
(1)微服务:微服务是一种经过良好架构设计的分布式架构方案。
(2)特征:
- 单一职责:微服务拆分力度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
- 面向服务:微服务对外暴露业务接口
- 自治:团队独立、技术独立、数据独立、部署独立
- 隔离性强:服务调用做好隔离、容错、降级、避免出现级联问题
4.微服务技术对比
5.SpringCloud
(1)SpringCloud与SpringBoot的版本兼容关系:
二、服务拆分与远程调用
1.服务拆分注意事项
(1)不同微服务,不要重复开发相同业务
(2)微服务数据独立,不要访问其他微服务的数据库
(3)微服务可以将自己的业务暴露为接口,供其他微服务调用
2.提供者与消费者
- 服务提供者:一次业务中,被其他微服务调用的服务。(提供接口给其他微服务)
- 服务消费者:一次业务中,调用其他微服务的服务。(调用其他微服务提供的接口)
- 提供者与消费者角色是相对的
- 一个服务可以同时是服务提供者和服务消费者
三、Eureka注册中心
1.Eureka的作用
(1)注册服务信息
(2)拉取服务
(3)负载均衡
(4)远程调用
2.Eureka架构中的微服务角色
- EurekaServer:服务端,注册中心
- 记录服务信息
- 心跳监控
- EurekaClient:客户端
- Provider:服务提供者
- 注册自己的信息到EurekaServer
- 每隔30秒向EurekaServer发送心跳
- consumer:服务消费者
- 根据服务名称从EurekaServer拉取服务列表
- 基于服务列表做负载均衡,选中一个微服务后发起远程调用
- Provider:服务提供者
四、Ribbon负载均衡
1.负载均衡流程
2.负载均衡策略
(1)Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每一个子接口都是一种规则
(2)默认实现是ZoneAvoidanceRule,根据zone选择服务列表,然后轮询
(3)通过定义IRule实现可以修改负载均衡规则,有两种方式:
-
代码方式:在order-service中的OrderApplication类中,定义一个新的IRule
-
配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则
3.饥饿加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问时的耗时,通过下面配置开启饥饿加载:
五、Nacos注册中心
1.nacos服务分级存储模型
2.NacosRule负载均衡策略
- 优先选择同集群服务实例列表
- 本地集群找不到提供者,才去其他集群寻找,并且会报警告
- 确定了可用实例列表后,再采用随机负载均衡挑选实例
3.根据权重负载均衡
(1)Nacos控制台可以设置实例的权重值,0-1之间
(2)同集群的多个实例,权重越高被访问的频率越高
(3)权重设置为0则完全不会被访问
4.环境隔离-namespace
(1)namespace用来做环境隔离
(2)每个namespace都有唯一id
(3)不同namespace下的服务不可见
5.Nacos注册中心细节分析
六、Nacos与Eureka的对比
1.共同点
(1)都支持服务注册和服务拉取
(2)都支持服务提供者心跳方式做健康检测
2.区别
(1)Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
(2)临时实例心跳不正常会被剔除,非临时实例则不会被剔除
(3)Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
(4)Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
七、Nacos配置管理
1.统一配置管理
- 配置更改热更新
2.将配置交给Nacos管理的步骤
(1)在Nacos中添加配置文件
(2)在微服务中引入nacos的config依赖
(3)在微服务中添加bootstrap.yml,配置nacos地址、当前环境、服务名称、文件后缀名。
3.配置热更新
(1)方式:
- 方式一:通过@Value注解注入,结合@RefreshScope来刷新
- 方式二:通过@ConfigurationProperties注入,自动刷新
(2)注意事项:
- 不是所有的配置都适合放到配置中心,维护起来比较麻烦
- 建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义配置
4.多环境配置共享
(1)微服务启动时会从nacos读取多个配置文件:
- [spring.application.name]-[spring.profiles.cative].yaml
- [spring.application.name].yaml
(2)无论profile如何变化,[spring.application.name].yaml这个文件一定会加载,因此多环境共享配置可以写入这个文件
(3)多种配置的优先级:
- 服务名-profile.yaml > 服务名称.yaml > 本地配置
5.Nacos集群搭建步骤
(1)搭建MySQL集群并初始化数据库表
(2)下载解压nacos
(3)修改集群配置(节点信息)、数据库配置
(4)分别启动多个nacos节点
(5)nginx反向代理
八、Feign
1.使用步骤
(1)引入依赖:
(2)在启动类添加注解@EnableFeignClients
(3)编写Feign客户端
主要是基于SpringMVC的注解来声明远程调用的信息
2.自定义配置
(1)
(2)配置Feign日志两种方式
方式一:
-
全局生效:
-
局部生效:
方式二:
-
如果是全局配置,则把它放到@EnableFeignClients这个注解中
-
如果是局部配置,则把它放到@FeignClient这个注解中
3.Feign的性能优化
(1)Feign底层的客户端实现:
- URLConnection:默认实现,不支持连接池
- Apache HttpClient:支持连接池
- OKHttp:支持连接池
(2)优化Feign的性能主要包括:
- 使用连接池代替默认的URLConnection
- 日志级别,最好用basic或none
(3)连接池配置:
-
引入依赖
-
配置连接池:
4.Feign的最佳实践
(1)方式一(继承):给消费者的FeignClient和提供者的controller定义统一的父接口作为标准
- 服务紧耦合
- 父接口参数列表中的映射不会被继承
(2)方式二(抽取):将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用
实现步骤:
- 首先创建一个moudule,命名为feign-api,引入feign的starter依赖
- 将消费者中的Client、实体类、DefaultFeignConfiguration都复制到feign-api项目中
- 将消费者引入feign-api的依赖
- 修改消费者中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
- 重启测试
(3)当定义的FeignClient不在SPringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种方式解决:
-
方式一:指定FeignClient所在包
-
方式二:指定FeignClient字节码
九、Gateway网关
1.网关功能
(1)身份认证和权限校验
(2)服务路由、负载均衡
(3)请求限流
2.网关的技术实现
(1)gateway
(2)zuul
Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式的编程,具备更好的性能
3.搭建网关服务步骤
(1)创建新的moudule,引入SpringCloudGateway的依赖和nacos的服务发现依赖
(2)编写路由配置及nacos地址
4.路由断言工厂Route Predicate Factory
Spring提供了11种基本的Predicate工厂:
5.路由过滤器 GatewayFilter
(1)GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:
(2)过滤器工厂GatewayFilterFactory:
Spring提供了31种不同的路由过滤器工厂
(3)过滤器作用:
- 对路由的请求或响应做加工处理
- 配置在路由下的过滤器只对当前路由的请求生效
(4)defaultFilters:对所有路由都生效的过滤器
6.全局过滤器GlobalFilter
(1)作用:
全局过滤器的作用是处理一切进入网关的请求和微服务响应。
(2)与GatewayFilter的区别:
区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。
(3)实现步骤:
- 实现GlobalFilter接口。
- 添加@Order注解或实现Ordered接口
- 编写处理逻辑
7.过滤器执行顺序
(1)请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器
(2)每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前
(3)GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值。
(4)路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增
(4)当过滤器的order值一样时,会按照defaultFilter>路由过滤器>GlobalFilter的顺序执行
8.跨域问题处理
网关处理跨域采用的同样是CORS方案,并且只需要简单配置即可
十、Docker
1.Docker架构
(1)镜像:Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像
(2)容器:镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器做隔离,对外不可见。
(3)Docker是一个CS架构的程序,由两部分组成:
- 服务端(server):Docker守护进程,负责处理Docker指令,管理镜像、容器等
- 客户端(client):通过命令或RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令。
2.镜像命令
(1)镜像名称一般分两部分组成:[repository]:[tag]
(2)在没有指定tag时,默认是latest,代表最新版本的镜像
(3)镜像操作命令:
3.容器命令
4.数据卷命令
(1)容器与数据耦合的问题:
- 不便于修改
- 数据不可复用
- 升级维护困难
(2)数据卷是一个虚拟目录,指向宿主机文件系统中的某个目录。
(3)数据卷操作基本语法:docker volume [COMMAND]
- create 创建一个volume
- inspect 显示一个或多个volume的信息
- ls 列出所有的volume
- prune 删除未使用的volume
- rm 删除一个或多个指定的volume
5.自定义镜像
(1)镜像结构:镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。
- BashImage层:包含基本的系统函数库、环境变量、文件系统
- Entrypoint:入口,是镜像中应用启动的命令
- 其他:再BaseImage基础上添加依赖、安装程序、完成整个应用的安装和配置
(2)Dockerfile:Dockerfile就是一个文本文件,其中包含一个个的指令,用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。
6.DockerCompose
(1)Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器!
(2)Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行
十一、MQ
1.同步调用优缺点
(1)优点:时效性强,可以立即得到结果
(2)问题:
- 耦合度高
- 性能下降
- 资源浪费
- 级联失败
2.异步通讯优缺点
(1)优势:
- 服务解耦
- 性能提升,吞吐量提高
- 服务没有强依赖,不担心级联失败问题
- 流量削峰
(2)缺点:
- 依赖于Broker的可靠性、安全性、吞吐能力
- 结构复杂,业务没有明显的流程线,不好追踪管理
3.MQ
4.MQ的不同用法
(1)基本消息队列(BasicQueue)
(2)工作消息队列(WorkQueue)
(3)发布订阅(Publish、Subscribe),根据交换机类型分为三种
- Fanout Exchange:广播
- Direct Exchange:路由
- Topic Exchange:主题
5.SpringAMQP
(1)发送消息:
- 引入amqp的starter依赖
- 配置RabbitMQ地址
- 利用RabbitTemplate的convertAndSend方法
(2)接收消息:
-
引入amqp的starter依赖
-
配置RabbitMQ地址
-
定义类,添加@Component注解
-
类中声明方法,添加@RabbitListener注解,方法参数就是消息
注意:消息一旦消费就会从队列删除,RabbitMQ没有消息回溯功能
(3)Work模型的使用
- 多个消费者绑定到一个队列,同一条消息只会被一个消费者处理
- 通过设置prefetch来控制消费者预取的消息数量
(4)发布订阅模型:发布订阅模式与work的区别就是允许将统一消息发送给多个消费者。实现方式是加入了exchange(交换机)。
注意:exchange负责消息路由,而不是存储,路由失败则消息丢失
(5)Fanout Exchange
Fanout Exchange会将接收到的消息路由到每一个跟其绑定的queue
(6)交换机作用:
- 接受publisher发送的消息
- 将消息按照规则路由到与之绑定的队列
- 不能缓存消息,路由失败,消息丢失
- FanoutExchange的会将消息路由到每个绑定的队列
(7)DirectExchange
Direct Exchange会将接收到的消息根据规则路由到指定的Queue,因此称为路由模式(routes)。
- 每一个Queue都与Exchange设置一个BindingKey
- 发布者发送消息时,指定消息的RoutingKey
- Exchange将消息路由到BindingKey与消息RoutingKey一致的队列
(8)TopicExchange
-
TopicExchange与DirectExchange类似,区别在于routingKey必须是多个单词的列表,并且以.分割
-
Queue与Exchange指定多个BindingKey时可以使用通配符:
:代指0个或多个单词
*:代指一个单词
(9)消息转换器
- SpringAMQP中消息的序列化和反序列化是利用MessageConverter实现的,默认是JDK的序列化
- 注意发送方与接收方必须使用相同的MessageConverter