传输层 --- UDP

目录

1. 传输层是什么呢?

2. 再谈端口号

2.1. 端口号是什么 

2.2. 协议号是什么 

2.3. 认识知名端口号

2.4. 端口号的相关问题

2.4.1. 一个进程可以绑定多个端口号吗?

2.4.2. 一个端口号可以被多个进程绑定吗?

2.4.3. 为什么不使用PID来标识一个进程呢

2.5. netstat 命令

2.6. pidof 命令

3. 前置性认识

4. UDP 协议

4.1. UDP的基础认识

4.2. 理解UDP报文本身

4.3. UDP字段

4.4. UDP相关特性

4.5. UDP的缓冲区

4.6. 全双工 vs 半双工

4.7. UDP 使用注意事项 

4.8. 基于UDP的应用层协议 


1. 传输层是什么呢?

传输层是计算机网络中的一种网络协议层,位于网络层和应用层之间。

它的主要作用是将应用层的数据从发送端传输给服务端,确保数据在源主机和目标主机之间可靠地传输。

2. 再谈端口号

2.1. 端口号是什么 

端口号是一个16位整数 (0 --- 65535),用于标识网络通信中的特定进程或服务的。在TCP/IP协议中,每个端口号都与一个特定的网络服务或进程相关联。端口号的作用是在主机上区分不同的网络服务和进程,使得数据能够正确地路由到目标进程。

在 TCP/IP 协议中, 用 "源IP","源端口号","目的IP","目的端口号","协议号",这样的一个五元组来标识一个通信, 在 Linux 中可以用 netstat 命令查看这个五元组。

2.2. 协议号是什么 

协议号,通常指在网络协议栈中用于标识不同网络层之间协议的数字。

在传输层,特别是在IP协议(网络层)的上下文中,协议号用来标识上一层(即传输层)的具体协议类型,以便正确地处理和转发数据包。

在IP头部中,存在一个字段专门用于指示传输层的协议类型,这就是所谓的 "协议号"。这个字段告诉IP层数据包应该如何进一步处理,即数据包应该交给哪种传输层协议(如TCP、UDP或其他协议)进行处理。例如:

  • TCP 的协议号是 6。这意味着当IP层接收到一个数据包,如果看到协议号是6,它就知道这个数据包应该交给TCP协议处理。
  • UDP 的协议号是 17。同样地,IP层收到协议号为17的数据包,就会将其转发给UDP协议处理。

总的来说,协议号是网络协议栈中用于标识和区分不同传输层协议的机制,即服务端和客户端采用什么传输层协议进行通信的,它是确保数据包能够被正确处理和传输的关键。

2.3. 认识知名端口号

由于端口号是一个16位的整数, 故它的范围是0 到 65535,通常情况下, 端口号可以分为两类:

  1. 系统端口系统端口是预留给一些常用的网络服务的端口号,范围从0到1023。例如,HTTP服务默认使用端口号80,HTTPS服务默认使用端口号443,FTP服务默认使用端口号21,等等。这些端口号被广泛应用于各种网络服务,并且通常由操作系统或网络协议栈预先分配。

  2. 动态端口动态端口是指那些未被预先分配给特定服务的端口号,范围从1024到65535。这些端口号通常由客户端在需要与服务器建立连接时动态地分配使用。例如,当你使用Web浏览器访问一个网站时,操作系统会随机选择一个未被占用的动态端口号,并将其用于与Web服务器建立连接,因此我们以前在写套接字时, 客户端不需要显式的bind端口,操作系统在特定时机会自动给客户端bind端口号。

在 Linux 操作系统中,通常有一个特定的文件用来记录特定网络服务所使用的默认端口号,这个文件通常位于 /etc/services 路径下。

cat /etc/services

用户在实现自己的网络服务时,尽量避免使用0到1023范围内的端口号,因为这些端口号通常被系统或常见的网络服务占用。如果你的应用程序尝试绑定这些端口号,可能会引发权限问题或与系统服务冲突。

一般都使用1024到65535的端口,不会与系统服务发生冲突。

2.4. 端口号的相关问题

2.4.1. 一个进程可以绑定多个端口号吗?

可以, 端口号是用于标识网络通信中的特定进程或服务的,一个端口号标识一个进程,但并不是说,一个进程只能绑定一个端口号。

