8.4.1 使用Redis来缓存查找

现在先从设置许可证服务以使用Redis开始。幸运的是,Spring Data已经简化了将Redis引入许可证服务中的工作。要在许可证服务中使用Redis,需要做以下4件事情。     (1)配置许可证服务以包含Spring Data Redis依赖项。     (2)构造一个到Redis服务器的数据库连接。     (3)定义Spring Data Redis存储库,代码将使用它与一个Redis散列进行交互。     (4)使用Redis和许可证服务来存储和读取组织数据。      1.配置许可证服务以包含Spring Data Redis依赖项     需要做的第一件事就是将spring-data-redis、jedis以及common-pools2依赖项包含在许可证服务的pom.xml文件中。代码清单8-7展示了要包含的依赖项。      代码清单8-7 添加Spring Redis依赖项 <dependency>    <groupId>org.springframework.data</groupId>    <artifactId>spring-data-redis</artifactId>    <version>1.7.4.RELEASE</version>  </dependency> <dependency>    <groupId>redis.clients</groupId>    <artifactId>jedis</artifactId>    <version>2.9.0</version>  </dependency>  <dependency>    <groupId>org.apache.commons</groupId>    <artifactId>commons-pool2</artifactId>    <version>2.0</version>  </dependency>      2.构造一个到Redis服务器的数据库连接     既然已经在Maven中添加了依赖项,接下来就需要建立一个到Redis服务器的连接。Spring使用开源项目Jedis与Redis服务器进行通信。要与特定的Redis实例进行通信,需要在licensing-service/src/main/java/com/thoughtmechanix/licenses/Application.java中的Application类中公开一个JedisConnectionFactory作为Spring bean。一旦连接到Redis,将使用该连接创建一个Spring RedisTemplate对象。我们很快会实现Spring Data存储库类,它们将使用RedisTemplate对象来执行查询,并将组织服务数据保存到Redis服务中。代码清单8-8展示了这段代码。      代码清单8-8 确定许可证服务将如何与Redis进行通信  package com.thoughtmechanix.licenses; // 为了简洁,省略了大部分import语句 import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker @EnableBinding(Sink.class) public class Application {     @Autowired     private ServiceConfig serviceConfig;     ⇽--- jedisConnectionFactory()方法设置到Redis服务器的实际数据库连接          @Bean     public JedisConnectionFactory jedisConnectionFactory() {         JedisConnectionFactory jedisConnFactory = new JedisConnectionFactory();         jedisConnFactory.setHostName( serviceConfig.getRedisServer());         jedisConnFactory.setPort( serviceConfig.getRedisPort() );         return jedisConnFactory;     }     ⇽--- redisTemplate()方法创建一个RedisTemplate,用于对Redis服务器执行操作              @Bean     public RedisTemplate<String, Object> redisTemplate() {         RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();         template.setConnectionFactory(jedisConnectionFactory());         return template;     } }          建立许可证服务与Redis进行通信的基础工作已经完成。现在让我们来编写从Redis查询、添加、更新和删除数据的逻辑。      3.定义Spring Data Redis存储库 Redis是一个键值数据存储,它的作用类似于一个大的、分布式的、内存中的HashMap。在最简单的情况下,它存储数据并按键查找数据。Redis没有任何复杂的查询语言来检索数据。它的简单性是它的优点,也是这么多项目采用它的原因之一。     因为我们使用Spring Data来访问Redis存储,所以需要定义一个存储库类。读者可能还记得在第2章中,Spring Data使用用户定义的存储库类为Java类提供一个简单的机制来访问Postgres数据库,而无须开发人员编写低级的SQL查询。     对于许可证服务,我们将为Redis存储库定义两个文件。将要编写的第一个文件是一个Java接口,它将被注入任何需要访问Redis的许可证服务类中。这个OrganizationRedisRepository接口(在licensing- service/src/main/java/com/thoughtmechanix/licenses/repository/OrganizationRedisRepository.java中)如代码清单8-9所示。      代码清单8-9 OrganizationRedisRepository定义用于调用Redis的方法 package com.thoughtmechanix.licenses.repository; import com.thoughtmechanix.licenses.model.Organization; public interface OrganizationRedisRepository {     void saveOrganization(Organization org);     void updateOrganization(Organization org);     void deleteOrganization(String organizationId);     Organization findOrganization(String organizationId); }   第二个文件是OrganizationRedisRepository接口的实现。这个接口的实现,即licensing-service/src/main/java/com/thoughtmechanix/licenses/repository/OrganizationRedisRepositoryImpl.java中的OranizationRedisRepositoryImpl类,使用了之前在代码清单8-8中定义的RedisTemplate来与Redis服务器进行交互,并对Redis服务器执行操作。代码清单8-10展示了所使用的代码。      代码清单8-10 OrganizationRedisRepositoryImpl实现 package com.thoughtmechanix.licenses.repository;   import com.thoughtmechanix.licenses.model.Organization; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Repository;   import javax.annotation.PostConstruct; ⇽--- 这个@Repository注解告诉Spring,这个类是一个与Spring Data一起使用的存储库类 @Repository public class OrganizationRedisRepositoryImpl implements OrganizationRedisRepository {     ⇽--- 在Redis服务器中存储组织数据的散列的名称     private static final String HASH_NAME ="organization";       private RedisTemplate<String, Organization> redisTemplate;     ⇽--- HashOperations类包含一组用于在Redis服务器上执行数据操作的辅助方法     private HashOperations hashOperations;       public OrganizationRedisRepositoryImpl(){         super();     }       @Autowired     private OrganizationRedisRepositoryImpl(RedisTemplate redisTemplate) {         this.redisTemplate = redisTemplate;     }       @PostConstruct     private void init() {         hashOperations = redisTemplate.opsForHash();     }            @Override     public void saveOrganization(Organization org) {         ⇽--- 与Redis的所有交互都将使用由键存储的单个Organization对象         hashOperations.put(HASH_NAME, org.getId(), org);     }       @Override     public void updateOrganization(Organization org) {         hashOperations.put(HASH_NAME, org.getId(), org);     }       @Override     public void deleteOrganization(String organizationId) {         hashOperations.delete(HASH_NAME, organizationId);     }       @Override     public Organization findOrganization(String organizationId) {        return (Organization) hashOperations.get(HASH_NAME, organizationId);     } } OrganizationRedisRepositoryImpl包含用于从Redis存储和检索数据的所有CRUD(Create、Read、Update和Delete)逻辑。在代码清单8-10所示的代码中有两个关键问题需要注意。      Redis中的所有数据都是通过一个键存储和检索的。因为是存储从组织服务中检索到的数据,所以自然选择组织ID作为存储组织记录的键。     一个Redis服务器可以包含多个散列和数据结构。在针对Redis服务器的每个操作中,需要告诉Redis执行操作的数据结构的名字。在代码清单8-10中,使用的数据结构名称存储在HASH_NAME常量中,其值为organization”。      4.使用Redis和许可证服务来存储和读取组织数据     在完成对Redis执行操作的代码之后,就可以修改许可证服务,以便每次许可证服务需要组织数据时,它会在调用组织服务之前检查Redis缓存。检查Redis的逻辑将出现在licensing- service/src/main/java/com/thoughtmechanix/licenses/clients/OrganizationRestTemplateClient.java中的OrganizationRestTemplateClient类中。这个类的代码如代码清单8-11所示。       代码清单8-11 OrganizationRestTemplateClient将实现缓存逻辑 @Component public class OrganizationRestTemplateClient {     @Autowired     RestTemplate restTemplate;     ⇽--- OrganizationRedisRepository被自动装配到OrganizationRestTemplateClient          @Autowired     OrganizationRedisRepository orgRedisRepo;       private static final Logger logger = LoggerFactory.getLogger(OrganizationRestTemplateClient.class);     ⇽--- 尝试使用组织ID从Redis中检索Organization类     private Organization checkRedisCache(String organizationId) {         try {             return orgRedisRepo.findOrganization(organizationId);         }         catch (Exception ex){             logger.error("Error encountered while trying to retrieve organization {} check Redis Cache.  Exception {}", organizationId, ex);             return null;         }     }       private void cacheOrganizationObject(Organization org) {         try {             orgRedisRepo.saveOrganization(org);         }catch (Exception ex){             logger.error("Unable to cache organization {} in Redis. Exception {}", org.getId(), ex);         }     }       public Organization getOrganization(String organizationId){         logger.debug("In Licensing Service.getOrganization: {}", UserContext.getCorrelationId());           Organization org = checkRedisCache(organizationId);          ⇽--- 如果无法从Redis中检索出数据,那么将调用组织服务从源数据库检索数据         if (org!=null){             logger.debug("I have successfully retrieved an organization {} from the redis cache: {}", organizationId, org);             return org;         }           logger.debug("Unable to locate organization from the redis cache: {}.", organizationId);           ResponseEntity<Organization> restExchange =                 restTemplate.exchange(                         "http://zuulservice/api/organization/v1/organizations/{organizationId}",                         HttpMethod.GET,                         null, Organization.class, organizationId);           /*将记录保存到缓存中*/           org = restExchange.getBody();         ⇽--- 将检索到的对象保存到缓存中              if (org!=null) {             cacheOrganizationObject(org);         }           return org;     } } getOrganization()方法是调用组织服务的地方。在进行实际的REST调用之前,尝试使用checkRedisCache()方法从Redis中检索与调用相关联的组织对象。如果该组织对象不在Redis中,则代码将返回一个null值。如果从checkRedisCache()方法返回一个null值,那么代码将调用组织服务的REST端点来检索所需的组织记录。如果组织服务返回一条组织记录,那么将使用cacheOrganizationObject()方法缓存返回的组织对象。     注意          在与缓存进行交互时,要特别注意异常处理。为了提高弹性,如果无法与Redis服务器通信,我们绝对不会让整个调用失败。相反,我们会记录异常,并让调用转到组织服务。在这个特定的用例中,缓存旨在帮助提高性能,而缓存服务器的缺失不应该影响调用的成功。     有了Redis缓存代码,接下来应该访问许可证服务(是的,目前只有两个服务,但是有很多基础设施),并查看代码清单8-10中的日志消息。如果读者连续访问以下许可证服务端点http://localhost:5555/api/licensing/v1/organizations/e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a两次,那么应该在日志中看到以下两个输出语句:      licensingservice_1    | 2016-10-26 09:10:18.455 DEBUG 28 --- [nio-8080-exec-      1] c.t.l.c.OrganizationRestTemplateClient   : Unable to locate      organization from the redis cache: e254f8c-c442-4ebe-a82a-e2fc1d1ff78a.  licensingservice_1    | 2016-10-26 09:10:31.602 DEBUG 28 --- [nio-8080-exec-      2] c.t.l.c.OrganizationRestTemplateClient   : I have successfully      retrieved an organization e254f8c-c442-4ebe-a82a-e2fc1d1ff78a from the      redis cache: com.thoughtmechanix.licenses.model.Organization@6d20d301      来自控制台的第一行显示,第一次调用尝试为组织访问许可证服务端点e254f8c-c442-4ebe-a82a-e2fc1d1ff78a。许可证服务首先检查了Redis缓存,但找不到要查找的组织记录。 然后代码调用组织服务来检索数据。从控制台显示出来的第二行表明,在第二次访问许可证服务端点时,组织记录已被缓存了。 注意本地运行的时候需要修改几个地方: 1、OrganizationRestTemplateClient.java的getOrganization方法: 将url:"http://zuulservice/api/organization/v1/organizations/{organizationId}" 修改为"http://192.168.237.132:5555/api/organizationservice/v1/organizations/{organizationId}" 8.4.1 使用Redis来缓存查找8.4.1 使用Redis来缓存查找 2、licensingservice.yml文件中的redis.server: "redis"要改成:redis.server: "localhost"   8.4.1 使用Redis来缓存查找8.4.1 使用Redis来缓存查找   3、整个项目要启动kafka、redis和zookeeper。  
上一篇:minicom在虚拟机(linux)安装配置过程


下一篇:Linux 格式化 挂载 Gdisk