Byteman 使用指南(九)
本文重点讲解 Byteman
规则状态管理操作,以下是具体的内容。
1. 链接映射(LinkMaps)
规则引擎提供了 LinkMaps,用于在规则触发时记录信息,供其他规则或测试运行结束时检索。链接映射本质上是一个命名的 Map
,它将一个 Object
与另一个 Object
关联。帮助器类定义的 API 如下:
API 方法中省略了 mapName
参数的版本会在默认映射上操作,该映射是预先定义的映射,标签为全局 String
名称 "default"
。然而,您可以拥有任意数量的映射,使用任何方便的对象作为规则触发时的标签。当运行多线程程序时,通常使用当前线程来命名 LinkMap 是有用的,因为这确保了一个线程保存的值不会被其他线程覆盖。
createLinkMap
:用于在使用之前创建一个 LinkMap。然而,这并不总是必要的,因为其他 API 函数会在需要时自动创建映射。如果映射不存在,它返回true
;如果映射已存在,则返回false
。deleteLinkMap
:用于删除 LinkMap。这很有用,因为它确保了清除了映射中链接的所有对象的所有引用。如果找到了标签为mapName
的映射并已删除,它返回true
;否则返回false
。link
:用于向映射中添加从name
到value
的链接。没有mapName
参数的版本将链接添加到默认映射中(标签为"default"
的映射)。调用的返回值是之前与name
链接的任何先前值,如果没有先前的链接存在于映射中,则为null
。linked
:用于从映射中检索通过name
链接的值。没有mapName
参数的版本从默认映射中检索值。调用的返回值是从name
链接的值,如果没有链接存在于映射中,或者找不到标签为mapName
的映射,则为null
。unlink
:用于从映射中移除任何从name
的链接。没有mapName
参数的版本将默认映射中的链接移除。调用的返回值是从name
解除链接的值,如果没有链接存在于映射中,则为null
。linkNames
:用于检索映射中用作链接名称的所有对象的列表。没有mapName
参数的版本检索默认映射中所有链接的名称。调用的返回值将是一个可能为空的列表,如果在调用时找到了映射。如果找不到标签为mapName
的映射,它将为null
。linkValues
:用于检索映射中作为链接值出现的所有对象的列表。没有mapName
参数的版本检索默认映射中所有链接的值。调用的返回值将是一个可能为空的列表,如果在调用时找到了映射。如果找不到标签为mapName
的映射,它将为null
。clearLinks
:用于原子地从映射中清除所有链接。没有mapName
参数的版本清除了标签为"default"
的默认映射中的所有链接。如果在调用时找到了一个非空的标签为mapName
的映射并清除了它,返回值将是true
。如果没有找到映射,或者找到了一个空映射,它将返回false
。
2. 倒计时(CountDowns)
规则引擎提供了 CountDowns,用于确保在触发其他规则或一定次数后,某些规则才会触发。帮助器类定义的 API 如下:
CountDowns 由任意对象标识,允许连续对 countdown
API 的调用应用于相同或不同的情况。这种识别可以跨不同的规则和帮助器实例进行。例如,一个规则可能包括动作 createCountDown($0, 1)
,另一个规则可能包括条件 countDown($0)
。由第一个规则创建的 CountDown 只有在第二个规则被触发的方法调用具有相同的值时才会减少。由不同值的调用创建的 CountDown 将相应地匹配。然而,如果 CountDown 是用一个共同的 String
文字(即动作和条件都是 createCountDown("counter", 1)
和 countDown("counter")
)标识的,那么第一个规则创建的 CountDown 将被下一个触发的第二个规则减少,无论触发方法调用是否在相关实例上。
createCountDown
:用于创建 CountDown。count
参数指定 CountDown 将被减少的次数,然后减少操作失败,即如果count
是 1,那么 CountDown 将减少一次,然后在下一次减少时失败。如果count
以小于 1 的值提供,它将被替换为值 1。createCountDown
通常在规则动作中使用。然而,它被定义为如果创建了新的 CountDown 则返回true
,如果已经与标识符关联了 CountDown 则返回false
。这允许它在规则条件中使用,其中几个规则可能在竞争创建 CountDown。getCountDown
:用于在规则条件中使用,以测试是否存在与给定标识符关联的 CountDown,如果存在则返回true
,否则返回false
。countDown
:用于在规则条件中使用,以减少 CountDown。如果减少成功,或者没有与标识符关联的 CountDown,它返回false
。如果 CountDown 失败,即它的计数为 0,它返回true
。在后一种情况下,标识符和 CountDown 之间的关联被删除,允许使用相同的标识符开始新的 CountDown。请注意,这种行为确保了多个线程尝试从规则条件减少计数器之间的竞争只有一个赢家。
3. 标志(Flags)
规则引擎提供了一个简单的机制来设置、测试和清除全局标志。帮助器类定义的 API 如下:
与以前一样,标志由任意对象标识。所有三个方法都旨在在规则条件或动作中使用。
flag
:可以被调用以确保标识为identifier
的标志被设置。如果标志以前是清除的,它返回true
,否则返回false
。请注意,API 设计旨在确保多个线程尝试从规则条件设置标志之间的竞争只有一个赢家。flagged
:测试标识为identifier
的标志是否被设置。如果标志被设置,它返回true
,否则返回false
。clear
:可以被调用以确保标识为identifier
的标志被清除。如果标志以前是设置的,它返回true
,否则返回false
。请注意,API 设计旨在确保多个线程尝试从规则条件清除标志之间的竞争只有一个赢家。
4. 计数器(Counters)
规则引擎提供了 Counters,用于维护独立规则触发之间的全局计数。它们可以被创建和初始化,读取,递增和递减,以跟踪和响应各种触发或触发发生的次数。请注意,与 CountDowns 不同,递减计数器到零没有特殊语义。它们甚至可能有负值。帮助器类定义的 API 如下:
与以前一样,计数器由任意对象标识。所有方法都旨在在规则条件或动作中使用。
createCounter
:可以被调用以创建一个新的与o
相关联的 Counter。如果未提供参数count
,则新 Counter 的值默认为0
。createCounter
返回true
如果创建了一个新的 Counter,如果已经与o
关联了 Counter,则返回false
。请注意,API 设计旨在确保多个线程尝试从规则条件创建 Counter 之间的竞争只有一个赢家。deleteCounter
:可以被调用以删除与o
相关联的任何现有 Counter。它返回true
如果已删除 Counter,如果与o
没有关联的 Counter,则返回false
。请注意,API 设计旨在确保多个线程尝试从规则条件删除 Counter 之间的竞争只有一个赢家。incrementCounter
:可以被调用以递增与o
相关联的 Counter。如果不存在这样的 Counter,它将创建一个初始值为 0 的 Counter,然后递增它。incrementCounter
返回新 Counter 的值。如果省略了amount
,则默认为 1。decrementCounter
:等同于调用incrementCounter(o, -1)
,即它将计数器的值加上 -1。readCounter
:可以被调用以读取与o
相关联的 Counter 的值。如果不存在这样的 Counter,它将创建一个初始值为 0 的 Counter。如果省略了可选标志参数zero
,则默认为false
。
5. 计时器(Timers)
规则引擎提供了 Timers,允许测量触发之间的经过时间。可以通过以下 API 创建、读取、重置和删除计时器:
与以前一样,计时器由任意对象标识。所有方法都旨在在规则条件或动作中使用。
createTimer
:可以被调用以创建一个新的与o
相关联的计时器。createTimer
返回true
如果创建了一个新的计时器,如果已经与o
关联了计时器,则返回false
。getElapsedTimeFromTimer
:可以被调用以获得自与o
相关联的计时器创建或上次调用resetTimer
以来的经过毫秒数。如果没有与o
相关联的计时器存在,将创建一个新的计时器,然后返回经过时间。resetTimer
:可以被调用以将与o
相关联的计时器归零。它返回自计时器创建或上次调用resetTimer
以来的秒数。如果没有与o
相关联的计时器存在,将创建一个新的计时器,然后返回经过时间。deleteTimer
:可以被调用以删除与o
相关联的计时器。deleteTimer
返回true
如果已删除计时器,如果没有与o
相关联的计时器存在,则返回false
。
6. 递归触发
当规则被触发时,它执行事件、条件和动作中的 Java 代码,这可能包括对帮助器方法或应用程序在测试或 JVM 运行时定义的方法的调用。如果这些方法中的任何一个匹配 Byteman 规则,这可能导致规则执行引擎的递归条目。在某些情况下,这可能是可取的。然而,在其他情况下,这种递归条目可能导致无限触发链,并且有必要在规则执行时禁用触发。例如,以下规则将因这个问题而失败:
问题是,在对内置方法 traceln(Object, String)
的第一次调用中,默认帮助器类尝试打开一个跟踪文件,并将 "openlog"
与其关联。这样做时,它调用了 FileOutputStream.open
并重新触发了规则。
解决这个问题的一个方法是指定一个条件,该条件将打破链。跟踪文件将有一个名为 "trace_NNN_.txt"
的名称,因此以下版本的规则按预期工作:
这个版本在 traceln
调用下递归触发规则,但条件阻止了它被触发,打破了递归。
当然,在其他情况下,可能没有那么简单就能想出一个避免递归触发的条件。所以,默认帮助器提供了以下方法,允许在规则执行时禁用或重新启用触发。
如果 enabled
是 false
,那么在规则正文中随后表达式的执行期间禁用触发。如果它是 true
,则重新启用触发。
这可以用来实现上面示例中显示的行为,而无需识别合适的条件。
setTriggering
总是返回布尔值 true
,允许将其 AND 到 IF
子句的条件中,或用于初始化在 BIND
子句中声明的规则变量。有时这是必要的,以确保触发在 IF
或 BIND
子句中评估其他表达式之前尽早被禁用。
版权声明: 本文为 InfoQ 作者【FunTester】的原创文章。
原文链接:【http://xie.infoq.cn/article/1416fe048c8d1c2c5cc25884f】。文章转载请联系作者。
评论