1 Redis
Redis下載:蒼老師網站
1.1 什么是Redis?
Redis是一個使用ANSI C編寫的開源、支持網絡、基於內存、可選持久性的鍵值對存儲數據庫。目前Redis的開發由Redis Labs贊助。根據月度排行網站DB-Engines.com的數據,Redis是最流行的鍵值對存儲數據庫。
Redis是一個開發好的軟件,有固定的使用方式。
Redis的特征:
-
Redis是一個內存(緩存)數據庫,因為數據保存在內存中,所以速度快,每秒可執行10萬次讀寫操作。
-
雖然Redis是一個內存數據庫,但是它允許將數據保存在硬盤上,以便出現運行異常時恢復(Redis數據保存到硬盤上的策略有兩種:AOF和RDB,可同時開啟)
-
Redis保存數據使用key-value的格式,類似java中的Map類型集合,這樣使用key-value保存數據的數據庫統稱為"非關系型數據庫",英文"no-sql"(關系型數據庫,sql,通過外鍵等建立關系)
-
Redis的key為string類型,value支持各種類型:string、list、set、zset、hash。
-
Redis支持微服務系統需要的分布式部署,支持master-slave(一主多從)的模式,以達到"高並發、高可用、高性能"的目的
-
Redis的競品軟件memcached,關於它們的區別可以自學
-
Redis是一個緩存數據庫,以鍵值對的形式將數據保存到內存中,屬於非關系型數據庫(nosql),特點是快速響應
-
內存數據斷電消失
-
Redis將數據保存到硬盤的兩種方式:AOF、RDB,可同時進行
-
有些數據庫軟件也以key-value形式將數據保存到硬盤上,但是是關系型數據庫(sql),如:MongoDB
-
關系型數據庫主要通過外鍵等建立關系,一般是存在硬盤上,主要用途是用來保存全部數據,如:MySQL、Oracle、MongoDB、ServerSocket
-
非關系型數據庫主要以鍵值對形式將數據保存到內存中,主要是輔助關系型數據庫使用,特點是響應速度快
-
注意:並非所有的非關系型數據庫數據都存在內存中,也有存在硬盤中的
-
Redis的競品memcached,就性能而言,memcached性能好,但是redis可以進行備份到硬盤
-
redis可用於數據進行增減的場合,如商品搶購
-
redis數據存在內存中,只有重啟電腦數據才會清除,一般是1小時無人訪問就會自動回收,但是這不是絕對的,一般redis會根據內存分配靈活配置
-
1.2 為什么需要Redis?
如下圖所示,當faq模塊由多台服務器組成時,每個服務器都要緩存一份所有標簽,這樣會造成緩存冗余,造成內存的浪費。我們可以使用Redis保存所有標簽,當任何faq服務器需要時直接從Redis中獲取即可,這樣節省了內存,提高了服務器性能。
1.3 Redis的安裝及初步使用
解壓運行下載的Redis,文件夾內容如下所示:
-
雙擊redis-start.bat文件可以啟動redis,出現一個界面,這個界面不能關,一關redis就停止了
-
redis-cli.exe可以運行操作Redis的客戶端
由於每次開機都要啟動redis,界面還不能關,不方便。
我們可以實現每次開機自動啟動,需要運行下面的文件:
-
先運行: service-installing.bat---安裝Redis服務到操作系統
-
再運行:service-start.bat---啟動Redis服務
-
如果想停止服務: service-stop.bat
-
如果想卸載: service-uninstalling.bat
運行上面的步驟1和2即可,正常情況下,每次開機redis都會自動開啟了,然后打開redis-cli.exe,啟動后輸入info,輸出下面的信息就表示redis啟動成功了,之后可以在此界面進行代碼的編寫:
1.4 Redis基本操作
Redis支持如下幾種數據類型:
我們主要使用string字符串類型,string類型基本操作如下:
127.0.0.1:6379> set mystr "hello world!" //保存字符串類型
127.0.0.1:6379> get mystr //讀取字符串類型
除了保存字符串,Redis還特別適合保存頻繁變化的數字。因為如果頻繁修改硬盤數據庫(mysql)中的數字的話,每次都是硬盤操作,效率低;如果修改的是redis中的數據,那么支持的並發會較大。
除此之外,Redis是一個單線程的程序,沒有線程安全問題,所有即使是高並發的程序也能夠正確響應數字的變化。
下面是對數字增減的專門命令:
127.0.0.1:6379> set mynum "2" //創建數字,也可直接寫數字保存:set mynum 2,默認保存數字為字符串類型
OK
127.0.0.1:6379> get mynum //查詢數字
"2"
127.0.0.1:6379> incr mynum //數字增加
(integer) 3
127.0.0.1:6379> get mynum //查詢數字
"3"
127.0.0.1:6379> decr mynum //數字減少
(integer) 2
127.0.0.1:6379> get mynum //查詢數字
"2"
Redis基本命令補充:
List 列表
常用命令: lpush,rpush,lpop,rpop,lrange等
Redis的list在底層實現上並不是數組而是鏈表,Redis list 的應用場景非常多,也是Redis最重要的數據結構之一,比如微博的關注列表,粉絲列表,消息列表等功能都可以用Redis的 list 結構來實現。
Redis list 的實現為一個雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過帶來了部分額外的內存開銷。
另外可以通過 lrange 命令,就是從某個元素開始讀取多少個元素,可以基於 list 實現分頁查詢,這個很棒的一個功能,基於 redis 實現簡單的高性能分頁,可以做類似微博那種下拉不斷分頁的東西(一頁一頁的往下走),性能高。
lists的常用操作包括LPUSH、RPUSH、LRANGE、RPOP等。可以用LPUSH在lists的左側插入一個新元素,用RPUSH在lists的右側插入一個新元素,用LRANGE命令從lists中指定一個范圍來提取元素,RPOP從右側彈出數據。來看幾個例子::
//新建一個list叫做mylist,並在列表頭部插入元素"Tom"
127.0.0.1:6379> lpush mylist "Tom"
//返回當前mylist中的元素個數
(integer) 1
//在mylist右側插入元素"Jerry"
127.0.0.1:6379> rpush mylist "Jerry"
(integer) 2
//在mylist左側插入元素"Andy"
127.0.0.1:6379> lpush mylist "Andy"
(integer) 3
//列出mylist中從編號0到編號1的元素
127.0.0.1:6379> lrange mylist 0 1
1) "Andy"
2) "Tom"
//列出mylist中從編號0到倒數第一個元素
127.0.0.1:6379> lrange mylist 0 -1
1) "Andy"
2) "Tom"
3) "Jerry"
//從右側取出最后一個數據
127.0.0.1:6379> rpop mylist
"Jerry"
//再次列出mylist中從編號0到倒數第一個元素
127.0.0.1:6379> lrange mylist 0 -1
1) "Andy"
2) "Tom"
Set 集合
常用命令: sadd,smembers,sunion 等
set 是無序不重復集合,list是有序可以重復集合,當你需要存儲一個列表數據,又不希望出現重復數據時,set是一個很好的選擇,並且set提供了判斷某個成員是否在一個set集合內的重要功能,這個也是list所不能提供的。
可以基於 set 輕易實現交集、並集、差集的操作。比如:在微博應用中,可以將一個用戶所有的關注人存在一個集合中,將其所有粉絲存在一個集合Redis可以非常方便的實現如共同關注、共同粉絲、共同喜好等功能,也就是求交集的過程。set具體命令如下:
//向集合myset中加入一個新元素"Tom"
127.0.0.1:6379> sadd myset "Tom"
(integer) 1
127.0.0.1:6379> sadd myset "Jerry"
(integer) 1
//列出集合myset中的所有元素
127.0.0.1:6379> smembers myset
1) "Jerry"
2) "Tom"
//判斷元素Tom是否在集合myset中,返回1表示存在
127.0.0.1:6379> sismember myset "Tom"
(integer) 1
//判斷元素3是否在集合myset中,返回0表示不存在
127.0.0.1:6379> sismember myset "Andy"
(integer) 0
//新建一個新的集合yourset
127.0.0.1:6379> sadd yourset "Tom"
(integer) 1
127.0.0.1:6379> sadd yourset "John"
(integer) 1
127.0.0.1:6379> smembers yourset
1) "Tom"
2) "John"
//對兩個集合求並集
127.0.0.1:6379> sunion myset yourset
1) "Tom"
2) "Jerry"
3) "John"
Sorted Set 有序集合
常用命令: zadd,zrange,zrem,zcard等
和set相比,sorted set增加了一個權重參數score,使得集合中的元素能夠按score進行有序排列。
在直播系統中,實時排行信息包含直播間在線用戶列表,各種禮物排行榜,彈幕消息(可以理解為按消息維度的消息排行榜)等信息,適合使用 Redis 中的 SortedSet 結構進行存儲。
很多時候,我們都將redis中的有序集合叫做zsets,這是因為在redis中,有序集合相關的操作指令都是以z開頭的,比如zrange、zadd、zrevrange、zrangebyscore等等
來看幾個生動的例子:
//新增一個有序集合hostset,加入一個元素baidu.com,給它賦予score:1
127.0.0.1:6379> zadd hostset 1 baidu.com
(integer) 1
//向hostset中新增一個元素bing.com,賦予它的score是30
127.0.0.1:6379> zadd hostset 3 bing.com
(integer) 1
//向hostset中新增一個元素google.com,賦予它的score是22
127.0.0.1:6379> zadd hostset 22 google.com
(integer) 1
//列出hostset的所有元素,同時列出其score,可以看出myzset已經是有序的了。
127.0.0.1:6379> zrange hostset 0 -1 with scores
1) "baidu.com"
2) "1"
3) "google.com"
4) "22"
5) "bing.com"
6) "30"
//只列出hostset的元素
127.0.0.1:6379> zrange hostset 0 -1
1) "baidu.com"
2) "google.com"
3) "bing.com"
Hash
常用命令: hget,hset,hgetall 等。
Hash 是一個 string 類型的 field 和 value 的映射表,hash 特別適合用於存儲對象,后續操作的時候,你可以直接僅僅修改這個對象中的某個字段的值。 比如我們可以Hash數據結構來存儲用戶信息,商品信息等等。比如下面我就用 hash 類型存放了我本人的一些信息:
//建立哈希,並賦值
127.0.0.1:6379> HMSET user:001 username antirez password P1pp0 age 34
OK
//列出哈希的內容
127.0.0.1:6379> HGETALL user:001
1) "username"
2) "antirez"
3) "password"
4) "P1pp0"
5) "age"
6) "34"
//更改哈希中的某一個值
127.0.0.1:6379> HSET user:001 password 12345
(integer) 0
//再次列出哈希的內容
127.0.0.1:6379> HGETALL user:001
1) "username"
2) "antirez"
3) "password"
4) "12345"
5) "age"
6) "34"
1.5 SpringBoot操作Redis
添加依賴:像mysql一樣,java可以操作mysql數據庫,就可以操作Redis。底層我們使用jdbc操作mysql,redis方面底層使用Jedis操作Redis,但是和jdbc操作數據庫一樣,使用Jedis操作Redis步驟比較繁瑣。
我們可以使用Spring Boot Redis操作Redis,這樣會很簡單,先添加必要依賴,然后才能使用Spring Boot Redis框架,在knows-faq模塊的pom.xml文件添加如下:
<!--Spring連接Redis的依賴,上面為底層,下面為封裝優化-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
application.properties文件中需要配置Redis的ip地址和端口號,就像我們連接數據庫也要提供這些資料一樣。
# 配置Redis的ip和端口,localhost即127.0.0.1
spring.redis.host=localhost
spring.redis.port=6379
1.6 基本操作
我們可以在測試類中編寫代碼測試是否可以成功操作redis:
//我們添加Spring Redis的依賴就是向Spring容器中添加了一個可以操作Redis的對象
// RedisTemplate<[key的類型],[value的類型]>
輸出結果:
ok
東方不敗
1.7 優化標簽緩存
我們學習了怎么操作Redis,下面我們就將TagServiceImpl實現類中獲得所有標簽的方法修改為從Redis中獲取。
轉到knows-faq模塊: TagServiceImpl 實現類代碼修改如下:
重新啟動knows-faq服務,訪問學生首頁,路徑為:http://localhost:8080/index_student.html,輸出效果如下:
控制台輸出內容如下:
之后刷新或重啟服務,不再輸出該語句,因為數據已經存到redis緩沖中去了,除非重啟電腦,重啟后加載一次即可,以后不再需要重復加載,直接使用即可。
2 使用Ribbon實現服務間調用
注冊和查詢所有標簽我們已經完成了,下面要完成登錄功能,登錄功能涉及很多知識點和代碼,先來學習Ribbon。
2.1 什么Ribbon?
Ribbon是SpringCloud提供的一個組件,它能夠實現微服務之間的互相調用。它的使用不用添加額外依賴,因為使用的非常頻繁,在spring-cloud-starter-alibaba-nacos-discovery這個依賴中已經集成了。
2.2 Ribbon基本使用
步驟1 : 明確服務的提供者(方法的定義)
服務的提供者也叫生產者,本次調用我們將sys模塊作為生成者,需要定義一個方法作為被調用的方法,必須是一個控制器方法才能被Ribbon調用,我們將/v1/auth/demo這個路徑的方法作為調用目標。
步驟2 : 在發起調用的一方添加Ribbon的支持
本次調用的發起者是faq模塊,在SpringBoot啟動類中注入一個能夠發起Ribbon請求的對象
步驟3 : 發起調用
使用剛剛保存到Spring容器中的RestTemplate對象調用方法,我們先使用測試類來調用。實際開發中經常會在業務邏輯層中發起,測試類代碼如下:
測試結果:
sys:Hello World!!!
關系示意圖如下:
2.3 使用Ribbon通過用戶名獲得對象
我們下面將剛編寫的Ribbon升級,添加了參數,返回值變為了User。faq模塊還是請求的發起者(消費者),根據Ribbon的規則,我們首先要在sys模塊中定義一個根據用戶名返回用戶對象的控制器方法,沒有這個方法就要編寫這個方法,從業務邏輯層開始。
轉到knows-sys模塊:
(1)IUserService接口中添加方法:
// 根據用戶名查詢用戶對象
User getUserByUsername(String username);
UserServiceImpl類中實現業務邏輯層方法:
//根據用戶名查找用戶對象的邏輯層實現
(2)控制層代碼:AuthController添加方法
在faq模塊中進行測試,測試執行前保證sys模塊重新啟動過!
測試結果:
需要大家下載一個軟件:postman(郵遞員)
下載地址:https://www.postman.com/downloads/
這個軟件用於向服務器發送各種請求,get\post均可,還可以攜帶參數。
3 微服務的會話保持
3.1 什么是會話保持
會話就是多個請求和響應的集合,一般來講,打開瀏覽器到關閉瀏覽器就是一次會話。
會話對應java中的HttpSession對象,所謂會話保持,就是多次請求過程中,服務器都可以獲得session中的信息。
單體項目中會話保持是依靠session對象的。
3.2 微服務項目的會話保持問題
因為微服務具有多個項目,每個項目都有自己的session,在一個服務器中登錄並不能共享給其它項目,這樣單體項目中的會話保持方式就不能使用了。
如下圖所示,sys模塊登錄成功並不能把登錄信息發送給faq模塊,這樣就無法實現會話保持,微服務項目中有專門的會話保持技術稱之為"單點登錄"。
3.3 單點登錄實現思路
上次課我們講到了微服務項目因為有多個服務器組成,遇到了會話保持問題。
在一個服務器上登錄,能夠讓所有服務器知道當前用戶的信息的解決方案就是單點登錄。
單點登錄的辦法有很多,但是實現思路主要有兩種:
-
Session共享
-
Token令牌
3.3.1 方法一:Session共享
原理:讓用戶的登錄信息共享給所有模塊,用戶登錄時共享session。
下圖表示使用Session共享實現單點登錄:
基本思路:將用戶信息保存到Redis中,哪個模塊需要用戶信息從redis中取。
優點:
-
安全性高
-
框架支持比較完善,開發代碼量小
缺點:
-
每個模塊還有Redis都要消耗較多內存保存用戶信息
-
當用戶信息更新時,需要比較復雜的操作
3.3.2 方法二:Token令牌
原理:當用戶登錄時,將一個加密的令牌發送給客戶端,客戶端保存這個令牌,令牌中包含用戶信息,訪問其它模塊時,使用令牌表名身份。
下圖是Token令牌的解決方案:
基本思路:用戶登錄成功頒發令牌,由客戶端保存,訪問其它服務器時,客戶端提供令牌,服務器驗證。
優點:
-
解放session,服務器不需要再因為用戶信息占用內存
-
客戶端保存令牌,方便響應客戶端變化
缺點:
-
解析和頒發令牌需要Cpu消耗算力
-
絕對安全的業務,需要再次驗證
在達內知道項目中,我們使用第二種方式實現微服務的單點登錄,Token令牌這種方式有很多需要我們解決的問題,我們通過一些業界成熟的規范和標准實現這個過程。