我的消息吃了我的服務器!Kyle指出,通常,Web服務開發者開始經歷“內存溢出”的錯誤或者奇怪的“性能問題”時,總是會發現服
務器擁有極高的處理負載,CPU使用率接近100%,以及較低的吞吐量和高網絡延遲。導致這些癥狀的典型原因是非常大的(有時會達到50
MB或者更大)消息。而且,這些大消息往往包含了非常大的、作為XML消息主體的、采用base-64編碼的二進制編碼信息。導致其發生的原因通常是:
……開發者不理解技術的局限性:XML處理對解決許多問題都有用,但是你必須認識到消息是要被解析的——并且在大多數……產品中,這就意味著許多或者所有的消息都會駐留在內存中。
Kyle建議采用如下方法來改善這種情況:
- 不要發送冗余信息。在許多情況下,發送二進制數據時,你可能會發現消息高度重復。如果是這樣,你可能就要考慮在HTTP層面使用壓縮技術來改善你的網絡延遲。雖然這不會幫助你處理負載,但可能有助于減輕其中一個問題。
- 在XML消息體中,根本不要嵌入二進制信息。這是較好的解決方法,還有幾種不同的途徑可以實現這一效果。比如,你可以使用帶有附件的SOAP或者消息傳輸優化機制(MTOM)繞過解析開銷,盡管這無助于網絡延遲問題。
- ……還有一個更好的辦法,使用SOAP根本不發送大的二進制blob。替代方法,通過受控的文件傳輸系統,使用一個“帶外數據”傳輸……或者“聲明標簽(claim Check,參見《EIP模式》或這里)”模式,避免在SOAP和HTTP上發送大的二進制文件。
任何一種技術都有它使用的環境,在做架構設計的時候一定要避免因為個人的偏好,無意識的舍棄某些選擇。 一種簡單的方法論是,根據需要達到的目的,列出所有可能的實現方案,最后做出決定。
不好意思,你的數據正在顯示。根據Kyle所說,另一個典型的Web服務的“性能問題” 是,使用Web服務的層面非常、非常低——通常Web服務跟一個SQL語句相關,這是因為:
誤解了SOA架構原則。一個優秀SOA架構的關鍵原則是你的服務應該具有高復用性。
根據Kyle所說,這些情況通常發生在:
……如果設計是根據現有代碼“自上而下”衍生出服務,這類服務就會出現;通常,開發者會看著他們現有的架構圖并且決定將架構中的每一層(包括表現層)轉變成服務集。
相反,在SOA架構的正確位置使用粗粒度的Web服務會更好。再次強調,檢查一個架構的標準分層模型,通常在架構中會有一個明確定義的地方已經封裝
了系統業務邏輯。可以使用“遠程門面模式(Remote Facade Pattern)”來包裝這些服務,以便用合適的方式來暴露基于模型的服務。
同樣是可以利用方法論來避免問題,但對于粒度的把握就是一個經驗的問題。
模式(Schema)?我們不需要任何發臭的模式!
Kyle指出,通常開發者試圖重用現有代碼來生成和解析作為Web服務實現基礎的XML。這些實現通常使用XML解析器來編組/解組消息,同時使用
Java
HTTP類來發送和接收XML文檔。使用Web服務時,通用的方法是,創建使用模式元素的WSDL文檔,使XML不受阻地通過,然后在現有代碼中對它們進
行解析。
這個問題的癥狀是組織沒有看到SOA承諾的好處,而且維護他們的解決方案似乎比以前使用Web服務的時候更難(而不是更容易)
簡單的解決方案是,每當寫Web服務時,不管使用WS-*標準還是使用REST方法,都要確保你創建了代表你文檔結構的完整準確的XML模式。
如果你正在構建WS-* Web服務,那么這個XML應該被包含在描述你的Web服務的WSDL之中。即使你在使用REST方法,擁有易于訪問的XML模式將鼓勵你的服務被重用。
如何看懂Java混淆后的反編譯代碼
作者:dozb
一般情況下Java應用的開發者為了保護代碼不被別人抄襲,在生成class文件的時候都java文件進行了混淆,這種class文件用反編譯工具得到的結果很難看懂,并且不能進行編譯。本文從研究的角度,淺析如何讀懂這種反編譯過來的文件。
例子一:賦值
反編譯過來的代碼如下:
Node node;
Node node1 = _$3.getChildNodes().item(0);
node1;
node1;
JVM INSTR swap ;
node;
getChildNodes();
0;
item();
getChildNodes();
0;
item();
getNodeValue();
String s;
s;
原始語句:
Node node;
Node node1 = currDocument.getChildNodes().item(0);
node = node1;
String s = node.getChildNodes().item(0).getChildNodes().item(0).getNodeValue();
注解:
JVM INSTR swap ; //賦值語句
練習:
String s1;
String s8 = node.getChildNodes().item(1).getChildNodes().item(0).getNodeValue();
s8;
s8;
JVM INSTR swap ;
s1;
10;
Integer.parseInt();
int i;
i;
例子二:不帶參數創建對象
反編譯過來的代碼如下:
JVM INSTR new #244 <Class CrossTable>;
JVM INSTR dup ;
JVM INSTR swap ;
CrossTable();
CrossTable crosstable;
crosstable;
原始語句:
CrossTable crosstable = new CrossTable();
注解:
練習:
JVM INSTR new #246 <Class Database>;
JVM INSTR dup ;
JVM INSTR swap ;
Database();
Object obj;
obj;
例子三:帶參數創建對象
反編譯過來的代碼如下:
JVM INSTR new #262 <Class StringBuffer>;
JVM INSTR dup ;
JVM INSTR swap ;
String.valueOf(s2);
StringBuffer();
s.substring(j, i);
append();
s6;
append();
toString();
s2;
原始語句:
s2 = (new StringBuffer(String.valueOf(s2))).append(s.substring(j, i)).append(s6).toString();
注解:
此語句實際上是:s2 += s.substring(j, i) + s6;
練習:
例子四:for循環
反編譯過來的代碼如下:
int k = 0;
goto _L4
_L8:
...
k++;
_L4:
if(k < as.length) goto _L8; else goto _L7
原始語句:
for(int k=0;k < as.length;k++)
{
...
}
注解:
例子五:while循環
反編譯過來的代碼如下:
String s1 = "";
goto _L1
_L3:
JVM INSTR new #262 <Class StringBuffer>;
JVM INSTR dup ;
JVM INSTR swap ;
String.valueOf(s1);
StringBuffer();
_$2(resultset, s, l);
append();
toString();
s1;
_L1:
if(resultset.next()) goto _L3; else goto _L2
原始語句:
String s1 = "";
while(resultset.next())
{
s1 = s1 + resultSetToString(resultset, s, l);
}
[說明]幾個關鍵字將不翻譯
-
ClassLoader
-
System
-
Context
-
Thread
走出ClassLoader的迷宮
System、Current和Context ClassLoader?分別在何種情形下使用?
1、問題:在何種情形下使用thread.getcontextclassloader()?
盡管沒經常遇到這個問題,但是想獲得準確的答案并不那么容易,特別是在開發應用框架的時候,你需要動態的加載一些類和資源,不可避免的你會被此困擾。一般來說,動態載入資源有三種ClassLoader可以選擇,System ClassLoader(也叫App ClassLoader)、當前類的ClassLoader和CurrentThread的Context ClassLoader。那么, 如何選擇使用?
首先可以簡單排除的是System ClassLoader,這個ClassLoader負責從參數-classpath、-cp、和操作系統CLASSPATH中載入資源。并且,任何ClassLoader的getSystemXXX()方法都是有以上幾個路徑指定的。我們應該很少需要編寫直接使用ClassLoader的程序,否則你的代碼將只能在命令行運行,發布你的代碼成為ejb、web應用或者java web start應用,我肯定他們會崩潰!
接下來,我們只剩下兩個選擇了:當前ClassLoader和Thread Context ClassLoader
Current ClassLoader:當前類所屬的ClassLoader,在虛擬機中類之間引用,默認就是使用這個ClassLoader。另外,當你使用Class.forName(), Class.getResource()這幾個不帶ClassLoader參數的方法是,默認同樣適用當前類的ClassLoader。你可以通過方法XX.class.GetClassLoader()獲取。
Thread Context ClassLoader,沒一個Thread有一個相關聯系的Context ClassLoader(由native方法建立的除外),可以通過Thread.setContextClassLoader()方法設置。如果你沒有主動設置,Thread默認集成Parent Thread的 Context ClassLoader(注意,是parent Thread 不是父類)。如果 你整個應用中都沒有對此作任何處理,那么 所有的Thread都會以System ClassLoader作為Context ClassLoader。知道這一點很重要,因為從web服務器,java企業服務器使用一些復雜而且精巧的ClassLoader結構去實現諸如JNDI、線程池和熱部署等功能以來,這種簡單的情況越發的少見了。
這篇文章中為什么把Thread Context ClassLoader放在首要的位置,別人并沒有大張旗鼓的介紹它?很多開發者都對此不甚了解,因為sun沒有提供很好的說明文檔。
事實上,Context ClassLoader提供一個突破委托代理機制的后門。虛擬機通過父子層次關系組織管理ClassLoader,沒有個ClassLoader都有一個Parent ClassLoader(BootStartp不在此范圍之內),當要求一個ClassLoader裝載一個類是,他首先請求Parent ClassLoader去裝載,只有parent ClassLoader裝載失敗,才會嘗試自己裝載。
但是,某些時候這種順序機制會造成困擾,特別是jvm需要動態載入有開發者提供的資源時。就以JNDI為例,JNDI的類是由bootstarp ClassLoader從rt.jar中間載入的,但是JNDI具體的核心驅動是由正式的實現提供的,并且通常會處于-cp參數之下(注:也就是默認的System ClassLoader管理),這就要求bootstartp ClassLoader去載入只有SystemClassLoader可見的類,正常的邏輯就沒辦法處理。怎么辦呢?parent可以通過獲得當前調用Thread的方法獲得調用線程的Context ClassLoder 來載入類。
順帶補充一句,JAXP從1.4之后也換成了類似JNDI的ClassLoader實現,嘿嘿,剛剛我說什么來著,SUN文檔缺乏 ^_^
介紹完這些之后,我們走到的十字路口,任一選擇都不是萬能的。一些人認為Context ClassLoader將會是新的標準。但是 一旦你的多線程需要通訊某些共享數據,你會發現,你將有一張極其丑陋的ClassLoader分布圖,除非所有的線程使用一樣的Context ClassLoader。并且委派使用當前ClassLoder對一些方法來說是默認繼承來的,比如說Class.forName()。盡管你明確的在任何你能控制的地方使用Context ClassLoader,但是畢竟還有很多代碼不歸你管(備注:想起一個關于UNIX名字來源的笑話)。
某些應用服務器使用不同的ClassLoder作為Context ClassLoader和當前ClassLoader,并且這些ClassLoader有著相同的ClassPath,但沒有父子關系,這使得情況更復雜。請列位看官,花幾秒鐘時間想一想,為什么這樣不好?被載入的類在虛擬機內部有一個全名稱,不同的ClassLoader載入的相同名稱的類是不一樣的,這就隱藏了類型轉換錯誤的隱患。(注:奶奶的 俺就遇到過,JBOSSClassLoader機制蠻挫的)
這種混亂事實上在java類中也有,試著去猜測任何一個包含動態加載的java規范的ClassLoader機制,以下是一個清單:
- JNDI uses context classloaders
- Class.getResource() and
Class.forName()
use the current classloader
- JAXP uses context classloaders (as of J2SE 1.4)
- java.util.ResourceBundle uses the caller's current classloader
- URL protocol handlers specified via
java.protocol.handler.pkgs
system property are looked up in the bootstrap and system classloaders only
- Java Serialization API uses the caller's current classloader by default
而且關于這些資源的類加載機制文檔時很少。
java開發人員應該怎么做?
如果你的實現是利用特定的框架,那么恭喜你,實現它遠比實現框架要簡單得多!例如,在web應用和EJB應用中,你僅僅只要使用 Class.getResource()就足夠了。
其他的情形下,俺有個建議(這個原則是俺工作中發現的,侵權必究,抵制盜版。),
下面這個類可以在整個應用中的任何地方使用,作為一個全局的ClassLoader(所有的示例代碼可以從download下載):
1 public abstract class ClassLoaderResolver {
2 /**
3 * This method selects the best classloader instance to be used for
4 * class/resource loading by whoever calls this method. The decision
5 * typically involves choosing between the caller's current, thread context,
6 * system, and other classloaders in the JVM and is made by the
7 * {@link IClassLoadStrategy} instance established by the last call to
8 * {@link #setStrategy}.
9 *
10 * @return classloader to be used by the caller ['null' indicates the
11 * primordial loader]
12 */
13 public static synchronized ClassLoader getClassLoader() {
14 final Class caller = getCallerClass(0);
15 final ClassLoadContext ctx = new ClassLoadContext(caller);
16
17 return s_strategy.getClassLoader(ctx);
18 }
19
20 public static synchronized IClassLoadStrategy getStrategy() {
21 return s_strategy;
22 }
23
24 public static synchronized IClassLoadStrategy setStrategy(
25 final IClassLoadStrategy strategy) {
26 final IClassLoadStrategy old = s_strategy;
27 s_strategy = strategy;
28
29 return old;
30 }
31
32 /**
33 * A helper class to get the call context. It subclasses SecurityManager to
34 * make getClassContext() accessible. An instance of CallerResolver only
35 * needs to be created, not installed as an actual security manager.
36 */
37 private static final class CallerResolver extends SecurityManager {
38 protected Class[] getClassContext() {
39 return super.getClassContext();
40 }
41
42 } // End of nested class
43
44 /*
45 * Indexes into the current method call context with a given offset.
46 */
47 private static Class getCallerClass(final int callerOffset) {
48 return CALLER_RESOLVER.getClassContext()[CALL_CONTEXT_OFFSET
49 + callerOffset];
50 }
51
52 private static IClassLoadStrategy s_strategy; // initialized in <clinit>
53
54 private static final int CALL_CONTEXT_OFFSET = 3; // may need to change if
55 // this class is
56 // redesigned
57 private static final CallerResolver CALLER_RESOLVER; // set in <clinit>
58
59 static {
60 try {
61 // This can fail if the current SecurityManager does not allow
62 // RuntimePermission ("createSecurityManager"):
63
64 CALLER_RESOLVER = new CallerResolver();
65 } catch (SecurityException se) {
66 throw new RuntimeException(
67 "ClassLoaderResolver: could not create CallerResolver: "
68 + se);
69 }
70
71 s_strategy = new DefaultClassLoadStrategy();
72 }
73 } // End of class.
74
75
76
通過ClassLoaderResolver.getClassLoader()方法獲得一個ClassLoader的引用,并且利用正常的ClassLoader的api去加載資源,你也可以使用 ResourceLoader
API作為備選方案
1 public abstract class ResourceLoader {
2
3 /**
4 * @see java.lang.ClassLoader#loadClass(java.lang.String)
5 */
6 public static Class loadClass (final String name)throws ClassNotFoundException{
7
8 final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);
9
10 return Class.forName (name, false, loader);
11
12 }
13
14 /**
15
16 * @see java.lang.ClassLoader#getResource(java.lang.String)
17
18 */
19
20
21 public static URL getResource (final String name){
22
23 final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);
24
25 if (loader != null)return loader.getResource (name);
26 else return ClassLoader.getSystemResource (name);
27 }
28 more methods
29
30 } // End of class
而決定使用何種ClassLoader策略是由接口實現的,這是一種插件機制,方便變更。
public interface IClassLoadStrategy{
ClassLoader getClassLoader (ClassLoadContext ctx);
} // End of interface
它需要一個ClassLoader Context 對象去決定使用何種ClassLoader策略。
1 public class ClassLoadContext{
2
3 public final Class getCallerClass (){
4 return m_caller;
5 }
6
7 ClassLoadContext (final Class caller){
8 m_caller = caller;
9
10 }
11
12 private final Class m_caller;
13
14 } // End of class
ClassLoadContext.getCallerClass()返回調用者給ClassLoaderResolver 或者 ResourceLoader,因此能獲得調用者的ClassLoader。需要注意的是,調用者是不會變的 (注:作者使用的final修飾字)。俺的方法不需要對現有的業務方法做擴展,而且可以作為靜態方法是用。而且,你可以根據自己的業務場景實現獨特的ClassLoaderContext。
看出來沒,這是一種很熟悉的設計模式,XD ,把獲得ClassLoader的策略從業務中獨立出來,這個策略可以是"總是用ContextClassLoader"或者"總是用當前ClassLoader"。想預先知道那種策略是正確的比較困難,那么這種模式可以讓你簡單的改變策略。
俺寫了一個默認的實現,基本可以對付95%的場景(enjoy yourself)
1 public class DefaultClassLoadStrategy implements IClassLoadStrategy{
2
3 public ClassLoader getClassLoader (final ClassLoadContext ctx){
4
5 final ClassLoader callerLoader = ctx.getCallerClass ().getClassLoader ();
6
7 final ClassLoader contextLoader = Thread.currentThread ().getContextClassLoader ();
8
9 ClassLoader result;
10 // If 'callerLoader' and 'contextLoader' are in a parent-child
11 // relationship, always choose the child:
12 if (isChild (contextLoader, callerLoader))result = callerLoader;
13 else if (isChild (callerLoader, contextLoader))result = contextLoader;
14 else{
15 // This else branch could be merged into the previous one,
16 // but I show it here to emphasize the ambiguous case:
17 result = contextLoader;
18 }
19 final ClassLoader systemLoader = ClassLoader.getSystemClassLoader ();
20
21
22 // Precaution for when deployed as a bootstrap or extension class:
23 if (isChild (result, systemLoader))result = systemLoader;
24 return result;
25 }
26
27
28
29
more methods 
30
31 } // End of class
32
上面的邏輯比較簡單,如果當前ClassLoader和Context ClassLoader是父子關系,那就總選兒子,根據委托原則,這個很容易理解。
如果兩人平級,選擇正確的ClassLoader很重要,運行時不允許含糊。這種情況下,我的代碼選擇Context ClassLoader(這是俺個人的經驗之談),當然也不要擔心不能改變,你能隨便根據需要改變。一般而言,Context ClassLoader比較適合框架,而Current ClassLoader在業務邏輯中用的更多。
最后,檢查確保選中的ClassLoader不是System ClassLoader的parent,一旦高于System ClassLoader ,請使用System ClassLoader(你的類部署在Ext路徑下面,就會出現這種情況)。
請注意,俺故意沒關注被載入資源的名稱。Java XML API 成為java 核心api的經歷告訴我們,根據資源名稱過濾是很不cool的idea。而且 我也沒有去確認到底哪個ClassLoader被取得了,因為只要清楚原理,這很容易被推理出來。(哈哈,俺是強淫)
盡管討論java 的ClassLoader不是一個很cool的話題(譯者注,當年不cool,但是現在很cool),而且Java EE的ClassLoader策略越發的依賴各種平臺的升級。如果這沒有一個更好的設計的話,將會變成一個大大的問題。不敢您是否同意俺的觀點,俺尊重你說話的權利,所以請給俺分享您的意見經驗。
作者介紹:
Vladimir Roubtsov,曾經使用多種語言有超過13年的編程經歷(恩 現在應該超過15年了 hoho),95年開始接觸java(hoho 俺是99年看的第一本java書)。現在為Trilogy in Austin, Texas開發企業軟件。
翻譯完了,MMD 翻譯還是很麻煩的。 XD ........
43 Things : ClassLoader