領先一步
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 支援用來自動建立 MBean 管理介面,當每個 Bean 部署到 JMX MBeanServer 時。
最後,將所有內容連接在一起。 首先,我們將構成流程的元件連接在一起。 接下來,我們宣告將追蹤器放置在每個元件上的 Aspect。 在這種情況下,我們使用非常棒的 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 Management 運行時運行此應用程式,因為它給了我一個免費的 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 毫秒,則向管理員發送電子郵件如何?
追蹤/監控的可能性是無限的(並且是可插拔的!)。 你會怎麼做呢?
當然,還有 原始碼。