disgare 的博客
首页
博客
分类
标签
首页
博客
分类
标签
  • 网络

    • 计算机网络学习笔记
    • 网络安全相关
    • 域名和子网掩码
    • CORS 跨域资源共享
    • DNS、HTTP 与 HTTPS
    • Server-Sent Events (SSE)
    • WebSocket 长连接
  • 计算机基础

    • 操作系统 IO 相关知识
    • 操作系统学习笔记
    • 程序的机器级表示
    • 音频文件基础
    • 正则表达式相关概念
    • ffmpeg 的安装以及实现音频切分功能
    • Hex 和 Base64 编码
    • XML 的使用
  • 数据结构与算法

    • 动态规划算法学习笔记
    • 基于比较的排序算法的最坏情况下的最优下界为什么是O(nlogn)
    • 集合与数据结构学习笔记
    • 面试常见算法总结
    • 算法导论第二部分排序学习笔记
    • 算法导论第一部分学习笔记
  • Java

    • 对象之间的映射与转换
    • 反射学习笔记
    • 泛型相关概念
    • 关于 boolean 类型的坑
    • 如何使用 lambda 表达式实现排序
    • CompletableFuture 相关用法
    • CompletableFuture 源码浅要阅读
    • FutureTask 源码阅读
    • Guava 常用 API
    • Guava 源码阅读:Multimap 相关
    • Jackson 的各种使用
    • Java 的 Excel 相关操作
    • java 的常见性能问题分析以及出现场景
    • java 基础知识
    • JAVA 枚举的基础和原理
    • Java 图片文件上传下载处理
    • Java 序列化
    • Java 异常
    • Java 语法糖
    • Java 中关于字符串处理的常用方法
    • Java 中强、软、弱、虚引用
    • JAVA 注解小结
    • Java Http 访问框架
    • Java Stream 的使用
    • Java8 新特性
    • netty 学习笔记
    • Scanner 的各种用法
    • Servlet 学习笔记
    • String、StringBuffer、StringBuilder 学习笔记
  • JVM

    • 虚拟机执行子系统
    • JVM 自动内存管理
    • Linux 中 JVM 常用工具以及常见问题解决思路
  • Linux

    • crontab 表达式
    • Linux 常见命令
    • Linux 文件系统
  • 中间件

    • 关于定时任务原理
    • 详解 kafka
    • ES 搜索引擎
    • flink 提交流程
    • Grape-RAG
    • Hadoop 基础原理
  • 多线程

    • 多线程基础学习笔记
    • 简单了解并发集合
    • 如何手写单例
    • 深入理解 java 多线程安全
    • 生产者消费者问题
    • 线程池作用、用法以及原理
    • AQS 组件
    • ThreadLocal 原理以及使用
  • 非关系型数据库

    • Redis 集群
    • Redis 数据结构、对象与数据库
    • Redis 学习笔记
  • 关系型数据库

    • B+ 树的插入、删除和数据页分裂机制
    • MySQL 的 binglog、redolog、undolog
    • MySQL 的记录存储结构、存储引擎与 Buffer Pool
    • MySQL 基本的特性
    • MySQL 开发规范
    • MySQL 事务与锁与 MVCC
    • MySQL 数据类型、字符集相关内容
    • MySQL 索引与索引优化
    • PostgreSQL 更新数据时 HOT优化
    • PostgreSQL 相关用法
  • Python

    • Python 基础语法
    • Python 学习
  • Spring 项目

    • Lombok 的常用注解
    • maven 小结
    • MyBatis 框架的使用
    • MyBatis 重要知识点总结
    • MybatisPlus 的使用
    • Spring 框架基础使用
    • Spring 事务相关
    • Spring IOC 的原理及源码
    • Spring AOP 的使用和原理
    • SpringBoot 的原理
    • SpringBoot 基础使用
    • SpringWeb 重要知识点
  • 分布式

    • 初步了解 docker
    • 从 ACID 到 BASE 事务处理的实现
    • 访问远程服务
    • 分布式 id
    • 分布式缓存相关问题
    • 分布式集群理论和分布式事务协议
    • 分布式架构的观测
    • 分布式一致性算法
    • 负载均衡 Load Balancing
    • 关于分布式系统 RPC 中高可用功能的实现
    • 集群间数据同步的目的
    • 三高问题下的系统优化
    • 数据库分库分表
    • 详解 Spring Cloud
      • 服务发现
        • 分类
        • 注册中心需要考虑的问题
      • 服务调用
        • 负载均衡
        • 客户端负载均衡
      • 服务容灾
        • 容灾策略
        • 容灾设计实现
      • 服务网关
        • 限流算法
        • 流量统计指标
        • 限流设计模式
      • 消息总线和配置中心
      • 常用的框架
        • Nacos、Zookeeper、Euraka、Consul
        • Feign
        • 基础使用
        • 最佳实践
        • Ribbon
        • Hystrix
        • 使用
        • Gateway
      • 高可用和高并发
        • 在微服务系统中如何保持高可用
        • 在微服务系统中如何保持高并发
        • 高并发情况下如何保证接口的幂等性
    • Dubbo 基础概念
    • Gossip 协议
    • nginx 学习笔记
    • Protobuf 通信协议
    • Zookeeper 基础学习
  • 架构设计

    • 参数校验与异常处理
    • 抽象方法与设计模式
    • 代码整洁之道
    • 权限系统设计
    • 用低内存处理大量数据
    • 设计模式——策略模式
    • 设计模式——过滤器模式在 Spring 中的实践
    • 状态模式
    • 统一结果返回
    • 为什么要打日志?怎么打日志?打什么日志?
    • 运维监控常见指标含义
    • 资深研发进阶
    • DDD 架构学习笔记
    • Java 常用的规则引擎
    • MVC 架构学习笔记
  • AI

    • 如何编写 Prompt
    • Agent 工程架构
    • LLM 相关内容
    • NLP 相关知识
    • vibe coding 最佳实践
    • windows 下 ollama 迁移到 D 盘
  • 开发工具

    • 如何画时序图、流程图、状态流转图
    • excel 关于 =vlookup 的用法
    • git 的学习以及使用
    • IDEA 插件推荐
    • IDEA 常用快捷键以及调试
    • Shell 脚本
    • swagger 的使用
  • 前端

    • 简单了解前端页面开发
    • 伪静态是什么
    • GitHub Pages 部署教程
    • Vercel 部署教程
    • vue-admin-template 简单使用
    • VuePress 博客搭建指南
  • 项目

    • 面试刷题网——技术方案
    • 影视资源聚合站——技术方案
  • 问题记录

    • 定时任务单线程消费 redis 中数据导致消费能力不足
    • 提供可传递的易受攻击的依赖项
    • Liteflow 在 SpringBoot 启动时无法注入组件问题 couldn‘t find chain with the id[THEN(NodeComponent)]
  • 金融

    • 股票分析——关于电力
    • 股票技术面——量价关系
    • 股票技术面——盘口
    • 股票技术面——基础
    • 基础的金融知识
    • 基金与股票
    • 韭菜的自我总结
    • 聊聊价值投资
  • 其他

    • 程序员职场工作需要注意什么
    • 创业全链路SOP:从灵光一现到系统化增长的实战指南
    • 观罗翔讲刑法随笔
    • 价格和价值
    • 立直麻将牌效益理论
    • 梅花易数学习笔记
    • 压力管理
2022-05-03
分布式
目录

详解 Spring Cloud

Spring Cloud 是一系列框架的集合,它里面的框架解决了微服务需要解决的各种问题

而这一系列框架分别实现了如服务发现注册 、配置中心 、消息总线 、负载均衡 、断路器 、数据监控等操作

本文简单说明一下,各个组件的思想、为什么会出现这些组件以及组建的简单实现

# 服务发现

所有的远程服务调用都是使用全限定名(Fully Qualified Domain Name,FQDN (opens new window))、端口号与服务标识所构成的三元组来确定一个远程服务的精确坐标的

全限定名代表了网络中某台主机的精确位置,端口代表了主机上某一个提供了 TCP/UDP 网络服务的程序,服务标识则代表了该程序所提供的某个具体的方法入口

那我们如何让程序知道哪些服务器提供了哪些服务、哪些服务不能用呢

# 分类

服务发现一般分为以下两种:

第一种是类似 DNS 服务一样的服务发现,即将用户的域名转换为网络上的真实地址全限定名加端口

第二种是以 UDDI 为代表的百科全书式的服务发现,上至提供服务的企业信息(企业实体、联系地址、分类目录等等),下至服务的程序接口细节(方法名称、参数、返回值、技术规范等等)都在服务发现的管辖范围之内

由于注册中心是整个服务协调的灵魂,如果注册中心挂了那么就算提供了服务也无法被调用,需要在高准确性的同时满足高性能,以此必须同时满足可用性和可靠性

# 注册中心需要考虑的问题

RPC 中的注册中心,用于让不同服务器之间了解对方的方法,因此需要有一套完整的注册、校验、删除机制,万一提供服务的机器挂了或者想在集群中加一台机器,此时应该做些什么呢

服务注册:当客户端向注册中心注册时,它提供自身的元数据,比如 IP 地址、端口,运行状况指示符 URL 等

服务续约:心跳检验机制,为了了解客户仍然存在,没有出现问题,如果注册中心没有在一定时间收到服务提供方的消息,它会将实例从其注册表中删除

获取注册列表信息:客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期更新一次(客户端负载均衡)

服务务发现:这里的发现是特指狭义上消费者从服务发现框架中,把一个符号转换为服务实际坐标的过程,这个过程现在一般是通过 HTTP API 请求或者通过 DNS Lookup 操作来完成,也还有一些相对少用的方式

服务下线:客户端在程序关闭时向服务器发送取消请求。 发送请求后,该客户端实例信息将从服务器的实例注册表中删除

服务剔除:客户端出现故障时,注册中心会通过心跳校验了解情况,并自动将其从注册表中剔除

# 服务调用

服务发现之后,需要使用一些框架进行不同服务器之间的方法相互调用

服务调用就和 RPC 过程相似,使用序列化协议与传输协议来进行数据的传输,springcloud 的框架就是解决这一问题

# 负载均衡

在服务调用时,一般会进行负载均衡的判断,由此来选择最为合适的服务器。因为微服务本身就是解决大量请求的方案,因此为了整个系统的高可用 ,我们需要将服务器做一个集群,为了不让集群中的某一个机器承受更多请求,负载均衡器也是必须的

负载均衡优先级配置如下:

  • 客户端方法级别配置
  • 客户端接口级别配置
  • 服务端方法级别配置
  • 服务端接口级别配置

总结来说就是方法级大于接口级,客户端大于服务端

# 客户端负载均衡

服务端负载均衡(集中式):之前说到的服务调用负载均衡在客户端进行负载均衡才进行请求的服务网关负载均衡在网关或者 nginx 进行负载均衡,这是一种集中式的负载均衡,也叫客户端负载均衡

客户端负载均衡(分布式):客户端均衡器是和服务实例一一对应的,而且与服务实例并存于同一个进程之内(也就是说均衡器在请求方),均衡器与服务之间信息交换是进程内的方法调用,不存在任何额外的网络开销,以此调用方法会少一次网络请求

# 服务容灾

在微服务中有一些特征是无法做出妥协的,其中的典型就是容错性设计。一个大的服务集群中,程序可能崩溃、节点可能宕机、网络可能中断,这些意外情况其实全部都在意料之中,发生错误之后如何才能让整个服务正常向外部提供服务,这个问题便是重中之重

# 容灾策略

容错策略指的是面对故障,我们该做些什么,这些策略提供一些指导思想,让我们遇到错误时不至于漫无目的。一般对于远程调用框架而言,

1,故障转移

故障转移是指如果调用的服务器出现故障,系统不会立即向调用者返回失败结果,而是自动切换到其他服务副本,尝试其他副本能否返回成功调用的结果

故障转移的实现应当有一定的调用次数限制,以防止过多的错误调用影响系统性能

同时,被调用的接口应当有幂等性,比如 get、remove、put,不然可能会生成脏数据

2,快速失败

