領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多在我曾經工作的一個專案中,我們有一個系統可以從裝置接收訊息,並決定是否將該資訊傳遞給使用者。存在多個決策級別,我們總是發現自己會問的一個問題是,訊息是否在透過系統的過程中丟失了。
在我們搬到 Spring 之前,幾乎不可能知道那個問題的答案。我們嘗試使用日誌記錄,但是因為有大量訊息需要做出決策,這使得日誌記錄充其量也只是乏味的。其他嘗試使用偵錯程式,但由於訊息量和時間變化的綜合影響,只取得了間歇性的成功。
不幸的是,我在我們能夠實現一個更合適的解決方案之前就離開了,但如果我還在,這大概就是它會是什麼樣子。最後,我將討論在這種工作中可能有用的一些擴充套件。
首先,我們有一組介面及其實現
package flowtracingexample;
public interface Component1 {
void forwardCall();
}
package flowtracingexample;
import java.util.Random;
public class DefaultComponent1 implements Component1 {
private Component2 child;
private Random r = new Random();
public DefaultComponent1(Component2 child) {
this.child = child;
}
public void forwardCall() {
if (r.nextBoolean()) {
child.forwardCall();
}
}
}
package flowtracingexample;
public interface Component2 {
void forwardCall();
}
package flowtracingexample;
import java.util.Random;
public class DefaultComponent2 implements Component2 {
private Component3 child;
private Random r = new Random();
public DefaultComponent2(Component3 child) {
this.child = child;
}
public void forwardCall() {
if (r.nextBoolean()) {
child.forwardCall();
}
}
}
package flowtracingexample;
public interface Component3 {
void forwardCall();
}
package flowtracingexample;
public class DefaultComponent3 implements Component3 {
public void forwardCall() {
}
}
這是一個非常簡單的例子,但要點是使用 fowardCall() 方法將訊息 50% 的時間傳遞給下一個子元件(在這種情況下按數字升序)。請注意,這些 POJO 中沒有涉及跟蹤的邏輯。
為了實現我們的跟蹤行為,我們希望有一組計數器;每個元件一個。此外,我們希望有方法來重置計數器、啟動和停止監視以及確定是否正在進行監視。為此,我們實現了一個帶有計數器的類。
package flowtracingexample;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
@ManagedResource
public class FlowTracer {
private long component1Count = 0;
private long component2Count = 0;
private long component3Count = 0;
private boolean tracing = false;
@ManagedAttribute
public long getComponent1Count() {
return this.component1Count;
}
@ManagedAttribute
public long getComponent2Count() {
return this.component2Count;
}
@ManagedAttribute
public long getComponent3Count() {
return this.component3Count;
}
@ManagedAttribute
public boolean getTracing() {
return this.tracing;
}
public void incrementComponent1Count() {
if (this.tracing) {
component1Count++;
}
}
public void incrementComponent2Count() {
if (this.tracing) {
component2Count++;
}
}
public void incrementComponent3Count() {
if (tracing) {
component3Count++;
}
}
@ManagedOperation
public void resetAllComponentCount() {
resetComponent1Count();
resetComponent2Count();
resetComponent3Count();
}
@ManagedOperation
public void resetComponent1Count() {
this.component1Count = 0;
}
@ManagedOperation
public void resetComponent2Count() {
this.component2Count = 0;
}
@ManagedOperation
public void resetComponent3Count() {
this.component3Count = 0;
}
@ManagedOperation
public void startTracing() {
tracing = true;
}
@ManagedOperation
public void stopTracing() {
tracing = false;
}
}
該類的方法及其內容都非常簡單。對您來說可能比較新穎的是該類上的註解。這些註解由 Spring 的 JMX 支援使用,以便在每個 bean 部署到 JMX MBeanServer 時自動構建 MBean 管理介面。
最後,是把整個東西連線起來。首先,我們連線構成流的元件。接下來,我們宣告將跟蹤器放置在每個元件上的切面。在這種情況下,我們使用的是非常棒的 AspectJ 切入點語言。最後,我們設定 JMX 匯出器以自動檢測帶有 @ManagedResource 註解的類的例項。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- Components -->
<bean id="component3" class="flowtracingexample.DefaultComponent3" />
<bean id="component2"
class="flowtracingexample.DefaultComponent2">
<constructor-arg ref="component3" />
</bean>
<bean id="component1"
class="flowtracingexample.DefaultComponent1">
<constructor-arg ref="component2" />
</bean>
<!-- Aspect -->
<bean id="flowTracer" class="flowtracingexample.FlowTracer" />
<aop:config>
<aop:aspect id="component1Aspect" ref="flowTracer">
<aop:before method="incrementComponent1Count"
pointcut="execution(public void flowtracingexample.Component1.forwardCall())" />
</aop:aspect>
<aop:aspect id="component2Aspect" ref="flowTracer">
<aop:before method="incrementComponent2Count"
pointcut="execution(public void flowtracingexample.Component2.forwardCall())" />
</aop:aspect>
<aop:aspect id="component3Aspect" ref="flowTracer">
<aop:before method="incrementComponent3Count"
pointcut="execution(public void flowtracingexample.Component3.forwardCall())" />
</aop:aspect>
</aop:config>
<!-- JMX -->
<bean class="org.springframework.jmx.export.MBeanExporter">
<property name="autodetectModeName" value="AUTODETECT_ALL" />
<property name="assembler">
<bean
class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource">
<bean
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />
</property>
</bean>
</property>
<property name="namingStrategy">
<bean
class="org.springframework.jmx.export.naming.IdentityNamingStrategy" />
</property>
</bean>
</beans>
接下來我們需要做的是有一個驅動類。在這種情況下,驅動類只是以小於 750 毫秒的隨機延遲傳送一條訊息。
package flowtracingexample;
import java.io.IOException;
import java.util.Random;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class FlowTracingExample {
public static void main(String[] args) throws InterruptedException,
IOException {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:flowtracingexample/applicationContext.xml");
Component1 comp = (Component1) ctx.getBean("component1");
Random r = new Random();
System.out.print("Ready...");
System.in.read();
for (;;) {
comp.forwardCall();
Thread.sleep(r.nextInt(750));
}
}
}
在我的情況下,我將執行帶有 Java VM 管理的這個應用程式,因為它為我提供了一個免費的 MBean 伺服器(而且我喜歡漂亮的記憶體圖)。如果你沒有聽說過,它是Java 5 VM 中的一個系統屬性,它使 VM 使用 JMX 來管理自身。它有關於記憶體消耗、執行緒和無數其他東西的 bean。你只需在執行應用程式的命令列上新增 -Dcom.sun.management.jmxremote 即可啟動它。在另一個方便的 Java 5 補充中,我將使用 jconsole 來顯示我的結果。
根據我不怎麼靈光的數學技能,從長遠來看,我預計元件 1 被呼叫 100%,元件 2 被呼叫 50%,元件 3 被呼叫 25%。讓我們看看
很高興看到我記對了我的機率。最好的部分是這仍然符合良好的設計原則。例如,這些元件都不知道任何關於追蹤的事情,因為那不是它們的工作。同樣,這個子系統的所有追蹤要求都包含在一個類中,並且有一個實現滿足 AOP 的 1:1 要求到實現目標。最後,透過關閉追蹤的能力,任何效能影響都或多或少地被抵消了。我知道,我知道增加一個整數並不昂貴,但是如果你的追蹤做了昂貴的事情,擁有它很好,而且你不必擔心是否要將其投入生產;你可以簡單地停用監控,直到你的客戶打電話尋求支援。
所以圖表確實很漂亮,如果你知道你預期的百分比,甚至可能會告訴你一些東西,但你還能做些什麼呢?那麼最近的 100 條訊息及其決策呢?那麼訊息被丟棄的原因日誌呢?那麼丟棄決策和管道末端訊息缺失之間的關聯呢?如果一條訊息因為你從未故意丟棄它但在其進入 500 毫秒內未能到達末端而丟失(可能是由於執行緒問題),那知道這一點難道不好嗎?沿著這條線,如果從管道一端到另一端所需的時間超過 250 毫秒,那麼給管理員傳送一封電子郵件怎麼樣?
跟蹤/監控的可能性是無限的(而且是可插拔的!)。你會用它做什麼?
當然,還有原始碼。