2012-11-04 29 views
34

Tôi đang sử dụng Redis Data Redis with Jedis. Tôi đang cố gắng lưu trữ một băm với khóa vc:${list_id}. Tôi đã có thể chèn thành công vào redis. Tuy nhiên khi tôi kiểm tra các phím thông qua redis-cli, tôi không thấy khóa vc:501381. Thay vào đó, tôi thấy \xac\xed\x00\x05t\x00\tvc:501381. Tại sao điều này xảy ra và làm cách nào để thay đổi điều này?Phím redis lạ với dữ liệu mùa xuân Jedis

Trả lời

56

Ok, googled xung quanh một lúc và tìm thấy trợ giúp tại http://java.dzone.com/articles/spring-data-redis.

Điều đó xảy ra do tuần tự hóa Java.

Các serializer then chốt cho redisTemplate cần phải được cấu hình để StringRedisSerializer ví dụ như thế này:

<bean 
    id="jedisConnectionFactory" 
    class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" 
    p:host-name="${redis.server}" 
    p:port="${redis.port}" 
    p:use-pool="true"/> 

<bean 
    id="stringRedisSerializer" 
    class="org.springframework.data.redis.serializer.StringRedisSerializer"/> 

<bean 
    id="redisTemplate" 
    class="org.springframework.data.redis.core.RedisTemplate" 
    p:connection-factory-ref="jedisConnectionFactory" 
    p:keySerializer-ref="stringRedisSerializer" 
    p:hashKeySerializer-ref="stringRedisSerializer" 
/> 

Bây giờ chìa khóa trong redis là vc:501381.

Hoặc như @niconic nói, chúng ta cũng có thể thiết lập các serializer mặc định riêng của mình để serializer chuỗi như sau:

<bean 
    id="redisTemplate" 
    class="org.springframework.data.redis.core.RedisTemplate" 
    p:connection-factory-ref="jedisConnectionFactory" 
    p:defaultSerializer-ref="stringRedisSerializer" 
/> 

có nghĩa là tất cả các khóa và giá trị của chúng tôi là chuỗi. Tuy nhiên, lưu ý rằng điều này có thể không thích hợp hơn, vì bạn có thể muốn các giá trị của bạn không chỉ là các chuỗi.

Nếu giá trị của mình là một đối tượng tên miền, sau đó bạn có thể sử dụng Jackson serializer và cấu hình một serializer như đã đề cập here tức là như thế này:

<bean id="userJsonRedisSerializer" class="org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer"> 
    <constructor-arg type="java.lang.Class" value="com.mycompany.redis.domain.User"/> 
</bean> 

và cấu hình mẫu của bạn như:

<bean 
    id="redisTemplate" 
    class="org.springframework.data.redis.core.RedisTemplate" 
    p:connection-factory-ref="jedisConnectionFactory" 
    p:keySerializer-ref="stringRedisSerializer" 
    p:hashKeySerializer-ref="stringRedisSerializer" 
    p:valueSerialier-ref="userJsonRedisSerializer" 
/> 
+3

Nếu thay đổi tất cả các bộ nối tiếp tham chiếu, có thể hữu ích khi thay đổi trực tiếp bộ nối tiếp mặc định. – reallynice

+0

lưu ý rằng nếu một người đang sử dụng 'convertAndSend' với' redisTemplate' thì 'p: keySerializer' và' hashKeySerializer' là không đủ. thay đổi 'serializer mặc định' đã thực hiện công việc – oak

+1

Cảm ơn! Bởi vì tôi đã sử dụng khởi động mùa xuân và tôi đã giải quyết nó bằng cách tham khảo tài liệu này: http://stackoverflow.com/questions/34201135/spring-redis-read-configuration-from-application-properties-file – zhuguowei

0

Bạn phải serialize các đối tượng mà bạn đang gửi nó đến redis. Dưới đây là ví dụ chạy hoàn chỉnh của nó. Nó sử dụng giao diện DomainObject như Serializable

Dưới đây là các bước

1) làm pom.xml maven bạn với lọ sau

<dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-core</artifactId> 
     <version>${spring.version}</version> 
    </dependency> 

    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-context</artifactId> 
     <version>${spring.version}</version> 
    </dependency> 

    <dependency> 
     <groupId>cglib</groupId> 
     <artifactId>cglib</artifactId> 
     <version>2.2.2</version> 
    </dependency> 

    <dependency> 
      <groupId>org.springframework.data</groupId> 
      <artifactId>spring-data-redis</artifactId> 
      <version>1.3.0.RELEASE</version> 
     </dependency> 

      <dependency> 
       <groupId>redis.clients</groupId> 
       <artifactId>jedis</artifactId> 
       <version>2.4.1</version> 
      </dependency> 

    <dependency> 
     <groupId>org.apache.commons</groupId> 
     <artifactId>commons-pool2</artifactId> 
     <version>2.0</version> 
    </dependency> 

2) làm xml cấu hình của bạn như sau

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p" 
     xmlns:c="http://www.springframework.org/schema/c" 
     xmlns:cache="http://www.springframework.org/schema/cache" 
    xsi:schemaLocation=" 
     http://www.springframework.org/schema/beans  
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-3.0.xsd 

     http://www.springframework.org/schema/cache 
     http://www.springframework.org/schema/cache/spring-cache.xsd"> 



    <bean id="jeidsConnectionFactory" 
     class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" 
     p:host-name="localhost" p:port="6379" p:password="" /> 

    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" 
     p:connection-factory-ref="jeidsConnectionFactory" /> 

    <bean id="imageRepository" class="com.self.common.api.poc.ImageRepository"> 
     <property name="redisTemplate" ref="redisTemplate"/> 
    </bean> 

</beans> 

3) Tạo các lớp học của bạn như sau

package com.self.common.api.poc; 

import java.awt.image.BufferedImage; 
import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.File; 
import java.io.IOException; 

import javax.imageio.ImageIO; 

import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 

import sun.misc.BASE64Decoder; 
import sun.misc.BASE64Encoder; 

