go语言网络编程
- 网络编程
- Go语言网络编程相关API
- Go语言网络编程架构
- Go语言的网络编程实现基于以下几个关键原理:
- bufio
- bufio 包的主要功能和使用场景
- 主要类型
- 示例 tcp通信
- 解决粘包
- 粘包和拆包的产生原因
- 解决方法
- 示例
网络编程
Go语言网络编程相关API
1.1 net包net.Listen(network, address string): 创建一个网络监听器,等待进入的连接。它的第一个参数是网络类型(如"tcp"、"udp"等),第二个参数是要监听的地址。net.Dial(network, address string): 连接到指定的地址,返回一个连接对象。net.Conn接口: 提供了与网络连接相关的方法,包括:Read(b []byte) (n int, err error): 从连接中读取数据。Write(b []byte) (n int, err error): 向连接写入数据。Close() error: 关闭连接。net.Listener接口: 用于接受传入连接的接口,包含方法:Accept() (Conn, error): 接受一个连接请求。1.2 net/http包http.HandleFunc(pattern string, handler func(ResponseWriter, *Request)): 注册处理函数,当请求的URL匹配指定模式时,执行该处理函数。http.ListenAndServe(addr string, handler Handler) error: 启动HTTP服务器并监听指定地址上的请求。http.Request结构体: 包含请求的各种信息,如方法、URL、头部信息等。http.ResponseWriter接口: 用于构造HTTP响应,包含方法来设置响应头和写入响应体。http.Get(url string): 发起一个GET请求并返回响应。
Go语言网络编程架构
Go的网络编程架构主要基于事件驱动和goroutine的并发模型。
每当一个新的连接被接受时,服务器会为其启动一个新的goroutine来处理该连接。
因此,Go语言能够轻松地处理大量的并发连接,而无需使用复杂的线程管理。
服务器架构:
监听端口:服务器通过net.Listen监听指定端口。
接受连接:使用listener.Accept()接收连接。
处理连接:为每个连接创建goroutine,执行自定义的处理逻辑。
关闭连接:在处理完成后,关闭连接释放资源。
客户端架构:
创建连接:通过net.Dial或http.Get创建连接。
发送请求:向服务器发送请求或数据。
接收响应:获取服务器的响应数据。
关闭连接:处理完成后,关闭连接。
Go语言的网络编程实现基于以下几个关键原理:
Goroutine和通道: Go的并发模型建立在轻量级goroutine之上。
每一个网络连接都可以在单独的goroutine中处理,这样可以有效利用系统资源,提高并发处理能力。
通道(channel)用于在多个goroutine之间传递数据,保证数据的安全和同步。
I/O多路复用: Go使用操作系统的I/O多路复用机制,如epoll(Linux)或kqueue(BSD),
来处理大量连接的读写事件,从而减少线程上下文切换的开销。
封装性和易用性: Go的net和net/http包提供了高层次的抽象,简化了网络编程的复杂性。
开发者不需要深入底层的socket编程,只需调用简单的API即可实现复杂的网络操作。
bufio
bufio 是 Go 语言标准库中的一个包,用于提供 buffered I/O(缓冲输入/输出)功能。
它通过对输入和输出操作进行缓冲处理,从而提高程序的性能,
减少系统调用的次数。bufio 常用于处理文件、网络连接等 I/O 操作。
bufio 包的主要功能和使用场景
提高效率:
bufio 使用内存中的缓冲区来存储数据,从而减少直接的系统调用次数(如读写磁盘或网络),提高了读写效率。
简单的接口:
bufio 提供了简单易用的接口,让开发者能够更方便地处理文本和二进制数据。
处理文本数据:
bufio 特别适合处理行或单词输入输出,可以方便地读取和写入文本数据。
主要类型
以下是 bufio 包中几个重要的类型:
Reader:bufio.Reader 是一个结构体,用于缓存输入。通过 Read() 方法,可以从底层的 io.Reader 中读取数据,使用缓冲可以减少调用次数。
常用方法:Read(p []byte) (n int, err error): 从缓冲区读取数据到切片 p 中。ReadString(delim byte) (string, error): 读取直到遇到分隔符 delim 的数据,并返回作为字符串。ReadBytes(delim byte) ([]byte, error): 与 ReadString 类似,但返回字节切片。示例:reader := bufio.NewReader(conn)message, err := reader.ReadString('\n') // 读取一行数据Writer:bufio.Writer 是一个结构体,用于缓存输出。可以将数据写入缓冲区,并在缓冲区填满后再一次性写入到底层的 io.Writer。
常用方法:Write(p []byte) (n int, err error): 将数据写入缓冲区。Flush() error: 将缓冲区中的数据写入到底层的 io.Writer。
示例:writer := bufio.NewWriter(conn)writer.Write([]byte("Hello, Client!\n")) // 写入数据到缓冲区writer.Flush() // 确保将数据写入连接Scanner:bufio.Scanner 提供了一个方便的方式来逐行读取输入,常用于处理文本数据。
常用方法:Scan() bool: 读取下一个 token,返回 true 表示成功。Text() string: 返回上一个 token 作为字符串。
示例:scanner := bufio.NewScanner(os.Stdin)for scanner.Scan() {line := scanner.Text() // 得到输入的一行fmt.Println(line)}
示例 tcp通信
这段代码实现了一个简单的聊天服务器,能够接收并广播消息到所有连接的客户端。
使用了 goroutines 来处理每个客户端的连接,
确保服务器能够同时处理多个连接。
通过 sync.Mutex 来保护对共享数据结构 clients 的并发访问。package mainimport ("bufio" // 导入 bufio 包,用于读取输入"fmt" // 导入 fmt 包,用于格式化输出"net" // 导入 net 包,用于网络操作"sync" // 导入 sync 包,用于同步操作
)// 定义全局变量
var (// 保存所有连接的客户端,使用 map 结构clients = make(map[net.Conn]bool)clientsMux sync.Mutex // 保护 clients 的并发访问,防止数据竞态
)func main() {// 监听指定的端口(8080)listener, err := net.Listen("tcp", ":8080")if err != nil {// 如果监听失败,打印错误信息并返回fmt.Println("Error starting server:", err)return}defer listener.Close() // 在 main 函数结束时关闭监听器fmt.Println("Chat server started on :8080") // 服务器启动成功提示for {// 接受新的连接conn, err := listener.Accept()if err != nil {// 如果接受连接失败,打印错误信息并继续下一次循环fmt.Println("Error accepting connection:", err)continue}// 将新客户端添加到客户端列表clientsMux.Lock() // 加锁以保护 clients 的并发访问clients[conn] = true // 将连接添加到 clientsclientsMux.Unlock() // 解锁// 启动一个 goroutine 来处理该连接go handleConnection(conn)}
}// 处理连接的函数
func handleConnection(conn net.Conn) {defer func() {// 关闭连接,并从 clients 中移除conn.Close() // 关闭连接clientsMux.Lock() // 加锁以保护 clientsdelete(clients, conn) // 从 clients 中移除该连接clientsMux.Unlock() // 解锁}()// 创建一个读取器reader := bufio.NewReader(conn)for {// 读取客户端发送的消息直到换行符message, err := reader.ReadString('\n')if err != nil {// 如果读取失败,打印错误信息并返回fmt.Println("Error reading from connection:", err)return}// 打印接收到的消息fmt.Printf("Received: %s", message)// 广播消息到所有其他客户端broadcastMessage(message, conn) // 调用广播函数}
}// 广播消息给所有连接的客户端
func broadcastMessage(message string, sender net.Conn) {clientsMux.Lock() // 加锁以保护 clients 的并发访问defer clientsMux.Unlock() // 在函数结束时解锁// 遍历所有连接的客户端for client := range clients {// 不向发送消息的客户端发送消息if client != sender {_, _ = client.Write([]byte(message)) // 发送消息}}
}
这段代码实现了一个简单的聊天客户端,能够连接到服务器并发送消息,同时也会接收并显示服务器发来的消息。
通过 goroutines 来异步处理接收消息和发送消息,确保用户可以一边发送消息一边接收来自服务器的消息。
使用 bufio.Scanner 和 bufio.Reader 来处理输入和输出的读取。
package mainimport ("bufio" // 导入 bufio 包,用于读取输入"fmt" // 导入 fmt 包,用于格式化输出"net" // 导入 net 包,用于网络操作"os" // 导入 os 包,用于与操作系统交互
)func main() {// 连接到聊天服务器,指定服务器地址(localhost:8080)conn, err := net.Dial("tcp", "localhost:8080")if err != nil {// 如果连接失败,打印错误信息并返回fmt.Println("Error connecting to server:", err)return}defer conn.Close() // 在 main 函数结束时关闭连接// 启动一个 goroutine 来读取服务器的消息go readMessages(conn)// 从标准输入读取消息并发送给服务器sendMessages(conn)
}// 从连接中读取消息
func readMessages(conn net.Conn) {reader := bufio.NewReader(conn) // 创建一个读取器for {// 读取服务器发送的消息直到换行符message, err := reader.ReadString('\n')if err != nil {// 如果读取失败,打印错误信息并返回fmt.Println("Error reading from server:", err)return}// 打印接收到的消息fmt.Print("Received: ", message)}
}// 发送消息到服务器
func sendMessages(conn net.Conn) {scanner := bufio.NewScanner(os.Stdin) // 创建一个扫描器来读取标准输入fmt.Println("消息发送中:") // 提示用户开始输入消息for scanner.Scan() {// 从输入中读取一行消息message := scanner.Text()// 发送消息到服务器,并在结尾加上换行符_, err := conn.Write([]byte(message + "\n"))if err != nil {// 如果发送失败,打印错误信息并返回fmt.Println("Error sending message:", err)return}}
}
解决粘包
在网络编程中,尤其是使用TCP进行通信时,常会遇到“粘包”与“拆包”现象。这是因为TCP是一个流式协议,
数据在传输过程中可能会被合并成一个大的数据包,或者一个大的数据包可能被分割成多个小的数据包,从而导致接收方无法正确解析消息。
粘包和拆包的产生原因
粘包:发送方可能连续发送多个消息,而TCP将这些消息合并为一个包,接收方收到的数据中包含多个消息。
拆包:发送方发送一个较大的消息,TCP将其分成多个小包发送,接收方在一次读取中只获取到部分消息。
解决方法
为了防止粘包和拆包现象,通常采用以下几种策略来处理:
-
使用固定长度的消息
在这种方法中,每个消息都使用固定的字节长度来进行编码。这样接收方可以根据固定的长度来读取数据。优点:实现简单。
缺点:不适用于消息长度不一致的情形,浪费带宽(如果消息较短)。 -
使用分隔符
在每个消息的末尾加入一个特定的分隔符,比如换行符、特定字符等,接收方在读取数据时,可以根据分隔符进行解析。优点:可以处理可变长度的消息。
缺点:需要确保分隔符不会出现在消息内容中。 -
消息头部长度
在每个消息前添加一个固定大小的头部,头部包含消息的长度信息,接收方首先读取头部,获取消息长度,再按长度读取数据。优点:适用于任意长度的消息。
缺点:稍显复杂,需处理头部。
示例
此代码实现了一个聊天服务器,能够接受多个客户端连接,
接收消息并将其广播给所有连接的客户端。
它通过使用二进制数据处理来确保消息的完整性和准确性,使用协程来并发处理每个连接,
确保服务器在处理多个客户端时的高效性。
通过使用 sync.Mutex 来避免对共享数据结构 clients 的竞争访问。
package mainimport ("encoding/binary" // 导入 encoding/binary 包,用于在网络中进行字节序转换"fmt" // 导入 fmt 包,用于格式化输出"net" // 导入 net 包,用于网络操作"sync" // 导入 sync 包,用于同步操作
)// 定义全局变量
var (clients = make(map[net.Conn]bool) // 用于保存所有连接的客户端clientsMux sync.Mutex // 保护 clients 的并发访问
)func main() {// 监听指定的端口(8080)listener, err := net.Listen("tcp", ":8080")if err != nil {// 如果启动失败,打印错误信息并返回fmt.Println("Error starting server:", err)return}defer listener.Close() // 在 main 函数结束时关闭监听器fmt.Println("Chat server started on :8080") // 服务器启动成功提示for {// 接受新的连接conn, err := listener.Accept()if err != nil {// 如果接受连接失败,打印错误信息并继续下一次循环fmt.Println("Error accepting connection:", err)continue}// 将新客户端添加到客户端列表clientsMux.Lock() // 加锁以保护 clients 的并发访问clients[conn] = true // 将连接添加到 clientsclientsMux.Unlock() // 解锁// 启动一个 goroutine 来处理该连接go handleConnection(conn)}
}// 处理连接的函数
func handleConnection(conn net.Conn) {defer func() {// 关闭连接,并从 clients 中移除conn.Close() // 关闭连接clientsMux.Lock() // 加锁以保护 clientsdelete(clients, conn) // 从 clients 中移除该连接clientsMux.Unlock() // 解锁}()for {// 读取消息长度(前4个字节)lengthBuffer := make([]byte, 4) // 创建一个4字节的缓冲区_, err := conn.Read(lengthBuffer) // 从连接中读取消息长度if err != nil {// 如果读取长度失败,打印错误信息并返回fmt.Println("Error reading length:", err)return}// 根据读取到的长度转换成整型msgLength := int(binary.BigEndian.Uint32(lengthBuffer))messageBuffer := make([]byte, msgLength) // 根据长度创建消息缓冲区// 读取消息/* @param messageBuffer 接收消息的缓冲区 @param conn 客户端连接 @return 返回读取的字节数和错误信息*/_, err = conn.Read(messageBuffer) // 从连接中读取消息if err != nil {// 如果读取消息失败,打印错误信息并返回fmt.Println("Error reading message:", err)return}// 打印接收到的消息fmt.Printf("Received message: %s\n", string(messageBuffer))// 将接收到的消息广播给所有其他客户端broadcastMessage(messageBuffer, conn)}
}// 广播消息给所有连接的客户端
func broadcastMessage(message []byte, sender net.Conn) {clientsMux.Lock() // 加锁以保护 clients 的并发访问defer clientsMux.Unlock() // 在函数结束时解锁// 获取消息长度msgLength := uint32(len(message))lengthBuffer := make([]byte, 4) // 创建一个4字节的缓冲区binary.BigEndian.PutUint32(lengthBuffer, msgLength) // 将消息长度转换为大端字节序// 遍历所有连接的客户端for client := range clients {// 不向发送者发送消息if client != sender {// 首先发送消息长度_, _ = client.Write(lengthBuffer) // 发送长度_, _ = client.Write(message) // 然后发送消息}}
}
此代码实现了一个简单的聊天客户端,能够连接到服务器、发送消息并接收服务器的消息。
通过二进制传输消息长度,确保了消息的完整性。
package mainimport ("bufio" // 导入 bufio 包,用于读取输入"encoding/binary" // 导入 encoding/binary 包,用于字节序转换"fmt" // 导入 fmt 包,用于格式化输出"net" // 导入 net 包,用于网络操作"os" // 导入 os 包,用于与操作系统交互
)func main() {// 连接到聊天服务器,指定服务器地址(localhost:8080)conn, err := net.Dial("tcp", "localhost:8080")if err != nil {// 如果连接失败,打印错误信息并返回fmt.Println("Error connecting to server:", err)return}defer conn.Close() // 在 main 函数结束时关闭连接// 启动一个 goroutine 来读取服务器的消息go readMessages(conn)// 从标准输入读取消息并发送给服务器sendMessages(conn)
}// 从连接中读取消息的函数
func readMessages(conn net.Conn) {for {// 创建一个4字节的缓冲区用于读取消息长度lengthBuffer := make([]byte, 4)_, err := conn.Read(lengthBuffer) // 从连接中读取消息长度if err != nil {// 如果读取长度失败,打印错误信息并返回fmt.Println("Error reading length:", err)return}// 将读取到的长度转换为整型msgLength := int(binary.BigEndian.Uint32(lengthBuffer))messageBuffer := make([]byte, msgLength) // 根据长度创建消息缓冲区_, err = conn.Read(messageBuffer) // 从连接中读取消息if err != nil {// 如果读取消息失败,打印错误信息并返回fmt.Println("Error reading message:", err)return}// 打印接收到的消息fmt.Printf("Received message: %s\n", string(messageBuffer))}
}// 发送消息到服务器的函数
func sendMessages(conn net.Conn) {scanner := bufio.NewScanner(os.Stdin) // 创建一个扫描器来读取标准输入fmt.Println("Type your messages below (end with Enter):") // 提示用户开始输入消息for scanner.Scan() {// 从输入中读取一行消息message := scanner.Text()// 获取消息长度msgLength := uint32(len(message))lengthBuffer := make([]byte, 4) // 创建一个4字节的缓冲区binary.BigEndian.PutUint32(lengthBuffer, msgLength) // 将消息长度转换为大端字节序// 先发送消息长度_, err := conn.Write(lengthBuffer) // 发送长度if err != nil {// 如果发送失败,打印错误信息并返回fmt.Println("Error sending length:", err)return}// 再发送消息_, err = conn.Write([]byte(message)) // 发送消息if err != nil {// 如果发送失败,打印错误信息并返回fmt.Println("Error sending message:", err)return}}
}相关文章:
go语言网络编程
网络编程Go语言网络编程相关APIGo语言网络编程架构Go语言的网络编程实现基于以下几个关键原理:bufiobufio 包的主要功能和使用场景主要类型示例 tcp通信解决粘包粘包和拆包的产生原因解决方法示例 网络编程 Go语言网络编程相关API 1.1 net包net.Listen(network, a…...
LeetcodeLCR 116. 省份数量
文章目录 题目原题链接思路C代码 题目 原题链接 LCR 116. 省份数量 思路 利用并查集的思想,将连接的诚实放在一个集合当中,最后遍历并查集数组判断有几颗树 初始化一个并查集;将连通的城市合并;统计并查集中树的个数;…...
Linux系统上搭建Vulhub靶场
Linux系统上搭建Vulhub靶场 vulhub 是一个开源的漏洞靶场,它提供了各种易受攻击的服务和应用程序,供安全研究人员和学习者测试和练习。要在 Linux 系统上安装和运行 vulhub,可以按照以下步骤进行: 1. 安装 Docker 和 Docke…...
Avalonia的第三方UI库SukiUI详细教程
文章目录 一、SukiUI 简介二、安装与配置1、安装 SukiUI 库:2、配置 Avalonia 项目以使用 SukiUI:三、基本组件使用1、按钮(SukiButton):2、文本框(SukiTextBox):3、标签(SukiLabel):4、下拉列表(SukiComboBox):四、布局与容器1、布局容器介绍:2、使用布局容器组…...
https协议文件上传比http协议慢
一.自己写一个文件上传的接口,在浏览器文件上传https协议比http协议慢(速度上https协议是http协议的八分之一左右),在postman上传是正常的(证明代码是没有问题的),那就是协议的问题 二.经发现&…...
Elasticsearch在大数据处理中的优势
Elasticsearch 在大数据处理中的优势主要体现在以下几个方面: 1. 分布式架构 水平扩展:Elasticsearch 设计为分布式系统,可以轻松地通过增加节点来水平扩展,处理 PB 级别的数据。数据分片和复制:数据自动分片并跨多个…...
cmake--target_compile_definitions
作用 笼统的说是:该命令添加预编译选项到编译目标中。 预编译选项 预编译选项(Preprocessor Options)是一类用于控制 C/C 预处理器行为的编译选项。预处理器是 C/C 编译过程中的第一个处理阶段,主要负责对源代码中的预处理指令…...
MATLAB数据文件读写:1.格式化读写文件
格式化读写文件 matlab提供了对数据文件建立、打开、读取、写入、关闭等操作的函数。 数据文件可以分为两类: 文本文件:以ASCII码形式存储的文本文件;编码基于字符定长,译码相对容易二进制文件:以二进制形式存储的文…...
NFTScan | 09.16~09.23 NFT 市场热点汇总
欢迎来到由 NFT 基础设施 NFTScan 出品的 NFT 生态热点事件每周汇总。 周期:2024.09.16~ 2024.09.22 NFT Hot News 01/ DeGods 推出代币 DEGOD,用户可通过 DeGods、y00ts 或 DUST 进行转换 9 月 16 日,Solana NFT 项目 DeGods 推出代币…...
rabbitmq整合skywalking并编写自定义插件增强
rabbitmq整合skywalking 首先先下载准备好skywalking 的服务端和ui控制台,java-agent https://skywalking.apache.org/downloads/ 整合skywalking 我的流程是在生产者和消费者服务中去引入一个mq的sdk,具体SDK的内容可以查看这篇文章 在sdk的pom文件…...
sftp登录ipv6用中括号 `sftp x@[ipv6]`
sftp登录ipv6用中括号 sftp x[ipv6] 实例 sftp root[2::fd40:1:1]SFTP(Secure File Transfer Protocol,安全文件传输协议)是一种基于SSH(Secure Shell)的安全协议,用于在网络上安全地传输文件。当需要登录…...
Python 从入门到实战25(模块)
我们的目标是:通过这一套资料学习下来,通过熟练掌握python基础,然后结合经典实例、实践相结合,使我们完全掌握python,并做到独立完成项目开发的能力。 上篇文章我们讨论了类继承的相关知识。今天我们将学习一下模块的…...
Leetcode面试经典150题-172.阶乘后的零
给定一个整数 n ,返回 n! 结果中尾随零的数量。 提示 n! n * (n - 1) * (n - 2) * ... * 3 * 2 * 1 示例 1: 输入:n 3 输出:0 解释:3! 6 ,不含尾随 0示例 2: 输入:n 5 输出&a…...
【机器学习】揭秘GBDT:梯度提升决策树
目录 🍔 提升树 🍔 梯度提升树 🍔 举例介绍 3.1 初始化弱学习器(CART树) 3.2 构建第一个弱学习器(CART树) 3.3 构建第二个弱学习器(CART树) 3.4 构建第三个弱学习…...
Android Studio 2024 安装、项目创建、加速、优化
文章目录 Android Studio安装Android Studio项目创建Android Studio加速修改GRADLE_USER_HOME位置减少C盘占用空间GRADLE加速 修改模拟器位置减少C盘占用空间参考资料 Android Studio安装 下载android studio download android-studio-2024.1.2.12-windows.exe 或者 android-…...
JSP(Java Server Pages)基础使用
首先在web文件夹中新建一个jsp/jspx文件,这个文件就是jsp文件 <%--Created by IntelliJ IDEA.User: ***Date: 2024/9/23Time: 18:43To change this template use File | Settings | File Templates. --%> <% page contentType"text/html;charsetUTF-…...
数据结构 - 概述及其术语
经过上一章节《数据结构与算法之间有何关系?》的阐述,相信大家对数据结构多少有了点了解,今天我们将进入数据结构的正式学习中。 在计算机科学中,数据结构是一种数据管理、组织和存储的格式。它是相互之间存在一种或多种特定关系的…...
UE5——在线子系统
Unreal Engine 5 (UE5) 的在线子系统(Online Subsystem)实现多人在线游戏的原理涉及到网络编程和分布式系统设计中的多个方面。以下是该系统工作的一些核心概念和技术: 1. 客户端-服务器架构: - 大多数现代多人在线游戏采用客户端-服务器模型…...
9.23-部署项目
部署项目 一、先部署mariadb [rootk8s-master ~]# mkdir aaa [rootk8s-master ~]# cd aaa/ [rootk8s-master aaa]# # 先部署mariadb [rootk8s-master aaa]# # configmap [rootk8s-master aaa]# vim mariadb-configmap.yaml apiVersion: v1 kind: ConfigMap metadata:name: ma…...
非标独立设计选型--二十六--电磁阀的选型件算
电磁阀:电磁控制---自动化的关键 PLC ---- 继电器----电磁阀----调速阀----气缸 供气源--- 【电磁阀主要负责:换向,实现气缸的动作变化】 电磁阀有哪些参数是会影响到使用的? …...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...