快速失败一般使用于一些在与金额支付相关的操作中,比如在支付场景中,需要调用银行的扣款接口,如果该接口返回的结果是网络异常,程序是很难判断到底是扣款指令发送给银行时出现的网络异常,还是银行扣款后返回结果给服务时出现的网络异常的。为了避免这种情况,程序应当尽快抛出异常,由调用者自行处理

3,安全失败

对于一些不重要的业务,一种理想的容错策略是即使旁路逻辑调用实际失败了,也当作正确来返回,如果需要返回值的话,系统就自动返回一个符合要求的数据类型的对应零值,然后自动记录一条服务调用出错的日志备查即可,这种策略被称为安全失败

4,沉默失败

在大量的请求需要等到超时(或者长时间处理后)才宣告失败的时候,很容易由于某个远程服务的请求堆积而消耗大量的线程、内存、网络等资源。此时我们让机器在一段时间内不在对外提供同类型服务,因为该次失败很可能下一次调用也失败。沉默失败会让系统不再向错误机器分配请求流量,将错误隔离开来,避免对系统其他部分产生影响

5,并行调用和广播调用

这两种算是以性能获取准确性的方法,希望在调用之前就开始考虑如何获得最大的成功概率。并行调用是指一开始就同时向多个服务副本发起调用,只要有其中任何一个返回成功,那调用便宣告成功。而广播调用则是要求所有的请求全部都成功,这次调用才算是成功

6,故障恢复

故障恢复一般不单独存在,而是作为其他容错策略的补充措施,一般在微服务管理框架中,如果设置容错策略为故障恢复的话,通常默认会采用快速失败加上故障恢复的策略组合。它是指当服务调用出错了以后,将该次调用失败的信息存入一个消息队列中,然后由系统自动开始异步重试调用

由于故障恢复可能会发送多次请求,因此他与故障转移有一些相同的限制条件,比如服务必须具备幂等性的,有最大重试限制,同时故障恢复策略一般用于对实时性要求不高的主路逻辑,同时也适合处理那些不需要返回值的旁路逻辑

# 容灾设计实现

1,断路器模式

又称为服务熔断,断路器模式是为了处理服务雪崩而出现的,是快速失败策略的一种实现。简单来说处理过程是,当满足某种条件时(一定时间内响应请求的成功率较小并且数量较大,比如10秒内请求成功率为百分之五十,发了三百个请求),系统将通过断路器直接将此请求的所有链路都断开

服务被熔断之后,熔断器将自动(一般是由下一次请求而不是计时器触发的,所以这里自动带引号)切换到半开启状态。该状态下,会放行一次远程调用,然后根据这次调用的结果成功与否,转换自身的状态,以实现断路器的弹性恢复

下面来说一下两个基础概念:

服务雪崩:当某一台机器出现自身故障等情况时,它不能及时处理请求,而调用这个机器的客户端不得不等待回应。这个等待时间是需要消耗 IO 和线程资源的。由于这一系列的连锁反应导致整个系统都会崩掉,这种情况叫服务雪崩

熔断和降级的区别:服务熔断对应断路器模式,满足条件则拒绝所有的服务请求。服务降级对应后备处理模式、安全失败,这种模式面向用户,为了给用户一个好的体验,它会执行另外一种方法来给用户友好的回应,在服务熔断之后,上游应当处理这个错误,给用户返回合理的回应,这里的处理就是服务降级。降级的目的在于应对系统自身的故障,而熔断的目的在于应对当前系统依赖的外部系统或者第三方系统的故障

2,舱壁隔离模式

又称服务隔离,是沉默失败的一种实现,它原本的意思是设计舰船时,要在每个区域设计独立的水密舱室,一旦某个舱室进水,也只是影响这个舱室中的货物,而不至于让整艘舰艇沉没

在微服务的场景下,舱室进水指的是,一些请求的处理时间相当长,占用了线程资源,一般来说,普通的 java 程序只会设置 200 到 300 条线程,如果该类型请求过多,导致所有的线程被占满,此时该服务就无法处理其他的请求了。水密舱室指的是处理方法,在这种情况出现时,我们一般有以下两种处理方法:

  • 使用线程池来接受该类型请求,线程池设置了最大线程数,就算接受了过多的请求,也不会占用太多机器线程资源
  • 使用信号量来统计该类型线程数,并且设置一个最大阈值,达到这个阈值就不给该资源分配线程资源

3,重试模式

请求返回错误后放进消息队列再发一次请求,是服务恢复的实现。重试模式实现并不困难,即使完全不考虑框架的支持,靠程序员自己编写十几行代码也能够完成。在实践中,重试模式面临的风险反而大多来源于太过简单而导致的滥用。我们判断是否应该且是否能够对一个服务进行重试时,应同时满足以下几个前提条件

  • 仅在主路逻辑的关键服务上进行同步的重试,不是关键的服务,一般不把重试作为首选容错方案,尤其不该进行同步重试
  • 仅对由瞬时故障导致的失败进行重试
  • 仅对具备幂等性的服务进行重试
  • 重试必须有明确的终止条件,最好一下两种都配上,一是超时终止,并不限于重试,所有调用远程服务都应该要有超时机制避免无限期的等待;二是次数终止、重试必须要有一定限度,不能无限制地做下去

# 服务网关

网关是一个系统对外的唯一出入口,网关最重要的是路由功能,即负载均衡,当然你也可以加其他的功能,比如限流、过滤、安全、认证、授权、监控、缓存等等

因此,简单来说

网关 = 路由器(基础职能) + 过滤器(可选职能)
1

由于网关是所有服务对外的总出口,是流量必经之地,所以网关的路由性能将导致全局的、系统性的影响,如果经过网关路由会有 1 毫秒的性能损失,就意味着整个系统所有服务的响应延迟都会增加 1 毫秒。因此我们应当额外关注网关的性能与可用性

# 限流算法

当遇到瞬时请求量激增时,会导致接口占用过多服务器资源,使得其他请求响应速度降低或是超时,更有甚者可能导致服务器宕机。这种情况常见于热点业务或者黑客攻击,早期的 12306 系统就明显存在这样的问题,全国人民都上去抢票的结果是全国人民谁都买不上票。限流算法可以解决这一问题,利用流量控制减少进入系统的请求量,以避免系统的崩溃

所有的限流算法都是当请求到达一定数目之后,丢弃溢出的请求来实现的,那么我们如何实现这一功能呢

# 流量统计指标

每秒事务数:TPS 是衡量信息系统吞吐量的最终标准。事务可以理解为一个逻辑上具备原子性的业务操作

每秒请求数:HPS 是指每秒从客户端发向服务端的请求数,如果只要一个请求就能完成一笔业务,那 HPS 与 TPS 是等价的,但在一些场景(尤其常见于网页中)里,一笔业务可能需要多次请求才能完成

每秒查询数:QPS 是指一台服务器能够响应的查询次数

# 限流设计模式

常用的限流算法有四种:

1,固定窗口:又称流量计数器模式,这种算法将每一个时间段设置为一个窗口,当窗口容纳请求的数量满了之后,丢弃所有的溢出请求。这种算法有一个缺点,如下图所示

image-2026-01-31-21-06-17.png 2,滑动窗口:这个算法使用先进先出解决上面的问题,在不断向前流淌的时间轴上,漂浮着一个固定大小的窗口,窗口与时间一起平滑地向前滚动。任何时刻静态地通过窗口内观察到的信息,都等价于一段长度与窗口大小相等、动态流动中时间片段的信息

假如我们准备观察时间片段为 10 秒,并以 1 秒为统计精度的话,那可以设定一个长度为 10 的数组(设计通常是以双头队列去实现,这里简化一下)和一个每秒触发 1 次的定时器。这也说明,滑动窗口其实和固定窗口差不多,但是将每一个窗口都切的更细以获取精确度

3,漏桶算法:处理完一个请求代表从桶中流出一颗水滴,每接受一个请求代表向桶中加入一颗水滴,桶可以接受的水滴有限。漏桶以固定速率向外漏出水滴,当水桶已满时便拒绝新的请求进入

现实中系统的处理速度往往受到其内部拓扑结构变化和动态伸缩的影响,所以能够支持变动请求处理速率的令牌桶算法往往可能会是更受程序员青睐的选择

4,令牌桶算法:和漏桶算法相反,固定时间段内向桶中加入一个令牌,令牌满了则丢弃令牌,当请求到达时,会尝试从令牌桶中取令牌,取到了令牌的请求就可以执行;如果桶空了,那么尝试取令牌的请求会被直接丢弃

# 消息总线和配置中心

消息总线用于将服务和服务实例与分布式消息系统链接在一起的事件总线。在集群中传播状态更改很有用(例如配置更改事件)

配置为分布式系统中的外部化配置提供服务器和客户端支持,可以在中心位置管理所有环境中应用程序的外部属性

配置中心的流程如下:

1,管理员通过控制台 Nacos API 发布配置 2,Nacos Server 将配置持久化到数据库(MySQL) 3,同时写入 Server 的内存缓存(ConcurrentHashMap) 4,触发配置变更通知

不同的配置中心推送配置的方式不一样,可能使用长轮询,可能使用长连接

image-2026-01-31-21-06-32.png

# 常用的框架

# Nacos、Zookeeper、Euraka、Consul

这些框架承当了服务注册功能,非常方便实用。其原理都差不多:

1,服务发现:消费应用从注册中心获取服务列表,从而得知每个服务方的信息,知道去哪里调用服务方。而服务应用则会在启动的时候去联系注册中心,将自己记录到注册中心中

2,服务续约:在注册服务完成以后,服务提供者会维持一个心跳(定时向 EurekaServer 发起 Rest 请求),有两个重要参数可以修改服务续约的行为

eureka: 
	instance: 
	    #服务失效时间,默认值90秒
		lease-expiration-duration-in-seconds: 90 
		#服务续约(renew)的间隔,默认为30秒
		lease-renewal-interval-in-seconds: 30
1
2
3
4
5
6

以上配置表示,默认情况下每隔30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心跳,EurekaServer 就会认为该服务宕机,会定时(eureka.server.eviction-interval-timer-in-ms 设定的时间)从服务列表中剔除

3,失效剔除和自我保护:服务注册中心在启动时会创建一个定时任务,默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除。

Eureka Server 在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况,Eureka Server 会将当前的实例注册信息保护起来,不再删除服务注册表中的数据(也就是不会注销任何微服务)

# Feign

这个框架用于解决服务调用相关问题,是一种声明式、模板化的 HTTP 客户端,和 dubbo、grpc 等 rpc 框架功能类似,不过它主要是对访问三方接口的代码进行封装,可以封装 http 接口,而 dubbo 做的更加全面,是从新开始写了一套 rpc 框架。内嵌 Ribbon 框架用于解决负载均衡、内嵌 Hystrix 用于解决熔断降级问题,限流需要使用 Resilience4j、Sentinel,而超时则是需要依赖 http 客户端,比如 OkHttp、HttpClient 等

# 基础使用

Feign 可以替换一些网关比如 nginx 或者一些服务调用框架比如 dubbo,我们可以使用 feign 包装 http 接口,来进行服务调用:

依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
1
2
3
4

封装接口:

// fallbackFactory 就是服务调用失败后,会执行的操作,我们可以在 ApiFallbackFactory 中进行一些降级、重试等操作
@FeignClient(name = "fleet-api", fallbackFactory = ApiFallbackFactory.class)
public interface TestApi {
	// 我们可以使用 RequestHeader、RequestParam 等字段,制定数据绑定到请求的什么地方
    @GetMapping(path = "/test1")
    ManagerConveyResp test1(@RequestHeader(API_KEY_HEADER) String caller, @RequestParam(name = "driver_id") long driverId);

	// 如果需要发送的字段太多,可以使用 SpringQueryMap 注解,将要发送的字段都绑定到 FleetBaseQueryRequest 中
    @GetMapping(path = "/test2")
    ManagerConveyResp test2(@RequestHeader(API_KEY_HEADER) String caller, @SpringQueryMap FleetBaseQueryRequest fleetBaseQueryRequest);
}
1
2
3
4
5
6
7
8
9
10
11