public class RedisMainApp { 

public static void main(String[] args) throws IOException { 
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("mvc-dispatcher-servlet.xml"); 
    ImageRepository imageRepository = (ImageRepository) applicationContext.getBean("imageRepository"); 

    BufferedImage img = ImageIO.read(new File("files/img/TestImage.png")); 
    BufferedImage newImg; 
    String imagestr; 
    imagestr = encodeToString(img, "png"); 
    Image image1 = new Image("1", imagestr); 

    img = ImageIO.read(new File("files/img/TestImage2.png")); 
    imagestr = encodeToString(img, "png"); 
    Image image2 = new Image("2", imagestr); 

    imageRepository.put(image1); 
    System.out.println(" Step 1 output : " + imageRepository.getObjects()); 
    imageRepository.put(image2); 
    System.out.println(" Step 2 output : " + imageRepository.getObjects()); 
    imageRepository.delete(image1); 
    System.out.println(" Step 3 output : " + imageRepository.getObjects()); 

} 

/** 
    * Decode string to image 
    * @param imageString The string to decode 
    * @return decoded image 
    */ 
public static BufferedImage decodeToImage(String imageString) { 

    BufferedImage image = null; 
    byte[] imageByte; 
    try { 
     BASE64Decoder decoder = new BASE64Decoder(); 
     imageByte = decoder.decodeBuffer(imageString); 
     ByteArrayInputStream bis = new ByteArrayInputStream(imageByte); 
     image = ImageIO.read(bis); 
     bis.close(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    return image; 
} 

/** 
    * Encode image to string 
    * @param image The image to encode 
    * @param type jpeg, bmp, ... 
    * @return encoded string 
    */ 
public static String encodeToString(BufferedImage image, String type) { 
    String imageString = null; 
    ByteArrayOutputStream bos = new ByteArrayOutputStream(); 

    try { 
     ImageIO.write(image, type, bos); 
     byte[] imageBytes = bos.toByteArray(); 

     BASE64Encoder encoder = new BASE64Encoder(); 
     imageString = encoder.encode(imageBytes); 

     bos.close(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    return imageString; 
} 
} 

package com.self.common.api.poc; 

public class Image implements DomainObject { 

public static final String OBJECT_KEY = "IMAGE"; 

public Image() { 
} 

public Image(String imageId, String imageAsStringBase64){ 
    this.imageId = imageId; 
    this.imageAsStringBase64 = imageAsStringBase64; 
} 
private String imageId; 
private String imageAsStringBase64; 

public String getImageId() { 
    return imageId; 
} 

public void setImageId(String imageId) { 
    this.imageId = imageId; 
} 

public String getImageName() { 
    return imageAsStringBase64; 
} 

public void setImageName(String imageAsStringBase64) { 
    this.imageAsStringBase64 = imageAsStringBase64; 
} 

@Override 
public String toString() { 
    return "User [id=" + imageAsStringBase64 + ", imageAsBase64String=" + imageAsStringBase64 + "]"; 
} 

@Override 
public String getKey() { 
    return getImageId(); 
} 

@Override 
public String getObjectKey() { 
    return OBJECT_KEY; 
} 
} 

package com.self.common.api.poc; 

import java.io.Serializable; 

public interface DomainObject extends Serializable { 

String getKey(); 

String getObjectKey(); 
} 

package com.self.common.api.poc; 

import java.util.List; 

import com.self.common.api.poc.DomainObject; 

public interface Repository<V extends DomainObject> { 

void put(V obj); 

V get(V key); 

void delete(V key); 

List<V> getObjects(); 
} 

package com.self.common.api.poc; 

import java.util.ArrayList; 
import java.util.List; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.data.redis.core.RedisTemplate; 

import com.self.common.api.poc.DomainObject; 

public class ImageRepository implements Repository<Image>{ 

@Autowired 
private RedisTemplate<String,Image> redisTemplate; 

public RedisTemplate<String,Image> getRedisTemplate() { 
    return redisTemplate; 
} 

public void setRedisTemplate(RedisTemplate<String,Image> redisTemplate) { 
    this.redisTemplate = redisTemplate; 
} 

@Override 
public void put(Image image) { 
    redisTemplate.opsForHash() 
    .put(image.getObjectKey(), image.getKey(), image); 
} 

@Override 
public void delete(Image key) { 
    redisTemplate.opsForHash().delete(key.getObjectKey(), key.getKey()); 
} 

@Override 
public Image get(Image key) { 
    return (Image) redisTemplate.opsForHash().get(key.getObjectKey(), 
    key.getKey()); 
} 

@Override 
public List<Image> getObjects() { 
    List<Image> users = new ArrayList<Image>(); 
    for (Object user : redisTemplate.opsForHash().values(Image.OBJECT_KEY)){ 
    users.add((Image) user); 
    } 
    return users; 
} 

} 

Để tham khảo thêm về jedis sprinf bạn có thể thấy http://www.javacodegeeks.com/2012/06/using-redis-with-spring.html

Mẫu Mã được lấy từ http://javakart.blogspot.in/2012/12/spring-data-redis-hello-world-example.html

4

Sử dụng StringRedisTemplate để thay thế RedisTemplate.

Theo mặc định, RedisTemplate sử dụng tuần tự hóa Java, StringRedisTemplate sử dụng StringRedisSerializer.

<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> 
    <property name="connectionFactory" ref="jedisConnectionFactory" /> 
</bean> 
+0

Đây là câu trả lời chính xác. RedisTemplate sử dụng tuần tự hóa Java do đó đảm bảo lớp học của bạn được viết bằng "triển khai thực hiện java.io.Serializable", điều đó là đủ. –

3

Tôi biết câu hỏi này đã được một thời gian, nhưng tôi đã làm một số nghiên cứu về chủ đề này một lần nữa thời gian gần đây, vì vậy tôi muốn chia sẻ cách này "bán băm" chìa khóa được tạo ra bằng cách đi qua một phần của mùa xuân mã nguồn ở đây.

Trước hết, Spring tận dụng AOP để giải quyết chú thích như @Cacheable, @CacheEvict or @CachePut vv Lớp tư vấn là CacheInterceptor từ phụ thuộc ngữ cảnh mùa xuân, là phân lớp của CacheAspectSupport (cũng từ ngữ cảnh mùa xuân). Để dễ giải thích này, tôi sẽ sử dụng @Cacheable làm ví dụ để chuyển qua một phần mã nguồn ở đây.

Khi phương pháp chú thích như @Cacheable được gọi, AOP sẽ tuyến đường nó để phương pháp này protected Collection<? extends Cache> getCaches(CacheOperationInvocationContext<CacheOperation> context, CacheResolver cacheResolver) từ CacheAspectSupport lớp, trong đó nó sẽ cố gắng giải quyết @Cacheable chú thích này. Đổi lại, nó dẫn đến việc triệu gọi phương thức này public Cache getCache(String name) trong CacheManager thực hiện. Đối với giải thích này, việc thực hiện CacheManage sẽ là RedisCacheManager (từ Spring-data-redis dependency).

Nếu bộ nhớ cache không được nhấn, nó sẽ đi trước để tạo bộ nhớ cache. Dưới đây là những phương pháp quan trọng từ RedisCacheManager:

protected Cache getMissingCache(String name) { 
    return this.dynamic ? createCache(name) : null; 
} 

@SuppressWarnings("unchecked") 
protected RedisCache createCache(String cacheName) { 
    long expiration = computeExpiration(cacheName); 
    return new RedisCache(cacheName, (usePrefix ? cachePrefix.prefix(cacheName) : null), redisOperations, expiration, 
      cacheNullValues); 
} 

Về cơ bản, nó sẽ tạo một đối tượng RedisCache. Để làm điều này, nó yêu cầu 4 tham số, cụ thể là, cacheName, prefix (đây là tham số chính liên quan đến trả lời câu hỏi này), redisOperation (aka, redisTemplate), hết hạn (mặc định là 0) và cacheNullValues ​​(mặc định là sai). Hàm khởi tạo bên dưới hiển thị thêm chi tiết về RedisCache.

/** 
* Constructs a new {@link RedisCache} instance. 
* 
* @param name cache name 
* @param prefix must not be {@literal null} or empty. 
* @param redisOperations 
* @param expiration 
* @param allowNullValues 
* @since 1.8 
*/ 
public RedisCache(String name, byte[] prefix, RedisOperations<? extends Object, ? extends Object> redisOperations, 
     long expiration, boolean allowNullValues) { 

    super(allowNullValues); 

    Assert.hasText(name, "CacheName must not be null or empty!"); 

    RedisSerializer<?> serializer = redisOperations.getValueSerializer() != null ? redisOperations.getValueSerializer() 
      : (RedisSerializer<?>) new JdkSerializationRedisSerializer(); 

    this.cacheMetadata = new RedisCacheMetadata(name, prefix); 
    this.cacheMetadata.setDefaultExpiration(expiration); 
    this.redisOperations = redisOperations; 
    this.cacheValueAccessor = new CacheValueAccessor(serializer); 

    if (allowNullValues) { 

     if (redisOperations.getValueSerializer() instanceof StringRedisSerializer 
       || redisOperations.getValueSerializer() instanceof GenericToStringSerializer 
       || redisOperations.getValueSerializer() instanceof JacksonJsonRedisSerializer 
       || redisOperations.getValueSerializer() instanceof Jackson2JsonRedisSerializer) { 
      throw new IllegalArgumentException(String.format(
        "Redis does not allow keys with null value ¯\\_(ツ)_/¯. " 
          + "The chosen %s does not support generic type handling and therefore cannot be used with allowNullValues enabled. " 
          + "Please use a different RedisSerializer or disable null value support.", 
        ClassUtils.getShortName(redisOperations.getValueSerializer().getClass()))); 
     } 
    } 
} 

Vì vậy, việc sử dụng prefix trong RedisCache này là gì? -> Như đã trình bày trong các nhà xây dựng về, nó được sử dụng trong bản Tuyên Bố này this.cacheMetadata = new RedisCacheMetadata(name, prefix);, và các nhà xây dựng của RedisCacheMetadata dưới đây cho thấy thêm chi tiết:

/** 
    * @param cacheName must not be {@literal null} or empty. 
    * @param keyPrefix can be {@literal null}. 
    */ 
    public RedisCacheMetadata(String cacheName, byte[] keyPrefix) { 

     Assert.hasText(cacheName, "CacheName must not be null or empty!"); 
     this.cacheName = cacheName; 
     this.keyPrefix = keyPrefix; 

     StringRedisSerializer stringSerializer = new StringRedisSerializer(); 

     // name of the set holding the keys 
     this.setOfKnownKeys = usesKeyPrefix() ? new byte[] {} : stringSerializer.serialize(cacheName + "~keys"); 
     this.cacheLockName = stringSerializer.serialize(cacheName + "~lock"); 
    } 

Tại thời điểm này, chúng ta biết rằng một số tham số tiền tố đã được thiết lập để RedisCacheMetadata , nhưng tiền tố này được sử dụng như thế nào để tạo khóa trong Redis (ví dụ: \ xac \ xed \ x00 \ x05t \ x00 \ tvc: 501381 như bạn đã đề cập)?

Về cơ bản, CacheInterceptor sẽ sau đó di chuyển về phía trước để gọi một phương pháp private RedisCacheKey getRedisCacheKey(Object key) từ các đối tượng nêu trên RedisCache, mà trả về một thể hiện của RedisCacheKey bằng cách sử dụng tiền tố từ RedisCacheMetadata và keySerializer từ RedisOperation.

private RedisCacheKey getRedisCacheKey(Object key) { 
    return new RedisCacheKey(key).usePrefix(this.cacheMetadata.getKeyPrefix()) 
      .withKeySerializer(redisOperations.getKeySerializer()); 
} 

Bằng đạt thời điểm này, "trước" lời khuyên của CacheInterceptor được hoàn thành, và nó sẽ đi trước để thực hiện các phương pháp thực tế chú thích bởi @Cacheable. Và sau khi hoàn thành việc thực hiện phương pháp thực tế, nó sẽ thực hiện lời khuyên "post" của CacheInterceptor, về cơ bản đặt kết quả cho RedisCache. Dưới đây là phương pháp đưa kết quả đến redis cache:

public void put(final Object key, final Object value) { 

    put(new RedisCacheElement(getRedisCacheKey(key), toStoreValue(value)) 
      .expireAfter(cacheMetadata.getDefaultExpiration())); 
} 

/** 
* Add the element by adding {@link RedisCacheElement#get()} at {@link RedisCacheElement#getKeyBytes()}. If the cache 
* previously contained a mapping for this {@link RedisCacheElement#getKeyBytes()}, the old value is replaced by 
* {@link RedisCacheElement#get()}. 
* 
* @param element must not be {@literal null}. 
* @since 1.5 
*/ 
public void put(RedisCacheElement element) { 

    Assert.notNull(element, "Element must not be null!"); 

    redisOperations 
      .execute(new RedisCachePutCallback(new BinaryRedisCacheElement(element, cacheValueAccessor), cacheMetadata)); 
} 

Trong đối tượng RedisCachePutCallback, callback method của nó doInRedis() thực sự gọi một phương pháp để tạo phím thực tế trong redis, và tên phương pháp là getKeyBytes() từ RedisCacheKey dụ.Dưới đây cho thấy các chi tiết của phương pháp này:

/** 
* Get the {@link Byte} representation of the given key element using prefix if available. 
*/ 
public byte[] getKeyBytes() { 

    byte[] rawKey = serializeKeyElement(); 
    if (!hasPrefix()) { 
     return rawKey; 
    } 

    byte[] prefixedKey = Arrays.copyOf(prefix, prefix.length + rawKey.length); 
    System.arraycopy(rawKey, 0, prefixedKey, prefix.length, rawKey.length); 

    return prefixedKey; 
} 

Như chúng ta có thể thấy trong các phương pháp getKeyBytes, nó sử dụng cả hai phím thô (vc: 501.381 trong trường hợp của bạn) và chìa khóa tiền tố (\ xac \ cố định \ x00 \ x05t \ x00 \ t trong trường hợp của bạn).

Các vấn đề liên quan