Kryo 序列化历程

使用的版本是 Kryo-4.0.0

背景: 使用redis 进行存储对象,随着业务不断的攀升,redis 的空间占用率增长迅猛.读取数据时引发了网卡流量超过1m等问题. 因此引入序列话+压缩比高的技术,对要存储的对象进行压缩后存储.

相关资料:

https://github.com/EsotericSoftware/kryo
各种序列化方式的性能、空间比较
https://github.com/eishay/jvm-serializers/wiki

1,选择序列化方式

FieldSerializer 速度快,压缩比高,缺点是不能向前,向后兼容
VersionFieldSerializer : 可以向后兼容,但不能向前兼容,也就是类可以扩展一个字段 ,扩展后仍然能读取线上的缓存,但是新序列化的数据用旧的对象读取 会报错
CompatibleFieldSerializer :可以向前,向后兼容,但是会稍慢

最后结合项目中要存储对象 变更频率非常低的特性,决定使用 FieldSerializer

 

2,实现了第一个版本后测试 测试发现类没有构造函数导致反序列化失败

因此增加了通用的对无默认构造函数的扩展支持kryox

http://my.oschina.net/yangming0322/blog/739882

引入kryox

 private ThreadLocal<Kryo> holder = new ThreadLocal<Kryo>() {
    @Override
    protected Kryo initialValue() {
      return newKryo();
    }
  };

  /**
   * 返回新的kryo
   * 
   * @return
   */
  private Kryo newKryo() {
    Kryox kryo = new Kryox();
    // set property
    kryo.setDefaultSerializer(serializerClass);
    UnmodifiableCollectionsSerializer.registerSerializers( kryo );
    SynchronizedCollectionsSerializer.registerSerializers( kryo );
    return kryo;
  }

引入kryox 后发现新的问题 序列化的性能降低,经过一顿分析折腾后 ,遭到原因,就是无默认构造函数的类,反序列化,虽然通过kryx 已经能支持. 但是效率和有构造函数对比4:1 左右,代价不容小觑,尤其是针对访问量特别大的类来说.直接导致了系统性能的大幅下降.

 

3,去掉kryox ,给无构造函数的类增加包装类, 所有引用的地方 存储和读取时进行转换修改.

ok ,性能和压缩比都提升上来了.

效果:

161052_pXBt_1182033.png

总结:  kryo 在此业务中的压缩比在 70% ,单个用户对象整合引用关系后可以减少1k左右

4,现在进入了第四阶段,批量压力测试.

 a.不断的发现,没有构造函数的类报错,逐一增加包装类.

 b.实体类中使用了UUID 类型(无默认构造函数),//解决方案就是替换成String 当然类比较少所以算是简单应付.,因为修改了类的定义,所以上线的一刻会有老数据反序列化异常的风险,经过评估,增加了一个控制.如果遇到反序列化异常,就重新读取db加载覆盖到缓存中.

 c.部分类 序列化后不能反序列化  类中有特殊的属性 如: 

155618_TNKF_1182033.png

此类在反序列化时 提示:

com.esotericsoftware.kryo.KryoException: Unable to find class: OLE_USER
Serialization trace:
authorities (com.chanjet.csp.boss.cia.security.wrap.OAuth2AuthenticationWrap)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readName(DefaultClassResolver.java:160)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readClass(DefaultClassResolver.java:133)
    at com.esotericsoftware.kryo.Kryo.readClass(Kryo.java:693)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:804)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:134)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:40)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:731)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:125)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:540)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:813)
    at com.chanjet.infrastructure.cache.impl.KryoJedisCacheServiceImpl.deserializeValue(KryoJedisCacheServiceImpl.java:159)
    at com.chanjet.infrastructure.cache.impl.KryoJedisCacheServiceImpl.get(KryoJedisCacheServiceImpl.java:226)
    at com.chanjet.infrastructure.cache.impl.JedisCacheServiceImplProxy.get(JedisCacheServiceImplProxy.java:62)
    at com.chanjet.csp.boss.cia.security.authentication.CiaAuthenticationManager.getOAuthentication(CiaAuthenticationManager.java:56)
    at com.chanjet.csp.boss.cia.security.authentication.CiaAuthenticationManager.getReleatedOAuth2AuthenticationByKey(CiaAuthenticationManager.java:50)
    at com.chanjet.csp.boss.cia.security.oauth2.token.CacheTokenStore.readAuthentication(CacheTokenStore.java:76)
    at com.chanjet.csp.boss.cia.security.oauth2.token.CacheTokenStore.getAccessToken(CacheTokenStore.java:61)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:198)
    at com.sun.proxy.$Proxy76.getAccessToken(Unknown Source)
    at com.chanjet.csp.boss.cia.security.oauth2.token.DefaultTokenServices._createAccessToken(DefaultTokenServices.java:124)
    at com.chanjet.csp.boss.cia.security.oauth2.token.DefaultTokenServices.createAccessToken(DefaultTokenServices.java:116)
    at org.springframework.security.oauth2.provider.token.AbstractTokenGranter.getAccessToken(AbstractTokenGranter.java:68)
    at org.springframework.security.oauth2.provider.token.AbstractTokenGranter.grant(AbstractTokenGranter.java:60)
    at com.chanjet.csp.boss.cia.api.AuthenticationApi.clientAuthenticationWithUserInfo(AuthenticationApi.java:1219)
    at com.chanjet.csp.boss.cia.api.AuthenticationApi$$FastClassByCGLIB$$8943a6dc.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
    at com.chanjet.csp.boss.cia.aop.log.MqHandlerAspect.aroundAdvice(MqHandlerAspect.java:42)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
    at com.chanjet.csp.boss.cia.aop.log.ApiLogHandlerAspect.aroundAdvice(ApiLogHandlerAspect.java:119)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
    at com.chanjet.csp.boss.cia.api.AuthenticationApi$$EnhancerByCGLIB$$b471f4e.clientAuthenticationWithUserInfo(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:768)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:703)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1148)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1148)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:387)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417)
    at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
    at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:534)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:879)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:747)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:520)
Caused by: java.lang.ClassNotFoundException: OLE_USER
    at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.unsynchronizedLoadClass(ClassRealm.java:259)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:235)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:227)
    at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:401)
    at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:363)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readName(DefaultClassResolver.java:154)
    ... 115 more

 

(插曲:  在解决序列化方案的问题,我们同时分析了一下,现有的redis 使用模式.发现有些对象重复的写入到redis.这样体积偏大. 

于是穿插增加对 业务代码的修改,对象只写入一次,其他地方存 引用关系.

使用的时候两次查找.)

这个问题终于解决了 ,问题的表象是反序列化失败, 实际是序列化和反序列化时用的kryo版本不一致导致.

具体为a工程序列化的结果 b工程反序列化失败,但是a自己可以反序列化(呃.算是废话吧.)

分析发现编译后有kryo3个版本

kryo2.24.0,kryo-3.0.3,kryo-4.0.0

工程依赖了dubbo dubbo默认依赖了kryo2.24.0

工程依赖了kryo-serializers-0.38 ,kryo-serializers 依赖了 kryo-3.0.3

最终解决方案:

工程依赖了kryo-serializers,他又依赖了kryo-3.0.3 ,我的工程要依赖4.0.0 于是出现三个依赖. 导致出现诡异的现象,所以最终的解决方案是在pom中声明去掉 dubbo 和 kryo-serializers,只保留4.0.0

备注: 因为工程中统一使用kryo4.0.0, 已经去掉了dubbo 的kryo依赖, 此时要注意所有dubbo工程中最好使用其他的序列化包,避免出现kryo 因此的工程间序列化问题(kryo 各版本间是有兼容风险的)

待续,不断的踩雷中  如果有大侠遇到过此类问题,请指路 多谢 ..... 

 

转载于:https://my.oschina.net/yangming0322/blog/739881

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值