ThinkPHP 集成 Redis 队列:从入门到实战技术分享
笔记哥 /
04-24 /
1点赞 /
0评论 /
294阅读
# **ThinkPHP 集成 Redis 队列:从入门到实战技术分享**
## **一、引言**
在分布式系统架构中,异步处理、服务解耦和流量削峰是提升系统性能的核心需求。Redis 作为高性能内存数据库,凭借其丰富的数据结构(如 List、Stream、Sorted Set)和轻量级特性,成为实现队列功能的理想选择。本文将结合 ThinkPHP 框架的特性,详细阐述如何通过 Redis 队列构建高可用、可扩展的异步处理系统,涵盖基础概念、环境配置、实战案例及最佳实践。
## **二、Redis 队列核心概念解析**
### **2.1 为何选择 Redis 队列?**
Redis 队列的核心优势体现在三方面:
1. **极致性能**:基于内存操作,单节点支持万级 QPS,满足高并发场景下的实时响应需求。
2. **轻量部署**:无需像 Kafka/RabbitMQ 等中间件的复杂配置,可直接通过 PHP 扩展集成,适合中小规模业务快速落地。
3. **结构灵活**:提供多种数据结构适配不同业务场景:
◦ **FIFO 队列(List)**:基于左进右出(LPUSH/RPOP)实现简单异步任务,如订单状态更新。
◦ **优先级队列(Sorted Set)**:通过分值(Score)控制任务执行顺序,适用于高优先级订单加急处理。
◦ **持久化队列(Stream)**:支持消息持久化、分组消费和确认机制,适合微服务架构下的可靠消息传递。
### **2.2 核心数据结构对比**
| | | | | |
| --- | --- | --- | --- | --- |
| **数据结构** | **特性** | **典型场景** | **Redis 核心命令** | **ThinkPHP 操作示例** |
| List | 先进先出,简单高效 | 短信发送、日志异步写入 | lpush/rpop, brpop | $redis->lpush('queue:log', json\_encode($log)) |
| Stream | 持久化、分组消费 | 分布式任务调度、消息重试 | xadd, xgroup, xreadgroup | $redis->xadd('stream:task', '\*', $fields) |
| Sorted Set | 优先级 / 延迟处理 | 优惠券过期提醒、超时订单取消 | zadd, zrange, zrem | $redis->zadd('delay:order', time()+60, $oid) |
## **三、开发环境搭建与配置**
### **3.1 依赖安装**
#### **3.1.1 PHP Redis 扩展安装**
| | | | | |
| --- | --- | --- | --- | --- |
| # 方式一:通过 PECL 安装 phpredis(推荐)
pecl install redis
# 方式二:通过 Composer 安装 Predis(适用于集群环境)
composer require predis/predis | #### **3.1.2 ThinkPHP 配置调整** 修改 config/redis.php,配置 Redis 连接参数: | | | | | | | --- | --- | --- | --- | --- | | return [
'default' => [
'type' => 'redis',
'host' => env('REDIS.HOST', '127.0.0.1'), // 支持环境变量注入
'port' => env('REDIS.PORT', 6379),
'password' => env('REDIS.PASS', ''),
'select' => 0, // 数据库索引(0-15)
'timeout' => 5, // 连接超时时间(秒)
'persistent' => true, // 开启长连接(生产环境建议启用)
],
// 集群配置示例(适用于高可用场景)
'cluster' => [
'type' => 'redis',
'mode' => 'cluster',
'nodes' => [
['host' => 'node1.com', 'port' => 6380],
['host' => 'node2.com', 'port' => 6381],
],
'password' => 'cluster\_pass',
'timeout' => 3,
]
]; | ## **四、基于 List 的基础队列实战** ### **4.1 队列操作核心代码** #### **4.1.1 入队操作(左压栈)** | | | | | | | --- | --- | --- | --- | --- | | use think\facade\Cache;
$redis = Cache::store('redis')->handler();
// 存储 JSON 格式任务数据(推荐方式)
$task = [
'task\_id' => uniqid(),
'type' => 'order\_process',
'data' => ['order\_id' => '20231205001', 'amount' => 299.99]
];
$redis->lpush('queue:default', json\_encode($task)); | #### **4.1.2 出队操作(阻塞式右弹出)** | | | | | | | --- | --- | --- | --- | --- | | // 消费者脚本专用(阻塞等待任务,避免空轮询)
$result = $redis->brpop('queue:default', 10); // 10 秒超时
if ($result) {
[$queueName, $taskJson] = $result;
$task = json\_decode($taskJson, true);
// 执行业务逻辑
$this->handleTask($task);
} | ### **4.2 订单异步处理案例** #### **4.2.1 前端下单接口(控制器)** | | | | | | | --- | --- | --- | --- | --- | | // app/controller/Order.php
public function submitOrder() {
$orderData = $this->request->post();
// 验证订单数据...
// 入队异步处理
$redis = Cache::store('redis')->handler();
$redis->lpush('queue:order', json\_encode([
'order\_id' => $orderData['order\_id'],
'product\_id' => $orderData['product\_id'],
'quantity' => $orderData['quantity']
]));
return json(['code' => 200, 'msg' => '下单成功,系统正在处理']);
} | #### **4.2.2 后台消费者脚本(scripts/order\_consumer.php)** | | | | | | | --- | --- | --- | --- | --- | | <?php
require \_\_DIR\_\_ . '/../../thinkphp/base.php';
$redis = app(\think\cache\driver\Redis::class)->handler();
while (true) {
$result = $redis->brpop('queue:order', 10);
if (!$result) continue;
$task = json\_decode($result[1], true);
try {
// 模拟库存扣减(实际需调用服务)
$this->deductStock($task['product\_id'], $task['quantity']);
// 模拟物流通知
$this->sendLogisticsNotice($task['order\_id']);
echo "[".date('Y-m-d H:i:s')."] 任务完成:{$task['order\_id']}\n";
} catch (\Exception $e) {
// 重试机制(最多 3 次)
$this->retryTask($task, $e, 3);
}
} | #### **4.2.3 启动消费者服务** | | | | | | | --- | --- | --- | --- | --- | | # 前台运行(便于调试)
php scripts/order\_consumer.php
# 后台守护进程运行
nohup php scripts/order\_consumer.php > order.log 2>&1 & | ## **五、基于 Stream 的高级队列应用** ### **5.1 Stream 队列核心特性** - **持久化存储**:消息默认持久化到磁盘,支持重启后继续处理未完成任务。 - **分组消费**:多个消费者组成消费组(Consumer Group),实现任务负载均衡(如多个 worker 节点共同处理订单)。 - **消息确认机制**:通过 XACK 命令标记消息已处理,避免重复执行或数据丢失。 ### **5.2 分布式任务处理示例** #### **5.2.1 创建 Stream 并生产消息** | | | | | | | --- | --- | --- | --- | --- | | // 生产端:添加带重试次数的任务
$redis->xadd('stream:task', '\*', [
'task\_type' => 'payment\_notify',
'order\_id' => '20231206001',
'retry' => 0, // 初始重试次数
'create\_at' => time()
]); | #### **5.2.2 初始化消费者组** | | | | | | | --- | --- | --- | --- | --- | | // 首次运行时创建消费组(从最新消息开始消费)
$redis->xgroup('CREATE', 'stream:task', 'group\_workers', '$', true);
// 如需消费历史消息,将 '$' 替换为 '0-0' | #### **5.2.3 消费组节点处理逻辑** | | | | | | | --- | --- | --- | --- | --- | | // 消费者节点 1(worker1.php)
$messages = $redis->xreadgroup(
'GROUP', 'group\_workers', 'worker\_1',
'STREAMS', 'stream:task', '>' // 获取未确认的消息
);
if ($messages) {
foreach ($messages[0][1] as $msgId => $fields) {
try {
$this->handlePaymentNotify($fields['order\_id']);
$redis->xack('stream:task', 'group\_workers', $msgId); // 确认消息
echo "Worker1 处理:{$fields['order\_id']}\n";
} catch (\Exception $e) {
if ((int)$fields['retry'] < 3) {
// 增加重试次数并重新入队
$fields['retry'] = (int)$fields['retry'] + 1;
$redis->xadd('stream:task', '\*', $fields);
} else {
// 记录死信队列
$redis->xadd('stream:deadletter', '\*', $fields);
}
}
}
} | ## **六、生产环境最佳实践** ### **6.1 消息序列化规范** - **强制使用 JSON 格式**: | | | | | | | --- | --- | --- | --- | --- | | // 推荐做法
$redis->lpush('queue', json\_encode($data, JSON\_UNESCAPED\_UNICODE));
// 禁止使用 PHP 原生序列化
// $redis->lpush('queue', serialize($data)); | - **数据校验**:消费端需对反序列化后的数据进行字段校验,避免因格式错误导致服务异常。 ### **6.2 持久化与高可用配置** #### **6.2.1 Redis 持久化策略** - **AOF 模式**:推荐配置 appendfsync everysec,兼顾性能与数据安全性(最多丢失 1 秒数据)。 - **RDB 备份**:定期生成 RDB 快照用于灾难恢复,建议配合云存储(如 S3)实现异地备份。 #### **6.2.2 集群方案** - **Redis Cluster**:适用于超大规模数据,支持自动分片和故障转移。 - **Sentinel 哨兵模式**:监控主从节点状态,自动完成主从切换,配置示例: | | | | | | | --- | --- | --- | --- | --- | | // ThinkPHP 哨兵模式配置
'sentinel' => [
'type' => 'redis',
'mode' => 'sentinel',
'master' => 'mymaster',
'sentinels' => [
['host' => 'sentinel1.com', 'port' => 26379],
['host' => 'sentinel2.com', 'port' => 26379],
],
'password' => 'sentinel\_pass',
] | ### **6.3 性能优化技巧** 1. **批量操作**:使用 LPUSH 一次推送多个任务,减少网络 I/O 次数: | | | | | | | --- | --- | --- | --- | --- | | $redis->lpush('queue:batch', $task1, $task2, $task3); | 1. **队列长度控制**:通过 LTRIM 限制队列最大长度,防止内存溢出: | | | | | | | --- | --- | --- | --- | --- | | $redis->ltrim('queue:order', 0, 999); // 保留最新 1000 条消息 | 1. **连接池复用**:在 ThinkPHP 中开启长连接(persistent => true),避免频繁创建连接的开销。 ### **6.4 幂等性设计** - **唯一任务 ID**:每个任务携带 UUID 或业务唯一标识(如订单号),消费端通过 Redis 分布式锁保证幂等性: | | | | | | | --- | --- | --- | --- | --- | | $lockKey = "lock:task:{$task['task\_id']}";
if ($redis->set($lockKey, 1, ['NX', 'PX' => 60000])) {
// 执行业务逻辑
} | ## **七、扩展功能与架构演进** ### **7.1 延迟队列实现** 利用 Sorted Set 的分值(时间戳)实现任务延迟执行: | | | | | | | --- | --- | --- | --- | --- | | // 入队时设置延迟时间(单位:秒)
$delayTime = 60; // 延迟 1 分钟执行
$redis->zadd('delay:queue', time() + $delayTime, json\_encode($task));
// 消费者定时扫描到期任务
$now = time();
$tasks = $redis->zrangebyscore('delay:queue', 0, $now, ['LIMIT' => 0, 100]);
foreach ($tasks as $taskJson) {
$redis->zrem('delay:queue', $taskJson);
$this->handleDelayedTask(json\_decode($taskJson, true));
} | ### **7.2 死信队列与监控** - **死信队列**:将重试失败的任务转移至独立队列(如 stream:deadletter),人工介入处理。 - **监控系统**: ◦ 队列长度预警:当 LLEN queue:order > 1000 时触发告警。 ◦ 消费者状态监控:通过 LASTMSGID 命令检查消费组滞后情况。 ### **7.3 技术选型建议** | | | | | | | --- | --- | --- | --- | --- | | **业务场景** | **推荐数据结构** | **核心优势** | **典型配置** | | 简单异步通知 | List | 轻量高效,毫秒级响应 | 单节点 + 非持久化 | | 分布式任务调度 | Stream | 分组消费,消息可靠性保证 | 消费组 + AOF 持久化 | | 高优先级任务处理 | Sorted Set | 动态优先级调整 | 分值(Score)+ 定期扫描 | ## **八、总结** Redis 队列与 ThinkPHP 的结合为异步处理提供了轻量化解决方案,从基础的 List 队列到高级的 Stream 分组消费,可满足不同规模业务的需求。在实际开发中,需重点关注消息可靠性(持久化、重试机制)、性能优化(批量操作、连接池)和系统稳定性(幂等性、监控告警)。通过合理运用 Redis 数据结构与 ThinkPHP 框架特性,能够有效提升系统的可扩展性和抗风险能力,为分布式架构奠定坚实基础。 ## **九、参考资源** 1. [Redis 官方文档](https://redis.io/documentation) 2. [ThinkPHP 缓存驱动开发指南](https://www.thinkphp.cn/doc/zh-cn/guide/cache.html) 3. [Redis 设计与实现](https://redisbook.com/) 4. [PHP Redis 扩展手册](https://www.php.net/manual/en/book.redis.php) 本文完整覆盖了 ThinkPHP 集成 Redis 队列的全流程,从基础概念到生产实践均提供了可落地的代码示例。如需进一步探讨特定场景的优化方案或扩展功能,欢迎提供更多业务细节。
pecl install redis
# 方式二:通过 Composer 安装 Predis(适用于集群环境)
composer require predis/predis | #### **3.1.2 ThinkPHP 配置调整** 修改 config/redis.php,配置 Redis 连接参数: | | | | | | | --- | --- | --- | --- | --- | | return [
'default' => [
'type' => 'redis',
'host' => env('REDIS.HOST', '127.0.0.1'), // 支持环境变量注入
'port' => env('REDIS.PORT', 6379),
'password' => env('REDIS.PASS', ''),
'select' => 0, // 数据库索引(0-15)
'timeout' => 5, // 连接超时时间(秒)
'persistent' => true, // 开启长连接(生产环境建议启用)
],
// 集群配置示例(适用于高可用场景)
'cluster' => [
'type' => 'redis',
'mode' => 'cluster',
'nodes' => [
['host' => 'node1.com', 'port' => 6380],
['host' => 'node2.com', 'port' => 6381],
],
'password' => 'cluster\_pass',
'timeout' => 3,
]
]; | ## **四、基于 List 的基础队列实战** ### **4.1 队列操作核心代码** #### **4.1.1 入队操作(左压栈)** | | | | | | | --- | --- | --- | --- | --- | | use think\facade\Cache;
$redis = Cache::store('redis')->handler();
// 存储 JSON 格式任务数据(推荐方式)
$task = [
'task\_id' => uniqid(),
'type' => 'order\_process',
'data' => ['order\_id' => '20231205001', 'amount' => 299.99]
];
$redis->lpush('queue:default', json\_encode($task)); | #### **4.1.2 出队操作(阻塞式右弹出)** | | | | | | | --- | --- | --- | --- | --- | | // 消费者脚本专用(阻塞等待任务,避免空轮询)
$result = $redis->brpop('queue:default', 10); // 10 秒超时
if ($result) {
[$queueName, $taskJson] = $result;
$task = json\_decode($taskJson, true);
// 执行业务逻辑
$this->handleTask($task);
} | ### **4.2 订单异步处理案例** #### **4.2.1 前端下单接口(控制器)** | | | | | | | --- | --- | --- | --- | --- | | // app/controller/Order.php
public function submitOrder() {
$orderData = $this->request->post();
// 验证订单数据...
// 入队异步处理
$redis = Cache::store('redis')->handler();
$redis->lpush('queue:order', json\_encode([
'order\_id' => $orderData['order\_id'],
'product\_id' => $orderData['product\_id'],
'quantity' => $orderData['quantity']
]));
return json(['code' => 200, 'msg' => '下单成功,系统正在处理']);
} | #### **4.2.2 后台消费者脚本(scripts/order\_consumer.php)** | | | | | | | --- | --- | --- | --- | --- | | <?php
require \_\_DIR\_\_ . '/../../thinkphp/base.php';
$redis = app(\think\cache\driver\Redis::class)->handler();
while (true) {
$result = $redis->brpop('queue:order', 10);
if (!$result) continue;
$task = json\_decode($result[1], true);
try {
// 模拟库存扣减(实际需调用服务)
$this->deductStock($task['product\_id'], $task['quantity']);
// 模拟物流通知
$this->sendLogisticsNotice($task['order\_id']);
echo "[".date('Y-m-d H:i:s')."] 任务完成:{$task['order\_id']}\n";
} catch (\Exception $e) {
// 重试机制(最多 3 次)
$this->retryTask($task, $e, 3);
}
} | #### **4.2.3 启动消费者服务** | | | | | | | --- | --- | --- | --- | --- | | # 前台运行(便于调试)
php scripts/order\_consumer.php
# 后台守护进程运行
nohup php scripts/order\_consumer.php > order.log 2>&1 & | ## **五、基于 Stream 的高级队列应用** ### **5.1 Stream 队列核心特性** - **持久化存储**:消息默认持久化到磁盘,支持重启后继续处理未完成任务。 - **分组消费**:多个消费者组成消费组(Consumer Group),实现任务负载均衡(如多个 worker 节点共同处理订单)。 - **消息确认机制**:通过 XACK 命令标记消息已处理,避免重复执行或数据丢失。 ### **5.2 分布式任务处理示例** #### **5.2.1 创建 Stream 并生产消息** | | | | | | | --- | --- | --- | --- | --- | | // 生产端:添加带重试次数的任务
$redis->xadd('stream:task', '\*', [
'task\_type' => 'payment\_notify',
'order\_id' => '20231206001',
'retry' => 0, // 初始重试次数
'create\_at' => time()
]); | #### **5.2.2 初始化消费者组** | | | | | | | --- | --- | --- | --- | --- | | // 首次运行时创建消费组(从最新消息开始消费)
$redis->xgroup('CREATE', 'stream:task', 'group\_workers', '$', true);
// 如需消费历史消息,将 '$' 替换为 '0-0' | #### **5.2.3 消费组节点处理逻辑** | | | | | | | --- | --- | --- | --- | --- | | // 消费者节点 1(worker1.php)
$messages = $redis->xreadgroup(
'GROUP', 'group\_workers', 'worker\_1',
'STREAMS', 'stream:task', '>' // 获取未确认的消息
);
if ($messages) {
foreach ($messages[0][1] as $msgId => $fields) {
try {
$this->handlePaymentNotify($fields['order\_id']);
$redis->xack('stream:task', 'group\_workers', $msgId); // 确认消息
echo "Worker1 处理:{$fields['order\_id']}\n";
} catch (\Exception $e) {
if ((int)$fields['retry'] < 3) {
// 增加重试次数并重新入队
$fields['retry'] = (int)$fields['retry'] + 1;
$redis->xadd('stream:task', '\*', $fields);
} else {
// 记录死信队列
$redis->xadd('stream:deadletter', '\*', $fields);
}
}
}
} | ## **六、生产环境最佳实践** ### **6.1 消息序列化规范** - **强制使用 JSON 格式**: | | | | | | | --- | --- | --- | --- | --- | | // 推荐做法
$redis->lpush('queue', json\_encode($data, JSON\_UNESCAPED\_UNICODE));
// 禁止使用 PHP 原生序列化
// $redis->lpush('queue', serialize($data)); | - **数据校验**:消费端需对反序列化后的数据进行字段校验,避免因格式错误导致服务异常。 ### **6.2 持久化与高可用配置** #### **6.2.1 Redis 持久化策略** - **AOF 模式**:推荐配置 appendfsync everysec,兼顾性能与数据安全性(最多丢失 1 秒数据)。 - **RDB 备份**:定期生成 RDB 快照用于灾难恢复,建议配合云存储(如 S3)实现异地备份。 #### **6.2.2 集群方案** - **Redis Cluster**:适用于超大规模数据,支持自动分片和故障转移。 - **Sentinel 哨兵模式**:监控主从节点状态,自动完成主从切换,配置示例: | | | | | | | --- | --- | --- | --- | --- | | // ThinkPHP 哨兵模式配置
'sentinel' => [
'type' => 'redis',
'mode' => 'sentinel',
'master' => 'mymaster',
'sentinels' => [
['host' => 'sentinel1.com', 'port' => 26379],
['host' => 'sentinel2.com', 'port' => 26379],
],
'password' => 'sentinel\_pass',
] | ### **6.3 性能优化技巧** 1. **批量操作**:使用 LPUSH 一次推送多个任务,减少网络 I/O 次数: | | | | | | | --- | --- | --- | --- | --- | | $redis->lpush('queue:batch', $task1, $task2, $task3); | 1. **队列长度控制**:通过 LTRIM 限制队列最大长度,防止内存溢出: | | | | | | | --- | --- | --- | --- | --- | | $redis->ltrim('queue:order', 0, 999); // 保留最新 1000 条消息 | 1. **连接池复用**:在 ThinkPHP 中开启长连接(persistent => true),避免频繁创建连接的开销。 ### **6.4 幂等性设计** - **唯一任务 ID**:每个任务携带 UUID 或业务唯一标识(如订单号),消费端通过 Redis 分布式锁保证幂等性: | | | | | | | --- | --- | --- | --- | --- | | $lockKey = "lock:task:{$task['task\_id']}";
if ($redis->set($lockKey, 1, ['NX', 'PX' => 60000])) {
// 执行业务逻辑
} | ## **七、扩展功能与架构演进** ### **7.1 延迟队列实现** 利用 Sorted Set 的分值(时间戳)实现任务延迟执行: | | | | | | | --- | --- | --- | --- | --- | | // 入队时设置延迟时间(单位:秒)
$delayTime = 60; // 延迟 1 分钟执行
$redis->zadd('delay:queue', time() + $delayTime, json\_encode($task));
// 消费者定时扫描到期任务
$now = time();
$tasks = $redis->zrangebyscore('delay:queue', 0, $now, ['LIMIT' => 0, 100]);
foreach ($tasks as $taskJson) {
$redis->zrem('delay:queue', $taskJson);
$this->handleDelayedTask(json\_decode($taskJson, true));
} | ### **7.2 死信队列与监控** - **死信队列**:将重试失败的任务转移至独立队列(如 stream:deadletter),人工介入处理。 - **监控系统**: ◦ 队列长度预警:当 LLEN queue:order > 1000 时触发告警。 ◦ 消费者状态监控:通过 LASTMSGID 命令检查消费组滞后情况。 ### **7.3 技术选型建议** | | | | | | | --- | --- | --- | --- | --- | | **业务场景** | **推荐数据结构** | **核心优势** | **典型配置** | | 简单异步通知 | List | 轻量高效,毫秒级响应 | 单节点 + 非持久化 | | 分布式任务调度 | Stream | 分组消费,消息可靠性保证 | 消费组 + AOF 持久化 | | 高优先级任务处理 | Sorted Set | 动态优先级调整 | 分值(Score)+ 定期扫描 | ## **八、总结** Redis 队列与 ThinkPHP 的结合为异步处理提供了轻量化解决方案,从基础的 List 队列到高级的 Stream 分组消费,可满足不同规模业务的需求。在实际开发中,需重点关注消息可靠性(持久化、重试机制)、性能优化(批量操作、连接池)和系统稳定性(幂等性、监控告警)。通过合理运用 Redis 数据结构与 ThinkPHP 框架特性,能够有效提升系统的可扩展性和抗风险能力,为分布式架构奠定坚实基础。 ## **九、参考资源** 1. [Redis 官方文档](https://redis.io/documentation) 2. [ThinkPHP 缓存驱动开发指南](https://www.thinkphp.cn/doc/zh-cn/guide/cache.html) 3. [Redis 设计与实现](https://redisbook.com/) 4. [PHP Redis 扩展手册](https://www.php.net/manual/en/book.redis.php) 本文完整覆盖了 ThinkPHP 集成 Redis 队列的全流程,从基础概念到生产实践均提供了可落地的代码示例。如需进一步探讨特定场景的优化方案或扩展功能,欢迎提供更多业务细节。
本文来自投稿,不代表本站立场,如若转载,请注明出处:http//www.knowhub.vip/share/2/2541
- 热门的技术博文分享
- 1 . ESP实现Web服务器
- 2 . 从零到一:打造高效的金仓社区 API 集成到 MCP 服务方案
- 3 . 使用C#构建一个同时问多个LLM并总结的小工具
- 4 . .NET 原生驾驭 AI 新基建实战系列Milvus ── 大规模 AI 应用的向量数据库首选
- 5 . 在Avalonia/C#中使用依赖注入过程记录
- 6 . [设计模式/Java] 设计模式之工厂方法模式
- 7 . 5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
- 8 . SQL 中的各种连接 JOIN 的区别总结!
- 9 . JavaScript 中防抖和节流的多种实现方式及应用场景
- 10 . SaltStack 远程命令执行中文乱码问题
- 11 . 推荐10个 DeepSeek 神级提示词,建议搜藏起来使用
- 12 . C#基础:枚举、数组、类型、函数等解析
- 13 . VMware平台的Ubuntu部署完全分布式Hadoop环境
- 14 . C# 多项目打包时如何将项目引用转为包依赖
- 15 . Chrome 135 版本开发者工具(DevTools)更新内容
- 16 . 从零创建npm依赖,只需执行一条命令
- 17 . 关于 Newtonsoft.Json 和 System.Text.Json 混用导致的的序列化不识别的问题
- 18 . 大模型微调实战之训练数据集准备的艺术与科学
- 19 . Windows快速安装MongoDB之Mongo实战
- 20 . 探索 C# 14 新功能:实用特性为编程带来便利