Exception chaining simplifying debugging(中文)

本文介绍了一种名为异常链的技术,该技术将多个异常组织成一个列表,有助于简化调试过程。文章探讨了如何实现异常链,并提供了客户端和服务端应用的具体实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Exception chaining simplifying debugging
                     Auther:Barry Mosher
                     Translator:Leon·He
简介
异常链(也名”异常嵌套”)是一种异常处理的技术.它将一个原始的异常并且它在抽象层次上由低向高传递的过程中所产生的全部异常组成一个列表,这样在客户端和服务端环境都可以大量简化软件调试工作,并且不增加任何附加的复杂度。这篇文章讨论这种非常好异常处理技术,并且展示如何去实现和使用异常链
 
异常是接口的一部分
当我们的教授教我们有关封装与模块编程时,我们就被告知应该隐藏实现的细节。比如说,一个提供猎头服务的并不需要刊登广告告诉我们他们是如何及在什么地方找到雇员的,只需要将存取这些服务的方法设为公共的。通过把一个组件的实现从使用它的其它组件中分开,从而减少了复杂性。大多数的程序员意识到异常形成了任何一个类的重要的一部分,但是并不能确定如何处理那些他们在自己代码中无法直接处理的低层异常。从一个高层类当中抛出一个低层异常并不是一个好主意,因为暴露类的实现的细节。
对这个类来说,正确的解决方案就是捕获一个低层异常,然后抛出一个抽象层次高点的异常。
如果方法getEmployee()通过JDBC从数据库中取得employee对象,比如说,在方法内,一个SQLException可能被捕获,这异常可以被转化为EmployeeLookupException 并且重新抛给调用它的方法。
 
转换异常类的对象
当然不能简单的改变一个异常的类型。当我们把一个异常类对象在转换过程中把它的信息越转越少时候,我们就不禁会疑问这样做的必要性。我们不能把一种异常上抛给另外一些它们之间不存在父子关系的异常。比如,SQLException肯定不是从EmployeeLookupException继承而来,或者,反之已然
在这篇文章中所谓”转换异常”意味着捕获一种异常并且重新抛出一种不同类的全新异常。在某种意义上,转换而来的异常是由前一个异常而引起的,当异常”EmployeeLookupException”被创建和抛出时,这并不意味着第二个异常的出现,而它正是在一个相对高一点的抽象层上表示原先的异常
try {
stmt.execute(sql);
} catch (SQLException ex) {
throw new EmployeeLookupException();
}
对于多数程序员来说,显而易见,这种异常转换是有一个严重的缺陷的:异常的根结点丢失了,异常EmployeeLookupException 是一个logon失败引起的抑或是SQL查询错误?异常转换使得调试更困难了。我已经在上面的段落讨论了应该隐蔽方法的实现细节,但是我们并不想把调试工作弄得比它所需要还要复杂。这样,所能变动就会是那些被隐藏起来的实现细节。但这并不是一个正当的理由去混淆异常发生的原因和在运行时异常发生的位置。我们想使它容易被发现。而用异常链可以达到这两个目标。
 
上抛和其它异常处理技术
对于异常转换丢失了必要的信息这个问题一个通常的解决方案是通过上抛转换。通常情况下,上抛的是Exception异常和以隐含的方式在每个方法声明抛出Exception异常,这就破坏了在java中需要捕获异常的真正意图。编译器不需要程序员通过这种方法去捕获任何异常。
public Employee getEmployee() throws Exception
{
... database query code ..
}
 
上抛异常使得异常处理更加因难了,而好的异常转换应该添加更多的上下文,从而使得对问题的描述更精确了。期望中的异常转换,从抽象层次来说,是从低到高的。这样就很少有异常类的继承层次与之相匹配。要记住:一个程序员在设计一个类时所需要抽象的应该与他所正在要解决的问题所相关的。比如说,Exception 比SQLException更普遍。但对一个”an employee lookup service” 来说可能就没有在抽象层次更高的异常了
在这种情况EmployeeLookupException 和 NoPermissionException 则是抽象层次更高一点的一些好的例子。
 
