主頁 > 軟體設計 > 微服務入門(三)反向代理、負載均衡、Nginx、快取、分布式鎖、Redisson、SpringCache

微服務入門(三)反向代理、負載均衡、Nginx、快取、分布式鎖、Redisson、SpringCache

2021-10-22 07:39:36 軟體設計

反向代理

代理其實就是一個中介,A和B本來可以直連,中間插入一個C,C就是中介,根據代理的角色,可以分為正向代理,反向代理
正向代理即是客戶端代理, 代理客戶端, 服務端不知道實際發起請求的客戶端.
在這里插入圖片描述
比如我們國內訪問谷歌,直接訪問訪問不到,我們可以通過一個正向代理服務器,請求發到代理服務器,代理服務器能夠訪問谷歌,這樣由代理去谷歌取到回傳資料,再回傳給我們,這樣我們就能訪問谷歌了
這個現實中可以類比為買票的黃牛

反向代理即是服務端代理, 代理服務端, 客戶端不知道實際提供服務的服務端
在這里插入圖片描述
反向代理(Reverse Proxy)實際運行方式是指以代理服務器來接受internet上的連接請求,然后將請求轉發給內部網路上的服務器,并將從服務器上得到的結果回傳給internet上請求連接的客戶端,此時代理服務器對外就表現為一個服務器
反向代理的作用:
(1)保證內網的安全,阻止web攻擊,大型網站,通常將反向代理作為公網訪問地址,Web服務器是內網

(2)負載均衡,通過反向代理服務器來優化網站的負載
反向代理在現實中可以類比為租房的中介

Nginx

使用Nginx作為反向代理服務器
本機搭建域名環境
如果正式發布,需要購買域名什么的,測驗就在本地搭建,搭建步驟:
修改host檔案,是用于本地dns服務的,采用ip 域名的格式寫在一個文本檔案當中,其作用就是將一些常用的網址域名與其對應的IP地址建立一個關聯“資料庫”,當用戶在瀏覽器中輸入一個網址時,系統會首先自動從Hosts檔案中尋找對應的IP地址,一旦找到,系統會立即打開對應網頁,如果沒有找到,則系統再會將網址提交DNS域名決議服務器進行IP地址的決議,
這個檔案的位置在C:\Windows\System32\drivers\etc下
在這里插入圖片描述
如果嫌每次修改麻煩,可以使用SwitchHosts,這個可以自己嘗試,我電腦使用這個容易死機,就先不用了
因為我在本地測驗的,所以把本地的localhost修改為一個域名,比如mydomain.com,如果有虛擬機或者其他環境,可以換成對應的ip
在這里插入圖片描述
然后我訪問服務,http://127.0.0.1:7000/coupon/coupon/test,然后修改為對應域名

http://mydomain.com:7000/coupon/coupon/test

同樣可以訪問

Nginx配置
上邊演示的只是本地環境,如果在其他服務上,安裝nginx,這個域名對應那個nginx的ip地址,然后在nginx里邊對訪問進行分配,根據規則,對不同url分配不同的服務
Nginx下載解壓,然后在安裝目錄啟動命令列,運行nginx.exe,

E:\springbootLearn\nginx-1.20.1\nginx-1.20.1>nginx.exe

然后在瀏覽器輸入http://localhost:8080/,如果出現
在這里插入圖片描述
說明啟動成功,然后打開conf目錄下的nginx.conf,在里邊進行配置,比如這里我訪問http://mydomain.com:9001/coupon/coupon/test,正式環境,這個域名對應的ip設定一下

server {
        listen       9001;
        server_name  mydomain.com;

		location ~ /coupon/ {           
			proxy_pass http://127.0.0.1:7000;
		}
	}

這樣訪問上一個url,請求里包含coupon,就可以跳轉到對應ip的地址,這個正式發布,127.0.0.1可以改為對應服務的地址
Nginx基本使用

負載均衡

