menu dzf
search self_improvement
目录

微服务轻量级通信原理

dzf
dzf 2021年05月06日  ·  阅读 124

微服务概念
微服务架构强调将应用程序分解为一组小型、独立的服务,每个服务围绕特定的业务功能构建,并通过定义明确的API进行通信。这种架构方式能够提高系统的可扩展性、灵活性和可维护性。微服务架构的关键特性包括:

  • 模块化:每个服务都是一个独立的模块,可以单独开发、部署和扩展。
  • 独立部署:每个服务都可以独立部署,无需影响其他服务。
  • 松耦合:服务之间通过明确定义的API进行通信,通常是HTTP RESTful API或消息队列。
  • 技术多样性:不同的服务可以使用不同的技术栈,如Java、Python、Node.js等,充分利用各种技术的优势。

常见的通信机制

同步通信

  1. HTTP/REST:基于HTTP协议,使用标准的HTTP动词(GET, POST, PUT, DELETE等)进行通信。它是微服务间最常用的通信方式之一。
  2. gRPC:一种高性能的远程过程调用(RPC)框架,使用HTTP/2协议,支持多种语言。
  3. WebSocket:适用于需要实时双向通信的应用场景,如聊天应用、实时数据分析等
  4. Socket:直接基于TCP或UDP,所以它可以非常高效地处理大量的数据传输

异步通信

  1. AMQP (Advanced Message Queuing Protocol):一种为消息中间件设计的标准协议,常用于实现消息队列。
  2. Kafka:一种分布式流处理平台,用于构建实时数据管道和流应用。

最低层通信原理
在最底层,无论是同步还是异步通信,最终都会归结到网络通信层面,即通过TCP/IP协议栈进行数据交换。网络通信是所有通信的基础,它定义了数据在网络上的传输方式。下图是 OSI 模型 TCP/IP
OSI模型
七层

其中IP与TCP的区别见下面这张图:
TCP_IP
互联网上一般使用 源 IP 地址、目标 IP 地址、源端口号、目标端口号 来进行区分。IP 是Internet Protocol(网际互连协议)的缩写,是 TCP/IP 体系中的网络层协议。IP 是整个 TCP/IP 协议族的核心,也是构成互联网的基础。为了实现大规模网络的互通互联,IP 更加注重适应性、简洁性和可操作性,并在可靠性做了一定的牺牲。IP 不保证分组的交付时限和可靠性,所传送分组有可能出现丢失、重复、延迟或乱序等问题。既然 IP 不可靠,那么如何保证数据能够准确无误地到达呢?这就涉及到 TCP 传输机制的问题了。

TCP传输机制
TCP 的全称是 Transmission Control Protocol,它被称为是一种面向连接(connection-oriented) 的协议,这是因为一个应用程序开始向另一个应用程序发送数据之前,这两个进程必须先进行握手,握手是一个逻辑连接,并不是两个主机之间进行真实的握手。一旦主机 A 和主机 B 建立了连接,那么进行通信的应用程序只使用这个虚拟的通信线路发送和接收数据就可以保证数据的传输,TCP 协议负责控制连接的建立、断开、保持等工作。使用 TCP 或 UDP 通信时,会广泛用到套接字的 API,使用这套 API 设置 IP 地址、端口号,实现数据的发送和接收。

套接字(Socket)原理
在 TCP 或者 UDP 发送具体的报文信息前,需要先经过一扇 门,这个门就是套接字(socket),套接字向上连接着应用层,向下连接着网络层。在操作系统中,操作系统分别为应用和硬件提供了接口(Application Programming Interface)。而在计算机网络中,套接字同样是一种接口,它也是有接口 API 的。
套接字

Socket 和 TCP/IP 没有必然联系,Socket 的出现只是方便了 TCP/IP 的使用,套接字的主要类型有三种,下面我们分别介绍一下

  • 数据报套接字(Datagram sockets):数据报套接字提供一种无连接的服务,而且并不能保证数据传输的可靠性。数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP( User DatagramProtocol)协议进行数据的传输。由于数据报套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在程序中做相应的处理。
  • 流套接字(Stream sockets):流套接字用于提供面向连接、可靠的数据传输服务。能够保证数据的可靠性、顺序性。流套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即 TCP(The Transmission Control Protocol)协议
  • 原始套接字(Raw sockets): 原始套接字允许直接发送和接收 IP 数据包,而无需任何特定于协议的传输层格式,原始套接字可以读写内核没有处理过的 IP 数据包。

