2實(shí)現(xiàn)數(shù)據(jù)緩存和se共享_第1頁(yè)
2實(shí)現(xiàn)數(shù)據(jù)緩存和se共享_第2頁(yè)
2實(shí)現(xiàn)數(shù)據(jù)緩存和se共享_第3頁(yè)
已閱讀5頁(yè),還剩4頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

付費(fèi)下載

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、在實(shí)際工作中 Redis 最常用的兩個(gè)使用場(chǎng)景是什么?一個(gè)是數(shù)據(jù)緩存,另一個(gè)就是 Session 共享。Spring Boot 這兩個(gè)場(chǎng)景都做了一些優(yōu)化,讓我們?cè)趯?shí)際項(xiàng)目中使用非常的方便。數(shù)據(jù)緩存使用 Redis 做為數(shù)據(jù)緩存是最常用的場(chǎng)景了。我們知道絕大多數(shù)的 /系統(tǒng),最先遇到的一個(gè)性能瓶頸就是數(shù)據(jù)庫(kù),使用 Redis 做數(shù)據(jù)庫(kù)的前置緩存,可以非常有效的降低數(shù)據(jù)庫(kù)的 ,從而提升整個(gè)系統(tǒng)的響應(yīng)效率和并發(fā)量。Spring Boot 也提供了非常簡(jiǎn)單的解決方案,這里給大家演示最的三個(gè)注解: Cacheable 、 CacheEvict 、 CachePut 。spring-boot-starter

2、-cache在開始使用這三個(gè)注解之前,給大家介紹一個(gè)新的組件 spring-boot-starter-cache 。<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency>spring-boot-starter-cache 是 Spring Boot 提供緩存支持的 starter 包。 spring-boot-starter- cache 會(huì)

3、進(jìn) 行 緩 存 的 自 動(dòng) 化 配 置 和 識(shí) 別 , Spring Boot 為 Redis 自 動(dòng) 配 置 了RedisCacheConfiguration 等信息。spring-boot-starter-cache 中的注解也主要是使用了 Spring Cache 提供的支持。CacheableCacheable 是用來(lái) 方法是可緩存的,將結(jié)果 到緩存中以便后續(xù)使用相同參數(shù)調(diào)用時(shí)不需執(zhí)行實(shí)際的方法,直接從緩存中取值。 Cacheable 可以標(biāo)記在一個(gè)方法上,也可以標(biāo)記在一個(gè)類上。當(dāng)標(biāo)記在一個(gè)方法上時(shí)表示該方法是支持緩存的,當(dāng)標(biāo)記在一個(gè)類上 表示該類所有的方法都是支持緩存的。先來(lái)一個(gè)最簡(jiǎn)

4、單的例子體驗(yàn)一下:RequestMapping("/hello") Cacheable(value="helloCache") public String hello(String name) System.out.println("沒(méi)有走緩存!");return "hello "+name;來(lái)測(cè)試一下,啟動(dòng)項(xiàng)目后 : 地址:方法,內(nèi)容直接由緩存返回。輸出: 沒(méi)有走緩存! ,再次,輸出欄沒(méi)有變化,說(shuō)明這次沒(méi)有走 hello() 這個(gè)Cacheable(value="helloCache") 這個(gè)

5、注釋的意思是, 當(dāng)調(diào)用這個(gè)方法的時(shí)候, 會(huì)從一個(gè)名叫helloCache 的緩存中 ,如果沒(méi)有,則執(zhí)行實(shí)際的方法(也 數(shù)據(jù)庫(kù)),并將執(zhí)行的結(jié)果1/9存入緩存中,否則返回緩存中的對(duì)象。這里的緩存中的 key 就是參數(shù) name,value 就是返回的String 值。Cacheable 支持如下幾個(gè)參數(shù):value:緩存的名稱。key:緩存的 key,可以為空,如果指定要按照 SpEL 表達(dá)式編寫,如果不指定,則缺省按照方法的所有參數(shù)進(jìn)行組合。condition:觸發(fā)條件,只有滿足條件的情況才會(huì)加入緩存,默認(rèn)為空,既表示全部都加入緩 存,支持 SpEL。把上面的方法稍微改成這樣:RequestM