例如:一个Web服务器进程可能会同时监听80端口和443端口,分别提供HTTP和HTTPS服务。

2.4.2. 一个端口号可以被多个进程绑定吗?

不可以, 由于端口号是用来标识网络通信中的一个进程, 如果一个端口号被多个进程绑定, 那么这个端口号就无法标识唯一的一个进程,从而引发冲突。因此,一个端口号不可以被多个进程绑定,否则会造成端口冲突,影响通信的正常进行。

2.4.3. 为什么不使用PID来标识一个进程呢

我们以前不是学习过PID吗,PID作为进程的标识符,也是标识一个唯一的进程啊, 为什么网络模块还要自己在搞一个端口号呢?

原因如下: 

  • 首先,并不是所有的进程都需要提供网络服务。因此,对于网络模块而言,如果用PID来标识网络服务进程,那么操作系统是不是还需要判断哪些进程是进行网络服务的, 这会增加系统的复杂性和开销,对于要增加成本的事情,操作系统为什么要做呢?
  • 其次, PID 是属于进程管理模块的, 而端口号是属于网络通信的一部分。 如果在网络中用 PID 来标识进程,那么就代表着将网络模块和进程管理模块强耦合在一起,万一未来进程管理模块出现变更,那么网络模块是不是也要改,这样的设计不利于系统的灵活性和扩展性。

因此,出于系统复杂度和耦合度的角度, 网络模块需要有一个端口号来标识进程的唯一性,将网络功能和进程管理模块进行解耦,降低系统的复杂性和维护成本,同时也提高了系统的可扩展性和灵活性。

通过端口号,不同的网络服务进程可以并行地提供各自的网络服务,而无需过多考虑系统内部的进程管理机制。

2.5. netstat 命令

netstat 是一个用来查看网络状态的重要工具。

语法:netstat [选项]

常用选项:

n:拒绝显示别名, 用于显示数字格式的IP地址和端口号;

l:仅列出处于 Listen 状态的网络服务;

p:包含 PID/Program name 信息;

t:仅显示 TCP 的网络服务;

u:仅显示 UDP 的网络服务;

a:显式所有连接状态的服务,包括LISTEN、ESTABLISHED、TIME_WAIT等等。

对于有些网络服务,可能需要提升权限,才能看到更多的信息,如下: 

2.6. pidof 命令

pidof:通过一个进程名,获得进程id;

语法:pidof  [进程名]

会得到一个该进程的PID;

pidof myserver | xargs kill -9

pidof 命令用于查找指定服务进程的PID,然后通过管道将结果传递给kill命令。但由于,管道是将 pidof 进程的输出做为 kill 进程的输入,注意,而此时的 kill 命令需要的是命令行参数而不是标准输入的内容。因此这时候就需要使用 xargs 命令,它可以将标准输入的内容转换成命令行参数,并传递给 kill 命令,从而向指定服务进程发送特定信号。

标准输入: 代表的是特定文件。

命令行参数: 是 main () 的参数。

3. 前置性认识

接下来,我们就要正式进入传输层了, 就要真正的揭示UDP/TCP的底层细节问题, UDP为次,TCP为主;

同时,我们也知道, 传输层是位于应用层和网络层之间的。

应用层的数据并不是直接发送给对端主机的应用层, 而是将应用层的数据拷贝给传输层协议的缓冲区中,然后根据协议栈向下封装,通过网络发送给对端主机;对端主机收到数据后,传输层会将数据从网络层接收并进行解包,然后再将数据向上交付给对应的应用层。

因此,我们对于 UDP/TCP 都要谈论两个问题:

  • UDP/TCP 是如何区分报头和有效载荷的。 只要清楚了如何区分报头和有效载荷, 我就知道,如何对报头和有效载荷进行分离以及如何封装报头的。
  • UDP/TCP 如何向上交付。

4. UDP 协议

4.1. UDP的基础认识

UDP 报文格式如下:

对于UDP而言, 它是如何区分报头和有效载荷的呢?

UDP采用的是定长包头, 通过固定长度的报头 (8字节),来区分一个UDP报文中的报头和有效载荷。因此, 对于一个UDP报文而言, 前八个字节就是报文的报头, 后面都是有效载荷。

那UDP是如何向上交付的呢?

UDP在向上交付数据时,会根据UDP数据报文头部中的目的端口号信息,将数据交付给目标进程。这是因为在网络通信中,进程通过绑定端口号来监听网络上的数据,并且这个端口号是唯一的标识符。因此,UDP在接收到数据后,根据目的端口号确定了数据应该交付给哪个目标进程。

可是,作为一个接收方, 怎么知道UDP报文中哪一个是源端口号、哪一个是目的端口呢?

因为这是协议,既是一种约定,UDP报头是八字节,客户端知道,服务端也知道,且前16个比特位就是源端口,接下来的16个比特位就是目的端口, 这是一种约定,即协议。

为什么我们在编写应用层, 定义端口号时候, 都用的是 uint16_t 呢?

因为UDP协议用的端口号是16位的,而UDP协议是基于操作系统的,而操作系统提供了一系列的系统调用接口(比如socket接口)来支持网络通信,应用层通过这些接口与网络进行交互,因此,为了与UDP协议和操作系统保持一致,应用层需要使用16位的数据类型来定义端口号。

UDP是如何正确的提取整个完整报文的呢?

UDP报文头部中的16位UDP长度字段指示了整个UDP数据报文的长度,包括报头和数据部分。因此,通过提取UDP长度字段的数值,并减去8字节(UDP报头的大小),就可以得到UDP数据报文的有效载荷大小,即有效载荷的长度。

因此接收方可以根据UDP报文头部中的长度字段来正确提取整个完整的UDP数据报文,即UDP本身就能保证接收方获取一个完整的UDP报文,故我们说UDP是面向数据报的。

4.2. 理解UDP报文本身

这里的UDP报头如下 (用以举例):

struct udp_hdr
{
	uint32_t src_port : 16;
	uint32_t dst_port : 16;
	uint32_t udp_len : 16;
	uint32_t udp_check : 16;
};

未来我们所见到的所有报头都是位段,只不过位段中的字段不一样罢了。

注意,上面只是实现的大致思路,但实际上我们知道,位段有一个缺陷:可移植性太差,但是内核依旧采用位段来定义报头,并且我们之前自己定义协议时,也说过,建议不要定义结构化的数据,因为结构化的数据会存在内存对齐和大小端等问题,但是内核就这样做了,它的解决方案很复杂 (包括各种条件编译等等),它就是为了最大限度地减少内存消耗,让操作系统在有限的资源下运行尽可能多的任务,因为操作系统是所有软件都要用的基础设施,所以设计者在设计时,用尽了心思,以达到更好的效果。

有了上面的理解,我们需要在理解一下协议是如何封装的: 

当应用层将数据向下交给传输层时 (本质上是拷贝的过程),假如此时的传输层协议是UDP,那么操作系统就会开辟一段空间, 并在内核中实例化一个UDP报头,例如: struct udp_hdr hdr = {10000, 20000, 0, 0};并将应用层的数据与该报头一起封装成一个UDP数据报。这个过程涉及到内存的开辟和拷贝,以及报头中相关字段的更新,如UDP报文长度和校验和,而这就是一个UDP报文,即这就是一个封装的过程。

有了封装的理解, 我们也可以类比到解包的过程,

UDP数据报,在操作系统看来,本质上就是一个对象,因此,我们可以得到这个对象的起始地址,直接访问前八个字节,在前八个字节中找到UDP长度,用这个UDP长度减去八字节,得到有效载荷的长度,然后,可以使用相关的拷贝函数将有效载荷从UDP数据报中拷贝到应用层的缓冲区中,从而将报头和有效载荷分离开来。而这就是一个解包的大致过程,因为它将数据报分解成了其组成部分:报头和有效载荷。当然,操作系统在实际操作中肯定更为复杂,因为它要考虑更多的细节,但这是它的大致思路。

4.3. UDP字段

  • 源端口:代表这个UDP数据报是哪个进程发送的;
  • 目的端口:代表这个UDP数据报是发送给哪个进程的;
  • UDP长度:代表UDP数据报的整体大小 (报头 + 有效载荷)。UDP的报头固定为8个字节,故UDP长度最小是8字节 (只包含报头,无有效载荷);
  • UDP校验和: 这个字段用于检测UDP数据报在传输过程中是否发生了错误。如果接收方计算出的校验和与UDP数据报中的校验和不匹配,那么该数据报将被认为是损坏的并被丢弃;

4.4. UDP相关特性

  • 无连接UDP是一种无连接的协议,通信的双方在发送数据之前不需要建立连接。每个UDP数据报都是独立的,发送方发送数据时不需要等待接收方的确认,也不需要保留连接状态信息。
  • 不可靠UDP是一种不可靠的协议,它不提供数据可靠性的保证。UDP数据报在传输过程中可能会丢失、重复、失序或损坏,而且UDP本身不提供任何重传、确认、流量控制等机制。
  • 面向数据报面向数据报是指每个数据报都是独立的,UDP 不会对应用层交给它的报文进行拆分或合并,而是按照应用层传递给它的数据报原样发送。这意味着 UDP 不会对数据进行任何拆分或者组合操作,而是以数据报的形式将数据发送出去。简而言之,数据怎么给你的,你就怎么给我发出去。

无连接和面向数据报很好理解,但在这里要对不可靠强调一下。

  • 不可靠并不是UDP的缺点,而是它的特点;
  • 数据在进行长距离传输时, 如果要保证可靠性,那么就意味着需要做更多的工作 (比如重传、确认、流量控制等机制),这些机制会增加协议的复杂性,而一旦复杂,那么其使用成本和维护成本都会更高;
  • 而不可靠,意味着它会做更少的工作,只提供基本的数据传输服务,而不保证数据的可靠性,也就不考虑其他的机制,因此它就更简单,使用和维护成本就更低。

4.5. UDP的缓冲区

如何理解我们之前所学的IO接口,诸如 sendto / recvfrom、read / write、 recv / send 等等这些IO接口呢?

  • 不要认为这些函数是在网络中进行数据收发,例如,sendto 是将数据中直接发送给网络的,recvfrom 是从网络中读取数据到应用层的。
  • 上面的认识是错误的。
  • 上面的这些函数本质上都是拷贝函数。 例如, sendto 只是将应用层的数据拷贝给了内核层的缓冲区,如果传输层协议提供了缓冲区,那么就拷贝传输层协议的缓冲区;而 recvfrom 只是将传输层协议的缓冲区的数据拷贝到了应用层的缓冲区 (即用户自己定义的缓冲区)。
  • 可是,将应用层数据拷贝到传输层的缓冲区就完了吗?
  • 那么传输层的缓冲区的数据什么时候发送、发多少, 出错了怎么办?
  • 这些问题谁关心呢? 操作系统帮你关心,即传输层的协议解决这些问题。
  • 传输层的协议(如TCP或UDP)负责管理传输层缓冲区中的数据,包括何时发送数据、发送多少数据以及如何处理发送过程中的错误等问题。应用层只是将数据拷贝给传输层的缓冲区,至于后续的传输过程以及错误处理等任务,都由传输层协议来完成。
  • 因此,传输层的协议承担了管理数据传输的责任,而应用层只需将数据传递给传输层,无需关心具体的传输细节和错误处理等问题。
  • UDP 协议没有发送缓冲区。 调用 sendto 会直接将应用层数据交给内核,内核再将该数据传给网络层协议进行后续的传输动作;
  • UDP 协议具有接收缓冲区。 但这个缓冲区无法保证收到的UDP数据报的顺序和发送UDP数据报的顺序一致,即存在乱序问题; 且如果接收缓冲区如果满了, 再来UDP数据报则会直接被丢弃。

4.6. 全双工 vs 半双工

全双工:既可以收数据,又可以发数据,收发可以同时进行;

半双工:如果在收,就不能发;如果再发,就不能收, 收发并不能并行。

UDP和TCP都是全双工的协议,这意味着它们都支持在任意时刻进行并发的数据收发。

在UDP和TCP协议中,发送和接收是独立进行的。发送端可以在任何时候发送数据,而接收端可以在任何时候接收数据,这使得它们都具有全双工通信的能力。

那么UDP和TCP是如何支持全双工的呢?

只要发送和接受操作是相互独立的 (发送缓冲区和接收缓冲区并不冲突),那么就可以支持全双工。具体来说:

  • UDP:虽然UDP没有专门的发送缓冲区,但发送操作是立即执行的,它不会阻塞接收操作。这意味着在UDP通信中,发送数据时并不会影响接收缓冲区,因此UDP可以实现全双工通信。
  • TCP:TCP有发送缓冲区和接收缓冲区,但发送和接收操作是独立进行的。发送数据时,TCP会将数据放入发送缓冲区并立即返回,不会阻塞接收操作。同样,接收数据时,TCP会从接收缓冲区读取数据并立即返回,也不会影响发送操作。这使得TCP也能够实现全双工通信。

总的来说,UDP和TCP之所以能够支持全双工通信,是因为它们在发送和接收操作上相互独立,不会相互阻塞。

4.7. UDP 使用注意事项 

UDP协议的数据报文有一个最大长度限制 (因为它是16位的整数),即64K(包括UDP头部)。在当今的互联网环境下,这个限制可能会限制数据传输的效率和灵活性,特别是在需要传输大量数据的情况下。

  • 如果需要传输的数据超过了UDP数据报的最大长度限制,就需要在应用层进行手动的分包和多次发送。
  • 发送端将数据分割成适当大小的数据块,然后分别封装成UDP数据报进行发送。
  • 在接收端,需要接收所有的UDP数据报,并且按照顺序将它们拼装成完整的数据。
  • 这就需要在应用层处理数据的分段和组装,增加了复杂性和开销

4.8. 基于UDP的应用层协议 

  • NFS:网络文件系统
  • TFTP:简单文件传输协议
  • DHCP:动态主机配置协议
  • BOOTP:启动协议(用于无盘设备启动)
  • DNS:域名解析协议

当然,也包括用户在编写UDP时自定义的应用层协议。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/518204.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

向量数据库 | AI时代的航道灯塔

向量数据库 | AI时代的航道灯塔 什么是向量检索服务拍照搜商品 你使用过向量数据库吗?使用体验?为什么向量数据库能借由大模型引起众多关注向量数据库在当前AI热潮中是昙花一现,还是未来AI时代的航道灯塔? 今天的话题主要是讨论向…

python-基础篇-字符串、列表、元祖、字典-列表

文章目录 2.3.2列表2.3.2.1列表介绍2.3.2.1.1列表的格式2.3.2.1.2打印列表 2.3.2.2列表的增删改查2.3.2.2.1列表的遍历2.3.2.2.1.1使用for循环2.3.2.2.1.2使用while循环 2.3.2.2.2添加元素("增"append, extend, insert)2.3.2.2.2.1append 2.3.2.2.2.2extend2.3.2.2.2…

博客搭建(hexo+github)

简介 搭建完成网站的如下所示 https://polarday.top/ 使用github托管博客,完全免费不需要购买服务器 博客框架:hexo hexo主题:ICARUS 图床:githubPicGo 编辑:vscode 为什么使用hexo框架?因为hexo是静态框…

新手开抖店:选品过后如何有效对接达人?这些方法100%有效!

哈喽~我是电商月月 要说做抖音小店最主要的是什么?那当然是找品了 那出单最快的方法是什么?无疑是达人带货了! 但新手店铺没销量,没体验分,没好评怎么能让达人同意帮我们带货呢? 方法其实很简单&#x…

上位机图像处理和嵌入式模块部署(qmacvisual之plc通信)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 传统的非标自动化设备当中,plc发挥了很大的作用。这里面如何对这些电机和机构进行控制,大多数场景下用的就是plc设备了。目…

常用的AI绘画自动生成器介绍

AI绘画自动生成器是一种利用人工智能技术生成图像的工具。它可以根据用户输入的文本描述自动生成相应的图像。目前,有几种流行的AI绘画自动生成器,包括: 1. **DALL-E 2** DALL-E 2是由OpenAI开发的AI绘画生成器,它可以根据用户输入的自然语言描述生成高质量的图像。DALL-E 2使…

上位机图像处理和嵌入式模块部署(qmacvisual之tcp服务器端)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 上面一篇,我们谈到了tcp客户端,另外一种连接方法就是tcp服务器端。事实上,对于第三方系统,大多数情…

解析Apache Kafka:在大数据体系中的基本概念和核心组件

