微服务总结

推荐你阅读
互联网大厂万字专题总结
Redis总结
JUC总结
操作系统总结
JVM总结
Mysql总结
微服务总结
互联网大厂常考知识点
什么是系统调用
CPU底层锁指令有哪些
AQS与ReentrantLock原理
旁路策略缓存一致性
Java通配符看这一篇就够
Java自限定泛型

分布式架构相比于单体架构具有低耦合、业务拆分的特点,但也引入了一些问题比如数据一致性问题、复杂调用关系等等。微服务是一种分布式架构标准,微服务强调拆分粒度小 、每个微服务独立程度高 、每个微服务向外提供标准接口

微服务拆分原则需要遵守:

  • 不同微服务,不要重复开发相同业务
  • 微服务数据独立,不要访问其它微服务的数据库

SpringCloud 是目前国内使用最广泛的微服务框架 。SpringCloud 集成 了各种微服务功能组件,并基于 SpringBoot 实现了这些组件的自动装配。SpringCloud 和 SpringBoot 之间存在版本兼容关系。SpringCloud 中的各种微服务之间采用 http 协议进行接口调用。RestTemplate 接口提供了 http 接口调用的实现。

微服务注册中心

微服务注册中心支持服务提供者注册信息 ,支持服务消费者获取服务提供者的注册信息。相比于服务提供者的接口地址写死在服务消费者代码中的方式,微服务注册中心一方面支持服务提供者随时变动,另一方面也支持微服务调用的负载均衡。

Eureka

Eureka 支持服务提供者注册服务信息,支持服务消费者定时拉取服务信息。服务提供者通过心跳 的方式(默认 30s)告知 Eureka 自己的健康状态。

我们可以直接在 SpringBoot 中启动一个 Eureka 微服务(通过引入相关依赖、配置 yml 文件、配置注解),然后将其他微服务作为服务提供者注册到 Eureka(通过引入相关依赖、配置 yml 文件),最后作为服务消费者的任何微服务都可以拉取 Eureka 中的服务注册信息进行相应的微服务接口调用(通过引入相关依赖、配置 yml 文件、配置注解)。

微服务接口调用的负载均衡是通过 Ribbon 实现的,Ribbon 默认是采用懒加载。负载均衡接口 IRule 有一些默认的接口实现比如轮询(RoundRobin)、权重、区域优先等等。

Nacos

Nacos 是阿里的产品,相比 Eureka 功能更丰富。Nacos 直接提供了操作系统可执行文件(但依赖 jre)。

服务注册与拉取

Nacos 将一个服务分为了多个集群(通常一个集群指的是某区域的一个机房),每个集群又分为了多个实例。微服务互相访问时,应该尽可能访问同集群实例 ,因为本地访问速度更快。当本集群内不可用时,才访问其它集群。

我们同样可以通过引入相关依赖、配置 yml 文件、操作 Nacos 的 web 控制台的方式,完成服务提供者的集群注册、负载权重配置、服务消费者拉取信息等操作。

Nacos 注册中心对于服务提供者除了心跳检测还会主动询问 ,除了服务消费者会定时拉取 Nacos 注册中心信息外,Nacos 还会向服务消费者主动推送变更消息。

配置管理与热更新

我们可以在 Nacos 的 web 控制台上创建 yaml 配置,并将需要热更新 的配置放在其中,这样微服务的配置信息就由 bootstrap.yaml、Nacos 在线 yaml 和 application.yml 共同组成,我们通过 @Value 注解或@ConfigurationProperties 注解就可以读取热更新配置。当然上述过程还是需要通过引入相关依赖、配置 yml 文件、配置注解来完成。

Nacos 在线 yaml 配置有两种:

  • [spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml
  • [spring.application.name].yaml,例如:userservice.yaml

[spring.application.name].yaml不包含环境,因此可以被多个环境共享。

微服务远程调用

单体应用只需要提供接口给用户即可,但微服务由于其高内聚低耦合的特点,一方面微服务接口的实现需要依赖其他微服务接口,另一方面微服务接口也需同时提供给用户和其他微服务。

RestTemplate

Spring 官方提供的 http 接口调用框架

Feign

Feign 是一种接口声明式 的 http 微服务调用框架,我们可以对每个要调用的微服务声明一个接口,在里面为该微服务每个要调用的接口声明一个方法,可以指定微服务名称(用来从注册中心拉取信息)、请求方式(Get)、资源路径、pojo 返回类型。

Feign 底层依赖的 http 请求框架有多种,如果选择支持 http 连接池的框架则可能会带来性能优化。

微服务网关

Gateway

Gateway 基于响应式编程,提供了服务鉴权请求路由服务限流等核心功能。

  • 服务鉴权:网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。
  • 路由和负载均衡:一切请求都必须先经过 gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。
  • 限流:当请求流量过高时,在网关中按照对应微服务能够接受的速度来放行请求,避免服务压力过大。

引入相关依赖、配置 yml 文件即可启动 Gateway 网关微服务。

过滤器

过滤器可以对路由的请求或响应做加工处理,比如添加请求头等。Gateway 提供了多种类型的过滤器实现,我们可以在 yml 文件中配置路由过滤器和默认过滤器(只可以使用 Gateway 提供的过滤器),我们也可以实现自己的全局过滤器,通过编写自定义逻辑 实现登陆状态判断、权限校验、请求限流等功能。

多个过滤器执行的先后顺序取决于 order 值。

跨域问题

application.yml 中可以进行配置。

微服务部署

大型项目微服务很多,多个微服务之间可能存在依赖冲突,但每个微服务都单独部署在一个服务器不太实际,Docker 可以解决微服务间的依赖兼容性问题。

Docker

Docker 实现机制

docker 容器的运行由 docker 引擎管理 (docker 引擎由一系列负责各个功能的进程组成),根据 docker 官方推荐,大多数情况下我们只会在一个 docker 容器中运行一个进程,这个进程本质就是外部宿主机上 linux 内核的一个进程 ,可以直接在宿主机上查到。

docker 的关键是 linux 内核提供的隔离技术 ,多个 Docker 容器间通过 Namespace 技术进行进程隔离 ,通过 Cgroups 技术进行进程资源限制 。这也是我们进入 Docker 容器内部感觉像是一个完整操作系统的原因。

docker 镜像打包的其实是文件系统中的文件、要运行的程序等等。

docker 镜像是分层的,这使得多个 Docker 镜像间可以共享相同层。

Docker 与虚拟化技术对比

docker 的实现与虚拟化没有关系 ,本质就是进程隔离,因此硬盘占用低、启动速度快。

Vmware 这种虚拟化技术,是在宿主操作系统的基础上,用软件虚拟出一套计算机硬件 (这个中间层称为 Hypervisor),再基于虚拟出来的这套硬件安装任何想要的操作系统。因此应用运行在虚拟机上时,想要操作底层真实硬件需要依次通过虚拟机操作系统、Hypervisor、宿主机操作系统、底层真实硬件。因此硬盘占用大、启动慢、性能差。

Docker 关键点

  • 文件挂载:支持目录挂载、文件挂载。一方面方便修改配置文件 ,一方面容器删除后数据保留(如 mysql)。
  • 构建镜像:
    • 把当前容器构建为镜像
    • Dockerfile 构建镜像
  • DockerCompose:DockerCompose 脚本可以自动化构建镜像、运行容器。DockerCompose 可以实现微服务的自动部署。
  • 网络通信:
    • Docker 容器和外部网络通信需要在运行 Docker 容器的时候映射端口
    • Docker 容器与 Docker 容器之间的网络通信时,IP 写 Docker 容器名字即可,Docker 底层做好了实现。
  • 镜像仓库:可以搭建公司私有仓库。

消息队列 MQ

业务异步级联可以提高吞吐量降低业务耦合流量削峰填谷 。缺点是架构变复杂了,需要引入 MQ 消息队列。

常见的 MQ 技术包含:

  • ActiveMQ
  • RabbitMQ
  • RocketMQ
  • Kafka

RabbitMQ

五种消息模型

  • 只有队列
    • 基本消息队列:RabbitMQ 中只有一个队列,生产者往队列中直接放消息,消费者从队列中不断地取消息(直到达到设置的预取上限或者队列为空),消费者在异步线程中消费消息。RabbitMQ 保证了同一个队列放操作和取操作的原子性。
    • 工作消息队列:和基本消息队列的区别就是消费者变成了两个,整个过程基本不变。当我们把预取设为 1 的时候(也就是取一条消息,消费一条消息),那么两个消费者取消息的速率 和它们消费消息的速率成正比。预取上限需要在 yml 中配置 prefetch。
  • 交换机+队列
    交换机的作用是当有多个队列时,对于每个消息,决定将消息放到哪些队列中 。从每个队列自己的角度出发,仍然只存在生产者放消息和消费者取消息的过程,和之前一样。
    • 广播交换机(Fanout):消息放入绑定的所有队列
    • 直连交换机(Direct):根据绑定的 key 决定放入哪些队列
    • 主题交换机(Topic):根据主题匹配决定放入哪些队列

五大操作

主要有五大操作:创建交换机、创建队列、绑定交换机和队列、生产消息、消费消息

实现主要包括官方 maven 依赖原生实现、SpringAMQP 封装实现。AMQP 协议是一种基于 tcp 的专门用于应用程序间传递业务消息的标准(跨语言)。SpringAMQP 是 AMQP 协议在 Spring 框架中的实现,提供了 SpringBoot 自动装配。

最后需要注意 AMQP 传输的是数据字节,如果要发送 java 对象的话则需要序列化。SpringAMQP 的默认序列化方式是 jdk 序列化 ,jdk 序列化效率比较低,可以换成 Json 序列化,Json 序列化先将 java 对象转换为字符串,然后把字符串编码发送,接受后先解码成字符串再反序列化成对象即可。

死信队列

消息本身和队列都可以设置过期时间,达到两者的最小值时消息就会过期。

可以为队列设置死信交换机 ,这样队列中出现的所有死信不会被丢弃而是转到死信交换机进行重新路由,消息变成死信有三种情况:

  • 消息过期
  • 队列达到最大长度,队头消息会变成死信
  • 开启了手动确认,但消费者拒绝消息或收到消息后不确认,此消息就会变成死信

我们可以创建一个交换机,绑定一个队列,再为这个队列设置一个死信交换机,再为死信交换机绑定一个死信队列。

延迟队列

如果需要消息在延迟定长时间后被消费,可以通过死信队列来实现,设置队列的消息过期时间,然后再转到死信交换机,路由到死信队列,然后消费者消费死信队列即可。

Confirm 模式和 Return 模式

生产者和消费者的消息确认功能,用来保证可靠性。

备用交换机

当消息经过交换器准备路由给队列的时候,发现没有对应的队列可以投递信息,在 rabbitmq 中会默认丢弃消息,如果我们想要监测一个交换机有哪些消息没有对应的队列,可以为这个交换机绑定一个备用交换机,然后设置消费者接收备用交换机的消息,记录到日志或发送报警信息。

消息幂等性

消息幂等性是指对同一条消息的多次执行操作的结果与一次执行的结果相同。在分布式系统中,由于网络的不确定性,可能会导致消息被重复发送或者消息的顺序发生变化。为了确保系统的一致性,消息的处理需要具备幂等性。

保证消息幂等性也就是保证消息不被重复消费,可以通过为每个消息设置**全局唯一 ID **结合 redis **记录已消费消息 id **实现。

索引库 Elasticsearch

Elasticsearch 索引库和 mysql 等关系型数据库的存储模式类似,都是存储数据条目,也存在表、字段、增删改查等概念,但是在底层存储方式 上有很大区别。相比而言,mysql 的优势在于擅长事务类型操作 ,可以确保数据的安全与一致性,而 Elasticsearch 的优势在于擅长海量数据的高效搜索

Elasticsearch 底层是基于 lucene 来实现的。Lucene 是 Apache 公司采用 Java 语言实现的搜索引擎类库

浏览器搜索、电商搜索、网站内部搜索等都是由 Elasticsearch 完成的。

倒排索引

倒排索引的概念是基于 MySQL 这样的正向索引而言的。在 mysql 数据库中,如果表中某个字段没有建立索引,那么按照字段值查找条目的操作需要扫描整张表,如果字段建立了索引,那么按照字段值查找条目的操作就非常快,但是如果是按照字段值模糊匹配来查找条目的话,仍然是需要全表扫描的。

倒排索引可以解决这个问题。创建倒排索引的过程是对每一个条目的数据利用算法进行分词 ,然后建立分词到条目的映射 。倒排索引建立完成后,每个分词都映射了所有包含这个分词的条目,当查询某句话时,首先对这句话进行分词,然后取所有分词对应条目集合的并集。因此我们可以以 O(1) 的时间复杂度直接获取到所有和这句话有关的条目。
正向索引

  • 优点
    • 可以给多个字段创建索引
    • 根据索引字段搜索、排序速度非常快
  • 缺点
    • 根据非索引字段,或者索引字段中的部分词条查找时,只能全表扫描。

倒排索引

  • 优点
    • 根据词条搜索、模糊搜索时,速度非常快
  • 缺点
    • 无法根据字段做排序

Elasticsearch 对比 Mysql

Elasticsearch 中的条目是以 Json 格式存储的

MySQL Elasticsearch 说明
Table Index 索引(index),就是文档的集合,类似数据库的表(table)
Row Document 文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是 JSON 格式
Column Field 字段(Field),就是 JSON 文档中的字段,类似数据库中的列(Column)
Schema Mapping Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema)
SQL DSL DSL 是 elasticsearch 提供的 JSON 风格的请求语句,用来操作 elasticsearch,实现 CRUD

