首页
直播
壁纸
友链
搜索
1
微信小程序支付全链路实战:JSAPI 下单、调起支付、回调验签与退款
266 阅读
2
微信小程序云开发实战:云函数、云数据库与云存储的正确使用姿势
256 阅读
3
微信小程序自定义 tabBar 实战:custom-tab-bar 从适配到深色模式
255 阅读
4
微信小程序 Skyline 渲染引擎实战:worklet 动画从原理到落地
253 阅读
5
微信小程序分包进阶:独立分包、预下载与分包异步化实战
246 阅读
服务器运维
后端技术
前端技术
梯子
数据库
小程序
登录
搜索
标签搜索
fastadmin
Redis
微信小程序
前端开发
RabbitMQ
Go
服务器
codex
buildadmin
小程序
mysql
Nginx
Docker
Vue3
Node.js
MySQL优化
Linux
TypeScript
JWT
PHP
沿途的风景
累计撰写
74
篇文章
累计收到
0
条评论
首页
栏目
服务器运维
后端技术
前端技术
梯子
数据库
小程序
页面
直播
壁纸
友链
搜索到
24
篇与
» 后端技术
的结果
2026-03-22
FastAdmin CRUD 代码生成器使用指南:一键生成增删改查
前言FastAdmin 的 CRUD 代码生成器是框架最核心的功能之一——只需要一条命令,就能根据数据库表结构自动生成控制器、模型、视图、JS、验证器和菜单,实现完整的增删改查。本文将全面讲解 CRUD 生成器的使用方法和进阶技巧。一、基础用法1.1 准备数据表先创建一张示例表:CREATE TABLE `fa_article` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `title` varchar(100) NOT NULL DEFAULT '' COMMENT '标题', `content` text COMMENT '内容', `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分类ID', `author` varchar(50) NOT NULL DEFAULT '' COMMENT '作者', `status` enum('normal','hidden') NOT NULL DEFAULT 'normal' COMMENT '状态', `views` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '浏览量', `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章表';1.2 一键生成 CRUDphp think crud -t fa_article -c content/article参数说明:参数说明-t数据表名(带前缀)-c控制器路径,格式为 模块/控制器执行后自动生成以下文件:application/admin/controller/content/Article.php application/admin/model/Article.php application/admin/validate/Article.php application/admin/view/content/article/index.html application/admin/view/content/article/add.html application/admin/view/content/article/edit.html application/admin/view/content/article/recyclebin.html public/assets/js/backend/content/article.js1.3 生成菜单php think menu -c content/article执行后在后台菜单管理中会自动出现"文章管理"菜单。二、字段注释驱动生成FastAdmin 的 CRUD 生成器会读取数据库字段注释来决定前端控件的渲染方式。这是最强大的特性之一:2.1 字段注释格式`status` enum('normal','hidden') NOT NULL DEFAULT 'normal' COMMENT '状态:normal=正常,hidden=隐藏'生成器会解析注释中的 normal=正常,hidden=隐藏,自动渲染为下拉选择框:<select class="form-control selectpicker" name="status"> <option value="normal">正常</option> <option value="hidden">隐藏</option> </select>2.2 常用字段类型映射数据库字段类型自动生成的控件varchar文本输入框text富文本编辑器int(含 time 关键字)日期时间选择器enum下拉选择框decimal数字输入框date日期选择器2.3 特殊字段名约定FastAdmin 对特定字段名有特殊处理:字段名自动行为createtime / updatetime自动维护创建/更新时间status列表页显示状态切换按钮weigh自动添加拖拽排序功能image / images自动生成图片上传组件file / files自动生成文件上传组件ids生成多选关联组件三、进阶用法3.1 关联模型生成如果表中有外键字段,可以通过命名约定自动生成关联:-- 主表 CREATE TABLE `fa_category` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 从表 CREATE TABLE `fa_article` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(100) NOT NULL DEFAULT '', `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分类ID', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;在模型中定义关联:// application/admin/model/Article.php namespace app\admin\model; use think\Model; class Article extends Model { // 关联分类 public function category() { return $this->belongsTo('Category', 'category_id', 'id'); } }在控制器中启用关联查询:// application/admin/controller/content/Article.php public function index() { $this->relationSearch = true; $this->searchFields = 'id,title,category.name'; // 设置关联 $this->with = ['category']; return parent::index(); }3.2 强制覆盖生成php think crud -t fa_article -c content/article -f 1-f 1 参数会覆盖已存在的文件,适合修改表结构后重新生成。3.3 删除生成的文件php think crud -t fa_article -c content/article -d 1-d 1 参数会删除该表对应的所有 CRUD 文件。四、自定义模板4.1 修改生成模板CRUD 模板位于 extend/fast/crud/tpl/ 目录:extend/fast/crud/tpl/ ├── controller.tpl # 控制器模板 ├── model.tpl # 模型模板 ├── validate.tpl # 验证器模板 ├── index.tpl # 列表视图模板 ├── add.tpl # 添加视图模板 ├── edit.tpl # 编辑视图模板 └── javascript.tpl # JS 模板4.2 自定义列表字段显示在 JS 文件中控制列表列的显示:// public/assets/js/backend/content/article.js define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { var Controller = { index: function () { Table.init({ extend: { index_url: 'content/article/index', add_url: 'content/article/add', edit_url: 'content/article/edit', del_url: 'content/article/del', table: 'article', } }); var table = $("#table"); // 初始化表格 table.bootstrapTable({ url: $.extend({}, Table.config.url), columns: [ [ {checkbox: true}, {field: 'id', title: __('Id')}, {field: 'title', title: __('Title')}, {field: 'category.name', title: __('Category')}, {field: 'author', title: __('Author')}, {field: 'status', title: __('Status'), searchList: {"normal": __('Normal'), "hidden": __('Hidden')}, formatter: Table.api.formatter.status}, {field: 'views', title: __('Views')}, {field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime}, {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate} ] ] }); Table.api.bindevent(table); }, add: function () { Controller.api.bindevent(); }, edit: function () { Controller.api.bindevent(); }, api: { bindevent: function () { Form.api.bindevent($("form[role=form]")); } } }; return Controller; });五、CRUD 常用参数汇总参数说明示例-t数据表名(带前缀)-t fa_user-c控制器路径-c user/index-f强制覆盖-f 1-d删除生成文件-d 1-o输出文件路径-o /tmp/crud-uURL 绑定的模型-u model_name--local不调用远程 API--local=1六、最佳实践先设计表结构再生成:字段注释直接决定生成质量,务必写清楚生成后不要改生成的文件名:保持命名一致性,便于重新生成自定义逻辑在生成文件基础上修改:先 php think crud 生成骨架,再手动添加业务逻辑修改表结构后重新生成:用 -f 1 覆盖,但注意备份自定义代码善用关联搜索:$this->relationSearch = true 可以快速实现跨表搜索总结FastAdmin CRUD 生成器的核心思想是"约定优于配置"——通过数据库字段注释驱动代码生成,大幅减少重复劳动。掌握字段注释规范和关联模型用法后,90% 的后台增删改查功能可以在几分钟内完成。
2026年03月22日
11 阅读
0 评论
0 点赞
2026-03-20
FastAdmin 框架入门:安装配置与目录结构详解
前言FastAdmin 是一款基于 ThinkPHP 5.x 和 Bootstrap 的极速后台开发框架,凭借其强大的代码生成器和零配置上手的特点,在国内开发者群体中广受欢迎。本文将从安装配置开始,系统讲解 FastAdmin 的目录结构与核心概念。一、环境要求PHP >= 7.1(推荐 7.4+)MySQL >= 5.6(推荐 5.7+)Nginx / Apache / IISComposer二、安装步骤2.1 通过 Git 安装# 克隆项目 git clone https://gitee.com/karson/fastadmin.git # 进入目录 cd fastadmin # 安装依赖 composer install2.2 通过完整包安装从 FastAdmin 官网下载完整包,解压到 Web 目录即可。2.3 配置数据库编辑 application/database.php:return [ 'type' => 'mysql', 'hostname' => '127.0.0.1', 'database' => 'fastadmin', 'username' => 'root', 'password' => 'your_password', 'hostport' => '3306', 'prefix' => 'fa_', 'charset' => 'utf8mb4', ];2.4 导入初始数据库mysql -u root -p fastadmin < fastadmin.sql2.5 配置伪静态(Nginx)location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php/$1 last; break; } }2.6 访问安装浏览器访问 http://yourdomain.com/install.php,按提示完成安装后删除该文件。三、目录结构详解fastadmin/ ├── application/ # 应用目录 │ ├── admin/ # 后台模块 │ │ ├── controller/ # 后台控制器 │ │ ├── model/ # 后台模型 │ │ ├── view/ # 后台视图 │ │ └── validate/ # 验证器 │ ├── api/ # API 模块 │ │ ├── controller/ │ │ └── model/ │ ├── common/ # 公共模块 │ │ ├── model/ # 公共模型 │ │ └── library/ # 扩展类库 │ ├── index/ # 前台模块 │ ├── extra/ # 扩展配置 │ ├── command.php # 命令行配置 │ ├── database.php # 数据库配置 │ └── config.php # 全局配置 ├── public/ # Web 根目录 │ ├── assets/ # 静态资源 │ │ ├── js/backend/ # 后台 JS │ │ ├── css/ # 样式文件 │ │ └── libs/ # 前端库 │ ├── index.php # 入口文件 │ └── install.php # 安装文件(用后删除) ├── addons/ # 插件目录 ├── extend/ # 扩展类库 ├── runtime/ # 运行时缓存 ├── vendor/ # Composer 依赖 └── think # 命令行入口核心目录说明目录作用application/admin后台管理模块,所有后台控制器、模型、视图都在这里application/apiAPI 接口模块,用于前后端分离开发application/common/library框架核心类库,包含 Auth 权限认证、Token 等public/assets/js/backend后台各模块对应的 JS 文件,采用 RequireJS 模块化加载addons在线安装的插件存放目录四、核心概念4.1 模块化设计FastAdmin 采用多模块设计,默认包含 admin、api、index 三个模块:// application/admin/controller/Example.php namespace app\admin\controller; use app\common\controller\Backend; class Example extends Backend { // 自动继承后台基础控制器 // 拥有权限认证、菜单管理、CRUD 等能力 }4.2 权限认证(Auth)FastAdmin 内置基于 RBAC 的权限认证体系:// 检查当前用户是否有某权限 if (!$this->auth->check('example/index')) { $this->error('您没有权限'); } // 获取当前登录用户 $admin = $this->auth->getUser();4.3 一键 CRUDFastAdmin 最强大的功能是代码生成器,通过命令行一键生成控制器、模型、视图、JS:# 生成一个完整的 CRUD php think crud -t fa_user -c my/user # 参数说明 # -t 指定数据表 # -c 指定控制器路径(模块/控制器)生成的文件包括:application/admin/controller/my/User.php # 控制器 application/admin/model/User.php # 模型 application/admin/validate/User.php # 验证器 application/admin/view/my/user/index.html # 列表视图 application/admin/view/my/user/add.html # 添加视图 application/admin/view/my/user/edit.html # 编辑视图 public/assets/js/backend/my/user.js # 前端 JS五、配置优化建议5.1 修改默认密码安装后立即修改默认管理员密码(默认 admin/123456):# 后台 → 个人信息 → 修改密码5.2 关闭调试模式生产环境关闭调试:// application/config.php 'app_debug' => false, 'app_trace' => false,5.3 设置域名根目录Nginx 配置 root 指向 public 目录:server { listen 80; server_name admin.example.com; root /var/www/fastadmin/public; index index.php; location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php/$1 last; } } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }六、常见问题Q: 安装后访问首页 404?检查伪静态配置,确保 Nginx/Apache 的 rewrite 规则正确。Q: 后台菜单不显示?执行 php think menu -c controller_name 生成菜单。Q: 在线安装插件失败?检查 addons 目录权限,确保 Web 进程可写:chmod -R 755 addons/总结FastAdmin 通过模块化设计 + 代码生成器大幅提升了后台开发效率。掌握目录结构和核心概念后,配合 CRUD 命令,可以在几分钟内搭建出一个功能完整的后台管理系统。后续文章将深入讲解 CRUD 生成器、权限体系和插件开发。
2026年03月20日
10 阅读
0 评论
0 点赞
2026-03-18
RabbitMQ 消息队列入门与实战:从原理到 Spring Boot 整合
前言RabbitMQ 是基于 AMQP 协议的开源消息中间件,广泛应用于微服务架构中的异步解耦、削峰填谷和可靠投递场景。本文将从核心概念讲起,最终完成一个 Spring Boot 整合 RabbitMQ 的实战项目。一、核心概念1.1 AMQP 模型Producer → Exchange → (Binding) → Queue → Consumer概念说明Producer消息生产者Exchange交换机,接收消息并路由到队列Queue消息队列,存储消息Binding交换机与队列之间的绑定关系Routing Key路由键,Exchange 根据它决定消息去向Consumer消息消费者1.2 四种交换机类型类型说明适用场景Direct精确匹配 Routing Key点对点定向投递Fanout广播到所有绑定队列广播通知Topic模式匹配 Routing Key按主题订阅Headers根据消息头匹配复杂路由条件二、Docker 快速部署docker run -d --name rabbitmq \ -p 5672:5672 \ -p 15672:15672 \ -e RABBITMQ_DEFAULT_USER=admin \ -e RABBITMQ_DEFAULT_PASS=admin123 \ rabbitmq:3.13-management5672:AMQP 通信端口15672:管理界面端口,浏览器访问 http://localhost:15672三、Spring Boot 整合3.1 添加依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>3.2 配置spring: rabbitmq: host: localhost port: 5672 username: admin password: admin123 virtual-host: /3.3 队列与交换机配置@Configuration public class RabbitMQConfig { public static final String EXCHANGE = "order.exchange"; public static final String QUEUE = "order.queue"; public static final String ROUTING_KEY = "order.create"; @Bean public TopicExchange orderExchange() { return new TopicExchange(EXCHANGE); } @Bean public Queue orderQueue() { return QueueBuilder.durable(QUEUE) .withArgument("x-message-ttl", 60000) .withArgument("x-dead-letter-exchange", "order.dlx") .build(); } @Bean public Binding orderBinding() { return BindingBuilder.bind(orderQueue()) .to(orderExchange()) .with(ROUTING_KEY); } }3.4 生产者@Service public class OrderProducer { @Autowired private RabbitTemplate rabbitTemplate; public void sendOrder(OrderDTO order) { rabbitTemplate.convertAndSend( RabbitMQConfig.EXCHANGE, RabbitMQConfig.ROUTING_KEY, order, message -> { message.getMessageProperties() .setDeliveryMode(MessageDeliveryMode.PERSISTENT); return message; } ); } }3.5 消费者@Component public class OrderConsumer { @RabbitListener(queues = RabbitMQConfig.QUEUE) public void handleOrder(OrderDTO order, Channel channel, Message message) throws IOException { try { processOrder(order); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } catch (Exception e) { channel.basicNack( message.getMessageProperties().getDeliveryTag(), false, true ); } } private void processOrder(OrderDTO order) { System.out.println("处理订单: " + order.getId()); } }3.6 配置手动 ACKspring: rabbitmq: listener: simple: acknowledge-mode: manual prefetch: 10 retry: enabled: true max-attempts: 3四、可靠性投递方案4.1 生产者确认(Publisher Confirm)@Configuration public class RabbitConfig { @Bean public RabbitTemplate rabbitTemplate(ConnectionFactory cf) { RabbitTemplate template = new RabbitTemplate(cf); template.setConfirmCallback((correlationData, ack, cause) -> { if (!ack) { log.error("消息未到达Exchange: {}", cause); } }); template.setReturnsCallback(returned -> { log.error("消息未路由到Queue: {}", returned.getMessage()); }); template.setMandatory(true); return template; } }4.2 死信队列(DLX)消息变成死信的条件:消息被消费者拒绝(basicNack + requeue=false)消息 TTL 过期队列达到最大长度@Bean public Queue deadLetterQueue() { return QueueBuilder.durable("order.dlq").build(); } @Bean public FanoutExchange deadLetterExchange() { return new FanoutExchange("order.dlx"); } @Bean public Binding dlxBinding() { return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()); }五、常见问题问题原因解决方案消息丢失消费者宕机未 ACK手动 ACK + 持久化消息重复网络抖动导致重复投递消费端幂等性(唯一ID去重)消息堆积消费速度小于生产速度增加消费者 / 限流队列阻塞单条消息处理过慢合理设置 prefetch总结RabbitMQ 在微服务架构中承担着异步解耦和流量削峰的关键角色。生产环境中务必做到:持久化 + 手动 ACK + 死信队列 + 消费幂等,才能保证消息的可靠投递。
2026年03月18日
17 阅读
0 评论
0 点赞
2026-03-14
Go 语言基础教程:从语法到并发编程
前言Go 语言以简洁的语法、出色的并发模型和编译速度著称,在云原生和服务端开发领域占据重要地位。本文将系统介绍 Go 的核心语法和并发编程。一、环境与项目1.1 安装go version # go1.22.x # 设置国内代理 go env -w GOPROXY=https://goproxy.cn,direct go env -w GO111MODULE=on1.2 创建项目mkdir myapp && cd myapp go mod init github.com/yourname/myapp二、基础语法2.1 变量与类型package main import "fmt" func main() { var name string = "Go" var age int = 18 var score = 95.5 isReady := true x, y := 10, 20 fmt.Println(name, age, score, isReady, x, y) }2.2 结构体与方法type User struct { ID int Name string Age int } func (u User) Greet() string { return fmt.Sprintf("Hi, I am %s", u.Name) } func (u *User) SetAge(age int) { u.Age = age } func main() { u := User{ID: 1, Name: "张三", Age: 25} fmt.Println(u.Greet()) u.SetAge(26) fmt.Println(u.Age) }2.3 接口type Animal interface { Speak() string } type Dog struct{ Name string } type Cat struct{ Name string } func (d Dog) Speak() string { return d.Name + ": 汪汪!" } func (c Cat) Speak() string { return c.Name + ": 喵喵!" } func makeSpeak(a Animal) { fmt.Println(a.Speak()) } func main() { makeSpeak(Dog{"旺财"}) makeSpeak(Cat{"咪咪"}) }2.4 错误处理func divide(a, b float64) (float64, error) { if b == 0 { return 0, fmt.Errorf("division by zero") } return a / b, nil } func main() { result, err := divide(10, 0) if err != nil { fmt.Println("Error:", err) return } fmt.Println("Result:", result) }三、并发编程3.1 Goroutinefunc sayHello(name string) { fmt.Printf("Hello, %s!\n", name) } func main() { go sayHello("张三") go sayHello("李四") time.Sleep(time.Second) }3.2 Channelfunc main() { ch := make(chan string) go func() { ch <- "来自 goroutine 的消息" }() msg := <-ch fmt.Println(msg) }3.3 带缓冲的 Channelch := make(chan int, 3) ch <- 1 ch <- 2 ch <- 3 // ch <- 4 // 阻塞!缓冲区已满 fmt.Println(<-ch) // 13.4 select 多路复用func main() { ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(1 * time.Second) ch1 <- "来自 ch1" }() go func() { time.Sleep(2 * time.Second) ch2 <- "来自 ch2" }() select { case msg := <-ch1: fmt.Println(msg) case msg := <-ch2: fmt.Println(msg) } }3.5 WaitGroup 等待多个 goroutinefunc worker(id int, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("Worker %d started\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup for i := 1; i <= 5; i++ { wg.Add(1) go worker(i, &wg) } wg.Wait() fmt.Println("All workers done") }3.6 并发安全:Mutextype Counter struct { mu sync.Mutex count int } func (c *Counter) Increment() { c.mu.Lock() defer c.mu.Unlock() c.count++ } func (c *Counter) Value() int { c.mu.Lock() defer c.mu.Unlock() return c.count } func main() { c := &Counter{} var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done() c.Increment() }() } wg.Wait() fmt.Println("Final count:", c.Value()) }四、HTTP 服务func main() { http.HandleFunc("/api/hello", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "msg": "Hello from Go!", }) }) fmt.Println("Server running on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) }五、总结Go 的核心优势在于:语法简洁上手快、goroutine 轻量级并发、编译速度快、标准库强大。掌握 struct/interface/goroutine/channel 这四个核心概念,就能写出高质量的 Go 程序。建议在实际项目中配合 Gin/Echo 等 Web 框架和 GORM 等 ORM 框架,进一步提升开发效率。
2026年03月14日
10 阅读
0 评论
0 点赞
2026-03-10
Python asyncio 异步编程实战:从协程到任务调度
前言Python 的 asyncio 是编写并发代码的标准库,使用 async/await 语法实现单线程下的高并发 IO 操作。本文将从协程基础讲到实际应用,帮你掌握 Python 异步编程。一、同步 vs 异步1.1 同步代码的问题import requests import time def fetch_url(url): resp = requests.get(url) return resp.status_code start = time.time() for url in ["https://httpbin.org/delay/1"] * 10: fetch_url(url) print(f"同步耗时: {time.time() - start:.2f}s") # ~10s10 个请求串行执行,每个等待 1 秒,总共 10 秒。1.2 异步的优势import asyncio import aiohttp import time async def fetch_url(session, url): async with session.get(url) as resp: return resp.status async def main(): async with aiohttp.ClientSession() as session: tasks = [fetch_url(session, "https://httpbin.org/delay/1") for _ in range(10)] results = await asyncio.gather(*tasks) print(results) start = time.time() asyncio.run(main()) print(f"异步耗时: {time.time() - start:.2f}s") # ~1s10 个请求并发执行,总共约 1 秒。二、协程基础2.1 定义和调用协程import asyncio async def hello(): print("Hello") await asyncio.sleep(1) print("World") asyncio.run(hello())2.2 await 的含义await 只能在 async 函数内使用,它会:暂停当前协程的执行将控制权交还给事件循环等待 awaitable 对象完成后恢复执行async def task_a(): print("A start") await asyncio.sleep(1) print("A end") async def task_b(): print("B start") await asyncio.sleep(1) print("B end") async def main(): await task_a() await task_b() asyncio.run(main())三、并发执行3.1 asyncio.gatherasync def main(): await asyncio.gather(task_a(), task_b())3.2 asyncio.create_taskasync def main(): task1 = asyncio.create_task(task_a()) task2 = asyncio.create_task(task_b()) print("doing other work") await task1 await task23.3 超时控制async def slow_operation(): await asyncio.sleep(10) return "done" async def main(): try: result = await asyncio.wait_for(slow_operation(), timeout=3.0) print(result) except asyncio.TimeoutError: print("操作超时!") asyncio.run(main())3.4 asyncio.as_completedasync def fetch(simulated_time, name): await asyncio.sleep(simulated_time) return f"{name}: {simulated_time}s" async def main(): tasks = [ fetch(3, "task1"), fetch(1, "task2"), fetch(2, "task3"), ] for coro in asyncio.as_completed(tasks): result = await coro print(result) asyncio.run(main()) # 输出顺序: task2, task3, task1四、实际应用:并发爬虫import asyncio import aiohttp async def fetch_page(session, url): async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp: text = await resp.text() return { "url": url, "status": resp.status, "length": len(text), } async def crawl(urls, concurrency=5): semaphore = asyncio.Semaphore(concurrency) async def limited_fetch(session, url): async with semaphore: return await fetch_page(session, url) async with aiohttp.ClientSession() as session: tasks = [limited_fetch(session, url) for url in urls] return await asyncio.gather(*tasks, return_exceptions=True) urls = [f"https://httpbin.org/delay/{i % 3}" for i in range(20)] results = asyncio.run(crawl(urls, concurrency=5)) for r in results: if isinstance(r, Exception): print(f"Error: {r}") else: print(f"{r[\"status\"]} - {r[\"url\"]} ({r[\"length\"]} bytes)")五、异步上下文管理器class AsyncDBConnection: async def __aenter__(self): print("连接数据库...") await asyncio.sleep(0.5) return self async def __aexit__(self, exc_type, exc_val, exc_tb): print("关闭连接...") await asyncio.sleep(0.1) async def query(self, sql): await asyncio.sleep(0.1) return f"Result of: {sql}" async def main(): async with AsyncDBConnection() as db: result = await db.query("SELECT * FROM users") print(result) asyncio.run(main())六、常见陷阱6.1 在异步中调用同步阻塞代码# 错误:requests 是同步库,会阻塞事件循环 async def bad_fetch(url): return requests.get(url).text # 正确:使用 aiohttp async def good_fetch(url): async with aiohttp.ClientSession() as session: async with session.get(url) as resp: return await resp.text()6.2 忘记 awaitasync def main(): asyncio.sleep(1) # 不会执行!返回 coroutine 对象 await asyncio.sleep(1) # 正确6.3 在异步代码中使用 time.sleepimport time async def main(): time.sleep(1) # 阻塞事件循环! await asyncio.sleep(1) # 正确:让出控制权七、总结asyncio 的核心价值在于:在单线程内通过事件循环实现高并发 IO,避免了多线程的锁问题和上下文切换开销。记住三个关键点:IO 操作必须用异步库(aiohttp 而非 requests)、用 gather/create_task 实现并发、用 Semaphore 控制并发量。在高 IO 场景(爬虫、API 调用、数据库查询)中,asyncio 能带来数量级的性能提升。
2026年03月10日
10 阅读
0 评论
0 点赞
1
2
3
4
5
0:00