6、apping("/condition") Cacheable(value="condition",condition="#name.length() <= 4") public String condition(String name) System.out.println("沒(méi)有走緩存!");return "hello "+name;啟動(dòng)后瀏覽器執(zhí)行:, 第一次輸出欄輸出: 沒(méi)有走緩存! , 再次執(zhí)行無(wú)輸出表明, 已經(jīng)走緩存。瀏覽器執(zhí)行:?name=ityouknow ,瀏覽器執(zhí)行多次仍

7、然一直輸出: 沒(méi)有走緩存! 說(shuō)明條件 condition 生效。結(jié)合數(shù)據(jù)庫(kù)的使用來(lái)做測(cè)試:RequestMapping("/getUsers") Cacheable(value="usersCache",key="#public List<User> getUsers(Stringname",condition="#name.length() >= 6")name)List<User> users=userRepository.findBy System.out.println(&qu

8、ot;執(zhí)行了數(shù)據(jù)庫(kù)操作");return users;name(name);啟動(dòng)后瀏覽器執(zhí)行:。輸出欄輸出:Hibernate: select user0_.id as id1_0_, user0_.執(zhí)行了數(shù)據(jù)庫(kù)操作2_0_, user0_.name asnas多 次 執(zhí) 行 , 仍 然 輸 出 上 面 的 結(jié) 果 , 說(shuō) 明 每 次 請(qǐng) 求 都 執(zhí) 行 了 數(shù) 據(jù) 庫(kù) 操 作 。 再 輸 入 :進(jìn)試。只有第一次返回了上面的內(nèi)容,再次執(zhí)行輸出欄沒(méi)有變化,說(shuō)明后面的請(qǐng)求經(jīng)從緩存中拿取了數(shù)據(jù)。最后總結(jié)一下:當(dāng)執(zhí)行到一個(gè)被 Cacheable 注解的方法時(shí),Spring 首先檢查 cond

9、ition 條件是否滿足,如果不滿足,執(zhí)行方法,返回;如果滿足,在緩存空間中查找使用 key的對(duì)象,如果找2/9到,將找到的結(jié)果返回,如果沒(méi)有找到執(zhí)行方法,將方法的返回值以 key-value 對(duì)象的方式存入緩存中,然后方法返回。需要注意的是當(dāng)一個(gè)支持緩存的方法在對(duì)象內(nèi)部被調(diào)用時(shí)是觸發(fā)緩存功能的。CachePut項(xiàng)目運(yùn)行中會(huì)對(duì)數(shù)據(jù)庫(kù)的信息進(jìn)行更新,如果仍然使用 Cacheable 就會(huì)導(dǎo)致數(shù)據(jù)庫(kù)的信息和緩存的信息不一致。在以往的項(xiàng)目中,我們一般更新完數(shù)據(jù)庫(kù)后,再手動(dòng)刪除掉 Redis 中對(duì)應(yīng)的緩存, 以保證數(shù)據(jù)的一致性。Spring 提供了另外的一種解決方案,可以優(yōu)雅的去更新緩存。與 Cach

10、eable 不同的是使用 CachePut 標(biāo)注的方法在執(zhí)行前,去檢查緩存中是否存在之前執(zhí)行過(guò)的結(jié)果,而是每次都會(huì)執(zhí)行該方法,并將執(zhí)行結(jié)果以鍵值對(duì)的形式存入指定的緩存中。以上面的方法為例,再來(lái)做一個(gè)測(cè)試:RequestMapping("/getPutUsers") CachePut(value="usersCache",key="#public List<User> getPutUsers(Stringname")name) List<User> users=userRepository.findBy Syst

11、em.out.println("執(zhí)行了數(shù)據(jù)庫(kù)操作");return users;name(name);新增一個(gè) getPutUsers 方法,value、key 設(shè)置和 getUsers 方法保持一致,使用 CachePut。同時(shí)手動(dòng)在數(shù)據(jù)庫(kù)一條 nikename 為:ityouknow 的用戶數(shù)據(jù)。:戶信息,再并沒(méi)有返回用戶昵稱為 ityouknow 的用可以查看到此用戶的就可以看到用戶昵稱為地址:信息,再次地址:ityouknow 的信息了。說(shuō)明執(zhí)行在方法上CachePut 配置方法CachePut 會(huì)自動(dòng)執(zhí)行方法,并將結(jié)果存入緩存。value 緩存的名稱。key 緩存

12、的 key,可以為空,如果指定要按照 SpEL 表達(dá)式編寫,如果不指定,則缺省按照方法的所有參數(shù)進(jìn)行組合。condition 緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進(jìn)行緩存。可以看出 CachePut 的參數(shù)和使用方法基本和 Cacheable 一致。CachePut 也可以標(biāo)注在類上和方法上。CacheEvict3/9CacheEvict 是用來(lái)標(biāo)注在需要清除緩存元素的方法或類上的。當(dāng)標(biāo)記在一個(gè)類上時(shí)表示其中所有的方法的執(zhí)行都會(huì)觸發(fā)緩存的清除操作。CacheEvict 可以指定的屬性有 value、key、condition、all

13、Entries 和 beforeInvocation。其中 value、key 和 condition 的語(yǔ)義與 Cacheable 對(duì)應(yīng)的屬性類似。即 value 表示清除操作是發(fā)生在哪些 Cache 上的(對(duì)應(yīng) Cache 的名稱);key 表示需要清除的是哪個(gè) key,如未指定則會(huì)使用默認(rèn)策略生成的 key;condition 表示清除操作發(fā)生的條件。下面來(lái)介紹一下新出現(xiàn)的兩個(gè)屬性 allEntries 和 beforeInvocation。allEntries 屬性allEntries 是 boolean 類型,表示是否需要清除緩存中的所有元素。默認(rèn)為 false,表示不需要。當(dāng)指定了

14、 allEntries 為 true 時(shí),Spring Cache 將忽略指定的 key。有的時(shí)候我們需要 Cache 一下清除所有的元素,這比一個(gè)一個(gè)清除元素更有效率。在上一個(gè)方法中我們使用注解: CachePut(value="usersCache",key="#name") 來(lái)更新緩存,但如果不寫 key="#name" , Spring Boot 會(huì)以默認(rèn)的 key 值去更新緩存, 導(dǎo)致最上面的方法getUsers() 方法并沒(méi)有獲取最新的數(shù)據(jù)。但是現(xiàn)在使用 CacheEvict 就可以解決這個(gè)問(wèn)題了,它會(huì)將所有以 users

15、Cache 為名的緩存全部清除。來(lái)看個(gè)例子:RequestMapping("/allEntries")CacheEvict(value="usersCache", allEntries=true)public List<User> allEntries(Stringname) List<User> users=userRepository.findBy System.out.println("執(zhí)行了數(shù)據(jù)庫(kù)操作");return users;name(name);手 動(dòng) 修 改 用 戶 表 的 相 關(guān) 信 息 ,

16、 如時(shí) 間 。:?后,再name=ityouknow 發(fā)現(xiàn)緩存中的數(shù)據(jù)并沒(méi)有更新。再次地址會(huì)發(fā)現(xiàn)數(shù)據(jù)已經(jīng)更新,并且輸出欄輸出 執(zhí)行了數(shù)據(jù)庫(kù)操作 ,這表明已經(jīng)將名為 usersCache 的緩存清空。beforeInvocation 屬性清除操作默認(rèn)是在對(duì)應(yīng)方法執(zhí)行之后觸發(fā)的,即方法如果因?yàn)閽伋霎惓6茨芊祷夭粫?huì)觸發(fā)清除操作。使用 beforeInvocation 可以改變觸發(fā)清除操作的時(shí)間,當(dāng)我們指定該屬性值為true 時(shí),Spring 會(huì)在調(diào)用該方法之前清除緩存中的指定元素。RequestMapping("/beforeInvocation")CacheEvict(val

17、ue="usersCache", allEntries=true, beforeInvocation=true)public void beforeInvocation() throw new RuntimeException("test beforeInvocation");來(lái)做一個(gè)測(cè)試,在方法中添加一個(gè)異常,usersCache 的緩存是否被更新。地址:查看4/9按照上面的實(shí)驗(yàn)步驟: 手動(dòng)修改用戶表的相關(guān)信息,name=ityouknow發(fā)現(xiàn)緩存中的數(shù)據(jù):并沒(méi)?訪有更新。再問(wèn)會(huì) 報(bào) 錯(cuò) 誤 , 先 不 用 管 這 里 , 再 次地址會(huì)發(fā)現(xiàn)數(shù)據(jù)已經(jīng)更新

18、,并且輸出欄輸出 執(zhí)行了數(shù)據(jù)庫(kù)操作 。這表明雖然在測(cè)試的過(guò)程中方法拋出了異常,但緩存中名為 usersCache 的被清空。Cacheable 、 CacheEvict 、 CachePut 三個(gè)注解非常靈活,滿足了我們對(duì)數(shù)據(jù)緩存的絕大多數(shù)使用場(chǎng)景,并且使用起來(lái)非常的簡(jiǎn)單而又強(qiáng)大,在實(shí)際工作中我們可以靈活 搭配使用。Session 共享Session什么是 Session由于 HTTP 協(xié)議是無(wú)狀態(tài)的協(xié)議,所以服務(wù)端需要用戶的狀態(tài)時(shí),就需要用某種機(jī)制來(lái)識(shí)具體的用戶。Session 是另一種客戶狀態(tài)的機(jī)制,不同的是 Cookie 保存在客戶端瀏覽器中,而Session 保存在服務(wù)器上??蛻舳藶g覽

19、器服務(wù)器的時(shí)候,服務(wù)器把客戶端信息以某種形式在服務(wù)器上。這就是 Session。客戶端瀏覽器再次時(shí)只需要從該 Session 中查找該客戶的狀態(tài)就可以了。為什么需要 Session 共享在互聯(lián)網(wǎng)行業(yè)中用戶量巨大,往往需要多個(gè)節(jié)點(diǎn)共同對(duì)外提供某一種服務(wù),如下圖:用戶的請(qǐng)求首先會(huì)到達(dá)前置網(wǎng)關(guān),前置網(wǎng)關(guān)根據(jù)路由策略將請(qǐng)求分發(fā)到后端的服務(wù)器,這就會(huì)出現(xiàn)第一次的請(qǐng)求會(huì)交給服務(wù)器 A 處理,下次的請(qǐng)求可能會(huì)是服務(wù)B處理,如果不做 Session 共享的話,就有可能出現(xiàn)用戶在服務(wù) A 登錄了,下次請(qǐng)求的時(shí)候到達(dá)服務(wù) B 又要求用戶重新登錄。前置網(wǎng)關(guān)一般使用 lvs、Nginx 或者 F5 等軟硬件,有些軟件

20、可以指定策略讓用戶每次請(qǐng)求都分發(fā)到同一臺(tái)服務(wù)器中,這也有個(gè)弊端,如果當(dāng)其中一臺(tái)服務(wù) Down 掉之后,就會(huì)出現(xiàn)一批用戶失5/9效。在實(shí)際工作中建議使用外部的緩存設(shè)備來(lái)共享 Session,避免單個(gè)節(jié)點(diǎn)掛掉而影響服務(wù),使用外部緩存 Session 后,我們的共享數(shù)據(jù)都會(huì)放到外部緩存容器中,服務(wù)本身就會(huì)變成無(wú)狀態(tài)的服務(wù),可以隨意的根據(jù)流量的大小增加或者減少負(fù)載的設(shè)備。一般 Tomcat 有自帶的 Session 共享功能,但是使用起來(lái)并不是很便利, 用,最常見的方式就是使用 Redis 來(lái)實(shí)現(xiàn)后端服務(wù)的 Session 共享。也不推薦大規(guī)模的使Spring SessionSpring Sessio

21、n 提供了一套創(chuàng)建和管理 Servlet HttpSession 的方案。Spring Session 提供了集群Session(Clustered Sessions)功能,默認(rèn)采用外置的 Redis 來(lái)Session 共享的問(wèn)題。Session 數(shù)據(jù),以此來(lái)解決Spring Session 為企業(yè)級(jí) Java 應(yīng)用的 session 管理帶來(lái)了革新,使得以下的功能更加容易實(shí)現(xiàn):API 和用于管理用戶會(huì)話的實(shí)現(xiàn)。以應(yīng)用程序容器(即 Tomcat)中性的方式替換 HttpSession。HttpSession -將 session 所保存的狀態(tài)卸載到特定的外部 session中,如 Redis

22、或 Apache Geode 中,它們能夠以于應(yīng)用服務(wù)器的方式提供高質(zhì)量的集群。支持每個(gè)瀏覽器上使用多個(gè) session,從而能夠很容易地構(gòu)建更加豐富的終端用戶體驗(yàn)。session id 如何在客戶端和服務(wù)器之間進(jìn)行交換,這樣的話就能很容易地編寫 RestfulAPI,因?yàn)樗梢詮?HTTP 頭信息中獲取 session id,而不必再依賴于 cookie。當(dāng)用戶使用 WebSocket請(qǐng)求的時(shí)候,能夠保持 HttpSession 處于活躍狀態(tài)。需要說(shuō)明的很重要的一點(diǎn)就是,Spring Session 的項(xiàng)目并不依賴于 Spring 框架,所以,我們甚至能夠?qū)⑵鋺?yīng)用于不使用 Spring 框架

23、的項(xiàng)目中。Spring 為 Spring Session 和 Redis 的集成提供了組件:spring-session-data-redis,下面演示如何使用??焖偌梢胍蕾嚢?lt;dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency>Session 配置Configuration EnableRedisHttpSession(maxIna

24、ctiveIntervalInSeconds = 86400*30) public class SessionConfig 6/9maxInactiveIntervalInSeconds: 設(shè)置 Session 失效時(shí)間,使用 Redis Session 之后, 原 Boot 的 server.session.timeout 屬性不再生效。僅僅需要這兩步 Spring Boot 分布式 Session 就配置完成了。測(cè)試驗(yàn)證在 Web 層寫兩個(gè)方法進(jìn)行驗(yàn)證。RequestMapping(value = "/setSession", method = RequestMetho

25、d.GET) public Map<String, Object> setSession (HttpServletRequest request)Map<String, Object> map = new HashMap<>(); request.getSession().setAttribute("request Url", request.getRequestURL(); map.put("request Url", request.getRequestURL();return map;上面這個(gè)方法就是獲取頁(yè)面的請(qǐng)

26、求地址,并把請(qǐng)求地址放入到 Session 中,并將結(jié)果返回。RequestMapping(value = "/setSession")public Map<String, Object> setSession (HttpServletRequest request) Map<String, Object> map = new HashMap<>(); request.getSession().setAttribute("message", request.getRequestURL(); map.put("

27、request Url", request.getRequestURL();return map;第一個(gè)方法獲取頁(yè)面的請(qǐng)求地址,并把請(qǐng)求地址放入 Key 為 message 的 Session 中,并將結(jié)果返回。RequestMapping(value = "/getSession")public Object getSession (HttpServletRequest request) Map<String, Object> map = new HashMap<>(); map.put("sessionId", re

28、quest.getSession().getId();map.put("message", request.getSession().getAttribute("message");return map;第二個(gè)方式獲取 Session 中的請(qǐng)求中的 Session Id 和 Key 為 message 的信息封裝返回。在測(cè)試前需要將項(xiàng)目 spring-boot-redis-session一份,改名為 spring-boot-redis-session-1 并將端口改為:9090(server.port=9090)。修改完成后依次啟動(dòng)兩個(gè)項(xiàng)目。首先Url&

29、quot;:"8080 端口的服務(wù), 瀏覽器輸入" ;瀏覽器欄輸入:, 返回: "request,返回信息如下:7/9"sessionId":"432765e1-049e-4e76-980c-d7f55a232d42","message":"說(shuō)明 url 地址信息已經(jīng)存入到 Session 中。9090 端口的服務(wù),瀏覽器欄輸入:,返回信息如下:"sessionId":"432765e1-049e-4e76-980c-d7f55a232d42","

30、;message":"通過(guò)對(duì)比發(fā)現(xiàn),8080 和 9090 服務(wù)返回的 Session 信息完全一致,說(shuō)明已經(jīng)實(shí)現(xiàn)了 Session 共享。模擬登錄在實(shí)際中作中常常使用共享 Session 的方式去保存用戶的登錄狀態(tài),避免用戶在不同的頁(yè)面來(lái)回登錄。來(lái)簡(jiǎn)單模擬一下這個(gè)場(chǎng)景,假設(shè)有一個(gè) index 頁(yè)面,必須是登錄的用戶才可以,如果用戶沒(méi)有登錄給出提示。首先在一臺(tái)實(shí)例上登錄后,再次另外一臺(tái)的 index 看它是否需要登錄,來(lái)驗(yàn)證統(tǒng)一登錄是否。添加登錄方法,登錄后將用戶信息存放到 Session 中。RequestMapping(value = "/login")public String login (HttpServletRequest request,String userName,String password) String msg="logon failure!"User user= userRepository.findByUserName(userName);if (user!=null && user.getP

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論