调用代码:

    @Autowired
    UserFeignServuce userFeignServuce;

    @RequestMapping(value = "/test",method = RequestMethod.POST)
    private CommonResult test(){
        String name = "OK";
        String key = userFeignServuce.test(name);
        return new CommonResult(key );
    }
1
2
3
4
5
6
7
8
9

我们还需要开启 feign

@SpringBootApplication
@EnableFeignClients    //开启Feign
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class,args);
    }
}
1
2
3
4
5
6
7

feign 还有一些配置,可以在代码中配置或者在 yml 文件中配置:

feign:
    client:
        config:
            feignName:
                #防止由于服务器处理时间长而阻塞调用者
                connectTimeout: 5000
                #从连接建立时开始应用,在返回响应时间过长时触发
                readTimeout: 5000
1
2
3
4
5
6
7
8

还有一个 feign 组件,自定义拦截器,可以实现一些想实现的拦截器逻辑,需要实现 RequestInterceptor 接口

/**
 * feign拦截器
 */
@Slf4j
public class CustomFeignInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        //TODO  根据需求可以在拦截器中扩展需要的功能
        log.info("比如记录日志 /增加参数 / 修改路径 / 鉴权");
    }
}
1
2
3
4
5
6
7
8
9
10
11

拦截器可以只用于某个接口

#开启sentinel对feign组件远程调用服务降级的支持
feign:
  client:
    config:
      #需要配置的服务名称
      manage-client:
        loggerLevel: FULL
        #连接超时时间,默认2s
        connectTimeout: 5000
        #请求处理超时时间,默认5a
        readTimeout: 10000
        #拦截器配置
        requestInterceptors:
       	- com.car.hailing.saas.manage.feign.intercptor.CustomFeignInterceptor
1
2
3
4
5
6
7
8
9
10
11
12
13
14

我们还可以继承 Client 类,重写里面的 execute 方法,用来接入自己内部的 rpc 框架上文说用 feign 接 dubbo 是完全可以做到的,就是没啥必要

# 最佳实践

一,继承方式:定义一个 API 接口,利用定义方法,并基于 SpringMVC 注解做声明。Feign 客户端和 Controller 都集成该接口

image-2026-01-31-21-06-45.png 此时服务提供方、服务消费方紧耦合。适合我们作为服务提供者的场景

二、抽取方式:将 Feign 的 Client 抽取为独立模块,并且把接口有关的 POJO、默认的 Feign 配置都放到这个模块中,提供给所有消费者使用 image-2026-01-31-21-06-59.png

# Ribbon

Ribbon 作为一个负载均衡框架肯定有很多独特的实现,这里说一下常见的负载均衡算法,分轮询、hash、随机、最小活跃数这四类,这些算法都可以优化,比如加权轮询、随机权重、一致性 hash 等算法

负载均衡算法实现都需要考虑一个问题,算法如何在服务列表变化的情况下正确执行。比如在感知到服务列表变化时,应通过一些同步机制,及时的更新正在进行选择时的服务列表以及权重比例,及时重新计算相关数组以及总权重数

ribbon 是客户端负载均衡

修改负载均衡规则的配置:

springbootdemo:
  ribbon: 
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#格式是: {服务名称}.ribbon.NFLoadBalancerRuleClassName
1
2
3
4

以下是一些负载均衡策略

  • com.netflix.loadbalancer.RoundRobinRule :以轮询的方式进行负载均衡
  • com.netflix.loadbalancer.RandomRule :随机策略
  • com.netflix.loadbalancer.RetryRule :重试策略
  • com.netflix.loadbalancer.WeightedResponseTimeRule :权重策略
  • com.netflix.loadbalancer.BestAvailableRule :最佳策略
  • com.netflix.loadbalancer.AvailabilityFilteringRule :可用过滤策略

Ribbon 的第一次调用时间较长,因为它默认采用懒加载策略,是只有在第一次请求时才会初始化对应服务的负载均衡器。同时 HTTP 连接建立、Feign 自身的组件初始化也会导致第一次调用时间过长。我们可以配置预热与预加载来处理这个问题

