zoukankan      html  css  js  c++  java
  • springboot+mybatis+redis实现分布式缓存

    大家都知道springboot项目都是微服务部署,A服务和B服务分开部署,那么它们如何更新或者获取共有模块的缓存数据,或者给A服务做分布式集群负载,如何确保A服务的所有集群都能同步公共模块的缓存数据,这些都涉及到分布式系统缓存的实现。

    前面其实我已经介绍了springboot+mybatis+ehcache实现缓存数据,但是ehcache的设计并不适合做分布式缓存,所以今天用redis来实现分布式缓存。

    原理什么的,我就不多说了,直接上代码。

    pom依赖

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>2.2.4</version>
            </dependency>
            
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.1</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.42</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>

    表创建sql语句

    CREATE TABLE `t_user` (
      `id` varchar(50) NOT NULL,
      `username` varchar(255) DEFAULT NULL,
      `password` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    application.properties配置文件

    server.port=8860
    
    # redis
    spring.redis.database=1
    spring.redis.host=127.0.0.1
    spring.redis.password=test123
    spring.redis.pool.max-active=8
    spring.redis.pool.max-idle=8
    spring.redis.pool.max-wait=-1
    spring.redis.pool.min-idle=0
    spring.redis.port=6379
    #spring.redis.sentinel.master= # Name of Redis server.
    #spring.redis.sentinel.nodes= # Comma-separated list of host:port pairs.
    spring.redis.timeout=5000
    
    # cache
    spring.cache.cache-names=cache1,cache2
    spring.cache.redis.time-to-live=600000
    
    # datasource
    spring.datasource.name=ehcahcetest
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://127.0.0.1:3312/ehcahcetest
    spring.datasource.username=root
    spring.datasource.password=123456
    
    # mybatis
    mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
    mybatis.config-location=classpath:mybatis/mybatis-config.xml
    #mybatis.type-aliases-package=

    springboot启动类

    package com.rediscache;
    
    import java.lang.reflect.Method;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.cache.interceptor.KeyGenerator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    @SpringBootApplication
    @ComponentScan(basePackages="com.rediscache")//扫描组件
    @ServletComponentScan(basePackages="rediscache")//扫描拦截器,过滤器
    @EnableCaching
    public class RedisCacheTestApplication {
    
        /**
         * 设置缓存对象的序列化方式,不设置会报错
         * 另外对于json序列化,对象要提供默认空构造器
         * @param redisTemplate
         * @return
         */
        @Bean
        public CacheManager cacheManager(RedisTemplate redisTemplate) {
    
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    
            RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
            cacheManager.setDefaultExpiration(300);
            return cacheManager;
        }
    
        /**
         * 自定义key的生成策略
         * @return
         */
        @Bean
        public KeyGenerator myKeyGenerator(){
            return new KeyGenerator() {
                @Override
                public Object generate(Object target, Method method, Object... params) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(target.getClass().getName());
                    sb.append(method.getName());
                    for (Object obj : params) {
                        sb.append(obj.toString());
                    }
                    return sb.toString();
                }
            };
        }
        
        public static void main(String[] args) {
            SpringApplication.run(RedisCacheTestApplication.class, args);
        }
        
    }

    接口测试类

    package com.rediscache.controller;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.UUID;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.rediscache.entity.User;
    import com.rediscache.factory.UserFactory;
    import com.rediscache.service.UserService;
    import com.google.gson.Gson;
    
    
    
    @RestController
    @RequestMapping("/o")
    public class OperationController {
        
        @Autowired
        private StringRedisTemplate redisTemplate;
        
        @Autowired
        private UserService userService;
        
        Gson gson = new Gson();
        
        @RequestMapping(value = "/insert", method = RequestMethod.GET)
        public String insert(){
            
            // 保存一个新用户
            String uid = userService.save(UserFactory.createUser());
            
            return uid;
        }
        
        @RequestMapping(value = "/query", method = RequestMethod.GET)
        public String query(String uid){
            
            // 查询该用户
            System.out.println(gson.toJson(userService.getUserById(uid), User.class));
            System.out.println();
            
            return "success";
        }
        
        @RequestMapping(value = "/update", method = RequestMethod.GET)
        public String update(String uid, String username){
            
            User user = new User();
            user.setUid(uid);
            user.setUsername(username);
            
            // 更新该用户
            userService.update(user);
            
            
            return "success";
        }
        
        @RequestMapping(value = "/del", method = RequestMethod.GET)
        public String del(String uid){
            
            // 删除该用户
            userService.del(uid);
            System.out.println();
            
            
            return "success";
        }
        
        
        
    }

    service缓存类

    package com.rediscache.service;
    
    import java.util.Date;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    
    import com.rediscache.dao.UserDao;
    import com.rediscache.entity.User;
    
    
    @Service
    public class UserService {
        
        @Autowired
        private UserDao userDao;
    
        @CacheEvict(key="'user_'+#uid", value="userCache")
        public void del(String uid) {
            // TODO Auto-generated method stub
            userDao.del(uid);
        }
    
        @CachePut(key="'user_'+#user.uid", value="userCache")
        public void update(User user) {
            userDao.update(user);
        }
        
        @Cacheable(key="'user_'+#uid",value="userCache")
        public User getUserById(String uid){
            System.err.println("缓存里没有"+uid+",所以这边没有走缓存,从数据库拿数据");
            return userDao.findById(uid);
            
        }
    
        @CacheEvict(key="'user'",value="userCache")
        public String save(User user) {
            // TODO Auto-generated method stub
            return userDao.save(user);
        }
    
        
    }

    其它的dao类和mybatis的mapper就不贴了。

    复制上面写好的项目,把新项目的端口改为8862,现在分别启动8860和8862两个项目,模拟服务的集群部署。

    开始测试

    1、浏览器里输入http://localhost:8860/o/insert

    可以看到数据库里保存进一条数据

    2、输入http://localhost:8860/o/query?uid=ff4c5723-3ffc-438d-9d8c-8a2a7025b33e 查询刚刚的这条数据

    可以看到sql语句打印了,提示也表示这一次没有走缓存

    看看redis

     

    说明缓存数据已经进来了。

    3、再次http://localhost:8860/o/query?uid=ff4c5723-3ffc-438d-9d8c-8a2a7025b33e 查询

    可以看到这次没有从数据库查,而是从缓存里获取的数据

    说明单服务里的缓存已经奏效了,下面看看集群服务有没有效果

    4、http://localhost:8862/o/query?uid=ff4c5723-3ffc-438d-9d8c-8a2a7025b33e 在8862服务里查询

     

    这个是8862的控制台打印的结果,说明分布式缓存奏效了。

    项目结构截图

    注意点

    缓存设置了时效的,也就是redis的时效

  • 相关阅读:
    Git 分支管理
    Git 保存工作区
    Git 版本控制
    Git 基本命令-详细版本
    Git 初始化配置
    Git 基本概念:分区
    JavaScript 调试
    JavaScript 错误
    JS 判断字符串是否全部为字母
    JS 判断输入字符串是否为数字、字母、下划线组成
  • 原文地址:https://www.cnblogs.com/shamo89/p/8227559.html
Copyright © 2011-2022 走看看