目錄
一、Spring Data Redis
1.1、緩存功能
1.1.1、分析
1.1.2、案例實現(xiàn)
1.1.3、效果演示
1.2、計數(shù)功能(Redis + RabbitMQ)
1.2.1、分析
1.2.2、案例實現(xiàn)
一、Spring Data Redis
1.1、緩存功能
1.1.1、分析
使用 redis 作為緩存, MySQL 作為數(shù)據(jù)庫組成的架構(gòu)
整體思路:
應用服務器訪問數(shù)據(jù)的時候,先查詢 Redis,如果 Redis 上存在該數(shù)據(jù),就從 Redis 中取數(shù)據(jù)直接交給應用服務器,不用繼續(xù)訪問數(shù)據(jù)庫了;如果 Redis 上不存在該數(shù)據(jù),就會去 MySQL 中把讀到的結(jié)構(gòu)返回給應用服務器,同時,把這個數(shù)據(jù)也寫入到 Redis 中.
由于 Redis 這樣的緩存經(jīng)常用來存儲 “熱點數(shù)據(jù)”,也就是高頻使用的數(shù)據(jù),那什么樣的數(shù)據(jù)算高頻呢?這里暗含了一層假設,某個數(shù)據(jù)一旦被用到了,那么可能在最近這段時間就可能被反復用到.
隨著時間推移,越來越多的 key 在 redis 上訪問不到,那 redis 的數(shù)據(jù)不是越來越多么?
- 把數(shù)據(jù)寫給 redis 的同時,會給這個 key 設置一個過期時間.
- Redis 也有內(nèi)存不足的時候,因此提供了 淘汰策略(之前的文章展開講過).
1.1.2、案例實現(xiàn)
例如論壇網(wǎng)站,有些帖子的訪問評論很高,就需要設置成熱點文章,緩存起來(比起去 MySQL 數(shù)據(jù)庫中查詢文章要快的多).?
實現(xiàn)思路:
????????根據(jù)上面理論,暗含假設當前使用的文章就是熱點文章,也就是說,如果在緩存中有該文章,就直接返回,如果沒有,就去數(shù)據(jù)庫中查,然后再緩存起來,同時設置 30min(不同場景合理分配) 的過期時間.
帖子實體類.
@Data
public class Article {
private String title;
private String content;
}
文章 mapper.
@Mapper
public interface ArticleMapper {
/**
* 根據(jù) id 查詢文章
* @param id
* @return
*/
Article selectArticleById(@Param("id") Integer id);
}
<select id="selectArticleById" resultType="com.example.cyk.cache.Article">
select * from article where id = #{id};
</select>
帖子 controller
@RestController
@RequestMapping("/article")
public class ArticleController {
@Autowired
private IArticleService articleService;
@GetMapping("/get")
public HashMap<String, Object> get(@NonNull Integer id) {
//1.獲取文章服務
Article article = articleService.getArticleInfo(id);
//2.返回響應
return HandlerResponse(1000, "操作成功", article);
}
/**
* 處理返回格式
* @param code
* @param msg
* @param data
* @return
*/
private HashMap<String, Object> HandlerResponse(Integer code, String msg, Object data) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", code);
result.put("msg", msg);
result.put("data", data);
return result;
}
}
帖子 service .
@Slf4j
@Service
public class ArticleService implements IArticleService {
@Autowired
private ArticleMapper articleMapper;
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public Article getArticleInfo(Integer id) {
//1.非空校驗
if(id == null) {
log.warn("文章 id 為空");
throw new RuntimeException("文章 id 為空");
}
//2.先去 redis 上看有沒有文章對應的這個id
//我這里約定 redis 上存儲格式:
//key: art:id
//value: $title$content ($ 是分隔符)
//例如 key: art:1 value: $決定$今天要好好學習
String articleInfo = redisTemplate.opsForValue().get("art:" + id);
if(articleInfo != null) {
//存在直接返回
log.info("從 redis 中獲取到文章數(shù)據(jù)");
//1) 解析格式
Article article = analysisArticle(articleInfo);
//2) 返回數(shù)據(jù)
return article;
}
//3.redis 上沒有數(shù)據(jù),因此需要從 mysql 中取
Article article = articleMapper.selectArticleById(id);
if(article == null) {
log.warn("文章不存在");
throw new RuntimeException("文章不存在!");
}
//4.將文章存到 redis 中
//1) 合成 redis 所需格式的文章
articleInfo = synthesisArticle(article);
//2) 設置 5 分鐘過期時間(為了演示效果)
redisTemplate.opsForValue().set("art:" + id, articleInfo, 5, TimeUnit.SECONDS);
log.info("從 mysql 中獲取到文章數(shù)據(jù)");
return article;
}
/**
* 合成 redis 需要的格式(提前約定好的)
* @param article
* @return
*/
private String synthesisArticle(Article article) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("$");
stringBuilder.append(article.getTitle());
stringBuilder.append("$");
stringBuilder.append(article.getContent());
return stringBuilder.toString();
}
/**
* 解析文章格式
* @param articleInfo
* @return
*/
private Article analysisArticle(String articleInfo) {
Article article = new Article();
String title = articleInfo.split("\\$")[1];
String content = articleInfo.split("\\$")[2];
article.setTitle(title);
article.setContent(content);
return article;
}
}
1.1.3、效果演示
1.2、計數(shù)功能(Redis + RabbitMQ)
1.2.1、分析
許多都會使應用用?Redis 作為計數(shù)的基礎?具,它可以實現(xiàn)快速計數(shù)、查詢緩存的功能,例如網(wǎng)站視頻的播放量,點贊數(shù)量......
Ps:這些都是相比較 MySQL 數(shù)據(jù)庫而言的,Redis 可以通過簡單的鍵值對操作完成計數(shù)任務并且實在內(nèi)存中完成的,而 MySQL 就需要先查詢數(shù)據(jù)庫,然后 +1,然后再存入數(shù)據(jù)庫,是在需要進行硬盤存儲的
1.2.2、案例實現(xiàn)
實現(xiàn)思路:
????????假設,用戶點擊某個帖子,此時需要進行訪問量?+ 1 的操作,這時候應用服務器就會直接去操作 Redis ,執(zhí)行 incr 命令,然后將返回的數(shù)據(jù)反饋給用戶,最后 Redis 會以異步的方式(RabbitMQ 實現(xiàn)異步)將播放量同步到 MySQL 數(shù)據(jù)庫中(異步就表示:這里并不是每一個播放請求,都需要立即寫入數(shù)據(jù)~ 至于什么時候?qū)懭?,需要根?jù)實際的業(yè)務需求場景而定),將數(shù)據(jù)持久化.
Ps:實際中要開發(fā)?個成熟、穩(wěn)定的真實計數(shù)系統(tǒng),要?臨的挑戰(zhàn)遠不?如此簡單:防作弊、按 照不同維度計數(shù)、避免單點問題、數(shù)據(jù)持久化到底層數(shù)據(jù)源等。
文章實體類
@Data
public class Article implements Serializable {
private Integer id;
private String title;
private String content;
private Long visits; //訪問量
}
rabbit 交換機、隊列、綁定配置.
public class MqFinal {
//處理文章的直接交換機
public static final String UPDATE_DIRECT = "article.update.direct";
//用于修改文章數(shù)據(jù)的隊列
public static final String UPDATE_QUEUE = "article.update.queue";
//bindingKey
public static final String UPDATE_KEY = "article.update.key";
}
@Configuration
public class MqConfig {
/**
* 消息轉(zhuǎn)化器
* @return
*/
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public DirectExchange ArticleDirectExchange() {
return new DirectExchange(MqFinal.UPDATE_DIRECT, true, false);
}
@Bean
public Queue ArticleUpdateQueue() {
return new Queue(MqFinal.UPDATE_QUEUE, true);
}
@Bean
public Binding ArticleUpdateBinding() {
return BindingBuilder.bind(ArticleUpdateQueue()).to(ArticleDirectExchange()).with(MqFinal.UPDATE_KEY);
}
}
mq 監(jiān)聽配置
@Slf4j
@Component
public class MqListenerArticle {
@Autowired
private ArticleMapper articleMapper;
/**
* 同步數(shù)據(jù)庫
*/
@RabbitListener(queues = MqFinal.UPDATE_QUEUE)
public void syncVisits(HashMap<String, Object> data) {
Integer id = (Integer) data.get("id");
// Rabbitmq 這里有一個問題,Map<String, Object> 中 Object 傳入為 Long 類型,需要用 Integer 來接受,否則報錯
// 因此發(fā)送消息之前,體現(xiàn)將 Long 類型轉(zhuǎn)化為 String,接收到消息之后只需要將 String 轉(zhuǎn)化為 Long 即可
String visits = (String) data.get("visits");
articleMapper.updateArticleVisits(id, Long.valueOf(visits));
log.info("訪問量數(shù)據(jù)同步完成!");
}
}
訪問量增加服務(這里為了可讀性,只展示了本業(yè)務的核心邏輯)文章來源:http://www.zghlxwxcb.cn/news/detail-744862.html
@Override
public Article getArticleInfo(Integer id) {
//1.非空校驗
if(id == null) {
log.warn("文章 id 為空");
throw new RuntimeException("文章 id 為空");
}
//2.訪問量 +1
//注意:incr 這個命令執(zhí)行時,即使 key 不存在,也會自動生成 key,然后自增
Long visits = redisTemplate.opsForValue().increment("v_art:" + id);
//3.rabbitmq 實現(xiàn)異步數(shù)據(jù)同步(發(fā)送一個消息即可)
HashMap<String, Object> visitsInfo = new HashMap<>();
visitsInfo.put("id", id);
visitsInfo.put("visits", visits.toString()); //轉(zhuǎn)化原因前面解釋過了
rabbitTemplate.convertAndSend(MqFinal.UPDATE_DIRECT, MqFinal.UPDATE_KEY, visitsInfo);
//4.獲取文章數(shù)據(jù)
//業(yè)務邏輯(這里為了可讀性,就先不展示這里了)......
//5.放入文章
Article article = new Article();
article.setVisits(visits);
article.setId(id);
return article;
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-744862.html
到了這里,關(guān)于Spring Data Redis + RabbitMQ - 基于 string 實現(xiàn)緩存、計數(shù)功能(同步數(shù)據(jù))的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!