一、簡述
這幾天補充了下文檔,這裏接《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次的結果: