1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-08-14 05:21:42 +08:00

Compare commits

..

8 Commits

Author SHA1 Message Date
Guide
c58d5c3547
Merge pull request #1781 from longyulan/patch-1
Update http&https.md
2022-08-02 21:03:07 +08:00
Guide
f61996f464
Merge pull request #1780 from Raxcl/patch-3
Update arraylist-source-code.md
2022-08-02 21:02:37 +08:00
Guide
9d937a7189
Merge pull request #1779 from Raxcl/patch-2
Update io-basis.md
2022-08-02 20:55:36 +08:00
guide
a2db854734 [docs add]RabbitMQ常见面试题总结 2022-08-02 18:06:55 +08:00
guide
92a6046114 [docs add]既然有了 HTTP 协议,为什么还要有 RPC ? 2022-08-02 14:41:11 +08:00
longyulan
931974c335
Update http&https.md 2022-08-02 10:48:48 +08:00
Raxcl
577e6a790a
Update arraylist-source-code.md 2022-07-31 10:18:53 +08:00
Raxcl
0336878e8c
Update io-basis.md 2022-07-31 10:15:33 +08:00
13 changed files with 359 additions and 10 deletions

View File

@ -340,8 +340,8 @@ Dubbo 是一款国产的 RPC 框架,由阿里开源。相关阅读:
消息队列在分布式系统中主要是为了解耦和削峰。相关阅读: [消息队列常见问题总结](docs/high-performance/message-queue/message-queue.md)。
1. **RabbitMQ** : [RabbitMQ 入门](docs/high-performance/message-queue/rabbitmq-intro.md)
2. **RocketMQ** : [RocketMQ 入门](docs/high-performance/message-queue/rocketmq-intro)、[RocketMQ 的几个简单问题与答案](docs/high-performance/message-queue/rocketmq-questions.md)
1. **RabbitMQ** : [RabbitMQ 基础知识总结](docs/high-performance/message-queue/rabbitmq-intro.md)、[RabbitMQ 常见面试题](docs/high-performance/message-queue/rabbitmq-questions.md)
2. **RocketMQ** : [RocketMQ 基础知识总结](docs/high-performance/message-queue/rocketmq-intro)、[RocketMQ 常见面试题总结](docs/high-performance/message-queue/rocketmq-questions.md)
3. **Kafka** [Kafka 常见问题总结](docs/high-performance/message-queue/kafka-questions-01.md)
### 读写分离&分库分表

View File

@ -425,6 +425,7 @@ export const sidebarConfig = defineSidebarConfig({
"rocketmq-intro",
"rocketmq-questions",
"rabbitmq-intro",
"rabbitmq-questions",
],
},
],

View File

@ -11,7 +11,7 @@ tag:
### HTTP 协议介绍
HTTP 协议全称超文本传输协议Hypertext Transfer Protocol。顾名思义HTTP 协议就是用来规范超文本的传输,超文本,也就是网络上的包括文本在内的各式各样的消,具体来说,主要是来规范浏览器和服务器端的行为的。
HTTP 协议全称超文本传输协议Hypertext Transfer Protocol。顾名思义HTTP 协议就是用来规范超文本的传输,超文本,也就是网络上的包括文本在内的各式各样的消,具体来说,主要是来规范浏览器和服务器端的行为的。
并且HTTP 是一个无状态stateless协议也就是说服务器不维护任何有关客户端过去所发请求的消息。这其实是一种懒政有状态协议会更加复杂需要维护状态历史信息而且如果客户或服务器失效会产生状态的不一致解决这种不一致的代价更高。

View File

@ -1,5 +1,5 @@
---
title: Dubbo常见面试题总结
title: Dubbo 常见面试题总结
category: 分布式
tag:
- rpc

View File

@ -0,0 +1,194 @@
---
title: 有了 HTTP 协议,为什么还要有 RPC
category: 分布式
tag:
- rpc
---
> 本文来自[小白debug](https://juejin.cn/user/4001878057422087)投稿原文https://juejin.cn/post/7121882245605883934 。
我想起了我刚工作的时候,第一次接触 RPC 协议,当时就很懵,我 HTTP 协议用的好好的,为什么还要用 RPC 协议?
于是就到网上去搜。
不少解释显得非常官方,我相信大家在各种平台上也都看到过,解释了又好像没解释,都在**用一个我们不认识的概念去解释另外一个我们不认识的概念**,懂的人不需要看,不懂的人看了还是不懂。
这种看了,又好像没看的感觉,云里雾里的很难受,**我懂**。
为了避免大家有强烈的**审丑疲劳**,今天我们来尝试重新换个方式讲一讲。
## 从 TCP 聊起
作为一个程序员,假设我们需要在 A 电脑的进程发一段数据到 B 电脑的进程,我们一般会在代码里使用 socket 进行编程。
这时候,我们可选项一般也就**TCP 和 UDP 二选一。TCP 可靠UDP 不可靠。** 除非是马总这种神级程序员(早期 QQ 大量使用 UDP否则只要稍微对可靠性有些要求普通人一般无脑选 TCP 就对了。
类似下面这样。
```ini
fd = socket(AF_INET,SOCK_STREAM,0);
```
其中`SOCK_STREAM`,是指使用**字节流**传输数据,说白了就是**TCP 协议**。
在定义了 socket 之后,我们就可以愉快的对这个 socket 进行操作,比如用`bind()`绑定 IP 端口,用`connect()`发起建连。
![握手建立连接流程](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/f410977cda814d32b0eff3645c385a8a~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
在连接建立之后,我们就可以使用`send()`发送数据,`recv()`接收数据。
光这样一个纯裸的 TCP 连接,就可以做到收发数据了,那是不是就够了?
不行,这么用会有问题。
## 使用纯裸 TCP 会有什么问题
八股文常背TCP 是有三个特点,**面向连接**、**可靠**、基于**字节流**。
![TCP是什么](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/acb4508111cb47d8a3df6734d04818bc~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
这三个特点真的概括的 **非常精辟** ,这个八股文我们没白背。
每个特点展开都能聊一篇文章,而今天我们需要关注的是 **基于字节流** 这一点。
字节流可以理解为一个双向的通道里流淌的二进制数据,也就是 **01 串** 。纯裸 TCP 收发的这些 01 串之间是 **没有任何边界** 的,你根本不知道到哪个地方才算一条完整消息。
![01二进制字节流](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/b82d4fcdd0c4491e979856c93c1750d7~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
正因为这个没有任何边界的特点,所以当我们选择使用 TCP 发送 **"夏洛"和"特烦恼"** 的时候,接收端收到的就是 **"夏洛特烦恼"** ,这时候接收端没发区分你是想要表达 **"夏洛"+"特烦恼"** 还是 **"夏洛特"+"烦恼"** 。
![消息对比](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/4e120d0f1152419585565f693e744a3a~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
这就是所谓的 **粘包问题**,之前也写过一篇专门的[文章](https://mp.weixin.qq.com/s/0-YBxU1cSbDdzcZEZjmQYA)聊过这个问题。
说这个的目的是为了告诉大家,纯裸 TCP 是不能直接拿来用的,你需要在这个基础上加入一些 **自定义的规则** ,用于区分 **消息边界**
于是我们会把每条要发送的数据都包装一下,比如加入 **消息头** ,消息头里写清楚一个完整的包长度是多少,根据这个长度可以继续接收数据,截取出来后它们就是我们真正要传输的 **消息体**
![消息边界长度标志](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/cb29659d4907446e9f70551c44c6369f~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
而这里头提到的 **消息头** ,还可以放各种东西,比如消息体是否被压缩过和消息体格式之类的,只要上下游都约定好了,互相都认就可以了,这就是所谓的 **协议。**
每个使用 TCP 的项目都可能会定义一套类似这样的协议解析标准,他们可能 **有区别,但原理都类似**
**于是基于 TCP就衍生了非常多的协议比如 HTTP 和 RPC。**
## HTTP 和 RPC
### RPC 其实是一种调用方式
我们回过头来看网络的分层图。
![四层网络协议](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/04b603b5bd2443209233deea87816161~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
**TCP 是传输层的协议** ,而基于 TCP 造出来的 HTTP 和各类 RPC 协议,它们都只是定义了不同消息格式的 **应用层协议** 而已。
**HTTP****H**yper **T**ext **T**ransfer **P**rotocol协议又叫做 **超文本传输协议** 。我们用的比较多,平时上网在浏览器上敲个网址就能访问网页,这里用到的就是 HTTP 协议。
![HTTP调用](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/8f07a5d1c72a4c4fa811c6c3b5aadd3d~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
**RPC****R**emote **P**rocedure **C**all又叫做 **远程过程调用**,它本身并不是一个具体的协议,而是一种 **调用方式**
举个例子,我们平时调用一个 **本地方法** 就像下面这样。
```ini
res = localFunc(req)
```
如果现在这不是个本地方法,而是个**远端服务器**暴露出来的一个方法`remoteFunc`,如果我们还能像调用本地方法那样去调用它,这样就可以**屏蔽掉一些网络细节**,用起来更方便,岂不美哉?
```ini
res = remoteFunc(req)
```
![RPC可以像调用本地方法那样调用远端方法](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/761da6c30af244e19b1c44075d8b4254~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
基于这个思路,大佬们造出了非常多款式的 RPC 协议,比如比较有名的`gRPC``thrift`
值得注意的是,虽然大部分 RPC 协议底层使用 TCP但实际上 **它们不一定非得使用 TCP改用 UDP 或者 HTTP其实也可以做到类似的功能。**
到这里,我们回到文章标题的问题。
### 那既然有 RPC 了,为什么还要有 HTTP 呢?
其实TCP 是 **70 年** 代出来的协议,而 HTTP 是 **90 年代** 才开始流行的。而直接使用裸 TCP 会有问题,可想而知,这中间这么多年有多少自定义的协议,而这里面就有 **80 年代** 出来的`RPC`
所以我们该问的不是 **既然有 HTTP 协议为什么要有 RPC** ,而是 **为什么有 RPC 还要有 HTTP 协议?**
现在电脑上装的各种联网软件,比如 xx 管家xx 卫士它们都作为客户端Client 需要跟服务端Server 建立连接收发消息,此时都会用到应用层协议,在这种 Client/Server (C/S) 架构下,它们可以使用自家造的 RPC 协议,因为它只管连自己公司的服务器就 ok 了。
但有个软件不同浏览器Browser ,不管是 Chrome 还是 IE它们不仅要能访问自家公司的**服务器Server** 还需要访问其他公司的网站服务器因此它们需要有个统一的标准不然大家没法交流。于是HTTP 就是那个时代用于统一 **Browser/Server (B/S)** 的协议。
也就是说在多年以前,**HTTP 主要用于 B/S 架构,而 RPC 更多用于 C/S 架构。但现在其实已经没分那么清了B/S 和 C/S 在慢慢融合。** 很多软件同时支持多端,比如某度云盘,既要支持**网页版**,还要支持**手机端和 PC 端**,如果通信协议都用 HTTP 的话,那服务器只用同一套就够了。而 RPC 就开始退居幕后,一般用于公司内部集群里,各个微服务之间的通讯。
那这么说的话,**都用 HTTP 得了,还用什么 RPC**
仿佛又回到了文章开头的样子,那这就要从它们之间的区别开始说起。
### HTTP 和 RPC 有什么区别
我们来看看 RPC 和 HTTP 区别比较明显的几个点。
#### 服务发现
首先要向某个服务器发起请求,你得先建立连接,而建立连接的前提是,你得知道 **IP 地址和端口** 。这个找到服务对应的 IP 端口的过程,其实就是 **服务发现**
**HTTP** 中,你知道服务的域名,就可以通过 **DNS 服务** 去解析得到它背后的 IP 地址,默认 **80 端口**
**RPC** 的话,就有些区别,一般会有专门的中间服务去保存服务名和 IP 信息,比如 **Consul、Etcd、Nacos、ZooKeeper甚至是 Redis**。想要访问某个服务,就去这些中间服务去获得 IP 和端口信息。由于 DNS 也是服务发现的一种,所以也有基于 DNS 去做服务发现的组件,比如 **CoreDNS**
可以看出服务发现这一块,两者是有些区别,但不太能分高低。
#### 底层连接形式
以主流的 **HTTP1.1** 协议为例,其默认在建立底层 TCP 连接之后会一直保持这个连接(**keep alive**),之后的请求和响应都会复用这条连接。
**RPC** 协议,也跟 HTTP 类似,也是通过建立 TCP 长链接进行数据交互但不同的地方在于RPC 协议一般还会再建个 **连接池**,在请求量大的时候,建立多条连接放在池内,要发数据的时候就从池里取一条连接出来,用完放回去,下次再复用,可以说非常环保。
![connection_pool](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/72fcad064c9e4103a11f1a2d579f79b2~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
由于连接池有利于提升网络请求性能,所以不少编程语言的网络库里都会给 HTTP 加个连接池,比如 Go 就是这么干的。
可以看出这一块两者也没太大区别,所以也不是关键。
#### 传输的内容
基于 TCP 传输的消息,说到底,无非都是 **消息头 Header 和消息体 Body。**
**Header** 是用于标记一些特殊信息,其中最重要的是 **消息体长度**
**Body** 则是放我们真正需要传输的内容,而这些内容只能是二进制 01 串,毕竟计算机只认识这玩意。所以 TCP 传字符串和数字都问题不大,因为字符串可以转成编码再变成 01 串,而数字本身也能直接转为二进制。但结构体呢,我们得想个办法将它也转为二进制 01 串,这样的方案现在也有很多现成的,比如 **JSONProtocol Buffers (Protobuf)**
这个将结构体转为二进制数组的过程就叫 **序列化** ,反过来将二进制数组复原成结构体的过程叫 **反序列化**
![序列化和反序列化](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/d501dfc6f764430188ce61fda0f3e5d9~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
对于主流的 HTTP1.1,虽然它现在叫超文本协议,支持音频视频,但 HTTP 设计 初是用于做网页文本展示的所以它传的内容以字符串为主。Header 和 Body 都是如此。在 Body 这块,它使用 **JSON****序列化** 结构体数据。
我们可以随便截个图直观看下。
![HTTP报文](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/04e8a79ddb7247759df23f1132c01655~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
可以看到这里面的内容非常多的冗余,显得非常啰嗦。最明显的,像 Header 里的那些信息,其实如果我们约定好头部的第几位是 `Content-Type`,就不需要每次都真的把 `Content-Type` 这个字段都传过来,类似的情况其实在 Body 的 JSON 结构里也特别明显。
而 RPC因为它定制化程度更高可以采用体积更小的 Protobuf 或其他序列化协议去保存结构体数据,同时也不需要像 HTTP 那样考虑各种浏览器行为,比如 302 重定向跳转啥的。**因此性能也会更好一些,这也是在公司内部微服务中抛弃 HTTP选择使用 RPC 的最主要原因。**
![HTTP原理](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/284c26bb7f2848889d1d9b95cf49decb~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
![RPC原理](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/distributed-system/rpc/edb050d383c644e895e505253f1c4d90~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp.png)
当然上面说的 HTTP其实 **特指的是现在主流使用的 HTTP1.1**`HTTP2`在前者的基础上做了很多改进,所以 **性能可能比很多 RPC 协议还要好**,甚至连`gRPC`底层都直接用的`HTTP2`
那么问题又来了。
### 为什么既然有了 HTTP2还要有 RPC 协议?
这个是由于 HTTP2 是 2015 年出来的。那时候很多公司内部的 RPC 协议都已经跑了好些年了,基于历史原因,一般也没必要去换了。
## 总结
- 纯裸 TCP 是能收发数据,但它是个无边界的数据流,上层需要定义消息格式用于定义 **消息边界** 。于是就有了各种协议HTTP 和各类 RPC 协议就是在 TCP 之上定义的应用层协议。
- **RPC 本质上不算是协议,而是一种调用方式**,而像 gRPC 和 Thrift 这样的具体实现,才是协议,它们是实现了 RPC 调用的协议。目的是希望程序员能像调用本地方法那样去调用远端的服务方法。同时 RPC 有很多种实现方式,**不一定非得基于 TCP 协议**。
- 从发展历史来说,**HTTP 主要用于 B/S 架构,而 RPC 更多用于 C/S 架构。但现在其实已经没分那么清了B/S 和 C/S 在慢慢融合。** 很多软件同时支持多端,所以对外一般用 HTTP 协议,而内部集群的微服务之间则采用 RPC 协议进行通讯。
- RPC 其实比 HTTP 出现的要早,且比目前主流的 HTTP1.1 性能要更好,所以大部分公司内部都还在使用 RPC。
- **HTTP2.0****HTTP1.1** 的基础上做了优化,性能可能比很多 RPC 协议都要好,但由于是这几年才出来的,所以也不太可能取代掉 RPC。

View File

@ -1,5 +1,5 @@
---
title: RPC基础常见面试题总结
title: RPC 基础常见面试题总结
category: 分布式
tag:
- rpc
@ -137,3 +137,8 @@ Dubbo 也是 Spring Cloud Alibaba 里面的一个组件。
**内容概览**
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/image-20220308100605485.png)
## 既然有了 HTTP 协议,为什么还要有 RPC
[HTTP 和 RPC 详细对比](http&rpc.md) 。

View File

@ -0,0 +1,149 @@
---
title: RabbitMQ常见面试题总结
category: 高性能
tag:
- 消息队列
head:
- - meta
- name: keywords
content: RabbitMQ,AMQP,Broker,Exchange,优先级队列,延迟队列
- name: description
content: RabbitMQ 是一个在 AMQPAdvanced Message Queuing Protocol 基础上实现的可复用的企业消息系统。它可以用于大型软件系统各个模块之间的高效通信支持高并发支持可扩展。它支持多种客户端如Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等支持AJAX持久化用于在分布式系统中存储转发消息在易用性、扩展性、高可用性等方面表现不俗。
---
> 本篇文章由 JavaGuide 收集自网络,原出处不明。
## RabbitMQ 是什么?
RabbitMQ 是一个在 AMQPAdvanced Message Queuing Protocol 基础上实现的可复用的企业消息系统。它可以用于大型软件系统各个模块之间的高效通信支持高并发支持可扩展。它支持多种客户端如Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等支持AJAX持久化用于在分布式系统中存储转发消息在易用性、扩展性、高可用性等方面表现不俗。
RabbitMQ是使用Erlang编写的一个开源的消息队列本身支持很多的协议AMQPXMPP, SMTP, STOMP也正是如此使的它变的非常重量级更适合于企业级的开发。它同时实现了一个Broker构架这意味着消息在发送给客户端时先在中心队列排队对路由(Routing)、负载均衡(Load balance)或者数据持久化都有很好的支持。
PS:也可能直接问什么是消息队列?消息队列就是一个使用队列来通信的组件。
## RabbitMQ 特点?
- **可靠性**: RabbitMQ 使用一些机制来保证可靠性, 如持久化、传输确认及发布确认等。
- **灵活的路由** : 在消息进入队列之前,通过交换器来路由消息。对于典型的路由功能, RabbitMQ 己经提供了一些内置的交换器来实现。针对更复杂的路由功能,可以将多个 交换器绑定在一起, 也可以通过插件机制来实现自己的交换器。
- **扩展性**: 多个 RabbitMQ 节点可以组成一个集群,也可以根据实际业务情况动态地扩展 集群中节点。
- **高可用性** : 队列可以在集群中的机器上设置镜像,使得在部分节点出现问题的情况下队 列仍然可用。
- **多种协议**: RabbitMQ 除了原生支持 AMQP 协议,还支持 STOMP MQTT 等多种消息 中间件协议。
- **多语言客户端** :RabbitMQ 几乎支持所有常用语言,比如 Java、 Python、 Ruby、 PHP、 C#、 JavaScript 等。
- **管理界面** : RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息、集 群中的节点等。
- **插件机制** : RabbitMQ 提供了许多插件 以实现从多方面进行扩展,当然也可以编写自 己的插件。
## AMQP 是什么?
RabbitMQ 就是 AMQP 协议的 `Erlang` 的实现(当然 RabbitMQ 还支持 `STOMP2``MQTT3` 等协议 ) AMQP 的模型架构 和 RabbitMQ 的模型架构是一样的,生产者将消息发送给交换器,交换器和队列绑定 。
RabbitMQ 中的交换器、交换器类型、队列、绑定、路由键等都是遵循的 AMQP 协议中相 应的概念。目前 RabbitMQ 最新版本默认支持的是 AMQP 0-9-1。
**AMQP 协议的三层**
- **Module Layer**:协议最高层,主要定义了一些客户端调用的命令,客户端可以用这些命令实现自己的业务逻辑。
- **Session Layer**:中间层,主要负责客户端命令发送给服务器,再将服务端应答返回客户端,提供可靠性同步机制和错误处理。
- **TransportLayer**:最底层,主要传输二进制数据流,提供帧的处理、信道服用、错误检测和数据表示等。
**AMQP 模型的三大组件**
- **交换器 (Exchange)** :消息代理服务器中用于把消息路由到队列的组件。
- **队列 (Queue)** :用来存储消息的数据结构,位于硬盘或内存中。
- **绑定 (Binding)** :一套规则,告知交换器消息应该将消息投递给哪个队列。
## **说说生产者 Producer 和消费者 Consumer?**
**生产者** :
- 消息生产者,就是投递消息的一方。
- 消息一般包含两个部分:消息体(`payload`)和标签(`Label`)。
**消费者**
- 消费消息,也就是接收消息的一方。
- 消费者连接到 RabbitMQ 服务器,并订阅到队列上。消费消息时只消费消息体,丢弃标签。
## 说说 Broker 服务节点、Queue 队列、Exchange 交换器?
- **Broker** 可以看做 RabbitMQ 的服务节点。一般请下一个 Broker 可以看做一个 RabbitMQ 服务器。
- **Queue** :RabbitMQ 的内部对象,用于存储消息。多个消费者可以订阅同一队列,这时队列中的消息会被平摊(轮询)给多个消费者进行处理。
- **Exchange** : 生产者将消息发送到交换器,由交换器将消息路由到一个或者多个队列中。当路由不到时,或返回给生产者或直接丢弃。
## 什么是死信队列?如何导致的?
DLX全称为 `Dead-Letter-Exchange`,死信交换器,死信邮箱。当消息在一个队列中变成死信 (`dead message`) 之后,它能被重新被发送到另一个交换器中,这个交换器就是 DLX绑定 DLX 的队列就称之为死信队列。
**导致的死信的几种原因**
- 消息被拒(`Basic.Reject /Basic.Nack`) 且 `requeue = false`
- 消息 TTL 过期。
- 队列满了,无法再添加。
## 什么是延迟队列RabbitMQ 怎么实现延迟队列?
延迟队列指的是存储对应的延迟消息,消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。
RabbitMQ本身是没有延迟队列的要实现延迟消息一般有两种方式
1. 通过RabbitMQ本身队列的特性来实现需要使用RabbitMQ的死信交换机Exchange和消息的存活时间TTLTime To Live
2. 在RabbitMQ 3.5.7及以上的版本提供了一个插件rabbitmq-delayed-message-exchange来实现延迟队列功能。同时插件依赖Erlang/OPT 18.0及以上。
也就是说AMQP 协议以及RabbitMQ本身没有直接支持延迟队列的功能但是可以通过TTL和DLX模拟出延迟队列的功能。
## 什么是优先级队列?
RabbitMQ 自 V3.5.0 有优先级队列实现,优先级高的队列会先被消费。
可以通过`x-max-priority`参数来实现优先级队列。不过,当消费速度大于生产速度且 Broker 没有堆积的情况下,优先级显得没有意义。
## RabbitMQ 有哪些工作模式?
- 简单模式
- work 工作模式
- pub/sub 发布订阅模式
- Routing 路由模式
- Topic 主题模式
## RabbitMQ 消息怎么传输?
由于 TCP 链接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈,所以 RabbitMQ 使用信道的方式来传输数据。信道Channel是生产者、消费者与 RabbitMQ 通信的渠道,信道是建立在 TCP 链接上的虚拟链接,且每条 TCP 链接上的信道数量没有限制。就是说 RabbitMQ 在一条 TCP 链接上建立成百上千个信道来达到多个线程处理,这个 TCP 被多个线程共享,每个信道在 RabbitMQ 都有唯一的 ID保证了信道私有性每个信道对应一个线程使用。
## **如何保证消息的可靠性?**
消息到 MQ 的过程中搞丢MQ 自己搞丢MQ 到消费过程中搞丢。
- 生产者到 RabbitMQ事务机制和 Confirm 机制,注意:事务机制和 Confirm 机制是互斥的,两者不能共存,会导致 RabbitMQ 报错。
- RabbitMQ 自身:持久化、集群、普通模式、镜像模式。
- RabbitMQ 到消费者basicAck 机制、死信队列、消息补偿机制。
## 如何保证 RabbitMQ 消息的顺序性?
- 拆分多个 queue(消息队列),每个 queue(消息队列) 一个 consumer(消费者),就是多一些 queue (消息队列)而已,确实是麻烦点;
- 或者就一个 queue (消息队列)但是对应一个 consumer(消费者),然后这个 consumer(消费者)内部用内存队列做排队,然后分发给底层不同的 worker 来处理。
## 如何保证 RabbitMQ 高可用的?
RabbitMQ 是比较有代表性的,因为是基于主从(非分布式)做高可用性的,我们就以 RabbitMQ 为例子讲解第一种 MQ 的高可用性怎么实现。RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式。
**单机模式**
Demo 级别的,一般就是你本地启动了玩玩儿的?,没人生产用单机模式。
**普通集群模式**
意思就是在多台机器上启动多个 RabbitMQ 实例,每个机器启动一个。你创建的 queue只会放在一个 RabbitMQ 实例上,但是每个实例都同步 queue 的元数据(元数据可以认为是 queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。
你消费的时候,实际上如果连接到了另外一个实例,那么那个实例会从 queue 所在实例上拉取数据过来。这方案主要是提高吞吐量的,就是说让集群中多个节点来服务某个 queue 的读写操作。
**镜像集群模式**
这种模式,才是所谓的 RabbitMQ 的高可用模式。跟普通集群模式不一样的是,在镜像集群模式下,你创建的 queue无论元数据还是 queue 里的消息都会存在于多个实例上,就是说,每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,包含 queue 的全部数据的意思。然后每次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。RabbitMQ 有很好的管理控制台,就是在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候是可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点,再次创建 queue 的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。
这样的好处在于,你任何一个机器宕机了,没事儿,其它机器(节点)还包含了这个 queue 的完整数据,别的 consumer 都可以到其它节点上去消费数据。坏处在于第一这个性能开销也太大了吧消息需要同步到所有机器上导致网络带宽压力和消耗很重RabbitMQ 一个 queue 的数据都是放在一个节点里的,镜像集群下,也是每个节点都放这个 queue 的完整数据。
## 如何解决消息挤压问题?
**临时紧急扩容**。先修复 consumer 的问题,确保其恢复消费速度,然后将现有 cnosumer 都停掉。新建一个 topicpartition 是原来的 10 倍,临时建立好原先 10 倍的 queue 数量。然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的 10 倍数量的 queue。接着临时征用 10 倍的机器来部署 consumer每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将 queue 资源和 consumer 资源扩大 10 倍,以正常的 10 倍速度来消费数据。等快速消费完积压数据之后,得恢复原先部署的架构,重新用原先的 consumer 机器来消费消息。
## 如何解决消息队列的延时以及过期失效问题?
RabbtiMQ 是可以设置过期时间的,也就是 TTL。如果消息在 queue 中积压超过一定的时间就会被 RabbitMQ 给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在 mq 里,而是大量的数据会直接搞丢。我们可以采取一个方案,就是批量重导,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如大家一起喝咖啡熬夜到晚上 12 点以后,用户都睡觉了。这个时候我们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入 mq 里面去,把白天丢的数据给他补回来。也只能是这样了。假设 1 万个订单积压在 mq 里面,没有处理,其中 1000 个订单都丢了,你只能手动写程序把那 1000 个订单给查出来,手动发到 mq 里去再补一次。

View File

@ -922,7 +922,6 @@ public class EnsureCapacityTest {
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<Object>();
final int N = 10000000;
list = new ArrayList<Object>();
long startTime1 = System.currentTimeMillis();
list.ensureCapacity(N);
for (int i = 0; i < N; i++) {

View File

@ -41,7 +41,7 @@ Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来
`FileInputStream` 代码示例:
```java
try (InputStream fis = new FileInputStream("input.txt)) {
try (InputStream fis = new FileInputStream("input.txt")) {
System.out.println("Number of remaining bytes:"
+ fis.available());
int content;

View File

@ -25,6 +25,7 @@ category: 开源项目
7. [一款跨时代的高性能 Java 框架!启动速度快到飞起](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247520633&idx=1&sn=aec35af40e3ed3b1e844addd04e31af5&chksm=cea1deb2f9d657a46a0684bbcbcb2900cebff39a2b2746a4a809b6b5306bce08d4382efd5ca8&scene=178&cur_album_id=1345382825083895808#rd)
8. [Spring Boot+MyBatis Plus+JWT 问卷系统!开源!](https://mp.weixin.qq.com/s/kRgqHt73ZJGFQ2XmKG4PXw)
9. [手写一个简化版的 Spring Cloud](https://mp.weixin.qq.com/s/v3FUp-keswE2EhcTaLpSMQ)
10. [这个 SpringBoot+ Vue 开源博客系统太酷炫了!](https://mp.weixin.qq.com/s/CCzsX3Sn2Q3vhuBDEmRTlw)
推荐你在我的公众号“**JavaGuide**”回复“**开源**”在线阅读[「优质开源项目推荐」](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg2OTA0Njk0OA==&action=getalbum&album_id=1345382825083895808&scene=173&from_msgid=2247516459&from_itemidx=1&count=3&nolastread=1#wechat_redirect)系列。

View File

@ -35,7 +35,7 @@ category: 知识星球
随着时间推移,星球积累的干货资源越来越多,我花在星球上的时间也越来越多。于是,我将星球的定价慢慢调整为了 **159/年**!后续会将星球的价格调整为 **199/年**,想要加入的小伙伴一定要尽早。
你可以添加我的微信(没有手机号再申请微信,故使用企业微信。不过,请放心,这个号的消息也是我本人处理,平时最常看这个微信)领取星球专属优惠券,限时 **130/年** 加入!
你可以添加我的微信(没有手机号再申请微信,故使用企业微信。不过,请放心,这个号的消息也是我本人处理,平时最常看这个微信)领取星球专属优惠券,限时 **130/年** 加入(续费半价)
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/IMG_3007.jpg)

View File

@ -84,7 +84,7 @@ star: 5
随着时间推移,星球积累的干货资源越来越多,我花在星球上的时间也越来越多。于是,我将星球的定价慢慢调整为了 **159/年**!后续会将星球的价格调整为 **199/年**,想要加入的小伙伴一定要尽早。
你可以添加我的微信(没有手机号再申请微信,故使用企业微信。不过,请放心,这个号的消息也是我本人处理,平时最常看这个微信)领取星球专属优惠券,限时 **130/年** 加入!
你可以添加我的微信(没有手机号再申请微信,故使用企业微信。不过,请放心,这个号的消息也是我本人处理,平时最常看这个微信)领取星球专属优惠券,限时 **130/年** 加入(续费半价)
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/IMG_3007.jpg)

View File

@ -42,7 +42,7 @@ star: true
随着时间推移,星球积累的干货资源越来越多,我花在星球上的时间也越来越多。于是,我将星球的定价慢慢调整为了 **159/年**!后续会将星球的价格调整为 **199/年**,想要加入的小伙伴一定要尽早。
你可以添加我的微信(没有手机号再申请微信,故使用企业微信。不过,请放心,这个号的消息也是我本人处理,平时最常看这个微信)领取星球专属优惠券,限时 **130/年** 加入!
你可以添加我的微信(没有手机号再申请微信,故使用企业微信。不过,请放心,这个号的消息也是我本人处理,平时最常看这个微信)领取星球专属优惠券,限时 **130/年** 加入(续费半价)
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/IMG_3007.jpg)