在這里插入圖片描述
負載均衡,就是將負載分攤到多個服務器,有多種演算法,這個在nginx里邊配置,就需要配置多個服務器地址
nginx負載均衡

http {
   upstream gulimail{
        server 127.0.0.1:7000;
        server 192.168.0.28:8002;
    }
	
	server {
        listen       80;
        server_name  gulimail.com;

		location / {           
			proxy_pass http://gulimail
			proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		}
		
	}

}

gulimail是服務的域名或者專案的代號,然后設定這個proxy_set_header Host $host;原因是nginx會丟失Host,不加上,網關攔截host會失敗
專案中,gateWay網關可以設定為

        - id: gulimail_host_route
           uri: lb://gulimall-product
          predicates:
            - Host=**.gulimail.com,gulimail.com

攔截到這個gulimail.com,就會跳轉到商品服務
這種粗粒度的匹配最后放在最后,放在前邊,會直接攔截掉一些請求,走不到下邊配置的匹配

- id: product_route
          uri: lb://gulimall-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}

像這樣的可能無法匹配,在前邊就被攔截了

按我的理解,總結一下流程,首先在本地新建域名,對應nginx的ip地址,然后再配置nginx,根據url來分配對應的服務

動靜分離

專案中,有些請求是需要后臺處理的(如:.jsp,.do等等),有些請求是不需要經過后臺處理的(如:css、html、jpg、js等等檔案),這些不需要經過后臺處理的檔案稱為靜態檔案,否則動態檔案,如果將靜態檔案也放在服務器,訪問時候性能會降低,可以把靜態資源放到另一個服務器,比如將靜態資源放到nginx中,動態資源轉發到tomcat服務器中,
在這里插入圖片描述
nginx配置
通過location對請求url進行匹配即可,在/Users/Hao/Desktop/Test(任意目錄)下創建 /static/imgs 配置如下

###靜態資源訪問
server {
  listen       80;
  server_name  static.haoworld.com;
  location /static/imgs {
       root /Users/Hao/Desktop/Test;
       index  index.html index.htm;
   }
}
###動態資源訪問
 server {
  listen       80;
  server_name  www.haoworld.com;
    
  location / {
    proxy_pass http://127.0.0.1:8080;
     index  index.html index.htm;
   }
}

參考文章:
【Nginx】實作動靜分離
Nginx實作動靜分離
Nginx的動靜分離

快取

為了系統性能的提升, 我們一般都會將部分資料放入快取中, 加速訪問, 而 db 承擔資料落
盤作業,
哪些資料適合放入快取?
1.即時性、 資料一致性要求不高的
2. 訪問量大且更新頻率不高的資料(讀多, 寫少)
在這里插入圖片描述
偽代碼:

data = cache.load(id);//從快取加載資料
If(data == null){
	data = db.load(id);//從資料庫加載資料
	cache.put(id,data);//保存到 cache 中
} 
return data;

最簡單的快取是本地快取,在代碼里寫一個Map,然后查詢資料之前,查詢Map里是否包含資料,沒有就查詢資料庫,查詢出資料,保存一份到Map中,
在這里插入圖片描述
本地快取在分布式下會出現問題,負載均衡可能訪問A,B,C三臺服務器,有可能更改A服務器的快取,然后第二次到B服務器訪問,B服務器快取還是老的,沒辦法同步修改快取,保持資料一致性
分布式情況下,將快取提取出來,所有服務器訪問和操作同一快取
在這里插入圖片描述注意: 在開發中, 凡是放入快取中的資料我們都應該指定過期時間, 使其可以在系統即使沒有主動更新資料也能自動觸發資料加載進快取的流程, 避免業務崩潰導致的資料永久不一致問題
spring boot整合redis
pom.xml增加參考

<dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

application.yml中配置

 redis:
    host: 192.168.56.10
    port: 6379

使用 RedisTemplate 操作 redis