在企业中,往往是两者结合使用

  • 对安全性要求较高的写操作,使用 mysql 实现
  • 对查询性能要求较高的搜索需求,使用 elasticsearch 实现
  • 两者再基于某种方式,实现数据的同步,保证一致性

安装 Elasticsearch、kibana 和 ik 分词器

Elasticsearch、kibana 用 docker 部署即可,ik 分词器比较好的支持中文,有两种模式:

  • ik_smart:智能切分,粗粒度
  • ik_max_word:最细切分,细粒度

ik 分词器支持拓展词条和停用词条,分词器分词是依照字典来完成的,那么一些字典中没有的词语(比如最新的网络用语),就需要拓展词条,一些敏感词语则需要停用词条。

Elasticsearch 常见操作

mapping 是对索引库中文档的约束,常见的 mapping 属性包括:

  • type:字段数据类型,常见的简单类型有:
    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip 地址)
    • 数值:long、integer、short、byte、double、float
    • 布尔:boolean
    • 日期:date
    • 对象:object
  • index:是否创建索引,默认为 true
  • analyzer:使用哪种分词器
  • properties:该字段的子字段

索引库 CRUD

  • 创建索引库:创建时需要想好以下几点
    • 字段名
    • 字段数据类型
    • 是否参与搜索(或者说是否创建这个字段的倒排索引),需要根据业务来判断
    • 是否需要分词,内容如果是一个整体就无需分词,反之则要分词
    • 如果分词,分词器是什么?可以统一使用 ik_max_word
  • 查询索引库
  • 修改索引库:修改索引库只允许新增字段到 mapping 中,不允许修改原有字段(因为会对已经建立好的倒排索引产生影响)
  • 删除索引库

文档 CRUD

  • 新增文档
  • 查询文档:后面详述
  • 删除文档
  • 修改文档:全量修改/增量修改

ES 客户端

ES 官方提供了各种不同语言的客户端,用来操作 ES。这些客户端的本质就是组装 DSL 语句,通过 http 请求发送给 ES

我们可以用 Java HighLevel Rest Client 客户端 API 来操作 ES,导入相关 maven 依赖后即可使用。RestClient 进行索引库 CRUD 和文档 CRUD 的具体方式可以查询笔记。

Elasticsearch 查询文档

查询文档的 DSL 是一个大的 JSON 对象,包含下列属性:

  • query:查询条件
  • sort:排序条件
  • from 和 size:分页条件
  • highlight:高亮条件

RestClient 查询文档的 api 接口类似于查询文档的 DSL,具体可查笔记。

查询条件
  • 查询所有:查询出所有数据,match_all
  • 全文检索查询 :利用分词器对用户输入内容分词,然后去倒排索引库中匹配。
    • match_query:单字段
    • multi_match_query:多字段
  • 精确查询 :根据精确词条值查找数据,一般是查找 keyword、数值、日期、boolean 等类型字段。
    • range:查找数值或日期的范围
    • term:精准匹配 keyword、数值、日期和 boolean
  • 地理(geo)查询 :根据经纬度查询。
    • geo_distance:查半径范围内
    • geo_bounding_box:查矩形范围内
  • 复合(compound)查询 :复合查询可以将上述各种查询条件组合起来,合并查询条件。
    • bool:条件复合
    • function_score:如果我们没有制定排序顺序的话,ES 会对满足条件的所有文档按照相关度函数进行算分,打分高的文档排在前面。function_score 可以调整一些特定文档的算分,比如提高广告文档的排名。

