`
agapple
  • 浏览: 1582972 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

并行加载实施中遇到的问题

阅读更多

背景

  最近着手在一个已有的应用中实施并行加载技术,整理记录一下过程中遇到的问题,方便以后查阅。

 

关于并行加载可以访问:  (业务层)异步并行加载技术分析和设计

 

姊妹篇: (业务层)异步并行加载ChangeLog

 

问题集

1. ThreadLocal不支持 

原理分析: 因为并行加载,导致原先的代码快使用了新的独立的线程进行加载。导致原先代码中使用了ThreadLocal失效。

应用分析: 使用的ThreadLocal有几处地方

 

  *  request/response。 需要在biz层直接操作Http请求对象,web/biz层需要隔离,所以将其包装到ThreadLocal中。 

  *  cache机制。 应用中为了解决权限控制,会一次性通过rpc调用获取remote的权限数据。然后放在ThreadLocal中,web/biz,模板上直接获取ThreadLocal的数据。

  *  重复请求对象。  应用中是一个模块化的设计,为了减少耦合性。两模块直接不允许传递直接数据,但一些会员对象基本每个模块都需要依赖,所以需要进行控制,避免出现重复加载。

解决与否: 已解决,Change 2中已实现

 

2. annotation数据丢失

原理分析:因为并行加载,采用了cglib生成了代理对象,新的class类。

应用分析:一般使用annotation存在几处地方。

 *  参数绑定功能。 现在的MVC框架,包括公司自己的web框架,都喜欢使用在web层进行参数绑定,通过定义一个method参数级的annotation,给定具体的name,框架进行自动注入属性。

 *  spring的一些annotation,比如autowire,transtional等。

 

解决与否: 并不直接正面解决annotaion丢失,而是提供util类允许获取原始的代理对象,从中提取到annotaion信息。

备注: 测试过程中,不仅仅annotation数据丢失,generic泛型信息,field属性都存在丢失。所以针对class对象的一些操作,都是建议直接操作原始class。

 

3. 两次cglib代理,出现final类无法生成代理类

原理分析:因为并行加载,出于性能考虑采用了cglib字节码增强技术。还有一点,jdk代理是依赖必须有接口类,所以综合考虑选择了cglib代理。 

应用分析: final对象存在的几处地方

  *  原先的一些rpc调用,已经通过spring生成了一次代理,部分是使用jdk或者cglib。 通过设置proxyTargetClass=true/false属性。

  *  部分class类,被强制声明为final,避免子类继承

  *  原先应用中部署了一些profile信息,通过拦截器记录一些调用次数和时间等,也是使用了cglib。

 

解决与否: 没办法正面解决,无解的问题。 只能是通过注意使用层面进行规避。

建议的使用: 

 * 使用基于spring advise的并行加载机制,而不是代理整个service。 说白了,就是在原先的代理基础上,新增一个并行加载的拦截器。

 * 使用基于template+callback模板模式的并行加载机制,实现AsyncLoadCallback,传递一代码块。在这代码块中,你可以委托你的service(可能是被cglib代理过的)进行调用。比较嵌入业务

 

4. 缺少默认的构造函数,无法通过newInstance()实例化对象

原理分析:当前一些反射机制

应用分析: 

 *  没有默认的构造函数,新增了自定义的构造函数,特别是在一些model对象。

 

解决与否: 因为如果构造函数中对传递的参数有依赖时,比如进行一些业务判断。目前只是尽可能的去尝试解决,针对带参数的构造函数,尝试给出一些默认值。

 

5. 空指针判断( == )

原理分析: 为实现异步并行加载机制,在调用时会立马返回一个假的返回对象。也就是这个返回对象永远不会为null

应用分析: 

  *  应用中存在比较大量的,在进行一次service调用后,会针对其返回结果进行一次==null判断,避免所谓的NPE。

 

解决与否: 没法从根本上解决该问题,只能是规范和明确并行加载机制的适用范围。建议是:service在model对象默认不为空。

备注:

 *  提供了AsyncLoadUtils.isNull() 替换原先的==null判断方法。有一定的侵入性。

 *  使用AsyncLoadTempalte机制,自定义一个新的并行加载单元,将==null判断包在一个新的AsyncLoadCallback中

 

6. 返回对象为Object,运行过程进行类型强制转化

原理分析: 异步加载,需要在调用时立马返回一个假的对象,会根据当前method的返回值生成代理对象。

应用分析: 

  *  在应用接口中,为了实现所谓的扩展,会以很泛化的Object对象进行结果对象传递。然后通过instanceof在运行时进行强制转化

 

解决与否: 已解决,在扫描到返回对象如果为Object,进行直接调用。 另外:可以通过AsyncLoadTempalte机制,强制指定目标的真实返回对象

 

=========================================2010-04-08更新

7. 并发加载产生的问题: 

异常信息1:

 

Caused by: java.lang.NullPointerException
        at sun.reflect.GeneratedMethodAccessor208.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at com.alibaba.china.searchengine.dal.dao.search.GeneralSearchImpl$1.intercept(GeneralSearchImpl.java:322)
        at com.alibaba.china.searchengine.dal.dao.dto.OfferSearchResult$$EnhancerByCGLIB$$5dc08842.getComplexResult(<generated>)
        at com.alibaba.kylin.biz.esite.bo.dataprovider.offer.AbstractOfferDataFeeder.searchRecommendOffers(AbstractOfferDataFeeder.java:251)
        at com.alibaba.kylin.biz.esite.bo.dataprovider.winport.AbstractGlareRecOfferDataProvider.getData(AbstractGlareRecOfferDataProvider.java:117)
        at com.alibaba.kylin.biz.esite.bo.dataprovider.winport.GlareRecOffer2DataProvider$$FastClassByCGLIB$$21ffc53c.invoke(<generated>)
        at net.sf.cglib.reflect.FastMethod.invoke(FastMethod.java:53)
        at com.alibaba.kylin.biz.common.dataresolver.FunctionObject.apply(FunctionObject.java:89)
        ... 58 more

 

异常信息2:

Caused by: java.util.ConcurrentModificationException
        at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
        at java.util.AbstractList$Itr.next(AbstractList.java:343)
        at com.alibaba.china.searchengine.dal.dao.search.GeneralSearchImpl$1.intercept(GeneralSearchImpl.java:298)
        at com.alibaba.china.searchengine.dal.dao.dto.OfferSearchResult$$EnhancerByCGLIB$$5dc08842.getComplexResult(<generated>)
        at com.alibaba.kylin.biz.esite.bo.dataprovider.offer.AbstractOfferDataFeeder.searchRecommendOffers(AbstractOfferDataFeeder.java:251)
        at com.alibaba.kylin.biz.esite.bo.dataprovider.winport.AbstractGlareRecOfferDataProvider.getData(AbstractGlareRecOfferDataProvider.java:117)
        at com.alibaba.kylin.biz.esite.bo.dataprovider.winport.GlareRecOffer2DataProvider$$FastClassByCGLIB$$21ffc53c.invoke(<generated>)
        at net.sf.cglib.reflect.FastMethod.invoke(FastMethod.java:53)
        at com.alibaba.kylin.biz.common.dataresolver.FunctionObject.apply(FunctionObject.java:89)
        ... 58 more

 

异常信息3:

Caused by: java.lang.IllegalStateException: Current state = RESET, new state = FLUSHED
        at java.nio.charset.CharsetDecoder.throwIllegalStateException(CharsetDecoder.java:951)
        at java.nio.charset.CharsetDecoder.flush(CharsetDecoder.java:640)
        at java.lang.StringCoding$StringDecoder.decode(StringCoding.java:143)
        at java.lang.StringCoding.decode(StringCoding.java:173)
        at java.lang.StringCoding.decode(StringCoding.java:185)
        at java.lang.String.<init>(String.java:570)
        at java.io.ByteArrayOutputStream.toString(ByteArrayOutputStream.java:163)
        at com.danga.MemCached.SockIOPool$SockIO.readLine(SockIOPool.java:1595)
        at com.danga.MemCached.MemCachedClient.loadItems(MemCachedClient.java:1470)
        at com.danga.MemCached.MemCachedClient.get(MemCachedClient.java:1223)
        at com.danga.MemCached.MemCachedClient.get(MemCachedClient.java:1152)

 

说明:

1. 异常信息1和2,都是因为search的设计引起的。目前search是采用了lazyTask的加载模式,会尝试合并多次查询请求,并将查询请求lazyTask放到ThreadLocal中。而使用了并行加载后,首先多个work thread都能共享这个ThreadLocal,导致多个work thread同时都在处理请求,从而导致各种各样的异常。

2.  StringCoding代码中,使用了ThreadLocal decoder = new ThreadLocal()进行数据对象cache,因为并行加载进行threadLocal共享,所以导致不同线程使用了同一个decoder对象,从而出现线程安全问题。

 

悲剧阿,两个都是因共享ThreadLocal引出来的一个问题。

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics