当前位置: 首页 > news >正文

【后端开发】Go语言编程实践,Goroutines和Channels,基于共享变量的并发,反射与底层编程

【后端开发】Go语言编程实践,Goroutines和Channels,基于共享变量的并发,反射与底层编程
【后端开发】Go语言高级编程,CGO、Go汇编语言、RPC实现、Web框架实现、分布式系统

文章目录

      • 1、并发基础, Goroutines和Channels
      • 2、基于共享变量的并发, sync.WaitGroup和sync.Mutex
      • 3、反射与底层编程

参考资料:

  • 1、框架
    go语言源码-124k,
    go精选框架-133k,
    go-gin-HTTP Web框架-80k,
    go-Llama 3.2框架-90k,
    rclone-云存储挂载-50k,
    go-zero-原生微服务框架-20k

  • 2、教程
    The Go programming language - 124k
    Go语言圣经 《The Go Programming Language》 中文版-4.5k
    Go语言高级编程-20k,
    go成长路线-6k,
    go学习指南-3k
    go基础语法-菜鸟

  • 3、应用
    7天实现web-gee和数据库orm框架-16k
    构建goweb应用-43k

1、并发基础, Goroutines和Channels

Goroutine 是 Go 中实现并发的基本单位。它是一种轻量级线程,使用 go 关键字启动。

  • 如果你使用过操作系统或者其它语言提供的线程,那么你可以简单地把goroutine类比作一个线程,这样你就可以写出一些正确的程序了。
  • 当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们叫它main goroutine。新的goroutine会用go语句来创建。
  • 在语法上,go语句是一个普通的函数或方法调用前加上关键字go。go语句会使其语句中的函数在一个新创建的goroutine中运行。而go语句本身会迅速地完成。
go func() {// 这里是并发运行的代码
}()func aaa(str string){}
go aaa("aa")f()    // call f(); wait for it to return
go f() // create a new goroutine that calls f(); don't wait

Channels

  • 如果说goroutine是Go语言程序的并发体的话,那么channels则是它们之间的通信机制。一个channel是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息。
  • 每个channel都有一个特殊的类型,也就是channels可发送数据的类型。一个可以发送int类型数据的channel一般写为chan int。
  • 和map类似,channel也对应一个make创建的底层数据结构的引用。当我们复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者和被调用者将引用同一个channel对象。和其它的引用类型一样,channel的零值也是nil。
ch := make(chan int) ch <- x  // a send statement
x = <-ch // a receive expression in an assignment statement
<-ch     // a receive statement; result is discardedclose(ch) // 随后对基于该channel的任何发送操作都将导致panic异常。ch = make(chan int)    // unbuffered channel
ch = make(chan int, 0) // unbuffered channel
ch = make(chan int, 3) // buffered channel with capacity 3

操作不同状态的chan会引发三种行为:

panic

  • 向已经关闭的通道写数据
  • 重复关闭通道

阻塞

  • 向未初始化的通道写/读数据
  • 向缓冲区已满的通道写入数据
  • 通道中没有数据,读取该通道

非阻塞

  • 读取已经关闭的通道,这个操作会返回通道元素类型的零值(可用comma, ok语法)
  • 向有缓冲且没有满的通道读/写