结果排序

elasticsearch 默认是根据相关度算分排序,但也可以自己指定排序。

结果分页

elasticsearch 默认情况下只返回 top10 的数据,修改 from、size 参数可以控制要返回的分页,类似于 mysql 中的 limit。

关键字高亮

对于查询结果中所有匹配的关键字进行高亮显示,实现方式是给文档中的所有关键字都添加一个标签,例如<em>标签。前端可以给<em>标签编写 CSS 样式来高亮。

数据聚合

elasticsearch 的数据聚合和 mysql 的数据聚合功能类似,但是速度更快。

聚合常见的有三类:

  • **桶(Bucket)**聚合:用来对文档做分组
    • TermAggregation:按照文档字段值分组,例如按照品牌值分组、按照国家分组
    • Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组
  • **度量(Metric)**聚合:用以计算一些值
    • Avg:求平均值
    • Max:求最大值
    • Min:求最小值
    • Stats:同时求 max、min、avg、sum 等

默认情况下,Bucket 聚合是对索引库的所有文档做聚合,但真实情况下往往是对搜索结果进行聚合。

自动补全

当我们在搜索框中输入一些文字时,会显示一些自动补全文字。

elasticsearch 提供了 Completion Suggester 查询来实现自动补全功能。这个查询会匹配以用户输入内容开头的词条并返回。为了提高补全查询的效率,对于文档中字段的类型有一些约束:

  • 参与补全查询的字段必须是 completion 类型。
  • 字段的内容一般是用来补全的多个词条形成的数组。

如果希望能根据字母提示中文汉字的话,需要安装拼音分词器,然后自定义分词器,先用 ik 分词器进行分词,对于每个分出来的词语,在用拼音分词器进行分词。

数据同步

当 mysql 数据库发生增删改时,elasticsearch 也必须跟着进行增删改。

常见的数据同步方案有三种:

  • 同步调用:微服务 A 增删改 mysql 之后,马上调用微服务 B 的接口对 elasticsearch 进行相应的增删改。
  • 异步通知:微服务 A 增删改 mysql 之后,发送一个 MQ 消息,微服务 B 持续消费消息对 elasticsearch 进行相应的增删改。
  • 监听 binlog:mysql 开启 binlog 功能,canal 监听 binlog 变化,实时更新 elasticsearch 中的内容。
相关推荐
疯狂飙车的蜗牛1 小时前
从零玩转CanMV-K230(4)-小核Linux驱动开发参考
linux·运维·驱动开发
向前看-2 小时前
验证码机制
前端·后端
xlsw_2 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹3 小时前
基于java的改良版超级玛丽小游戏
java
远游客07133 小时前
centos stream 8下载安装遇到的坑
linux·服务器·centos
马甲是掉不了一点的<.<3 小时前
本地电脑使用命令行上传文件至远程服务器
linux·scp·cmd·远程文件上传
jingyu飞鸟3 小时前
centos-stream9系统安装docker
linux·docker·centos
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
工业甲酰苯胺3 小时前
分布式系统架构:服务容错
数据库·架构