一个更为糟糕的异常处理策略就是无论忽不忽略它之前捕获并记录每个异常,只要每个异常是被记录的。调试就会想对简单。很不幸,这种将会使调用方法去检测和处理错误变为不可能。而它也会抵销异常存在的意义。
[另外一个问题,在日志记录文件当中,面对一大堆混乱无序的异常信息记录你会很快找到对你解决问题有帮助的信息吗?一个原始异常引发了其它的异常,又因为在日志记录文件中找不到任何它们相关联的信息,你能解决完一个问题而去安心睡觉吗?]
什么是异常链?
异常链就是因为一个根异常而引发的一连串的异常,而这些异常组成一个链表。每个异常在被捕获后,将其转化为更高抽象级别后再次抛出,同时异常也被添加到了链表当中了。
这样就提供了一个异常是为什么发生的一个完整的记录。
try {
stmt.executeUpdate(sql);
} catch (SQLException ex) {
throw new EmployeeLookupException("Query failure",ex);
}
 
实现
异常链被实现为一个逆序的异常链表,每个异常在链中都是链的结点。链表中的第一个异常可以是任何类型的,并没有在功能上的任何限制。但每个接下来的异常必须有一种途径去指向前一个异常这样这个链才是可以被维护的。此外,必须还要一种方法读取和检测这个异常链。我把这种能提供这种的功能的异常叫做可链接的。也就是说,通过他们一个异常链可以被构建。
为了达到这种目的, 一个可链接的异常必须做两件事:
1.  至少提供一个构器函数使用另外一个异常作为参数并存储它。
2.  重写每个printStackTrace(),首先打印出它们自己的stack trace,然后调用先前异常的printStackTrace()方法打印出先前异常的stack trace.
第一个要求使得一个异常链可以被创建。第二个提供一种简单的方法即通过调用printStackTrace()来展显异常
 
实际上,这些简单的规则使得可以将异常类添加到链表当中去,而无需给使用的用户强加一些额外的要求,即使当用户使用可链接的异常。一个程序员也没必要了解异常链,如链被使用,然后在Stack Trace时就会获得很大的好处。
如果不使用,对开发人员而言,这也没有走了学习上弯路。而且并不会比使用不可链接的异常坏到那儿去。任何一个异常类都可以写成满足上面所描述的要求,而无论它的基类是什么。
让我们展示一个继承于Exception的异常链的简单实现。
 
当创建可链接的异常时,ChainedException 可以被当做模板。或者简单地使那些类继承于它也可以。注意要使构造函数把Throwables当参数。而非Exceptions,这使得异常链表可以包含JAVA所能抛出的所有异常。包括编译与运行异常同时也包含错误。这些构造函数的实现是非常简单的。它们将给定异常的引用保存在成员变量_previousTrowable。这个引用将在方法printStackTrace()中使用到。我们也注意到有些构造函数并不将任何Throwabe和Exception作为参数。这些构造函数是被用来构造根异常的。
 
服务端应用
在服务端使用异常链是非常简单的。像通常一样建立异常链,并且当记录日志时,确定使用方法printStackTrace()。当捕获异常时,无需去记录每个异常。只需要在链中最后一个异常时操作,因为在链中,会自动显示每个异常的信息和调用printStackTrace()方法。
如果对一个刚发生的异常单独记录起来是没什么危害,然而,一个可能不好的结果就是带来一个混乱的日志。Server管理员对于阅读日志判断错误很熟悉,同时他们也会感激对于使用
异常链提供的额外信息。下节我们将描述在客户端程序中一个特殊的对话框类是如何使用户可以读取异常链中的异常的。
 
客户端
JAVA语言的一个巨大好处就是它可以从错误中恢复过来的能力。 JAVA程序通常情况下并不会因为一个错误而自动停止运行的。相反,错误会以异常的形式被抛出。在JAVA客户端,如果运行异常没有被处理,主要的事件分发器将会在标准输出上打印异常的Stack trace。这个简单的动作实际上是非常有用的。当用户的按钮点击动作实际并没有完成任何事情时候,至少发生错误的有关信息是可以看到的。所以许多的程序员决定用这种方式处理所有的异常--à记录异常并中断操作。
 
一个还要好的方法就是通知用户程序所发生的任何错误。通常用一个对话框。好的对话框会清楚的显示两件东西:是什么样的操作失败了?同时为什么?我已经实现了一个对话框可以和能提供这两种信息的异常一起使用。无论是异常链还是单独的异常都能并无区别的使用它。
为了使用ExceptionDiolog, ”try-catch”块得加入到一个程序员已经注册了的GUI事件处理器
当一个异常被事件处理器所捕获后,ExceptionDiolog的事例会被创建。对话框的标题会向用户表明用户所请求的什么操作因为异常抛出而失败!对话框的信息会显示调用异常中的getLocalizedMessage()方法的结果,接下来,提供第二块重要的信息。即说明操作为什么不能完成。图3显示了将详细信息隐藏起来的对话框。
 
 
 
 
try {
...     event handler code ...
} catch (Exception ex) {
ExceptionDialog dlg = new ExceptionDialog(frame,ex,"Look-up failed.");
dlg.show();
}
为了更详细了查看发生异常的原因。用户可以点击”Show detaills”按钮展开对话框。展开来的对话框显示了异常的调用堆栈(the call stack of each exception)并且如是一个异常链的话同时也会显示链中的每个异常的调用堆栈。
 
 
Leon·He
Broaden Gate(ShenZhen) Corp
E-Mail:English98@tom.com
Do things as right as possible.
 
<think>我们正在讨论如何验证Cilium的链式模式(chaining-mode)配置是否生效。根据之前的引用和回答,我们可以总结出以下验证步骤。请注意,链式模式下,Cilium会与底层CNI(如AWS-CNI)协同工作,底层CNI负责IP分配和基本连接,而Cilium则通过附加eBPF程序来提供高级功能。 ### 验证步骤: #### 1. **检查ConfigMap配置** 首先,我们需要确认Cilium的ConfigMap中已经正确配置了链式模式。 ```bash kubectl -n kube-system get configmap cilium-config -o yaml | grep cni-chaining-mode ``` 如果配置生效,你应该看到类似这样的输出: ```yaml cni-chaining-mode: "aws-cni" # 这里可能是你使用的底层CNI名称,如portmap, aws-cni等 ``` 同时,确保`enable-cilium-endpoint-crd`被设置为`"false"`(因为链式模式下Cilium不会管理端点CRD)[^1]: ```bash kubectl -n kube-system get configmap cilium-config -o yaml | grep enable-cilium-endpoint-crd ``` 输出应为: ```yaml enable-cilium-endpoint-crd: "false" ``` #### 2. **查看Cilium Agent日志** 检查Cilium Agent的日志,确认它已经以链式模式启动。 ```bash # 获取任意一个Cilium Pod kubectl -n kube-system get pods -l k8s-app=cilium # 查看日志 kubectl -n kube-system logs <cilium-pod> | grep -i "chaining mode" ``` 如果链式模式生效,日志中应该包含类似这样的信息: ``` level=info msg="Using CNI chaining mode" mode=aws-cni ``` #### 3. **检查节点上的CNI配置文件** 在节点上,查看CNI配置文件(通常位于`/etc/cni/net.d/`),确认Cilium的链式配置已经正确写入。 你可以登录到一个节点上,或者通过Cilium Pod来查看(如果该Pod挂载了主机目录): ```bash # 进入Cilium Pod kubectl -n kube-system exec -it <cilium-pod> -- sh # 查看CNI配置文件(注意:具体文件名可能不同,但通常以字母顺序第一个为主配置) cat /etc/cni/net.d/00-cilium.conf # 或者类似的文件名 ``` 在配置文件中,你应该看到`type`字段设置为`cilium-cni`,并且有`chaining-mode`的配置[^1]: ```json { "name": "aws-cni", "cniVersion": "0.3.1", "plugins": [ { "name": "aws-cni", "type": "aws-cni", ... }, { "type": "cilium-cni", "chaining-mode": true, ... } ] } ``` #### 4. **验证eBPF程序是否附加** 链式模式的关键在于Cilium将eBPF程序附加到底层CNI创建的网络设备上。我们可以通过检查网络设备上的eBPF程序来验证。 ```bash # 进入Cilium Pod kubectl -n kube-system exec -it <cilium-pod> -- bash # 列出主机上的网络设备,通常主网卡是eth0(或根据你的环境可能是其他名称) ip link # 使用bpftool检查设备上的eBPF程序(确保bpftool已安装,或者使用cilium自带的) bpftool net show dev eth0 ``` 如果Cilium的eBPF程序已经附加,你应该看到类似下面的输出,其中包含`cil_`前缀的程序(如`cil_from_netdev`)[^2]: ``` xdp: eth0(2) driver cil_from_netdev ... ``` #### 5. **测试网络策略和高级功能** 最后,我们可以测试Cilium的高级功能(如网络策略)是否生效。注意,在链式模式下,Cilium的网络策略执行是有效的,但依赖于底层CNI的连通性。 - **部署测试Pod**: ```bash kubectl create ns test kubectl run nginx -n test --image=nginx kubectl run client -n test --image=busybox -- sleep 3600 ``` - **应用网络策略**(禁止所有入站流量): ```yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: deny-all namespace: test spec: endpointSelector: {} ingress: - {} ``` 应用策略后,尝试从client Pod访问nginx Pod应该失败。 - **验证策略生效**: ```bash kubectl exec -n test client -- wget -q -T 5 nginx && echo "Success" || echo "Failed" ``` 如果策略生效,应该输出`Failed`。同时,你可以通过Cilium的监控工具(如`cilium monitor`)查看策略执行情况。 #### 6. **检查Cilium Endpoint状态** 在链式模式下,Cilium不会创建CiliumEndpoint CRD(因为`enable-cilium-endpoint-crd`被设置为false),但Cilium Agent会管理端点。我们可以通过Cilium CLI检查端点状态: ```bash # 在Cilium Pod中执行 cilium status cilium endpoint list ``` 如果端点列表显示为`ENFORCEMENT`状态,并且策略计数正确,则说明Cilium在链式模式下正常工作。 ### 常见问题排查 - **配置未更新**:修改ConfigMap后,需要重启Cilium DaemonSet以应用配置:`kubectl rollout restart -n kube-system ds/cilium` - **日志错误**:检查Cilium Pod日志中是否有错误信息(如`kubectl logs -n kube-system <cilium-pod> | grep error`) - **eBPF程序未附加**:确认内核版本是否支持(至少4.9.17以上),并检查Cilium日志中的eBPF相关错误。 > ⚠️ 注意:在链式模式下,Cilium的某些功能(如ClusterIP服务的负载均衡)可能由底层CNI处理,而Cilium主要负责策略执行和可见性[^1]。 §§相关问题§§ 1. 如何将Cilium与AWS VPC CNI集成实现链式模式? 2. 链式模式下如何调试Cilium网络策略失效问题? 3. Cilium链式模式与独立模式的主要性能差异是什么? 4. 如何为链式模式配置自定义CNI配置文件? [^1]: 通过Cilium CNI Chaining,基本网络连接和IP地址管理由非Cilium CNI插件管理,但是Cilium将BPF程序附加到由非Cilium插件创建的网络设备上,以提供L3/L4/L7网络可见性和策略强制执行和其他高级功能,例如透明加密。 [^2]: 找到在该节点上运行的 Cilium pod: kubectl -n kube-system get po -l k8s-app=cilium --field-selector spec.nodeName=kind-worker2</think>### 验证Cilium链式模式配置生效的方法 #### 1. **检查ConfigMap配置** [^1] ```bash kubectl -n kube-system get configmap cilium-config -o yaml | grep -E "cni-chaining-mode|enable-cilium-endpoint-crd" ``` 生效标志: - `cni-chaining-mode: <底层CNI名称>`(如`aws-cni`或`portmap`) - `enable-cilium-endpoint-crd: "false"`(链式模式需禁用CRD) #### 2. **验证Agent日志** [^2] ```bash # 获取节点上的Cilium Pod kubectl -n kube-system get pod -l k8s-app=cilium --field-selector spec.nodeName=<节点名> # 检查日志 kubectl -n kube-system logs <cilium-pod> | grep -i "chaining mode" ``` 生效输出示例: ```log level=info msg="Using CNI chaining mode" mode=aws-cni ``` #### 3. **检查eBPF程序附加状态** ```bash # 进入Cilium Pod kubectl -n kube-system exec -it <cilium-pod> -- bash # 查看主网卡(如eth0)的eBPF程序 bpftool net show dev eth0 | grep -A5 "xdp" ``` 生效标志:显示`cil_from_netdev`等Cilium程序已附加: ``` xdp: eth0(2) driver cil_from_netdev ... ``` #### 4. **验证网络功能** ```bash # 创建测试Pod kubectl run test-pod --image=nginx -- sleep infinity # 检查网络策略执行 kubectl exec test-pod -- curl -I https://cilium.io ``` 生效标志: - 基础网络通信正常(底层CNI负责) - 网络策略可强制执行(Cilium负责) #### 5. **检查CNI配置文件** ```bash # 在节点上检查 cat /etc/cni/net.d/00-cilium.conf | grep -A3 "type" ``` 生效配置示例: ```json { "type": "cilium-cni", "chaining-mode": true } ``` ### 故障排查技巧 1. **配置未生效**: ```bash kubectl rollout restart -n kube-system ds/cilium ``` 2. **验证eBPF host-routing** [^4]: ```bash cilium status | grep -i "host routing" ``` 输出应为:`Host Routing: BPF` 3. **网络策略验证**: ```bash cilium connectivity test ``` > ⚠️ **关键验证点**:链式模式生效时,底层CNI(如AWS-CNI)负责IP分配和基础连接,而Cilium的eBPF程序提供网络策略、监控等高级功能[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值