spring-data-redis使用自定义序列化数据 使用 protobuf

来源:互联网 发布:可以ps图片的软件 编辑:程序博客网 时间:2024/05/18 03:16
一、 spring-data-redis序列化策略

spring-data-redis提供了多种serializer策略,这对使用jedis的开发者而言,实在是非常便捷。sdr提供了4种内置的serializer:

  • JdkSerializationRedisSerializer:使用JDK的序列化手段(serializable接口,ObjectInputStrean,ObjectOutputStream),数据以字节流存储,jdk序列化和反序列化数据
  • StringRedisSerializer:字符串编码,数据以string存储
  • JacksonJsonRedisSerializer:json格式存储
  • OxmSerializer:xml格式存储

其中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的序列化策略,其中“JacksonJsonRedisSerializer”与“OxmSerializer”都是基于stirng存储,因此它们是较为“高级”的序列化(最终还是使用string解析以及构建java对象)。

RedisTemplate中需要声明4种serializer,默认为“JdkSerializationRedisSerializer”:

1) keySerializer :对于普通K-V操作时,key采取的序列化策略
2) valueSerializer:value采取的序列化策略
3) hashKeySerializer: 在hash数据结构中,hash-key的序列化策略
4) hashValueSerializer:hash-value的序列化策略

无论如何,建议key/hashKey采用StringRedisSerializer。 这redis服务端用命令行好查看 配置如下:

< ! --   redis  template definition -- >      < bean   id = "redisTemplate"             class = "org.springframework.data.redis.core.RedisTemplate"            p : connection - factory - ref = "jedisConnFactory"     scope = "prototype" >          < ! -- 使用string主要是key 在 redis 端用命令好读 不然默认的序列化没办法读 -- >          < property   name = "keySerializer" >              < bean    class = "org.springframework.data.redis.serializer.StringRedisSerializer"     / >          < / property >          < property   name = "hashKeySerializer" >              < bean    class = "org.springframework.data.redis.serializer.StringRedisSerializer"     / >          < / property >      < / bean >   

二、 自定义序列化处理效果

默认是JdkSerializationRedisSerializer存储,占用空间比较大,如图:


类字段信息,字段信息都在里面,如果对于大数据量存储,内存成本很高。 其他的序列化存储也是类似,只是稍微少点。这里使用google protobuf改造序列化。改造后存储如下:


可以发现存储数据减少一半以上。


三、 自定义序列化处理过程

 1、添加自定义序列化处理类
主要是实现RedisSerializer接口,使用protocbuf,就需要使用它的序列化方式,serialize对应protocbuf生成类的toByteArray方法,deserialize对应protocbuf生成类的parseFrom方法,代码如下:
package org.springframework.data.redis.serializer; import java.lang.reflect.Method; import com.google.protobuf.GeneratedMessage; /*** @date 2013年12月27日 上午11:18:23 * @version V1.0  * @param <T>* @Description: protocbuf 序列化数据 减少存储空间* 存在问题,必须设置对应type,这样的话RedisTemplate单例就没有办法是用了!,是用多例 *     <bean id="redisTemplate"        class="org.springframework.data.redis.core.RedisTemplate"        p:connection-factory-ref="jedisConnFactory"  scope="prototype">*/public class ProtocbufRedisSerializer < T >    implements RedisSerializer < T > {      private Class < T > type;      public ProtocbufRedisSerializer(Class < T > type){         this .type = type;    }    @Override     public byte [] serialize(Object t) throws SerializationException {         if (t == null) {             return SerializationUtils.EMPTY_ARRAY;        }         try {            GeneratedMessage gm = (GeneratedMessage)t;             return gm.toByteArray();        } catch (Exception ex) {             throw new SerializationException( "Cannot serialize" , ex);        }    }     @SuppressWarnings( "unchecked" )    @Override     public T deserialize( byte [] bytes) throws SerializationException {         if (SerializationUtils.isEmpty(bytes)) {             return null;        }         try {                  Method method = type.getMethod( "parseFrom" , new Class[]{bytes.getClass()});                return (T)method.invoke(type, new Object[]{bytes});        } catch (Exception ex) {             throw new SerializationException( "Cannot deserialize" , ex);        }    }     public Class < T > getType() {         return type;    }     public void setType(Class < T > type) {         this .type = type;    }}   

2、使用protobuf序列化处理
应为在deserialize时候需要对应类的类型,所以这里反序列化需要使用对应的解析类来处理。这样导致一个问题,我们没有办法使用单例去配置,因为每个解析类都不一样。所以在配置的时候需要使用多例。scope="prototype",在应用中代码如下:

package com.vrv.im.service.impl.helper; import java.util.ArrayList;import java.util.List; import javax.annotation.Resource; import org.springframework.beans.BeanUtils;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.ProtocbufRedisSerializer;import org.springframework.stereotype.Component; import com.vrv.im.domain.IMCommentSubject;import com.vrv.im.protoc.Comment.CommentSubjectInfo; /*** @date 2013年12月26日 上午11:47:22 * @version V1.0  * @param <K>* @param <V>* @Description: 缓存新鲜事的最近两条评论,所有的新鲜事,* protocbuf序列化压缩数据* 存在问题,必须设置对应type,这样的话RedisTemplate单例就没有办法是用了!,是用多例 *     <bean id="redisTemplate"        class="org.springframework.data.redis.core.RedisTemplate"        p:connection-factory-ref="jedisConnFactory"  scope="prototype">*/@Componentpublic class CommentInfoCacheHandlerForProtocbuf<K, V> {     @Resource     private RedisTemplate<String, CommentSubjectInfo> template;      private String commentInfoKey="commentInfo";      /**       * 获取缓存评论       * @param subjectCommentID       * @return       */      @SuppressWarnings({ "unchecked", "rawtypes" })    public List<IMCommentSubject> getCommentInfoCache(long subjectCommentID){          List<IMCommentSubject>  commentList = new ArrayList<IMCommentSubject>();          template.setValueSerializer(new ProtocbufRedisSerializer(CommentSubjectInfo.class));          List<CommentSubjectInfo> list=  template.opsForList().range(commentInfoKey+"_"+subjectCommentID, 0, -1);//获取所有          if(list==null)             list= new ArrayList<CommentSubjectInfo>();          for(CommentSubjectInfo info :list){              IMCommentSubject subject = new IMCommentSubject();              BeanUtils.copyProperties(info, subject);              commentList.add(subject);          }          return commentList;      }      /**       * 添加缓存信息       * @param commentInfo       */      public void addCommentInfoCache(IMCommentSubject commentInfo){          template.setValueSerializer(new ProtocbufRedisSerializer(CommentSubjectInfo.class));         CommentSubjectInfo inf= CommentSubjectInfo.newBuilder()                 .setSubjectCommentID(commentInfo.getSubjectCommentID())                 .setCommentID(commentInfo.getCommentID())                 .setCommentUser(commentInfo.getCommentUser())                .setCreateTime(commentInfo.getCreateTime())                .setComment(commentInfo.getComment())                .setReplyToUser(commentInfo.getReplyToUser())                .build();         template.opsForList().leftPush(commentInfoKey+"_"+commentInfo.getSubjectCommentID(), inf);         template.opsForList().trim(commentInfoKey+"_"+commentInfo.getSubjectCommentID(), 0, 1);//保留2条      }      /**       * 删除评论主体缓存       * @param subjectCommentID       */     public void deleteCommentInfoCache(long subjectCommentID){          template.delete(commentInfoKey+"_"+subjectCommentID);//所有都删除     }       /**       * 删除评论主体的某条评论       * @param subjectCommentID       */     public void deleteCommentInfoCache(IMCommentSubject commentInfo){         template.setValueSerializer(new ProtocbufRedisSerializer(CommentSubjectInfo.class));         CommentSubjectInfo inf= CommentSubjectInfo.newBuilder()                 .setSubjectCommentID(commentInfo.getSubjectCommentID())                 .setCommentID(commentInfo.getCommentID())                 .setCommentUser(commentInfo.getCommentUser())                .setCreateTime(commentInfo.getCreateTime())                .setComment(commentInfo.getComment())                .setReplyToUser(commentInfo.getReplyToUser())                .build();         template.opsForList().remove(commentInfoKey+"_"+commentInfo.getSubjectCommentID(), 0, inf);//相等的评论信息     }}

   附proto文件
package com.vrv.im.protoc;//评论信息类message CommentSubjectInfo{  required sint64 subjectCommentID = 1;  required sint64 commentUser = 2;  required sint64 replyToUser = 3;  required string comment = 4;  required sint64 commentID = 5;  required sint64 createTime = 6; }

三、 遇到问题
RedisTemplate类型支持不全
默认:

 private  ValueOperations<K, V>  valueOps ;     private  ListOperations<K, V>  listOps ;     private   SetOperations <K, V>  setOps ;          private  ZSetOperations<K, V>  zSetOps ;

这样在使用上面的方式的时候,如果遇到hash类型,就没有办法设置setValueSerializer,尝试过用
 @Resource (name= "redisTemplate" )               private  HashOperations<String,String,Long>  subjectCommentNumHash


发现HashOperations类型根本就没有办法获取到RedisTemplate,这样也就没有办法设置setValueSerializer了。再想想是否可以new一个呢,结果发现DefaultHashOperations是包内可见,哎,只有改源码,或者建一个新类 extends DefaultHashOperations了
现在spring-data-redis还不完善,需要改进了。

0 0