2006-京淘Day12

2020-10-14 11:00:25

1. Redis入門案例

1.1 Redis常見用法

1.1.1 setex學習

/**
     * 2.需求:
     *      1.向redis中插入資料  k-v
     *      2.為key設定超時時間  60秒後失效.
     *      3.執行緒sleep 3秒
     *      4.獲取key的剩餘的存活時間.
     *
     *   問題描述: 資料一定會被刪除嗎??????
     *   問題說明: 如果使用redis 並且需要新增超時時間時 一般需要滿足原子性要求.
     *   原子性:   操作時要麼成功 要麼失敗.但是必須同時完成.
     */
    @Test
    public void test02() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.setex("寶可夢", 60, "小火龍 妙蛙種子");
        System.out.println(jedis.get("寶可夢"));

       /* Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.set("寶可夢", "小火龍 妙蛙種子");
        int a = 1/0;    //可能會出異常
        jedis.expire("寶可夢", 60);
        Thread.sleep(3000);
        System.out.println(jedis.ttl("寶可夢"));*/
    }

1.1.2 setnx

/**
     * 3.需求如果發現key已經存在時 不修改資料.如果key不存在時才會修改資料.
     *
     */
    @Test
    public void test03() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.setnx("aaaa", "測試nx的方法");
        /*if(jedis.exists("aaa")){
            System.out.println("key已經存在 不做修改");
        }else {
            jedis.set("aaa", "測試資料");
        }*/
        System.out.println(jedis.get("aaaa"));
    }

1.1.3 set 超時時間原子性操作

 /**
     * 需求:
     *  1.要求使用者賦值時,如果資料存在則不賦值.  setnx
     *  2.要求在賦值操作時,必須設定超時的時間 並且要求滿足原子性 setex
     */
    @Test
    public void test04() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        SetParams setParams = new SetParams();
        setParams.nx().ex(20);
        jedis.set("bbbb", "實現業務操作AAAA", setParams);
        System.out.println(jedis.get("bbbb"));


    }

1.1.4 list集合練習

  @Test
    public void testList() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.lpush("list", "1","2","3");
        System.out.println(jedis.rpop("list"));
    }

1.1.5 redis事務控制

 @Test
    public void testTx() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //1.開啟事務
        Transaction transaction = jedis.multi();
        try {
            transaction.set("aa", "aa");
            //提交事務
            transaction.exec();
        }catch (Exception e){
            e.printStackTrace();
            //回滾事務
            transaction.discard();
        }
    }

2.秒殺業務邏輯—分散式鎖機制

6988 —1塊 1部手機 20顯示搶購成功 並且支付了1塊錢…
問題:
1.tomcat伺服器有多臺
2.資料庫資料只有1份
3.必然會出現高並行的現象.
如何實現搶購…

2.1常規鎖操作

2.1.1 超賣的原因

在這裡插入圖片描述

2.1.2 同步鎖的問題

說明:同步鎖只能解決tomcat內部的問題,不能解決多個tomcat並行問題
在這裡插入圖片描述

2.1.3 分散式鎖機制

思想:
1.鎖應該使用第三方操作 ,鎖應該公用.
2.原則:如果鎖被人正在使用時,其他的使用者不能操作.
3.策略: 使用者向redis中儲存一個key,如果redis中有key表示有人正在使用這把鎖 其他使用者不允許操作.如果redis中沒有key ,則表示我可以使用這把鎖.
4.風險: 如何解決死鎖問題. 設定超時時間.
在這裡插入圖片描述

3. SpringBoot整合Redis

3.1 編輯組態檔 redis.pro

說明:由於該設定被其他的專案共同使用,則應該寫到jt-common中.
在這裡插入圖片描述

3.2 編輯設定類

說明: 編輯redis設定類.將Jedis物件交給Spring容器進行管理.

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {

    @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private Integer port;

    @Bean
    public Jedis jedis(){

        return new Jedis(host,port);
    }
}

3.3 物件與JSON轉化 ObjectMapper介紹

3.3.1 簡單物件轉化

/**
     * 測試簡單物件的轉化
     */
    @Test
    public void test01() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("商品詳情資訊")
                .setCreated(new Date()).setUpdated(new Date());
        //物件轉化為json
        String json = objectMapper.writeValueAsString(itemDesc);
        System.out.println(json);

        //json轉化為物件
        ItemDesc itemDesc2 = objectMapper.readValue(json, ItemDesc.class);
        System.out.println(itemDesc2.getItemDesc());
    }

