惨淡人生,平淡生活

The Feature Is Stupid

2008年5月30日 #

实现web服务的三个误区 读后感

我的消息吃了我的服务器!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模式将鼓励你的服务被重用。






posted @ 2009-03-16 14:55 季失羽 阅读(217) | 评论 (0)编辑 收藏

如何看懂Java混淆后的反编译代码(转)

如何看懂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);

 }

posted @ 2009-03-03 09:59 季失羽 阅读(1640) | 评论 (0)编辑 收藏

[翻译]走出ClassLoader的迷宫

[说明]几个关键字将不翻译

  1. ClassLoader
  2. System
  3. Context
  4. 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 :

posted @ 2008-05-30 18:22 季失羽 阅读(5286) | 评论 (0)编辑 收藏