一、handler 与 notify 是什么?
在 Ansible Playbook 中,handler 和 notify 是一对 "触发器",专门用来解决 "配置修改后,需要触发后续操作(比如重启服务)" 的场景。
简单来说:
notify是 "事件发起方" :当某个任务的状态发生changed(修改了配置文件、安装了新软件等),就会发出一个通知,告诉 Ansible:"我改东西了,需要触发对应的后续操作!"handler是 "事件接收方" :只有收到notify发出的通知,才会执行对应的任务(比如重启服务、重载配置);如果没有通知,它就不会执行。
它们的核心特点是:只有任务发生了实际变更,才会触发操作;如果配置没有变化,就不会重复执行重启这类操作,避免无效的资源消耗。
二、handler 与 notify 的核心作用
在我们的 httpd 部署场景中,这对组合解决了一个关键问题:修改了 httpd 监听端口的配置后,如何自动重启 httpd 服务,让新端口生效?
1. 传统写法的问题
如果不用 handler/notify,我们会直接在任务里写重启服务:
yaml
- name: 修改端口号为8080
lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen\s+80'
line: 'Listen 8080'
- name: 重启 httpd 服务
service:
name: httpd
state: restarted
但这样写有个问题:每次执行 Playbook,不管端口配置有没有修改,restarted 任务都会执行一次,导致 httpd 每次都被重启,完全没必要,还会影响服务可用性。
2. handler/notify 的优势
使用 handler/notify 后,逻辑变成了:
- 只有当
lineinfile任务真的修改了配置文件(状态为changed),才会通过notify发出通知。 handler收到通知后,才会执行重启操作;如果配置没变化,就不会重启服务。

这完美实现了 "按需触发",避免了无效重启,同时保证了配置修改后服务能自动生效。
三、使用方法
1. 在现有的 Playbook 中,handler/notify 的完整代码
yaml
---
- name: 给myweb-group主机组部署httpd
hosts: myweb-group
become: yes
gather_facts: true
tasks:
- name: 安装httpd服务
dnf:
name: httpd
state: present
- name: 修改端口号为8080
lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen\s+80'
line: 'Listen 8080'
backup: yes
notify:
restart apache # 1. 发出通知,名字要和handler的name完全一致
- name: 分发网页
copy:
src: ./index.html
dest: /var/www/html/index.html
owner: apache
group: apache
mode: '0644'
- name: 关闭防火墙
service:
name: firewalld
state: stopped
enabled: no
- name: 启动httpd服务
service:
name: httpd
state: started
enabled: yes
handlers: # 2. handler必须和tasks平级
- name: restart apache # 必须和notify里的名字完全匹配
service:
name: httpd
state: restarted
2. 逐部分解析关键细节
(1)notify 部分
yaml
notify:
- restart apache
- 它是
lineinfile任务的一个属性,必须和lineinfile:平级。 - 这里的
restart apache是一个 "通知名",必须和 handler 里的name完全一致,不能写错(大小写、空格都要完全匹配)。 - 当
lineinfile任务执行结果为changed时,就会发出这个通知;如果结果是ok(配置没变化),就不会发出通知。
(2)handlers 部分
yaml
handlers:
- name: restart apache
service:
name: httpd
state: restarted
handlers必须和tasks、hosts等字段平级,不能缩进在tasks里面。- 每个 handler 也是一个任务,写法和普通任务完全一样,只是它默认不会执行,只有收到对应的通知才会执行。
- 多个任务可以同时 notify 同一个 handler,比如修改了配置文件、安装了新版本,都可以通知同一个重启服务的 handler。
(3)执行顺序的关键
handler 有一个重要特性:它会在所有普通任务执行完成后,才会统一执行,而不是在发出通知的任务执行后立刻执行。比如现在的 Playbook 执行顺序是:
- 安装 httpd(ok)
- 修改端口配置(changed,发出通知)
- 分发网页(ok)
- 关闭防火墙(ok)
- 启动 httpd(changed)
- 最后执行 handler:重启 httpd
这样设计的好处是,即使有多个任务都触发了同一个 handler,也只会执行一次,避免重复操作。
四、Playbook 执行结果验证
从执行的输出可以看到:
TASK [修改端口号为8080] *****************************
changed: [192.168.190.145]
changed: [192.168.190.144]
RUNNING HANDLER [restart apache] *****************************
changed: [192.168.190.145]
changed: [192.168.190.144]
- 第一次执行时,
lineinfile任务状态是changed,所以触发了 handler,restart apache执行了。 
- 第二次执行时,
lineinfile任务状态是ok(端口已经是 8080,没有修改),所以 handler 不会执行,Playbook 里没有RUNNING HANDLER的输出。 
这完全符合我们的预期,也验证了 handler/notify 的 "按需触发" 特性。
五、handler/notify 使用的避坑指南
- 名字必须完全匹配 :notify 里的名字和 handler 的
name必须一字不差,包括大小写、空格,否则 handler 不会执行。 - handler 必须在 play 级别定义 :
handlers字段必须和tasks平级,不能缩进在任务里面。 - handler 只有在 changed 时触发 :只有发出通知的任务状态是
changed,handler 才会执行;如果任务状态是ok/failed,都不会触发。 - handler 会在所有任务后执行:不要在 handler 里写会影响后续任务的操作,它是最后才执行的。
六、总结
在 Ansible 中,handler 和 notify 是实现 "配置变更后自动触发操作"的核心机制,在服务部署场景中尤其常用:
- 它避免了无效的重复操作,保证服务的可用性。
- 它让 Playbook 更高效、更符合 "幂等性" 的设计原则。
- 在的 httpd 部署中,它完美实现了 "修改端口后自动重启服务" 的需求,让配置变更自动生效。