3.3.2 集合物件轉化

 /**
     * 測試集合物件的轉化
     */
    @Test
    public void test02() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("商品詳情資訊1")
                .setCreated(new Date()).setUpdated(new Date());
        ItemDesc itemDesc2 = new ItemDesc();
        itemDesc2.setItemId(100L).setItemDesc("商品詳情資訊2")
                .setCreated(new Date()).setUpdated(new Date());
        List<ItemDesc> lists = new ArrayList<>();
        lists.add(itemDesc);
        lists.add(itemDesc2);
        //[{key:value},{}]
        String json = objectMapper.writeValueAsString(lists);
        System.out.println(json);

        //將json串轉化為物件
        List<ItemDesc> list2 = objectMapper.readValue(json, lists.getClass());
        System.out.println(list2);
    }

3.4 編輯工具API

package com.jt.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jt.pojo.ItemDesc;
import org.springframework.util.StringUtils;

public class ObjectMapperUtil {

    /**
     * 1.將使用者傳遞的資料轉化為json串
     * 2.將使用者傳遞的json串轉化為物件
     */
    private static final ObjectMapper MAPPER = new ObjectMapper();

     //1.將使用者傳遞的資料轉化為json串
    public static String toJSON(Object object){

        if(object == null) {
            throw new RuntimeException("傳遞的資料為null.請檢查");
        }

        try {
            String json = MAPPER.writeValueAsString(object);
            return json;
        } catch (JsonProcessingException e) {
            //將檢查異常,轉化為執行時異常
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    //需求: 要求使用者傳遞什麼樣的型別,我返回什麼樣的物件  泛型的知識
    public static <T> T toObj(String json,Class<T> target){
        if(StringUtils.isEmpty(json) || target ==null){
            throw new RuntimeException("引數不能為null");
        }
        try {
           return  MAPPER.readValue(json, target);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

3.5 商品分類的快取實現

3.5.1 實現步驟

1.定義Redis中的key key必須唯一不能重複. 存 取 key = 「ITEM_CAT_PARENTID::70」
2.根據key 去redis中進行查詢 有資料 沒有資料
3.沒有資料 則查詢資料庫獲取記錄 之後將資料儲存到redis中 方便後續使用.
4.有資料 表示使用者不是第一次查詢 可以將快取資料直接返回即可.

3.5.2 編輯ItemCatController

在這裡插入圖片描述

3.5.3 編輯ItemCatService

@Override
    public List<EasyUITree> findItemCatListCache(Long parentId) {
        //0.定義公共的返回值物件
        List<EasyUITree> treeList = new ArrayList<>();
        //1.定義key
        String key = "ITEM_CAT_PARENTID::"+parentId;
        //2.檢索redis伺服器,是否含有該key

        //記錄時間
        Long startTime = System.currentTimeMillis();
        if(jedis.exists(key)){
            //資料存在
            String json = jedis.get(key);
            Long endTime = System.currentTimeMillis();
            //需要將json串轉化為物件
            treeList = ObjectMapperUtil.toObj(json,treeList.getClass());
            System.out.println("從redis中獲取資料 耗時:"+(endTime-startTime)+"毫秒");
        }else{
            //3.資料不存在  查詢資料庫
            treeList = findItemCatList(parentId);
            Long endTime = System.currentTimeMillis();
            //3.將資料儲存到快取中
            String json = ObjectMapperUtil.toJSON(treeList);
            jedis.set(key, json);
            System.out.println("查詢資料庫 耗時:"+(endTime-startTime)+"毫秒");
        }
        return treeList;
    }

3.5.4 使用Redis的速度差

在這裡插入圖片描述

4. AOP實現Redis快取

4.1 如何理解AOP

名稱: 面向切面程式設計
作用: 降低系統中程式碼的耦合性,並且在不改變原有程式碼的條件下對原有的方法進行功能的擴充套件.
公式: AOP = 切入點表示式 + 通知方法

4.2 通知型別

1.前置通知 目標方法執行之前執行
2.後置通知 目標方法執行之後執行
3.異常通知 目標方法執行過程中丟擲異常時執行
4.最終通知 無論什麼時候都要執行的通知
特點: 上述的四大通知型別 不能干預目標方法是否執行.一般用來做程式執行狀態的記錄.監控

5.環繞通知 在目標方法執行前後都要執行的通知方法 該方法可以控制目標方法是否執行.joinPoint.proceed(); 功能作為強大的.

4.3 切入點表示式

理解: 切入點表示式就是一個程式是否進入通知的一個判斷(IF)
作用: 當程式執行過程中 ,**滿足了切入點表示式時才會去執行通知方法,**實現業務的擴充套件.
種類(寫法):
1. bean(bean的名稱 bean的ID) 只能攔截具體的某個bean物件 只能匹配一個物件
lg: bean(「itemServiceImpl」)
2. within(包名.類名) within(「com.jt.service.*」) 可以匹配多個物件
粗粒度的匹配原則 按類匹配

	  3. execution(返回值型別 包名.類名.方法名(參數列))   最為強大的用法
	  lg : execution(* com.jt.service..*.*(..))
	  		返回值型別任意  com.jt.service包下的所有的類的所有的方法都會被攔截.
	  4.@annotation(包名.註解名稱)  按照註解匹配.