Spring Cloud | 總結 二: Mybatis多數據源+Redis二級緩存+Feign調用

一、簡述

這幾天補充了下文檔,這裏接《Spring Cloud終篇 | 總結 一:一個完整的企業級SpringCloud架構(Mybatis--多數據源)》之後,簡述一下Redis二級緩存feign的調用方式。


源碼案例:https://github.com/liujun19921020/SpringCloudDemo/blob/master/ProjectDemo/企業SpringCloud架構-xxljob-redis-elasticsearch
或 :鏈接:https://pan.baidu.com/s/1ooqaIRSeX6naOZ51aBWNHA  提取碼:cwsw

這裏是整了一個架構集合案例,其中的ES/xxl-job將在下兩章講解,該文主要源碼爲sc-muster-item-demo項目


二、概述Feign調用

案例中,在sc-muster-item-demo項目中調用sc-item-demo項目中的"/orderProduct/updateOrderStatus"接口(pom.xml引包、添加註解等詳細操作步驟可回顧下《Spring Cloud教程 | 第四篇:服務消費者(Feign調用)》等)

在sc-muster-item-demo項目中配置調用sc-item-demo項目的FeignClient,這裏把熔斷類也加上了,實際使用中可根據情況看是否需要。

/**
 * 調用sc-item-demo服務
 */
@FeignClient(name = "sc-item-demo",fallback = ScItemDemoFeignHystrix.class)
public interface ScItemDemoFeignClient {

    /**
     * 根據 訂單號(批量)/商品sku 修改訂單狀態
     * @param data
     * @return
     */
    @PostMapping("/orderProduct/updateOrderStatus")
    ResponseMsg updateOrderStatus(@RequestBody JSONObject data);
}

 

/**
 * 調用sc-item-demo服務熔斷類
 */
@Component
public class ScItemDemoFeignHystrix implements ScItemDemoFeignClient {
    @Override
    public ResponseMsg updateOrderStatus(JSONObject data) {
        return null;
    }
}

然後我們通過Service調用對應的FeignClient方法即可,如下,

@Service
public class OrderAmazonDetailServiceImpl implements IOrderAmazonDetailService {

    @Autowired
    private ScItemDemoFeignClient scItemDemoFeignClient;

    /**
     * 根據 訂單號/商品sku 批量修改訂單狀態(Feign方式調用)
     * @param data
     */
    @Override
    public void updateOrderStatus(JSONObject data) throws Exception {
        ResponseMsg responseMsg = scItemDemoFeignClient.updateOrderStatus(data);
        if (null == responseMsg) {// 調用服務失敗
            throw new Exception("調用sc-item-demo服務失敗");
        }
        if (responseMsg.getCode() != Code.SUCCESS) {// 調用服務報錯
            throw new Exception(responseMsg.getMsg());
        }
        //List<Map<String,Object>> categoryIdAndNameLine = JSON.parseObject(JSON.toJSONString(responseMsg.getData()), new TypeReference<List<Map<String,Object>>>(){}.getType());//如果data中有list返回值,可以如圖所示的轉譯
    }

}

通過調用sc-muster-item-demo項目的接口可以看到調用成功,Controller我就不貼出來了


三、概述Redis使用

1、啥也不說,先配置數據源,沒配置啥也不能幹

#----------------------------------redis配置. 線上請用Redis Cluster---------------------------------------------
#spring.redis.cluster.nodes=39.108.60.150:7001,39.108.60.150:7002,119.23.237.229:7003,119.23.237.229:7004,120.77.157.244:7005,120.77.157.244:7006
spring.redis.host=192.168.71.146
spring.redis.port=23230
spring.redis.password=yibai_dev@redis
spring.redis.jedis.pool.max-active=1000
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=2
spring.redis.jedis.timeout=2000


2、先說說用Redis做二級緩存,需要實現Cache類。

/**
 * 使用Redis來做Mybatis的二級緩存
 * 實現Mybatis的Cache接口
 */
public class MybatisRedisCache implements Cache {

    private static final Logger logger = LoggerFactory.getLogger(MybatisRedisCache.class);

    // 讀寫鎖
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    private String id;

    public MybatisRedisCache(final String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        logger.info("Redis Cache id " + id);
        this.id = id;
    }

    @Override
    public String getId() {
        return this.id;
    }

    @Override
    public void putObject(Object key, Object value) {
        logger.info("key==================>" + key);
        if (value != null) {
            // 向Redis中添加數據,有效時間是2天
            redisTemplate.opsForValue().set(key.toString(), value, 2, TimeUnit.DAYS);
        }
    }

    @Override
    public Object getObject(Object key) {
        try {
            if (key != null) {
                Object obj = redisTemplate.opsForValue().get(key.toString());
                return obj;
            }
        } catch (Exception e) {
            logger.error("redis ");
        }
        return null;
    }

    @Override
    public Object removeObject(Object key) {
        try {
            if (key != null) {
                redisTemplate.delete(key.toString());
            }
        } catch (Exception e) {
        }
        return null;
    }

    @Override
    public void clear() {
        logger.debug("清空緩存");
        try {
            Set<String> keys = redisTemplate.keys("*:" + this.id + "*");
            if (!CollectionUtils.isEmpty(keys)) {
                redisTemplate.delete(keys);
            }
        } catch (Exception e) {
        }
    }

    @Override
    public int getSize() {
        Long size = (Long) redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.dbSize();
            }
        });
        return size.intValue();
    }

    @Override
    public ReadWriteLock getReadWriteLock() {
        return this.readWriteLock;
    }
}

然後配置中指明實現二級緩存的方式,我這裏是放在application.yml中配置的:
 

spring.cache.type=REDIS

接下來就可以對你需要緩存的語句進行聲明瞭,通過Mapper接口的方式是
 

@CacheNamespace(implementation=(com.xxx.xxx.MybatisRedisCache.class))


3、再說說Redis做變量存儲
配置一個RedisConfig類,實現CachingConfigurerSupport,然後寫個RedisTemplate的實現方式通過@Bean初始化到工廠吧(源碼案例中實現的功能要多些,可以自己去看看)

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 重寫Redis序列化方式,使用Json方式:
     * 當我們的數據存儲到Redis的時候,我們的鍵(key)和值(value)都是通過Spring提供的Serializer序列化到數據庫的。RedisTemplate默認使用的是JdkSerializationRedisSerializer,StringRedisTemplate默認使用的是StringRedisSerializer。
     * Spring Data JPA爲我們提供了下面的Serializer:
     * GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
     * 在此我們將自己配置RedisTemplate並定義Serializer。
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // 設置值(value)的序列化採用Jackson2JsonRedisSerializer。
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // 設置鍵(key)的序列化採用StringRedisSerializer。
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

使用的時候,直接通過

redisTemplate.opsForValue().set(K var1, V var2);//寫入   
redisTemplate.opsForValue().get(K var1);//讀出

例如:

@RestController
@RequestMapping("/redisDemo")
public class RedisDemoController {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private RedisTemplate redisTemplate;

    private static final String REDIS_DEMO_CALL = "REDIS_DEMO_CALL";

    /**
     * 根據條件查詢訂單詳細信息列表(分頁)
     * @param data
     * @return
     */
    @PostMapping("/getRedisCall")
    public ResponseMsg getRedisCall(@RequestBody JSONObject data){
        try {
            String nowDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));//當前時間

            // 獲取上次調用時間
            Object callDateObj = redisTemplate.opsForValue().get(REDIS_DEMO_CALL);
            String lastDateObj = callDateObj == null ? "未調用" : callDateObj.toString();

            // 更新時間
            redisTemplate.opsForValue().set(REDIS_DEMO_CALL, nowDate);

            return new ResponseMsg(Code.SUCCESS, lastDateObj,"查詢上次調用時間成功!");
        } catch (Exception e) {
            logger.error("查詢上次調用時間失敗!", e);
            return new ResponseMsg<>(Code.FAIL, null, "查詢上次調用時間失敗==》" + e.getMessage());
        }
    }
}

調用2次的結果:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章