package mainimport ("testing"
)func TestChanOperateStatus(t *testing.T) {t.Run("向已经关闭的通道写数据", func(t *testing.T) {ch := make(chan int)close(ch) // 关闭通道ch <- 1   // 这里会引发panic,因为向已关闭的通道发送数据// panic: send on closed channel [recovered]})t.Run("重复关闭通道", func(t *testing.T) {ch := make(chan int)close(ch) // 第一次关闭通道close(ch) // 再次关闭通道会引发panic// panic: close of closed channel [recovered]})t.Run("向未初始化的通道写/读数据", func(t *testing.T) {var ch chan intgo func() {ch <- 1// x := <-ch}()_ = <-ch// fatal error: all goroutines are asleep - deadlock!})t.Run("向缓冲区已满的通道写入数据", func(t *testing.T) {ch := make(chan int, 1)ch <- 1 // 第一次写入,缓冲区未满ch <- 2 // 这里会阻塞,因为缓冲区已满,没有goroutine读取数据// fatal error: all goroutines are asleep - deadlock!})t.Run("通道中没有数据,读取该通道", func(t *testing.T) {ch := make(chan int)_ = <-ch // 这里会阻塞,因为没有goroutine发送数据到通道// fatal error: all goroutines are asleep - deadlock!})t.Run("读取已经关闭的通道,这个操作会返回通道元素类型的零值(可用comma, ok语法)", func(t *testing.T) {ch := make(chan int)close(ch)     // 关闭通道x, ok := <-ch // x 将会是int类型的零值,ok 将会是falseexpectx, expectok := 0, falseif ok != expectok && x != expectx {t.Errorf("expect 0, false, get %d, %t\n", x, ok)}})t.Run("向有缓冲且没有满的通道写,向有缓冲且不为空的通道读", func(t *testing.T) {ch := make(chan int, 2) // 1 也不会堵塞ch <- 1                 // 写入数据,不会阻塞_ = <-ch                // 读取数据,不会阻塞})
}

2、基于共享变量的并发, sync.WaitGroup和sync.Mutex

sync.WaitGroup 计数器,等待并发完成
sync.Mutex 互斥锁,保护共享资源
闭包,捕获外部变量的值

sync.WaitGroup 是一个计数器,用于等待一组 goroutine 完成。使用它的步骤如下:

  • 添加计数:使用 Add(n int) 方法增加计数,通常在启动 goroutine 之前调用。
  • 完成计数:在 goroutine 内部,使用 Done() 方法来减少计数。
  • 等待完成:使用 Wait() 方法阻塞当前 goroutine,直到计数器变为零。
var wg sync.WaitGroupfor i := 0; i < n; i++ {wg.Add(1) // 增加计数go func(i int) {defer wg.Done() // 在完成时减少计数// 某些操作}(i)
}wg.Wait() // 等待所有 goroutine 完成

sync.Mutex 是一种互斥锁,用于保护共享资源,确保同一时间只有一个 goroutine 访问它。使用方法如下:

  • 锁定:使用 Lock() 方法加锁,确保线程安全。
  • 解锁:使用 Unlock() 方法解锁。通常推荐使用 defer 来确保在函数退出时解锁。
var mtx sync.Mutexmtx.Lock() // 上锁
// 对共享资源的访问
mtx.Unlock() // 解锁

在并发操作中,通过收集错误并处理它们也是很常见的做法。可以使用切片来存储可能发生的错误,并且在访问这个切片时,需要使用 Mutex 来保证线程安全。

var errs []error
var mtx sync.Mutexif err != nil {mtx.Lock()errs = append(errs, err)mtx.Unlock()
}

闭包
在 goroutine 内部,可以使用闭包来捕获外部变量的值。这对于确保在并发执行时每个 goroutine 使用到的是正确的变量非常重要。

for _, value := range values {go func(v string) {// 使用 v,确保 v 是当前循环中的值}(value)
}

结合以上组件,可以实现并发的操作,例如:

var wg sync.WaitGroup
var mtx sync.Mutex
var errs []errorfor _, id := range ids {wg.Add(1)go func(id string) {defer wg.Done()// 假设这里是某个并发操作if err := doSomething(id); err != nil {mtx.Lock()errs = append(errs, err)mtx.Unlock()}}(id)
}wg.Wait()// 处理错误
if len(errs) > 0 {// 处理错误逻辑
}

3、反射与底层编程

反射:遍历不确定的结构体的每个字段,可以用反射来获取结构体的字段的值 。以及判断字段数据类型,在调用适当的函数,做神奇操作。
底层编程:cgo

反射是由 reflect 包提供的。它定义了两个重要的类型,Type 和 Value。一个 Type 表示一个Go类型。

  • 反射是程序在运行时能够“观察”并且修改自己的行为的能力。在Go语言中,反射是通过reflect包实现的,它提供了两个核心功能:Type和Value
  • 获取Type和Value:使用reflect.TypeOf()和reflect.ValueOf()可以获取变量的动态类型和值。TypeOf返回的是Type接口,而ValueOf返回的是Value接口
  • 类型和值的查询:通过Type和Value接口的方法,可以查询变量的类型信息和值。例如,Kind()方法可以返回一个常量,表示底层数据类型,如Uint、Float64、Slice等
  • 修改值:使用Value的Set()方法可以修改值,但需要注意的是,只有当值是可设置的(settable)时才能修改。可设置性意味着值必须通过指针传递,并且使用Elem()方法获取指针指向的值进行修改
  • 动态方法调用和字段访问:反射不仅可以用于基础类型和结构体,还可以用于动态地调用方法和访问字段。例如,可以通过MethodByName方法动态调用对象的方法
package mainimport ("fmt""reflect"
)// 定义一个示例结构体
type Employee struct {Name     stringAge      intSalary   float64Active   bool
}// 一个通用函数,使用反射打印并修改结构体字段
func inspectAndModify(v interface{}, newValue interface{}) {// 获取传入值的反射值val := reflect.ValueOf(v)// 检查是否是指针类型,如果是,获取其元素if val.Kind() == reflect.Ptr {val = val.Elem()}// 打印结构体的字段fmt.Printf("结构体类型: %s\n", val.Type())fmt.Println("字段:")for i := 0; i < val.NumField(); i++ {field := val.Field(i)fieldType := val.Type().Field(i)// 打印字段名称和类型fmt.Printf("  %s (%s): %v\n", fieldType.Name, fieldType.Type, field)// 这里我们简单示例,用传入的值替换第一个可设置的字段if i == 0 && field.CanSet() {value := reflect.ValueOf(newValue)if value.Type().AssignableTo(field.Type()) {field.Set(value)fmt.Printf("  %s字段已更新为: %v\n", fieldType.Name, field)} else {fmt.Printf("  无法将值%v赋给字段%s,类型不匹配。\n", newValue, fieldType.Name)}}}
}func main() {// 创建一个 Employee 实例emp := &Employee{Name: "Alice", Age: 30, Salary: 65000.0, Active: true}// 使用 inspectAndModify 函数打印结构体信息并修改字段inspectAndModify(emp, "Bob")// 打印修改后的结构体fmt.Println("修改后的 Employee:", emp)
}// 结构体类型: main.Employee
// 字段:
//   Name (string): Alice
//   Name字段已更新为: Bob
//   Age (int): 30
//   Salary (float64): 65000
//   Active (bool): true
// 修改后的 Employee: &{Bob 30 65000 true}
package mainimport ("fmt""reflect"
)type Person struct {Name    stringAge     intAddress string
}func main() {// 创建一个Person实例p := Person{Name:    "John Doe",Age:     30,Address: "123 Main St",}// 获取Person实例的反射值v := reflect.ValueOf(p)// 确保v是一个结构体if v.Kind() == reflect.Struct {// 遍历结构体的所有字段for i := 0; i < v.NumField(); i++ {field := v.Field(i)// 获取字段的名称fieldName := v.Type().Field(i).Name// 获取字段的值fieldValue := field.Interface()fmt.Printf("Field: %s, Value: %v\n", fieldName, fieldValue)}}
}

Go的底层编程

  • Go的底层编程涉及到更接近硬件和操作系统的细节,包括内存管理、指针操作等。
  • unsafe包:Go提供了unsafe包,它允许程序员绕过Go的类型系统,进行指针操作和内存对齐等操作。unsafe包中的Sizeof、Alignof和Offsetof可以用于获取类型的存储大小、内存对齐和字段偏移量
  • unsafe.Pointer:unsafe.Pointer是一个特殊的类型,它可以存储任何类型的指针,并允许进行指针转换和算术操作
  • 调用C代码:通过cgo,Go程序可以调用C语言代码,这涉及到底层的内存管理和类型转换
  • 性能考虑:底层编程和反射操作通常比直接的Go操作要慢,因为它们涉及到额外的动态查询和类型转换。因此,在性能敏感的应用中需要谨慎使用
  • 要编译和运行这个示例,你需要确保你的Go环境可以使用cgo。通常,对于大多数操作系统,cgo是默认启用的。

调用c语言代码

// main.go
package main/*
#include <stdio.h>
#include <stdlib.h>// 定义转置函数
void transpose(int* src, int* dest, int rows, int cols) {for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {dest[j * rows + i] = src[i * cols + j];}}
}
*/
import "C"
import ("fmt""unsafe"
)func main() {// 定义一个3x3的矩阵rows, cols := 3, 3src := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}dest := make([]int, rows*cols)// 打印原始矩阵fmt.Println("原始矩阵:")printMatrix(src, rows, cols)// 调用C代码进行转置C.transpose((*C.int)(unsafe.Pointer(&src[0])), (*C.int)(unsafe.Pointer(&dest[0])), C.int(rows), C.int(cols))// 打印转置后的矩阵fmt.Println("转置后的矩阵:")printMatrix(dest, cols, rows)
}func printMatrix(matrix []int, rows, cols int) {for i := 0; i < rows; i++ {for j := 0; j < cols; j++ {fmt.Printf("%d ", matrix[i*cols+j])}fmt.Println()}
}

相关文章:

【后端开发】Go语言编程实践,Goroutines和Channels,基于共享变量的并发,反射与底层编程

【后端开发】Go语言编程实践&#xff0c;Goroutines和Channels&#xff0c;基于共享变量的并发&#xff0c;反射与底层编程 【后端开发】Go语言高级编程&#xff0c;CGO、Go汇编语言、RPC实现、Web框架实现、分布式系统 文章目录 1、并发基础, Goroutines和Channels2、基于共享…...

PyTorch 2.5.1: Bugs修复版发布

一&#xff0c;前言 在深度学习框架的不断迭代中&#xff0c;PyTorch 社区始终致力于提供更稳定、更高效的工具。最近&#xff0c;PyTorch 2.5.1 版本正式发布&#xff0c;这个版本主要针对 2.5.0 中发现的问题进行了修复&#xff0c;以提升用户体验。 二&#xff0c;PyTorch 2…...

【Android】组件化嘻嘻嘻gradle耶耶耶

文章目录 Gradle基础总结&#xff1a;gradle-wrapper项目根目录下的 build.gradlesetting.gradle模块中的 build.gradlelocal.properties 和 gradle.properties 组件化&#xff1a;项目下新建一个Gradle文件定义一个ext扩展区域config.gradle全局基础配置&#xff08;使用在项目…...

vulnhub靶场【哈利波特】三部曲之Aragog

前言 使用virtual box虚拟机 靶机&#xff1a;Aragog : 192.168.1.101 攻击&#xff1a;kali : 192.168.1.16 主机发现 使用arp-scan -l扫描&#xff0c;在同一虚拟网卡下 信息收集 使用nmap扫描 发现22端口SSH服务&#xff0c;openssh 80端口HTTP服务&#xff0c;Apach…...

HarmonyOS开发中,如何高效定位并分析内存泄露相关问题

HarmonyOS开发中&#xff0c;如何高效定位并分析内存泄露相关问题 (1)Allocation的应用调试方式Memory泳道Native Allocation泳道 (2)Snapshot(3)ASan的应用使用约束配置参数使能ASan方式一方式二 启用ASanASan检测异常码 (4)HWASan的应用功能介绍约束条件使能HWASan方式一方式…...

java调用ai模型:使用国产通义千问完成基于知识库的问答

整体介绍&#xff1a; 基于RAG&#xff08;Retrieval-Augmented Generation&#xff09;技术&#xff0c;可以实现一个高效的Java智能问答客服机器人。核心思路是将预先准备的问答QA文档&#xff08;例如Word格式文件&#xff09;导入系统&#xff0c;通过数据清洗、向量化处理…...

2023年第十四届蓝桥杯Scratch国赛真题—推箱子

推箱子 程序演示及其源码解析&#xff0c;可前往&#xff1a; https://www.hixinao.com/scratch/creation/show-188.html 若需在线编程&#xff0c;在线测评模考&#xff0c;助力赛事可自行前往题库中心&#xff0c;按需查找&#xff1a; https://www.hixinao.com/ 题库涵盖…...

银河麒麟V10-SP1设置redis开机自启

前言&#xff1a; redis安装请看&#xff1a;银河麒麟V10-SP1离线安装redis5.0.1_银河麒麟v10 redis5.0-CSDN博客 一、编辑自启文件 vim /etc/systemd/system/redis.service [Unit] DescriptionRedis In-Memory Data Store Afternetwork.target [Service] Typeforking ExecS…...

释放超凡性能,打造鸿蒙原生游戏卓越体验

11月26日在华为Mate品牌盛典上&#xff0c;全新Mate70系列及多款全场景新品正式亮相。在游戏领域&#xff0c;HarmonyOS NEXT加持下游戏的性能得到充分释放。HarmonyOS SDK为开发者提供了软硬协同的系统级图形加速解决方案——Graphics Accelerate Kit&#xff08;图形加速服务…...

Node.js 实战: 爬取百度新闻并序列化 - 完整教程

很多时候我们需要爬取一些公开的网页内容来做一些数据分析和统计。而多数时候&#xff0c;大家会用到python &#xff0c;因为实现起来很方便。但是其实Node.js 用来爬取网络内容&#xff0c;也是非常强大的。 今天我向大家介绍一下我自己写的一个百度新闻的爬虫&#xff0c;可…...

106.【C语言】数据结构之二叉树的三种递归遍历方式

目录 1.知识回顾 2.分析二叉树的三种遍历方式 1.总览 2.前序遍历 3.中序遍历 4.后序遍历 5.层序遍历 3.代码实现 1.准备工作 2.前序遍历函数PreOrder 测试结果 3.中序遍历函数InOrder 测试结果 4.后序遍历函数PostOrder 测试结果 4.底层分析 1.知识回顾 在99.…...

qt QToolButton详解

1、概述 QToolButton是Qt框架中的一个控件&#xff0c;它继承自QAbstractButton。QToolButton通常用于工具栏&#xff08;QToolBar&#xff09;中&#xff0c;提供了一种快速访问命令或选项的方式。与普通的QPushButton按钮相比&#xff0c;QToolButton通常只显示一个图标而不…...

2024年大热,Access平替升级方案,也适合Excel用户

欢迎各位看官&#xff0c;您来了&#xff0c;就对了&#xff01; 您多半是Access忠实粉丝&#xff0c;至少是excel用户&#xff0c;亦或是WPS用户吧。那就对了&#xff0c;今天的分享肯定对您有用。 本文1100字&#xff0c;阅读时长2分50秒&#xff01; 现实总是不尽人意&am…...

探索Scala的模式匹配:身份证识别与等级判定!!! #Scala # scala #匹配模式

在Scala编程语言中&#xff0c;模式匹配是一个强大且表达力丰富的特性&#xff0c;它允许我们以声明式的方式处理多种情况。今天&#xff0c;我们将通过两个有趣的例子来展示Scala模式匹配的魅力&#xff1a;身份证号识别和等级判定。 1. 身份证号识别&#xff1a;定位你的家乡…...

python数据分析之爬虫基础:爬虫介绍以及urllib详解

前言 在数据分析中&#xff0c;爬虫有着很大作用&#xff0c;可以自动爬取网页中提取的大量的数据&#xff0c;比如从电商网站手机商品信息&#xff0c;为市场分析提供数据基础。也可以补充数据集、检测动态变化等一系列作用。可以说在数据分析中有着相当大的作用&#xff01;…...

【星海随笔】syslinux

Ubuntu相关资料 https://www.pugetsystems.com/labs/hpc/ubuntu-22-04-server-autoinstall-iso/#Step_2_Unpack_files_and_partition_images_from_the_Ubuntu_2204_live_server_ISO https://launchpad.net/ubuntu/source/squashfs-tools/1:4.6.1-1build1 sudo tar -xf my_compu…...

力扣C语言刷题记录 (二)移除元素

给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k&#xff0c;要通过此题&#xff0c;您需要执行以下操作&#xff1a; 更改…...

【Vue3】【Naive UI】<NAutoComplete>标签

【Vue3】【Naive UI】标签 <NAutoComplete> 是 Naive UI 库中的一个组件&#xff0c;用于实现自动完成或联想输入功能。 它允许用户在输入时看到与当前输入匹配的建议列表&#xff0c;从而帮助用户更快地填写表单字段。 这个组件通常用于搜索框、地址输入等场景&#xff…...

【Halcon】使用均值滤波出现假边怎么办?

在图像处理过程中,均值滤波是一种常见的平滑技术,用于减少图像中的噪声。然而,当应用于具有显著边缘或对比度变化的图像时,均值滤波可能会导致“假边”现象,即原本不存在的边缘在滤波后变得明显。以下是如何在Halcon中处理这一问题,并提供一个完整的示例代码。 示例背景…...

Flask+Minio实现断点续传技术教程

什么是MinIO MinIO是一个高性能的分布式对象存储服务&#xff0c;与Amazon S3 API兼容。它允许用户存储和检索任意规模的数据&#xff0c;非常适合于使用S3 API的应用程序。MinIO支持多租户存储&#xff0c;提供高可用性、高扩展性、强一致性和数据持久性。它还可以作为软件定义…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...