关联阅读博客文章:探讨在大数据体系中API的通信机制与工作原理 关联阅读博客文章:深入解析大数据体系中的ETL工作原理及常见组件 关联阅读博客文章:深度剖析:计算机集群在大数据体系中的关键角色和技术要点 关联阅读博客文章&a…

账号和权限管理

一、账号 1.用户的类型 1.超级管理:权限最高的用户 2.普通用户:权限受到限制的用户 3.程序用户:不是给人登录使用的,给程序使用的,这些用户一般不允许登录到系统,一般是为了支持程序运行(超级管理员和普…

C语言中的结构体:揭秘数据的魔法盒

前言 在C语言的广阔天地中,结构体无疑是一颗璀璨的明珠。它就像是一个魔法盒,能够容纳各种不同类型的数据,并按我们的意愿进行组合和排列。那么,这个魔法盒究竟有何神奇之处呢?让我们一探究竟。 一、结构体的诞生&…

SV学习笔记(七)

类型转换 写在前面 类型转换可以分为 静态转换和动态转换 。静态转换即需要在转换的表达式前 加上单引号 即可,该方式并不会对转换值做检查。如果发生转换失败,我们也无从得知。动态转换即需要使用 系统函数$cast(tgt, src) 做转换。静态转…

光猫桥接模式详细步骤

目录 一、前言 路由模式 (宽带默认) 桥接模式 二、桥接模式步骤 (一)图片记录备份 设备信息图 网络侧信息 远程管理密码 宽带上网设置 (二)桥接模式开始 光猫设置 路由器设置 一、前言 重点&a…

【学习笔记】java项目—苍穹外卖day10

文章目录 苍穹外卖-day10课程内容1. Spring Task1.1 介绍1.2 cron表达式1.3 入门案例1.3.1 Spring Task使用步骤1.3.2 代码开发1.3.3 功能测试 2.订单状态定时处理2.1 需求分析2.2 代码开发2.3 功能测试 3. WebSocket3.1 介绍3.2 入门案例3.2.1 案例分析3.2.2 代码开发3.2.3 功…

【python从入门到精通】-- 第四战:语句汇总

🌈 个人主页:白子寰 🔥 分类专栏:python从入门到精通,魔法指针,进阶C,C语言,C语言题集,C语言实现游戏👈 希望得到您的订阅和支持~ 💡 坚持创作博文…

Python人工智能应用----文本情感分析

1.问题引入 接着前两节课的内容,今天我们要构建一个人工智能系统。 它的目的是像人类一样,区分评价的情感是正面还是负面的。 接下来,我们要对提取的文本进行感情色彩的分析,这个就是文本情感分析,我们要使用机器学习…

RecyclerView 与 ListView(一):使用

RecyclerView 与 ListView 功能对比 对比项AbsListViewRecyclerView定向刷新不支持支持局部刷新不支持支持刷新动画不支持支持Item点击支持不支持分隔线样式单一自定义样式布局方式列表/网格自定义样式头尾添加支持不支持 Adapter Adapter:1.创建View 2.绑定数据…

理解Three.js的相机

大家都知道我们生活中的相机,可以留下美好瞬间。那Three.js的相机是什么呢?Three.js创建的场景是三维的,而我们使用的显示器显然是二维的,相机就是抽象的定义了三维空间到二维显示器的投影方式。Three.js常见的相机有两类&#xf…

(React组件基础)前端八股文修炼Day6

一 类组件与函数组件有什么异同 在React中,类组件和函数组件是创建组件的两种主要方式。随着React的发展,尤其是自Hooks在React 16.8中引入以来,函数组件的功能变得更加强大,使得它们能够更加方便地与类组件相竞争。下面是类组件…

Apache Paimon实时数据糊介绍

Apache Paimon 是一种湖格式,可以使用 Flink 和 Spark 构建实时 数据糊 架构,用于流式和批处理操作。Paimon 创新地将湖格式和 LSM(日志结构合并树)结构相结合,将实时流式更新引入湖架构中。 Paimon 提供以下核心功能: 实时更新: 主键表支持大规模更新的写入,具有非常…

helm安装chart报unclosed action

我在尝试使用helm安装mysql主从集群,使用bitnami/mysql的官方chart,但是执行helm install mysql-cluster mysql -n mysql-cluster报如下错误: Error: parse error at (mysql/charts/common/templates/_labels.tpl:14): unclosed action 百度…