@Autowired
StringRedisTemplate stringRedisTemplate;
@Test
public void testStringRedisTemplate(){
	ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
	ops.set("hello","world_"+ UUID.randomUUID().toString());
	String hello = ops.get("hello");
	System.out.println(hello);
}

Idea搜索jar包中的檔案,快捷鍵是ctrl+N,搜索redisautoconfiguration,
在這里插入圖片描述
在這里插入圖片描述
包含RedisTemplate和StringRedisTemplate,一個是任意型別的,另一個是方便操作字串的
在程式中加入快取邏輯

 public Map<String, List<Catelog2Vo>> getCatelogJson2() {
        //加入快取邏輯,快取中存的資料是JSON字串
        //JSON跨語言,跨平臺兼容
        //從快取中取出的資料要逆轉為能用的物件型別,序列化與發序列化
        String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON");
        if (StringUtils.isEmpty(catalogJSON)) {
            //快取中沒有資料,查詢資料庫
            Map<String, List<Catelog2Vo>> catelogJsonFromDb = getCatelogJsonFromDbWithRedisLock();
            //查到的資料再放入快取,將物件轉為JSON放入快取中
            String s = JSON.toJSONString(catelogJsonFromDb);
            stringRedisTemplate.opsForValue().set("catalogJSON", s, 1, TimeUnit.DAYS);
            return catelogJsonFromDb;
        }
        Map<String, List<Catelog2Vo>> result = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
        });
        return result;
    }

因為JSON是跨平臺的,所以redis里保存JSON字串,然后保存和取出都進行序列化和反序列化
壓力測驗發現,會產生堆外記憶體溢位(OutOfDirectMemoryError),這個是因為spring boot 2.0以后,默認使用lettuce操作Redis客戶端,它使用netty進行網路通信,lettuce的bug導致堆外記憶體溢位,解決方式有兩個,第一個是升級lettuce的客戶端,另一個是切換到jedis
切換jedis

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
	<exclusion>
		<groupId>io.lettuce</groupId>
		<artifactId>lettuce-core</artifactId>
	</exclusion>
</exclusions>
</dependency>
<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
</dependency>

lettuce和jedis是操作redis的底層客戶端,spring再次封裝redisTemplate,通過redisTemplate進行操作,不需要關心底層客戶端
高并發下快取會出現的問題
快取穿透
在這里插入圖片描述
快取雪崩
在這里插入圖片描述
快取擊穿
在這里插入圖片描述
解決辦法:空結果快取,解決快取穿透問題,設定過期時間(加隨機值),解決快取雪崩問題,設定鎖,解決快取擊穿問題

加鎖

本地鎖(單體服務)

  synchronized (this) {
            String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON");
            if (StringUtils.isEmpty(catalogJSON)) {
                Map<String, List<Catelog2Vo>> result = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
                });
                return result;
            }
        }

只要是同一把鎖,就能鎖住需要這個鎖的所有行程,synchronized(this):springboot所有的組件在容器中都是單例的,
本地鎖:synchronized,JUC(Lock),只能鎖住當前行程,在分布式情況下,想要鎖住所有,必須使用分布式鎖
使用本地鎖的時候,要把查詢資料庫和存放快取放在同一鎖內,如果放入快取在鎖外,高并發下,就會出現1號執行緒處理完邏輯,釋放鎖,2號執行緒獲取鎖,1號執行緒存入快取,在存快取的程序中,可能網路延遲等原因,2號執行緒讀取快取的時候,1號還沒有存入快取,查詢兩邊資料庫
在這里插入圖片描述
分布式鎖
分布式鎖演進原理:分布式鎖就是多個服務去占鎖,占住的執行邏輯,然后釋放鎖,在一個服務占鎖期間,其他服務只能等待,等待釋放鎖,然后再去占鎖,等待可以使用自旋方式
在這里插入圖片描述
分布式程鎖Redis
階段一:
在這里插入圖片描述
階段二
在這里插入圖片描述
階段三
在這里插入圖片描述
階段四
在這里插入圖片描述
最終結果
在這里插入圖片描述

 //分布式程鎖Redis
    public Map<String, List<Catelog2Vo>> getCatelogJsonFromDbWithRedisLock() {
        //占分布式鎖,同時設定鎖過期時間,必須和加鎖同步原子操作
        String uuid = UUID.randomUUID().toString();
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111", 300, TimeUnit.SECONDS);
        if (lock) {
            //加鎖成功,執行業務
            Map<String, List<Catelog2Vo>> dataFromDB;
            try {
                dataFromDB = getDataFromDB();
            } finally {
                String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1])else return 0 end";
                //洗掉鎖,Lua腳本
                Long lock1 = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock", uuid));
            }
            return dataFromDB;
        } else {
            //加鎖失敗,休眠2秒,重試
            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return getCatelogJsonFromDbWithRedisLock();//自旋
        }
    }

不要設定固定的字串,而是設定為隨機的大字串,可以稱為token,
通過腳本洗掉指定鎖的key,而不是DEL命令,
上述優化方法會避免下述場景:
a客戶端獲得的鎖(鍵key)已經由于過期時間到了被redis服務器洗掉,但是這個時候a客戶端還去執行DEL命令,而b客戶端已經在a設定的過期時間之后重新獲取了這個同樣key的鎖,那么a執行DEL就會釋放了b客戶端加好的鎖,

解鎖腳本的一個例子將類似于以下
if redis.call("get",KEYS[1]) == ARGV[1]
then
    return redis.call("del",KEYS[1])
else
    return 0
end

核心就是原子加鎖,原子解鎖
Redisson
Redisson是Redis官方推薦的Java版的Redis客戶端,它提供的功能非常多,也非常強大,可以使用它來實作分布式鎖
Redisson
maven引入

<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.16.3</version>
</dependency> 

有中文檔案可以查看(Github網站經常訪問不通,,,,,)
Wiki Home
config目錄下,新建MyRedissonConfig

@Configuration
public class MyRedissonConfig {

    @Bean(destroyMethod = "shutdown")
    RedissonClient redisson() throws IOException{
        Config config = new Config();
        //集群模式
        //config.useClusterServers().addNodeAddress("127.0.0.1:7004","127.0.0.1:7001");
        //單節點模式
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
        return Redisson.create(config);
    }
}

測驗redisson鎖

分布式鎖和同步器

 @ResponseBody
    @GetMapping("/hello")
    public String hello() {
        //只有鎖名字一樣,就是同一把鎖
        RLock lock = redisson.getLock("my-lock");
        //阻塞式等待,默認加的鎖都是30s時間,
        //1)、鎖的自動續期,如果業務超長,運行期間自動給鎖續上新的30s,不用擔心業務時間長,鎖自動過期被刪掉
        //2)、加鎖的業務只要運行完成,就不會給當前鎖續期,即使不手動解鎖,鎖默認在30s以后自動洗掉
        lock.lock();
        try {
            System.out.println("加鎖測驗,執行業務..." + Thread.currentThread().getId());
            Thread.sleep(10000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解鎖
            System.out.println("釋放鎖" + Thread.currentThread().getId());
            lock.unlock();
        }

        return "hello";
    }

Redisson鎖的特點:有個看門狗機制,在鎖到期之前自動續期

  //阻塞式等待,默認加的鎖都是30s時間,
        //1)、鎖的自動續期,如果業務超長,運行期間自動給鎖續上新的30s,不用擔心業務時間長,鎖自動過期被刪掉
        //2)、加鎖的業務只要運行完成,就不會給當前鎖續期,即使不手動解鎖,鎖默認在30s以后自動洗掉

如果自己指定解鎖時間,一定要大于業務時間,不會自動續期,可能在業務執行之前就已經解鎖了

// 加鎖以后10秒鐘自動解鎖
// 無需呼叫unlock方法手動解鎖
lock.lock(10, TimeUnit.SECONDS);

如果傳遞了鎖的超時時間,就發送給redis執行腳本,進行占鎖,默認時間就是我們制定的時間
如果未指定鎖的超時時間,就是用30*1000[LockWatchdogTimeout看門狗的默認時間]
在這里插入圖片描述
leaseTime!=-1L,說明自己指定了時間,進入
在這里插入圖片描述
執行腳本
未指定時間,從配置拿,

this.tryLockInnerAsync(this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),
在這里插入圖片描述

只要占鎖成功,就會啟動一個定時任務,重新給鎖設定過期時間,新的過期時間就是看門狗的默認時間

 this.internalLockLeaseTime = commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout();
 ...
protected RFuture<Boolean> renewExpirationAsync(long threadId) {
        return this.commandExecutor.evalWriteAsync(this.getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then redis.call('pexpire', KEYS[1], ARGV[1]); return 1; end; return 0;", Collections.singletonList(this.getName()), new Object[]{this.internalLockLeaseTime, this.getLockName(threadId)});
    }

在這里插入圖片描述
internalLockLeaseTime / 3L ,3分之一看門狗時間會自動續期,續到30s
最佳實戰:使用 lock.lock(30, TimeUnit.MINUTES);,省掉整個續期程序,手動解鎖

讀寫鎖
如果有寫操作,加寫鎖,其他讀取這個結果的都要等待,如果只是讀取,互不影響,如果都是寫,需要排隊等待

 @GetMapping("/write")
    @ResponseBody
    public String writeValue() {
        RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
        String s = "";
        //加寫鎖
        RLock rLock = lock.writeLock();
        try {
            rLock.lock();
            s = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set("writeValue", s);
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }
        return s;
    }

    @GetMapping("/read")
    @ResponseBody
    public String redValue() {
        RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
        String s = "";
        //獲取讀鎖,如果有其他的執行緒在寫操作, 則需要等到寫鎖釋放鎖以后才可進行讀的操作
        RLock rLock = lock.readLock();
        rLock.lock();
        try {
            s = redisTemplate.opsForValue().get("writeValue");
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }
        return  s;
    }

寫在操作的時候,讀等待,如果操作完成,寫釋放鎖,然后讀進行操作,獲得資料,讀寫鎖保證一定能讀到最新資料,修改期間,寫鎖是一個排他鎖(互斥鎖、獨享鎖),讀鎖是一個共享鎖,寫鎖沒釋放,讀就必須等待

+讀:相當于無鎖,并發讀,只會在redis中記錄好所有當前的讀鎖,同時加鎖成功
寫+讀:等待寫鎖釋放
寫+寫:阻塞方式
讀+寫:讀的時候,寫入資料,有讀鎖,寫也需要等待
只要有寫存在,都需要等待

信號量(Semaphore)
基于Redis的Redisson的分布式信號量(Semaphore)Java物件RSemaphore采用了與java.util.concurrent.Semaphore相似的介面和用法,同時還提供了異步(Async)、反射式(Reactive)和RxJava2標準的介面,

@GetMapping("/park")
    @ResponseBody
    public String park() throws InterruptedException {

        RSemaphore semaphore = redisson.getSemaphore("park");
        
//        semaphore.acquire(); // 阻塞等待,獲取一個信號,獲取一個值,占一個車位

        boolean flag = semaphore.tryAcquire();//不阻塞,獲取不到直接回傳false,可以做限流
        

        if (flag) {
            return "停車了!";
        } else {
            return "車庫滿了!";
        }

    }

 @GetMapping("/go")
    @ResponseBody
    public String go() {

        RSemaphore semaphore = redisson.getSemaphore("park");

        semaphore.release();//釋放一個車位

        return "車開走了!";
    }

閉鎖(CountDownLatch)

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(3);//等待3個
latch.await();//等待閉鎖都完成


RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();//計數減一

分布式鎖Redisson

 public Map<String, List<Catelog2Vo>> getCatelogJsonFromDbWithRedissonLock() {
        //鎖的名字,鎖的粒度,越細越快
        RLock lock = redisson.getLock("catalogJson-lock");
        lock.lock();
        //加鎖成功,執行業務
        Map<String, List<Catelog2Vo>> dataFromDB;
        try {
            dataFromDB = getDataFromDB();
        } finally {
           lock.unlock();
        }
        return dataFromDB;
    }

快取資料一致性

雙寫模式
在這里插入圖片描述
雙寫模式:寫入資料庫,同時寫入快取
失效模式
在這里插入圖片描述
失效模式:讀取資料庫,然后洗掉快取,下一次看快取中沒有,重新從資料庫獲取,可能出現的問題:操作1寫資料庫db-1,然后刪掉快取,操作2寫入資料庫db-2,同時操作時間比較長,然后操作3讀快取,快取已經被操作1洗掉,操作3讀取db-1,然后操作2洗掉快取,操作3更新快取,結果快取中保存db-1的資料,加入讀寫鎖,如果經常寫,經常讀,會有一點影響性能,偶爾讀寫影響不大
在這里插入圖片描述

canal,譯意為水道/管道/溝渠,主要用途是基于 MySQL 資料庫增量日志決議,提供增量資料訂閱和消費,
在這里插入圖片描述
超詳細的Canal入門,看這篇就夠了!

SpringCache快取簡化分布式快取的開發

整合springcache簡化快取開發
1) 、引入依賴
.spring-boot-starter-cache、 spring-boot-starter-data-redis2)
2)、寫配置
(i)、自動配置了哪些
	CacheAuroConfiguration會匯入 RedisCacheConfiguration;
	自動配好了快取管理器RedisCachcManager
( 2)、配置使用redis作為快取
	spring.cache.type=redis
3)、測驗使用快取
	@Cacheable: Triggers cache popuLation.:觸發將資料保存到快取的操作
	@CacheEvict: Triggers cache eviction.:觸發將資料從快取洗掉的操作
	@CachePut: Updates the cache without interfering with the method execution.:不影響方法執行更新快取
	@Caching:Regroups multiple cache operations to be applied on a method.:組合以上多個操作
	@CacheConfig: Shares some common cache-related settings at class-Level.:在類級別共享相同的快取配置
	1)、開啟快取功能@EnabLeCaching
	2) 、只需要使用注解就能完成快取操作
4) 、原理:
CacheAutoConfiguration -> RedisCacheConfiguration ->
自動配置了RedisCacheManager->初始化所有的快取->每個快取決定使用什么配置->如果redisCacheConfiguration有就用已有的,沒有就用默認配置
->想改快取的配置,只需要給容器中放一個RedisCacheConfiguration即可->就會應用到當前RedisCacheManager管理的所有快取磁區中

引入 pom依賴

		<!-- 因為lettuce導致堆外記憶體溢位 這里暫時排除他 使用jedis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
		<!--  分布式快取 需要和redis配合使用 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>

撰寫組態檔

# 配置使用redis作為快取
spring.cache.type=redis
# 以毫秒為單位 1小時
spring.cache.redis.time-to-live=3600000
# 配置快取名的前綴 如果沒配置則使用快取名作為前綴
# spring.cache.redis.key-prefix=CACHE_
# 配置前綴是否生效  默認為ture
#spring.cache.redis.use-key-prefix=false
# 是否快取空值 默認為true,防止快取穿透
spring.cache.redis.cache-null-values=true
#spring.cache.cache-names=

開啟快取
@EnableCaching
使用快取
1)@Cacheable: 觸發將資料寫入快取

     /**
     * 1、每一個需要快取的資料我們都要來指定放到哪個名字的快取;【按照業務型別來劃分取名】
	 *@Cacheable(value = {"category"})
     * 	代表當前方法的結果需要快取 如果快取中有,方法不呼叫;
     * 	如果快取中沒有,會呼叫該方法,最后將方法的結果放入快取
     * 2、默認行為
	 *	1)如果快取中有,方法不呼叫
	 *  2)key默認自動生成,快取的名字::SimpleKey[](自動生成的key值)
	 *	3)快取的value值,默認使用jdk序列化機制,將序列化后的資料存到redis
	 *  4)默認ttl時間 -1
     * 3、自定義
     * 	1)指定快取生成指定的key key屬性指定,接受一個spel
     * 	2)指定快取的過期時間  組態檔修改ttl
     * 	3)將快取的value保存為json格式
     * 4、快取的三大問題
     * 	1)快取擊穿: springcache 默認是不加鎖的,需要設定sync = true
     * 	2)
     */
@Cacheable(value = {"category"}, key = "#root.method.name", sync = true)
public List<CategoryEntity> getLeve1Categorys() {
		System.out.println("getLeve11categorys.....");
long l = System.currentTimeMillis();
List<CategoryEntity> categoryEntities = baseMapper.selectList(new Querywrapper<CategoryEntity>()return categoryEntities;
		
}

@CacheEvict: 觸發將資料從快取中洗掉

/**
級聯更新所有關聯的資料
* cacheEvict:失效模式
*1、同時進行多種快取操作@caching
* 2、指定洗掉某個磁區下的所有資料ecacheEvict(valug,= "category" , alLEntries = true)*3、存盤同一型別的資料,都可以指定成同一個磁區,磁區名默認就是快取名
*@param category
*/
//    @Caching(evict = {
//            @CacheEvict(value = "category", key = "'getLeve1Categorys'"),
//            @CacheEvict(value = "category", key = "'getCatalogJson'")
//    })
//     allEntries = true 洗掉category 磁區的所有快取 批量清除
   // @CacheEvict(value = {"category"}, allEntries = true) //失效模式
    @CachePut//雙寫模式
    public void updateCascade(CategoryEntity category) {
    }

@CachePut: 不影響方法執行更新快取

@Caching: 組合以上快取的操作

@Caching(evict = {
            @CacheEvict(value = "category", key = "'getLeve1Categorys'"),
            @CacheEvict(value = "category", key = "'getCatalogJson'")
    })

@CacheConfig: 在類級別共享相同的快取配置

修改快取存盤的序列化器

@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching
@Configuration
public class MyCacheConfig {

/**
組態檔中的東西沒有用上;
*
1、原來和組態檔系結的配置類是這樣子的
@ConfigurationProperties(prefix = "spring.cache")public cLass CacheProperties
*2、要讓他生效:@EnableConfigurationProperties(CacheProperties.class)
*@return
*/

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();

        // 修改默認的序列化器
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        CacheProperties.Redis redisProperties = cacheProperties.getRedis();

        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }

        return config;
    }
}

Spring-Cache的不足;
1)、讀模式:
快取穿透:查詢一個null資料,解決:快取空資料; ache-null-values=true
快取擊穿:大量并發進來同時查詢一個正好過期的資料,解決,加鎖;springcache 默認是不加鎖的,需要設定sync = true
快取雪崩:大量的key同時過期,解決:加隨機時間,加上過期時間,: spring.cache.redis.time-to-live=3600000
2) 、寫模式:(快取與資料庫一致)
1)、讀寫加鎖,
2)、引入Canal,感知到MysQL的更新去更新資料庫
3)、讀多寫多,直接去資料庫查詢就行

總結:
常規資料(讀多寫少,即時性,- 致性要求不高的資料) ;完全可以使用Spring-Cache
特殊資料:特殊設計
I

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/330269.html

標籤:其他

上一篇:給自己的網站接入https居然需要升級centos內核?

下一篇:Day425&426.購物車服務 -谷粒商城

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more