Prefácio
A mola encapsula um modelo relativamente poderoso para facilitar o uso no uso de redis; Eu usei o Redis no ecossistema da primavera antes, mas usei diretamente JEDIS para as interações correspondentes. Agora, vamos dar uma olhada em como o Redistemplate é implementado e se é mais conveniente de usar.
Jedis ainda é usado para gerenciamento de pool de conexões; portanto, além de introduzir a mola-dados-redis, além de dependências JEDIS, adicione o arquivo POM
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.8.4.RELEASE</version></dependency><dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version></dependency>
Se você precisar especificar parâmetros relacionados à serialização, também poderá apresentar Jackson. Este artigo é simples de entrada, então você não adicionará isso
Prepare os parâmetros de configuração relacionados ao Redis, os comuns incluem host, porta, senha, tempo limite ... a seguir, é uma configuração simples e fornece os significados correspondentes.
Redis.HostName = 127.0.0.1redis.port = 6379redis.password = https: //blog.hhui.top# Timeout de conexão Redis.Timeout = 10000#máximo de número interno Redis.Maxidle = 300#Controla o número de instantações de Jedis. Se for Jedis 2.4, use esta propriedade para redis.maxtotal = 1000#Estabelecimento de conexão máxima Tempo de espera. Se esse tempo exceder desta vez, uma exceção será recebida. Definido como -1 significa nenhum limite. Redis.maxwaitmillis = 1000#O tempo mínimo ocioso da conexão é padrão 180000ms (30 minutos) Redis.MineVictableIdLeTEMEMILLIS = 300000#O número máximo de conexões é liberado a cada vez que o intervalo de Evicting 3red.NumTestsperevictionRun = 1024#o intervalo de tempo para o SCANSTING 3 Padrão -1redis.TimeBetweenEvictionRunsmillis = 30000# Se você deve verificar antes de remover a conexão do pool, se a verificação falhar, remova a conexão do pool e tente remover outro redis.testonborrow = true# Validade de verificação quando ocioso, default falsedis.testwhileidle = verdadeiro#
ilustrar
Lembre -se de definir a senha do Redis, especialmente ao permitir acesso remoto. Se você não tiver uma senha, o número da porta padrão será facilmente digitalizado e injetado no script e depois começará a minerar para as pessoas (experiência pessoal ...)
De acordo com a idéia geral, primeiro, você deve carregar a configuração acima, criar um pool de conexão Redis e depois instanciar o objeto Redistemplate e, finalmente, manter essa força para iniciar várias operações de leitura e gravação
Use Javaconfig para configurar, principalmente dois feijões, leia o arquivo de configuração para definir vários parâmetros e o RedisteMplate esperado
@Configuration @PropertySource ("ClassPath: Redis.Properties") Classe public Redisconfig estende Jcacheconfigurersupport {@Autowired Private Environment Ambiente; @Bean public RedisconnectionFactory RedisconnectionFactory () {JedisconnectionFactory FAC = novo JedisconnectionFactory (); FAC.SethostName (Environment.GetProperty ("Redis.Hostname")); FAC.SetPort (Integer.parseint (Environment.getProperty ("Redis.port"))); FAC.SetPassword (Environment.getProperty ("Redis.Password")); FAC.SetTimeout (Integer.Parseint (Environment.getProperty ("Redis.Timeout"))); FAC.GetPoolConfig (). Setmaxidle (Integer.Parseint (Environment.getProperty ("Redis.maxidle"))); FAC.GetPoolConfig (). Setmaxtotal (Integer.Parseint (Environment.getProperty ("Redis.maxtotal"))); FAC.getPoolConfig (). SetmaxwaitMillis (Integer.parseint (Environment.getProperty ("Redis.maxwaitmillis"))); FAC.getPoolConfig (). SetMineVictableIdletimemillis (Integer.Parseint (Environment.getProperty ("Redis.mineVictableIdletImillis"))); FAC.getPoolConfig (). SetMineVictableIdletImillis "))); FAC.GetPoolConfig (). SetmineVictableIdletimemillis (Integer.parseint (Environment.getProperty (" Redis.mineVictableItimEmillis "); .setNumTestsperevictionRun (Integer.parseint (Environment.getProperty ("Redis.numTestsperevictionRun"))); FAC.GetPoolConfig (). REDISTEM (RedisconnectionFactory RedisconnectionFactory) {Redistemplate <String, String> Redis = New Redistemplate <> (); @Runwith (springjunit4classrunner.class) @ContextConfiguration (classes = {Redisconfig.class}) classe public Redistest {@AUTOWIRED PRIVADO REDISTEMPLATE <String, String> Redistemplate; @Test public void testRedisObj () {map <string, object> Properties = new Hashmap <> (); Propriedades.put ("123", "Hello"); Propriedades.put ("ABC", 456); redistemplate.opsforhash (). Putall ("hash", propriedades); Mapa <objeto, objeto> ans = redistemplate.opsforhash (). Entradas ("hash"); System.out.println ("Ans:" + Ans); }}Após a execução, a saída é a seguinte
Ans: {123 = Olá, ABC = 456}A julgar pela configuração e implementação acima, é muito simples. Basicamente, não há círculo, mas quando você o conecta com Redis-cli, você não pode consultar o conteúdo da chave de hash
127.0.0.1:6379> Get Hash (nil) 127.0.0.1:6379> chaves *1) "/xac/xed/x00/x05t/x00/x04hash"
Não há problema em usar o código para verificar. Eu conectei diretamente o console e descobri que essa chave é diferente do que esperávamos, com alguns prefixos, por quê?
Para resolver o problema acima, só posso depurar e ver o que o causou.
Localização correspondente do código -fonte:
// org.springframework.data.redis.core.abstractOperations#RawkeyByte [] RawKey (chave do objeto) {Assert.NotNull (Key, "não é necessário chave necessária"); Retorne this.KeySerializer () == NULL && KEY Instância de byte []? (byte []) ((byte []) key): this.KeySerializer (). Serialize (chave);}Você pode ver que essa chave não é a chave.GetBytes () que esperávamos, mas this.KeySerializer (). Serialize (Key) é chamado. O resultado da depuração é que o serializador padrão é o JDKSerializationRedisserializer.
Em seguida, siga as pistas e vá mais profunda passo a passo, o link é o seguinte
// org.springframework.core.serializer.serializingConverter#convert // org.springframework.core.serializer.defaultSerializer#serializepublic Class de throTings) IMPLEMENTS IMPRESTIMENTRIMENTRIMENTRIGEMIRENCILIZER ({) IMPRESTIMENT IMPRESTIMENT IMPORMENT IMPORMENT () {{} {}} {if (! (Instância do objeto de serializável)) {tire nova ilegalargumentException (this.getclass (). getSImpleName () + "requer uma carga útil serializável, mas recebeu um objeto do tipo [" + object.getClass (). getName () + "]"); } else {objectOutputStream ObjectOutputStream = new ObjectOutputStream (outputStream); objectOutputStream.WriteObject (Object); objectOutputStream.flush (); }}}Portanto, a implementação específica é muito clara, que é o objectutputStream. Essa coisa é a ferramenta de streaming desesiva serializada mais primitiva em Java. Ele conterá informações de tipo, por isso será equipado com o prefixo.
Portanto, para resolver esse problema, é mais claro. Substitua o JDKSerialization Redisserializer nativo e altere -o para String, que apenas fornece um StringReSerializer; portanto, na configuração do Redistemplate, modifique -o ligeiramente
@BeanPublic Redistemplate <String, String> Redistemplate (RedisconnectionFactory RedisconnectionFactory) {Redistemplate <String, String> Redis = new Redistemplate <> (); redis.setConnectionFactory (RedisconnectionFactory); // Defina o método de serialização padrão do Redis String/Value StringRedisSerializer stringRedisSerializer = new StringRedisSerializer (); Redis.setKeySerializer (StringRedisSerializer); redis.setValueSerializer (StringRedisSerializer); Redis.SethashKeySerializer (StringRedisSerializer); Redis.SethashValueSerializer (StringRedisSerializer); Redis.AfterPropertiEset (); retornar redis;}Executar novamente, e a coisa embaraçosa aconteceu, a exceção foi lançada e a conversão do tipo falhou
java.lang.classCastException: java.lang.integer não pode ser lançado para java.lang.string em org.springframework.data.redis.serializer.stringSerializer.serialize (stringSerializer.java:33) AT org.springframework.data.redis.core.abstractOperations.rawhashValue (abstractOperations.java:171) em org.springframework.data.redis.core.defaulthashoperations.putall (DefaulthashOperations.java:129) ...
Olhando para o caso de teste anterior, o valor no mapa possui número inteiro e o parâmetro recebido pelo StringReDisserializer deve ser string, portanto, não use isso e você ainda escreverá um compatível.
classe pública defaultstrSerializer implementa o redisseserializer <ject> {private final charset charset; public defaultStrSerializer () {this (charset.forname ("utf8")); } public defaultstrSerializer (charset charset) {assert.notNull (charset, "charset não deve ser nulo!"); this.charset = charset; } @Override public byte [] serialize (objeto o) lança serializationException {return o == null? null: string.valueof (o) .getBytes (charset); } @Override Public Object Deserialize (byte [] bytes) lança serializationException {return bytes == null? nulo: nova string (bytes, charset); }}Então você pode começar a se divertir e testar após a execução
Chaves *1) "/xac/xed/x00/x05t/x00/x04hash" 2) "Hash" 127.0.0.1:6379> HGETALL HASH1) "123" 2) "Hello" 3) "ABC" 4) "456"
Vamos ver brevemente a postura de uso do Redistemplate, e ler e encapsular o método de chamada usado pelo OPSFORXXX para diferentes estruturas de dados (String, List, Zset, Hash).
// Hash Data Struction Operação org.springframework.data.redis.core.redistemplate#opsforhash // listorg.springframework.data.redis.core.redistemplate#opsforlist // rumorg.springframework.data.redem.core.redemplistem //String.springframeworks.data.redem.core.coreM.Cerlist // Stringorg.springFramework setorg.springframework.data.redis.core.redistemplate#opsforzset
Além do método de uso acima, outro comum é usar diretamente executar. Um caso simples é o seguinte
@Testpublic void testredis () {string key = "hello"; String value = "mundo"; redistemplate.execute ((Rediscallback <Void>) con -> {con.set (key.getBytes (), value.getBytes ()); retornar null;}); String asn = redistemplate.execute ((Rediscallback <String>) con -> new String (con.get (key.getBytes ()))); System.out.println (ASN); String hkey = "hkey"; redistemplate.execute ((Rediscallback <Void>) con -> {con.hset (hkey.getbytes (), "23" .getBytes (), "o quê" .getBytes ()); return null;}); Map <byte [], byte []> map = redistemplate.execute ((Rediscallback <map <byte [], byte []>) con -> con.hgetall (hkey.getbytes ())); para (map.entry <byte [], byte [] >> entrada: map.entrySet ()) {System.out.println ("key:" + new string (entradas.getKey ()) + "| value:" + new string (entrada.getValue ())); }}O resultado da saída é o seguinte
mundo
Chave: 23 | Valor: O quê
Uma pergunta que pode ser pensada naturalmente é qual é a diferença entre os dois métodos acima?
A camada subjacente do OPSFORXXX é feita chamando Execute. Ele encapsula principalmente algumas posturas de uso e define a serialização, o que torna mais fácil e mais conveniente de usar. Dessa forma, a pequena trombeta é que ela precisa criar um novo objeto Defaultxxxoperations todas as vezes, e é preciso mais um passo. Com base nisso, trará desempenho adicional e sobrecarga de memória? Não testei, mas pessoalmente sinto que a quantidade é pequena, não deve haver impacto óbvio; E quando o QPS é muito alto, a ajuda que essa otimização conveniente pode trazer provavelmente não é muito.
Estudo-demo/Spring-Redis
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.