## 配置 Ribbon 在启动时立即初始化(Spring Cloud Netflix)
ribbon:
  eager-load:
    enabled: true
    clients: user-service,order-service  ## 指定需要预加载的服务
1
2
3
4
5

# Hystrix

Hystrix 就是一个能进行熔断和降级的库,通过使用它能提高整个系统的弹性

# 使用

在启动类上添加注解开启 Hystrix

@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class ConsumeServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumeServerApplication.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate returnBean() {
        return new RestTemplate();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

编写熔断降级逻辑。在方法上加上 @HystrixCommand 注解,并指定了 fallbackMethod 熔断方法

@ResponseBody
@RequestMapping(value = "/info", method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "backErrorInfo")
public String printInfo() {
    String url = "http://springbootdemo/backInfo";
    return restTemplate.getForObject(url, String.class);
}
public String backErrorInfo(){
    return "sorry,error";
}
1
2
3
4
5
6
7
8
9
10

测试,如果 springbootdemo 服务关闭,会发现页面返回了降级处理信息

# Gateway

Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式。Spring Cloud Gateway 作为 Spring Cloud 生态系中的网关,目标是替代 Netflflix ZUUL,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等

下面是 gateway 中的重要概念:

  • 1,路由:包含url、ID、一组断言和一组过滤器
  • 2,断言:全称叫断言函数,这个函数中写了匹配规则
  • 3,过滤器:一层层过滤器会对请求或者响应做出修改

gateway 一般由以下部分组成:

  • 1,Mapping:映射器,映射名称与url的组件
  • 2,handler:执行器,发送请求
  • 3,filter:过滤器

# 高可用和高并发

# 在微服务系统中如何保持高可用

  • 1,集群:相同的服务部署多份,避免单点故障。对外部提供服务时,通过 zk 等注册中心,将服务的多个实例注册到注册中心,当有请求过来时,通过负载均衡策略,将请求分配到不同的实例上。这样可以实现服务的高可用,同时还可以实现服务的水平扩展。
  • 2,限流:为了避免服务雪崩,限流为了对服务端的接口接受请求的频率进行限制,防止服务挂掉,一般由网关实现,也可以由业务程序实现
  • 3,降级/熔断:当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的应对,以此释放服务器资源以保证核心任务的正常运行。由熔断器实现

# 在微服务系统中如何保持高并发

  • 1,消息队列:消息队列在分布式系统中主要是为了解耦和削峰。当系统的流量突然增加时,消息队列可以将请求暂时存储起来,避免直接对数据库进行压力过大的操作。
  • 2,读写分离:读写分离主要是为了将数据库的读和写操作分不到不同的数据库节点上。主服务器负责写,从服务器负责读
  • 3,负载均衡:负载均衡系统通常用于将任务比如用户请求处理分配到多个服务器处理以提高网站、应用或者数据库的性能和可靠性

# 高并发情况下如何保证接口的幂等性

1,悲观锁:对数据上悲观锁,同一时间只有一个线程对数据进行操作,之后再有线程对其修改的话,通过一系列判断来拒绝该请求。比如说我们有150块,需要取出100块,第一个请求获取到锁之后,判断余额是否不足100,如果余额足够,则进行 update 操作。如果余额不足,说明是重复请求,则直接返回成功。高并发场景下,应当加分布式锁

2,乐观锁:又叫版本控制,加个版本号来处理写请求,如果版本号不一致则拒绝写入

3,修改前先查询:查询数据库是否有这行数据,如果有说明已经执行了一次操作,拒绝执行第二次操作

4,唯一索引或者唯一标识:为了防止重复数据的产生,我们都会在表中加唯一索引,这是一个非常简单,并且有效的方案。加了唯一索引之后,第一次请求数据可以插入成功。但后面的相同请求,插入数据时会报异常,表示数据有冲突

#Spring Cloud
最后更新: 2/28/2026, 11:31:29 AM
数据库分库分表
Dubbo 基础概念

← 数据库分库分表 Dubbo 基础概念→

最近更新
01
vibe coding 最佳实践
02-24
02
立直麻将牌效益理论
02-23
03
伪静态是什么
02-08
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式