下面主要讨论一下Tcp套接字:SOCK_STREAM和Udp套接字:SOCK_DGRAM
Tcp在发包前需要先建立连接,udp直接发包不需要建立连接。Socket的send和receive 的消息包必须是ascll 字节。如果想传输字符串或者字典列表,需要先进行编码,可以用utf-8编码方式。
Tcp:
SOCKET细节

  1. 创建Socket:在客户端和服务端分别创建Socket对象。
  2. 绑定地址:服务端Socket绑定到一个IP地址和端口。
  3. 监听连接:服务端Socket开始监听客户端连接。
  4. 连接建立:客户端Socket连接到服务端Socket。
  5. 数据交换:客户端和服务端通过Socket发送和接收数据。
  6. 连接关闭:完成数据交换后,关闭Socket连接。

代码实现demo:
server:

import socket
import time

server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_address = ('127.0.0.1',9991)
server_socket.bind(server_address)
server_socket.listen(1)
while True:
    client_socket,client_address = server_socket.accept()
    s_time = int(time.time())
    print(client_socket)
    print(client_address)
    while True:
        try:
            client_socket.settimeout(10)
            data = client_socket.recv(1024).decode('utf-8')
        except ConnectionAbortedError:
            break
        except TimeoutError:
            break
        print(data)
        if data == '吴亦凡':
            msg = '他在踩缝纫机'
        elif data == 'close':
            break
        else:
            msg = '没有小道消息'
        client_socket.sendall(msg.encode('utf-8'))
    client_socket.close()

client:

import socket
import time

client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_address = ('127.0.0.1',9991)
client_socket.bind(('127.0.0.1',9999))

client_socket.connect(server_address)
client_socket.sendall('吴亦凡'.encode('utf-8'))
res = client_socket.recv(1024).decode('utf-8')
print(res)
client_socket.sendall('罗志祥'.encode('utf-8'))
res = client_socket.recv(1024).decode('utf-8')
print(res)
client_socket.sendall('权志龙'.encode('utf-8'))
res = client_socket.recv(1024).decode('utf-8')
print(res)

client_socket.close()

以上模拟了一个TCP的长连接通信过程,下面模拟UDP的连接通信

server:

import socket

server_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server_addr = ('127.0.0.1',771)
server_socket.bind(server_addr)

while True:
    data,clien_address = server_socket.recvfrom(1024)
    print(data.decode('utf-8'))
    server_socket.sendto('server_ack'.encode('utf-8'),clien_address)

client:

import socket

client_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server_addr = ('127.0.0.1',771)
client_socket.sendto('啦啦啦'.encode('utf-8'),server_addr)
data,server_channel = client_socket.recvfrom(1024)
print(data.decode('utf-8'))
print(server_channel)

从代码实现细节上不难看出UDP是没有建立连接过程的,使用 UDP 协议在将数据报传递给目标主机时,发送方和接收方的运输层实体间是没有握手的。正因为如此,UDP 被称为是无连接的协议,正因为无连接属性,它是不可靠传输,但是效率高,成本低,一般作为流媒体应用、语音交流、视频会议所使用的传输层协议。

以上重点介绍了Socket轻量级通信机制,Socket 提供了直接与网络层交互的方式,因此可以避免某些高层协议带来的额外开销。允许开发者直接控制网络通信的细节,比如数据的分包、重组等,这可以用来优化特定场景下的通信效率。由于Socket直接基于TCP或UDP,所以它可以非常高效地处理大量的数据传输,在某些特定场景下是非常有用的,特别是当需要高性能或细粒度控制的情况下。

如何选型通信机制
选择轻量级通信机制时,需要考虑以下几个方面:

  1. 性能要求:对于高吞吐量和低延迟的应用,可以选择gRPC或Kafka。
  2. 复杂性:对于简单的微服务交互,可以使用HTTP/REST。
  3. 可靠性:对于需要保证消息传递的应用,可以使用AMQP或Kafka。
  4. 异步处理:对于需要异步处理的应用场景,可以使用消息队列如RabbitMQ或Kafka。
  5. 语言支持:考虑到团队的技术栈和技能,选择支持广泛的语言的通信机制。

总结
在微服务架构中,应用层协议如HTTP/REST、gRPC和WebSocket是常用的通信机制。传输层协议如TCP和UDP提供了基础的数据传输服务,但它们本身并不是直接用于微服务通信的协议。选择适当的通信机制需要根据项目的需求和技术背景来进行。

分类:
标签: