1.1 string
Format: (key:value)
SET key value
Add a key value (set key value)
127.0.0.1:6379> set user:1001 OK 127.0.0.1:6379> set name xiaoMing OK 127.0.0.1:6379> set name xiaoMing OK 127.0.0.1:6379> set name xiaoMing OKScenario: save verification code (with expiration time)
127.0.0.1:6379> set sms:13567890000 5763 ex 90
GET key
Get a value from a key (get key)
"" 127.0.0.1:6379> get name "xiaoMing" 127.0.0.1:6379> get password "123456" 127.0.0.1:6379> get number "123456" 127.0.0.1:6379> get sms:1356789000
GETRANGE key start end
Intercept the content of a specified range of a string, similar to the subString method in java (GETRANGE key start subscript end subscript)
That is: 0 represents the subscript of the first element on the left, - 1 represents the subscript of the first element on the right
127.0.0.1:6379> GETRANGE number 0 -1 "123456" 127.0.0.1:6379> GETRANGE number 0 -1 "123456" 127.0.0.1:6379> GETRANGE number 0 1 "12" 127.0.0.1:6379> GETRANGE number 1 -1 "23456" 127.0.0.1:6379> GETRANGE number 5 -1 "6" 127.0.0.1:6379> GETRANGE number -2 -1 "56" 127.0.0.1:6379> GETRANGE number -2 -2 "5" 127.0.0.1:6379> GETRANGE number -2 -5 "" 127.0.0.1:6379> GETRANGE number 0 -5 "12"
GETSET key value
Replace the value value in a key and return the replaced value
127.0.0.1:6379> GETSET number 123 "123456" 127.0.0.1:6379> get number "123"
MSET key value [key value ...]
Batch add key value values
127.0.0.1:6379> MSET name1 xiaoMing name2 xiaoHong name3 123 OK
MSET key value [key value ...]
Get the value values of multiple key s in batch
127.0.0.1:6379> MGET name1 name2 name3 1) "xiaoMing" 2) "xiaoHong" 3) "123"
DEL key [key ...]
Batch delete multiple key s
127.0.0.1:6379> del name1 name2 (integer) 2
INCR key
Increment. According to the integer number + 1 in a key, non integer numbers will report an error
127.0.0.1:6379> set number 1 OK 127.0.0.1:6379> incr number (integer) 2 127.0.0.1:6379> incr number 127.0.0.1:6379> set name zhan OK 127.0.0.1:6379> incr name (error) ERR value is not an integer or out of rangeScenario: generate order number
In the cluster server, the uniqueness of order generation is guaranteed through a redis server
INCRBY key increment
Increase the specified integer. According to the integer number in a key + the specified integer, non integer numbers will report an error
127.0.0.1:6379> incrby number 10 (integer) 13 127.0.0.1:6379> incrby name 10 (error) ERR value is not an integer or out of range
DECR key
According to the integer number - 1 in a key, non integer numbers will report an error
127.0.0.1:6379> DECR number (integer) 12 127.0.0.1:6379> DECR name (error) ERR value is not an integer or out of range
DECRBY key decrement
Specify an integer according to the integer number in a key. Non integer numbers will report an error
127.0.0.1:6379> DECRBY number 10 (integer) 2 127.0.0.1:6379> DECRBY name 10 (error) ERR value is not an integer or out of range
APPEND key value
The specified content is appended according to the content in a key. If the key does not exist, a new k-v record is generated and the length of the added value is returned
127.0.0.1:6379> APPEND name 123 (integer) 7 127.0.0.1:6379> get name "zhan123" 127.0.0.1:6379> APPEND name xiao (integer) 11 127.0.0.1:6379> get name "zhan123xiao"
STRLEN key
Returns the length of the value corresponding to the key. If the key does not exist, it returns 0
127.0.0.1:6379> STRLEN name (integer) 11 127.0.0.1:6379> STRLEN name12 (integer) 0
1.2 hset
Format: (key column1:value1 column2:value2 column3:value3)
Similar to object attribute 1: value 1 attribute 2: value 2 attribute 3: value 3 in java
HSET key field value
Set an object and set the corresponding properties and values
127.0.0.1:6379> hset user:1002 name xiaoMing (integer) 1 127.0.0.1:6379> hset user:1002 password 123456 (integer) 1 127.0.0.1:6379> hset user:1002 number 1002 (integer) 1
HMSET key field value [field value ...]
set multiple attributes and values for the object (one attribute corresponds to one value)
127.0.0.1:6379> hmset user:1003 name xiaoHui password 123456 number 1003 OK
HSETNX key field value
If the object or the attribute in the object does not exist, the set object or the attribute and value in the set object will be executed
127.0.0.1:6379> HSETNX user:1004 name xiaoHe (integer) 1 127.0.0.1:6379> HSETNX user:1004 name xiaoTian (integer) 0
HGET key field
Get a field value
127.0.0.1:6379> hget user:1004 name "xiaoHe" 127.0.0.1:6379> hget user:1003 name "xiaoHui"
HMGET key field [field ...]
Get multiple field values
127.0.0.1:6379> hmget user:1002 name password number 1) "xiaoMing" 2) "123456" 3) "1002"
HGETALL key
Gets all properties and values in the object
127.0.0.1:6379> HGETALL user:1002 1) "name" 2) "xiaoMing" 3) "password" 4) "123456" 5) "number" 6) "1002"
7)HDEL key field [field ...]
When one or more fields are deleted, the returned result shows the number of deleted fields. If there is no corresponding field, 0 is returned
127.0.0.1:6379> hdel user:1004 name (integer) 1 127.0.0.1:6379> hdel user:1004 name (integer) 0
8)HINCRBY key field increment
Increase the specified size and return the increased result value
Note: the field type must also be numeric, otherwise an error will be reported
127.0.0.1:6379> HINCRBY user:1002 number 10 (integer) 1012 127.0.0.1:6379> HINCRBY user:1002 name 10 (error) ERR hash value is not an integer
9)HEXISTS key field
Judge whether an object or a field in the object exists
127.0.0.1:6379> HEXISTS user:1002 name (integer) 1 127.0.0.1:6379> HEXISTS user:1002 name1 (integer) 0
HKEYS key
Gets all field names in the object
127.0.0.1:6379> HKEYS user:1002 1) "name" 2) "password" 3) "number" 127.0.0.1:6379> HKEYS user:1004 1) "name"
HVALS key
Gets all field values in the object
127.0.0.1:6379> HVALS user:1002 1) "xiaoMing" 2) "123456" 3) "1012" 127.0.0.1:6379> HVALS user:1004 1) "xiaoHong"
HLEN key
Gets the number of fields in the object
127.0.0.1:6379> HLEN user:1002 (integer) 3 127.0.0.1:6379> HLEN user:1004 (integer) 1
1.3 list
Features: orderly, repeatable, relying on subscript as index
Bottom layer: linkedlist bidirectional linked list
Usage scenario: Fan list, follow list, friend list / location operation
Redis lists are stored in linked lists, so the operation of redis list data type is to operate the data at both ends of the list.
LPUSH key value [value ...]
Add elements to the left of the list
127.0.0.1:6379> LPUSH list:a 1 (integer) 1 127.0.0.1:6379> LPUSH list:a 2 (integer) 2 127.0.0.1:6379> LPUSH list:a 3 (integer) 3
LPUSHX key value [value ...]
If the list exists, add elements to the left of the list
127.0.0.1:6379> LPUSHX list:a 4 (integer) 4 127.0.0.1:6379> LPUSHX list 1 (integer) 0
RPUSH key value [value ...]
Add elements to the right of the list
127.0.0.1:6379> rpush list:a 0 (integer) 5 127.0.0.1:6379> rpush list:a -1 (integer) 6 127.0.0.1:6379> rpush list:a -2 -3 (integer) 8
RPUSHX key value [value ...]
If the list exists, add elements to the right of the list
127.0.0.1:6379> rpushx list:a -4 (integer) 9 127.0.0.1:6379> rpushx list -4 (integer) 0
LRANGE key start stop
LRANGE command is one of the most commonly used commands of list type. To get a fragment in the list, all elements between start and stop (including elements at both ends) will be returned, and the index starts from 0. The index can be negative, for example: "- 1" represents an element of the last edge.
127.0.0.1:6379> LRANGE list:a 0 -1 1) "4" 2) "3" 3) "2" 4) "1" 5) "0" 6) "-1" 7) "-2" 8) "-3" 9) "-4" 127.0.0.1:6379> LRANGE list:a 0 1 1) "4" 2) "3" 127.0.0.1:6379> LRANGE list:a 5 -1 1) "-1" 2) "-2" 3) "-3" 4) "-4"
LPOP key || RPOP key
Remove elements from both ends of the list. The POP command pops up an element from the left or right of the list, which will be completed in two steps:
The first step is to remove the elements on the left or right of the list from the list
The second step is to return the value of the removed element.
127.0.0.1:6379> LPOP list:a "4" 127.0.0.1:6379> LRANGE list:a 0 -1 1) "3" 2) "2" 3) "1" 4) "0" 5) "-1" 6) "-2" 7) "-3" 8) "-4" 127.0.0.1:6379> RPOP list:a "-4" 127.0.0.1:6379> LRANGE list:a 0 -1 1) "3" 2) "2" 3) "1" 4) "0" 5) "-1" 6) "-2" 7) "-3"
BLPOP key || BRPOP key
The function is similar to lpop | rpop, but this method is blocked, and a key and value are returned. When passing parameters, an additional time is added to represent the blocking waiting time
PS: in this way, the data in multiple list s can be fetched at the same time. The end condition is who gets it first. If none is fetched, it will be blocked until it times out and return nil
127.0.0.1:6379> BLPOP list:a 2 1) "list:a" 2) "3" 127.0.0.1:6379> BLPOP list 10 (nil) (10.07s) 127.0.0.1:6379> BLPOP list 10 1) "list" 2) "3" (2.07s) 127.0.0.1:6379> LPUSH list 1 2 3 (integer) 3(This part is the green part, 2 after code execution s Code executed on another connection window)
LLEN key
Gets the number of elements in the list
127.0.0.1:6379> llen list:a (integer) 6 127.0.0.1:6379> llen list (integer) 2
LREM key count value
Delete the value specified in the list. The LREM command will delete the first count elements with value in the list and return the number of elements actually deleted. The execution method of this command varies according to the count value:
when count > 0, LREM will delete the specified number from the left of the list.
when count < 0, LREM will delete the specified number from the back of the list.
when count=0, LREM deletes all elements with value.
Note: count represents the total number of specified elements deleted
127.0.0.1:6379> LRANGE list:b 0 -1 1) "1" 2) "2" 3) "1" 4) "2" 5) "1" 6) "2" 7) "1" 127.0.0.1:6379> LREM list:b -1 1 (integer) 1 127.0.0.1:6379> LRANGE list:b 0 -1 1) "1" 2) "2" 3) "1" 4) "2" 5) "1" 6) "2" 127.0.0.1:6379> LREM list:b -2 2 (integer) 2 127.0.0.1:6379> LRANGE list:b 0 -1 1) "1" 2) "2" 3) "1" 4) "1" 127.0.0.1:6379> LREM list:b 1 1 (integer) 1 127.0.0.1:6379> LRANGE list:b 0 -1 1) "2" 2) "1" 3) "1" 127.0.0.1:6379> LREM list:b 0 1 (integer) 2 127.0.0.1:6379> LRANGE list:b 0 -1 1) "2"
LINDEX key index
Gets the element value of the specified index
127.0.0.1:6379> LRANGE list:a 0 -1 1) "2" 2) "1" 3) "0" 4) "-1" 5) "-2" 6) "-3" 127.0.0.1:6379> LINDEX list:a 0 "2" 127.0.0.1:6379> LINDEX list:a 1 "1" 127.0.0.1:6379> LINDEX list:a 2 "0"
LSET key index value
Set the element value of the specified index (modify and overwrite the original value)
127.0.0.1:6379> LINDEX list:a 2 "0" 127.0.0.1:6379> LSET list:a 2 9 OK 127.0.0.1:6379> LINDEX list:a 2 "9"
LTRIM key start stop
Only the start -stop subscript content in the current list is retained, and the rest are deleted
127.0.0.1:6379> LRANGE list:a 0 -1 1) "2" 2) "1" 3) "9" 4) "-1" 5) "-2" 6) "-3" 127.0.0.1:6379> LTRIM list:a 0 3 OK 127.0.0.1:6379> LRANGE list:a 0 -1 1) "2" 2) "1" 3) "9" 4) "-1"
LINSERT key BEFORE|AFTER pivot value
Insert an element into the list. The command will first find the element with the value pivot from left to right in the list, and then decide whether to insert the value BEFORE or AFTER the element according to the second parameter, BEFORE or AFTER.
127.0.0.1:6379> LRANGE list:a 0 -1 1) "2" 2) "1" 3) "9" 4) "-1" 127.0.0.1:6379> LINSERT list:a before -1 0 (integer) 5 127.0.0.1:6379> LRANGE list:a 0 -1 1) "2" 2) "1" 3) "9" 4) "0" 5) "-1" 127.0.0.1:6379> LINSERT list:a after -1 0 (integer) 6 127.0.0.1:6379> LRANGE list:a 0 -1 1) "2" 2) "1" 3) "9" 4) "0" 5) "-1" 6) "0"
RPOPLPUSH source destination
Moves the last element from one list to another
127.0.0.1:6379> LRANGE list:a 0 -1 1) "2" 2) "1" 3) "9" 4) "0" 5) "-1" 127.0.0.1:6379> LRANGE list 0 -1 1) "0" 2) "2" 3) "1" 127.0.0.1:6379> RPOPLPUSH list:a list "-1" 127.0.0.1:6379> LRANGE list:a 0 -1 1) "2" 2) "1" 3) "9" 4) "0" 127.0.0.1:6379> LRANGE list 0 -1 1) "-1" 2) "0" 3) "2" 4) "1"
1.4 set
Unordered, non repeatable, key dependent index
SADD key member [member ...]
Add element
127.0.0.1:6379> sadd setA 1 2 3 4 (integer) 4 127.0.0.1:6379> sadd setB 1 2 3 4 (integer) 4
SMEMBERS key
Show collection members
127.0.0.1:6379> SMEMBERS setA 1) "1" 2) "2" 3) "3" 4) "4" 127.0.0.1:6379> SMEMBERS setB 1) "1" 2) "2" 3) "3" 4) "4"
SREM key member [member ...]
Delete one or more elements
127.0.0.1:6379> SREM setA 1 2 (integer) 2 127.0.0.1:6379> SMEMBERS setA 1) "3" 2) "4"
SISMEMBER key member
Determine whether the element is in the collection
127.0.0.1:6379> SMEMBERS setA 1) "3" 2) "4" 127.0.0.1:6379> SISMEMBER setA 1 (integer) 0 127.0.0.1:6379> SISMEMBER setA 3 (integer) 1
SDIFF key [key ...]
The difference set operation A-B of a set is a set composed of elements belonging to a and not belonging to B.
Scenario: judge non common friends
127.0.0.1:6379> SMEMBERS setA 1) "1" 2) "2" 3) "3" 4) "4" 127.0.0.1:6379> SMEMBERS setB 1) "2" 2) "3" 3) "4" 4) "5" 127.0.0.1:6379> SDIFF setA setB 1) "1" 127.0.0.1:6379> SDIFF setB setA 1) "5"
SINTER key [key ...]
The intersection operation A ∩ B of A set is A set composed of elements belonging to A and B.
Scenario: judge common users
127.0.0.1:6379> SINTER setA setB 1) "2" 2) "3" 3) "4"
SUNION key [key ...]
The union operation A ∪ B of A set is A set composed of elements belonging to A or B
Scenario: merge of local address book and cloud backup address book
127.0.0.1:6379> SUNION setA setB 1) "1" 2) "2" 3) "3" 4) "4" 5) "5"
SCARD key
Gets the number of elements in the collection
127.0.0.1:6379> SCARD setA (integer) 4 127.0.0.1:6379> SCARD setB (integer) 4
SPOP key [count]
Pop up an element (or elements) from the collection
Note: since the collection is unordered, all SPOP commands will pop up by randomly selecting an element from the collection
Scenario: online lottery
127.0.0.1:6379> SPOP setA "3" 127.0.0.1:6379> SPOP setA "2" 127.0.0.1:6379> SPOP setA "4" 127.0.0.1:6379> SPOP setA "1" 127.0.0.1:6379> SPOP setA (nil) 127.0.0.1:6379> SPOP setB 2 1) "2" 2) "4" 127.0.0.1:6379> SPOP setB 2 1) "3" 2) "5"
1.5 sortedset
Orderly and non repeatable
sortedset, also known as zset, is an ordered set, sortable, but unique.
The difference between sortedset and set is that a score is added to the elements in set, and then sorted by this score.
ZADD key score member [score member ...]
Add an element. Add an element and its score to the ordered set. If the element already exists, the original score will be replaced with a new score. The return value is the number of elements newly added to the collection, excluding the existing elements.
127.0.0.1:6379> zadd hua 10 meigui (integer) 1 127.0.0.1:6379> zadd hua 11 juhua (integer) 1 127.0.0.1:6379> zadd hua 11 juhua 15 baihe (integer) 1 127.0.0.1:6379> zadd hua 11 juhua 15 baihe (integer) 0 127.0.0.1:6379> zadd hua 9 molo 20 hehua (integer) 2
ZSCORE key member
Gets the score of the element
127.0.0.1:6379> ZSCORE hua meigui "10" 127.0.0.1:6379> ZSCORE hua hehua "20"
ZREM key member [member ...]
Delete the element and remove one or more members in the ordered set key. Nonexistent members will be ignored. An error is returned when the key exists but is not an ordered set type.
127.0.0.1:6379> ZREM hua hehua (integer) 1 127.0.0.1:6379> ZREM hua hehua (integer) 0 127.0.0.1:6379> ZREM hua hehua meigui (integer) 1
ZRANGE key start stop [WITHSCORES]
Get the list of elements ranked in a certain range, and return all elements (including elements at both ends) between start and stop according to the order of element score from small to large
127.0.0.1:6379> ZRANGE hua 0 1 1) "molo" 2) "juhua" 127.0.0.1:6379> ZRANGE hua 0 2 1) "molo" 2) "juhua" 3) "baihe"
ZREVRANGE key start stop [WITHSCORES]
Return all elements (including elements at both ends) from start to stop in the order of element score from large to small. If you need to obtain the score of elements, you can add the WITHSCORES parameter at the end of the command, such as the following effect:
127.0.0.1:6379> ZREVRANGE hua 0 1 1) "baihe" 2) "juhua" 127.0.0.1:6379> ZREVRANGE hua 0 2 1) "baihe" 2) "juhua" 3) "molo" 127.0.0.1:6379> ZREVRANGE hua 0 2 WITHSCORES 1) "baihe" 2) "15" 3) "juhua" 4) "11" 5) "molo" 6) "9"
ZRANK key member
Get the ranking of elements from small to large
127.0.0.1:6379> ZRANK hua molo (integer) 0 127.0.0.1:6379> ZRANK hua juhua (integer) 1 127.0.0.1:6379> ZRANK hua baihe (integer) 2
ZREVRANK key member
From big to small
127.0.0.1:6379> ZREVRANK hua molo (integer) 2 127.0.0.1:6379> ZREVRANK hua baihe (integer) 0 127.0.0.1:6379> ZREVRANK hua juhua (integer) 1
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
Gets the element of the specified score range
127.0.0.1:6379> ZRANGEBYSCORE hua 0 100 1) "molo" 2) "juhua" 3) "baihe" 127.0.0.1:6379> ZRANGEBYSCORE hua 0 10 1) "molo"
ZINCRBY key increment member
Increase the score of an element, and the return value is the changed score
127.0.0.1:6379> ZINCRBY hua 10 molo "19" 127.0.0.1:6379> ZINCRBY hua 10 baihe "25" 127.0.0.1:6379> ZINCRBY hua 15 juhua "26"
ZCARD key
Gets the number of elements in the collection
127.0.0.1:6379> ZCARD hua (integer) 3 127.0.0.1:6379> zadd shui 10 wahaha 20 maidong (integer) 2 127.0.0.1:6379> ZCARD shui (integer) 2
ZCOUNT key min max
Gets the number of elements within the specified score range
127.0.0.1:6379> ZCOUNT hua 0 10 (integer) 0 127.0.0.1:6379> ZCOUNT hua 0 20 (integer) 1 127.0.0.1:6379> ZCOUNT hua 0 30 (integer) 3
ZREMRANGEBYRANK key start stop
Delete elements by ranking range
127.0.0.1:6379> ZREMRANGEBYRANK hua 0 10 (integer) 3 127.0.0.1:6379> ZREMRANGEBYRANK hua 0 10 (integer) 0
ZREMRANGEBYSCORE key min max
Delete elements by score range
127.0.0.1:6379> ZREMRANGEBYSCORE shui 0 10 (integer) 1 127.0.0.1:6379> ZREMRANGEBYSCORE shui 0 10 (integer) 0 127.0.0.1:6379> ZREMRANGEBYSCORE shui 0 20 (integer) 1 127.0.0.1:6379> ZREMRANGEBYSCORE shui 0 20 (integer) 02. Redis related application scenarios
2.1 counters (atomic, single thread, microsecond level)
Commodity views, video playback, common, key commands: incr,decr
127.0.0.1:6379> incr video:sanshengsanshi (integer) 1 127.0.0.1:6379> incr video: sanshengsanshi (integer) 2 127.0.0.1:6379> incr video: sanshengsanshi (integer) 3 127.0.0.1:6379> incr video: sanshengsanshi (integer) 4 127.0.0.1:6379> decr video: sanshengsanshi (integer) 3
2.2 cache mall home page data
Use Zset (score is the number of comments / time on the shelf / price, etc.), and value is the commodity id
Use string/hash to store product information
[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-XmHP2fFR-1626654480870)(Redis%E7%9B%B8%E5%85%B3%E5%91%BD%E4%BB%A4%E5%8F%8A%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%E4%BB%8B%E7%BB%8D.assets/image-20210701212119791.png)]
Use zset to store the id of all recommended goods, and use the price as the sorting basis
127.0.0.1:6379> zadd hotitem:price 1799 1001 1499 1002 626 1003 8999 1004 4999 1005 (integer) 5
Use string (json) to store commodity information
127.0.0.1:6379> mset 1001 OK
Use hash to store product information
127.0.0.1:6379> HSET item:1001 name skyworth (integer) 1 127.0.0.1:6379> HSET item:1001 price 1799 (integer) 1
Show product list
127.0.0.1:6379> ZREVRANGE hotitem:price 0 4 1) "1004" 2) "1005" 3) "1001" 4) "1002" 5) "1003"
Further query the product specific information according to the results
127.0.0.1:6379> get 1001 ""
2.3 shopping cart: hset hdel hlen hincrby hgetall
key: user id
field: Commodity id
Commodity quantity: value
Using HSE to implement shopping cart scenario
Operation: add items with item numbers 1001 and 1004 to the user's shopping cart with item number 1999
127.0.0.1:6379> hset cart:1999 1001 1 (integer) 1
Added item: hset cart:1999 1004 1
127.0.0.1:6379> hset cart:1999 1004 1 (integer) 1
Added quantity: hincrby cart: 1999 1004 1
127.0.0.1:6379> HINCRBY cart:1999 1004 1 (integer) 2
Total number of goods: Helen Cart: 1999
127.0.0.1:6379> hlen cart:1999 (integer) 2
Get all items in the shopping cart (select all) hgetall cart:1999
127.0.0.1:6379> HGETALL cart:1999 1) "1004" 2) "2" 3) "1001" 4) "1"
Deleted item: hdel cart:1999 1001
127.0.0.1:6379> HDEL cart:1999 1001 (integer) 1
2.4 rush purchase, purchase restriction, issuance of coupons, activation codes, etc
[external link picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-giScOFv7-1626654480875)(Redis%E7%9B%B8%E5%85%B3%E5%91%BD%E4%BB%A4%E5%8F%8A%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%E4%BB%8B%E7%BB%8D.assets/20200525192416499.png)]
Solution
- Take the merchant id as the key
- Take the commodity id participating in the rush purchase as the field
- Take the quantity of commodities participating in the rush purchase as the corresponding value
- During rush purchase, the product quantity is controlled by reducing the value
2.5 commodity screening
Preparing the noteBookSet collection
127.0.0.1:6379> sadd notebookset huawei14 vivo15 apple15 lenovo16 dell17 (integer) 5
Prepare i7Set collection
127.0.0.1:6379> sadd i7set huawei14 apple15 lenovo16 dell17 (integer) 4
Prepare 8gSet collection
127.0.0.1:6379> sadd 8gset huawei14 lenovo16 dell17 (integer) 3
Select 8g+i7+notebook
127.0.0.1:6379> SINTER notebookset i7set 8gset 1) "lenovo16" 2) "dell17" 3) "huawei14"
2.6 storage objects (infrequently changing): using json
String type (it is inconvenient to edit a single attribute in the data)
127.0.0.1:6379> set hua:1001 OK
2.7 storage object (frequent change): hset hincrby
Frequently changed use of hash (convenient for editing a single attribute)
127.0.0.1:6379> hset hehua name hehua (integer) 1 127.0.0.1:6379> hset hehua number 1001 (integer) 1 127.0.0.1:6379> hset hehua price 15 (integer) 1
2.8 using list to realize stack / queue / blocking queue
Stack: lpush + lpop -- > Filo
First in and last out principle: LPUSH enters D, C, B, a from the left of the queue, and lpop comes out a,b,c,d from the left of the queue
127.0.0.1:6379> lpush stack 1 2 3 4 (integer) 4 127.0.0.1:6379> LRANGE stack 0 -1 1) "4" 2) "3" 3) "2" 4) "1" 127.0.0.1:6379> lpop stack "4" 127.0.0.1:6379> lpop stack "3" 127.0.0.1:6379> lpop stack "2" 127.0.0.1:6379> lpop stack "1"
Queue: LPUSH+RPOP
First in first out principle: LPUSH enters d,c,b,a from the left of the queue, and rpop exits d,c,b,a from the right of the queue
127.0.0.1:6379> LPUSH queue a b c d (integer) 4 127.0.0.1:6379> LRANGE queue 0 -1 1) "d" 2) "c" 3) "b" 4) "a" 127.0.0.1:6379> rpop queue "a" 127.0.0.1:6379> rpop queue "b" 127.0.0.1:6379> rpop queue "c" 127.0.0.1:6379> rpop queue "d"
Blocking queue: LPUSH+BRPOP (b: blocking)
LPUSH+BRPOP has the functions of blocking and waiting on the basis of LPUSH+RPOP,
127.0.0.1:6379> BRPOP queue 2 (nil) (2.04s)
Because the queue does not exist, the waiting time of 2s is consumed. If the queue exists, the pop-up operation will be performed immediately
2.9 micro-blog news and official account message (MQ: Message Queue)
redis implements message queue
Lao Bi (id:9527) pays attention to Jay Chou and Stephen Chow
Jay Chou microblogging, message id:1001314
lpush msg:9527 1001314
Stephen Chow microblogging, message id: 1000520
lpush msg:9527 1000520
127.0.0.1:6379> lpush msg:9527 1001234 (integer) 1 127.0.0.1:6379> lpush msg:9527 1001314 (integer) 2 127.0.0.1:6379> lpush msg:9527 1001312 (integer) 3 127.0.0.1:6379> lpush msg:9527 1001315 (integer) 4
Query the latest microblog messages (15)
lrange msg:9527 0 14
127.0.0.1:6379> LRANGE msg:9527 0 14 1) "1001315" 2) "1001312" 3) "1001314" 4) "1001234"
2.10 high heat data access acceleration
[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-1j3ogsa9-1626654480880) (redis% E7% 9b% B8% E5% 85% B3% E5% 91% BD% E4% BB% A4% E5% 8F% 8A% E4% BD% BF% E7% 94% A8% E5% 9C% Ba% E6% 99% AF% E4% BB% 8b% E7% BB% 8D. Assets / 202005251456455408. PNG)]
Solution 1: the big V user can set the user information, take the user primary key and attribute value as the key, and set the time in the background to refresh regularly
127.0.0.1:6379> set user:id:5765898790:focuss:3050 OK 127.0.0.1:6379> set user:id:5765898790:fans:117492300 OK 127.0.0.1:6379> set user:id:5765898790:blogs:117744 OK
Solution 2: store large V users in Redis in json format and refresh them regularly
127.0.0.1:6379> set user: id :5765898790 OK
2.11 friends circle praise
Wechat friends circle like, and it is required to display like friend information in the order of like.
If you cancel liking, remove the corresponding friend information.
[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-ieq6rmnz-1626654480883) (redis% E7% 9b% B8% E5% 85% B3% E5% 91% BD% E4% BB% A4% E5% 8F% 8A% E4% BD% BF% E7% 94% A8% E5% 9C% Ba% E6% 99% AF% E4% BB% 8b% E7% BB% 8D. Assets / 2020052520029413. PNG)]
127.0.0.1:6379> lrange daqiao 0 -1 1) "zhangfei" 2) "liubei" 3) "guanyu" 4) "zhaoyun" 5) "caocao" 6) "xiaoqiao" 7) "zhangfei" 127.0.0.1:6379> lrem daqiao 1 liubei (integer) 1 127.0.0.1:6379> lrange daqiao 0 -1 1) "zhangfei" 2) "guanyu" 3) "zhaoyun" 4) "caocao" 5) "xiaoqiao" 6) "zhangfei" 127.0.0.1:6379> lrem daqiao 1 zhangfei (integer) 1 127.0.0.1:6379> lrange daqiao 0 -1 1) "guanyu" 2) "zhaoyun" 3) "caocao" 4) "xiaoqiao" 5) "zhangfei"
2.12 using set to implement attention model
My (9527) concern
127.0.0.1:6379> sadd focus:9527 zhangmeng hanyun liuyifei xuqing (integer) 4
He (9528) is concerned about
127.0.0.1:6379> sadd focus:9528 hanyun liuyifei xuqing xiaofeng (integer) 4
She (9529) is concerned
127.0.0.1:6379> sadd focus:9529 zhangmeng hanyun xuqing (integer) 3
I (9527) and he (9528) are of common concern
127.0.0.1:6379> SINTER focus:9527 focus:9528 1) "liuyifei" 2) "hanyun" 3) "xuqing"
The person I (9527) followed (Zhang Meng) and she (9529) also followed, but he (9528) didn't
127.0.0.1:6379> SISMEMBER focus:9527 zhangmeng (integer) 1 127.0.0.1:6379> SISMEMBER focus:9529 zhangmeng (integer) 1 127.0.0.1:6379> SISMEMBER focus:9528 zhangmeng (integer) 0
A person I may know (I (9527) and he (9528) are good friends. If that person is a good friend of him (9528), but not of me (9527), this person may be a good friend of me (9527))
127.0.0.1:6379> SDIFF focus:9528 focus:9527 1) "xiaofeng"
2.13 distributed lock (inventory of goods in shopping)
Overbought and oversold
1. Lock current inventory
2. Inventory-1 (core business)
3. Release the lock
Lock resources when placing an order
127.0.0.1:6379> SETNX item:001 true (integer) 1
Others cannot place orders
127.0.0.1:6379> SETNX item:001 true (integer) 0
Release the lock after the order operation is completed
127.0.0.1:6379> del item:001 (integer) 1
Others can continue to place orders
127.0.0.1:6379> SETNX item:001 true (integer) 1
2.14 network lottery
SRANDMEMBER lot 1: draw one person at a time (the user will not be excluded from the candidate, but can participate in the lucky draw repeatedly)
127.0.0.1:6379> sadd lot:9527 1 2 3 4 5 6 7 8 9 (integer) 9 127.0.0.1:6379> SRANDMEMBER lot:9527 1 1) "7" 127.0.0.1:6379> SRANDMEMBER lot:9527 1 1) "3" 127.0.0.1:6379> SRANDMEMBER lot:9527 1 1) "4" 127.0.0.1:6379> SRANDMEMBER lot:9527 1 1) "3" 127.0.0.1:6379> SRANDMEMBER lot:9527 1 1) "3" 127.0.0.1:6379> SMEMBERS lot:9527 1) "1" 2) "2" 3) "3" 4) "4" 5) "5" 6) "6" 7) "7" 8) "8" 9) "9"
SPOP lot 1 draws one person each time (the user is excluded from the candidate and cannot participate in the lottery repeatedly)
127.0.0.1:6379> SPOP lot:9527 1 1) "2" 127.0.0.1:6379> SPOP lot:9527 1 1) "6" 127.0.0.1:6379> SMEMBERS lot:9527 1) "1" 2) "3" 3) "4" 4) "5" 5) "7" 6) "8" 7) "9"
2.15 hot search ranking
Prepare the hot search news on the 19th
127.0.0.1:6379> zadd hotnews:20202819 1 1001 (integer) 1 127.0.0.1:6379> zadd hotnews:20202819 1 1002 (integer) 1 127.0.0.1:6379> zadd hotnews:20202819 1 1003 (integer) 1 127.0.0.1:6379> zadd hotnews:20202819 1 1004 (integer) 1 127.0.0.1:6379> zadd hotnews:20202819 1 1005 (integer) 1
After hot search on the 19th, click on the 19th + 1
127.0.0.1:6379> ZINCRBY hotnews:20202819 1 1001 "2" 127.0.0.1:6379> ZINCRBY hotnews:20202819 1 1002 "2" 127.0.0.1:6379> ZINCRBY hotnews:20202819 1 1003 "2" 127.0.0.1:6379> ZINCRBY hotnews:20202819 1 1002 "3" 127.0.0.1:6379> ZINCRBY hotnews:20202819 1 1002 "4" 127.0.0.1:6379> ZINCRBY hotnews:20202819 1 1003 "3"
Check the hot search list of No. 19
127.0.0.1:6379> ZREVRANGE hotnews:20202819 0 -1 withscores 1) "1002" 2) "4" 3) "1003" 4) "3" 5) "1001" 6) "2" 7) "1005" 8) "1" 9) "1004" 10) "1"
Prepare the hot search news on the 20th (the hot search on the 19th can also be on the hot search on the 20th)
127.0.0.1:6379> zadd hotnews:20202820 1 1001 (integer) 1 127.0.0.1:6379> zadd hotnews:20202820 1 1002 (integer) 1 127.0.0.1:6379> zadd hotnews:20202820 1 1006 (integer) 1
After hot search on the 20th, click on the 20th + 1
127.0.0.1:6379> ZINCRBY hotnews:20202820 1 1001 "2" 127.0.0.1:6379> ZINCRBY hotnews:20202820 1 1001 "3" 127.0.0.1:6379> ZINCRBY hotnews:20202820 1 1002 "2" 127.0.0.1:6379> ZINCRBY hotnews:20202820 1 1006 "2"
View the two-day hot search list
127.0.0.1:6379> ZUNIONSTORE 2days 2 hotnews:20202819 hotnews:20202820 (integer) 6 127.0.0.1:6379> ZREVRANGE 2days 0 -1 withscores 1) "1002" 2) "6" 3) "1001" 4) "5" 5) "1003" 6) "3" 7) "1006" 8) "2" 9) "1005" 10) "1" 11) "1004" 12) "1"3. Java operation redis jedis
3.1 Guide Package
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
3.2 operation string type
@SpringBootTest(classes = MainApplication.class) @RunWith(SpringRunner.class) public class TestJedis { //1. Prepare connection pool object private JedisPool jedisPool = null; //2. Prepare jedis objects private Jedis jedis = null; @Before public void init(){ // Establish connection jedisPool=new JedisPool("139.198.183.226",6379); //jedis client jedis = jedisPool.getResource(); //Verify permissions jedis.auth("!123456Bl"); } @Test public void testString(){ jedis.set("name","Zhang Sanfeng"); String result = jedis.get("name"); System.out.println(result); } @After public void close(){ jedisPool.close(); } }
Starting from hash, @ Before and @ After are omitted, which need to be added during the test
3.3 operation hash type
@Test public void testHashMap(){ //Add as key: jedis.hset("user","name","zhang"); jedis.hset("user","age","20"); // Add as map: /* Map map = new HashMap(); map.put("name","zhang"); map.put("age","20"); jedis.hset("user2", map);*/ String userName = jedis.hget("user", "name"); System.out.println(userName); // Get all features Map<String, String> userMap = jedis.hgetAll("user"); System.out.println(userMap); }
3.4 operation list type
@Test public void testList(){ //Action list type //storage jedis.lpush("mylist","a","b","c");//Save from left jedis.rpush("mylist2","a","b","c");//Save from right //list get List<String> mylist = jedis.lrange("mylist", 0, -1); System.out.println(mylist); //eject String e1 = jedis.lpop("mylist"); System.out.println(e1);//c String e2 = jedis.rpop("mylist"); System.out.println(e2);//a }
3.5 operation set type (unordered)
@Test public void testSet(){ //Collection is not repeatable and unordered jedis.sadd("set","a","c","b"); Set<String> set = jedis.smembers("set"); System.out.println(set); }
3.6 operation set type (sort)
@Test public void testZset(){ //Set set cannot be sorted repeatedly jedis.zadd("hotitem",90,"Electric toothbrush"); jedis.zadd("hotitem",60,"Basketball"); jedis.zadd("hotitem",80,"Pen"); Set<String> set = jedis.zrange("hotitem", 0, -1); System.out.println(set); }4. Spring boot integrates redis
4.1 adding dependencies
<!-- jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <!-- spring-boot-starter-data-redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
4.2 setting redis connection in YML
Add redis connection and connection pool configuration
spring: redis: database: 0 host: 139.198.183.226 port: 6379 password: "!123456Bl" jedis: pool: max-idle: -1 #No limit, maximum idle connection min-idle: 1000 #Minimum connection
4.3 design configuration class (define serializer)
@Configuration public class RedisConfig { //In order to call the operation class of redis in service, prepare it in advance through @Bean. @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { // Instantiate a redistemplate (operate crud of different data types in redis) RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); // Configure a factory for the template template.setConnectionFactory(factory); //Configure serializer: what serialization method is used for String and hash (what format is used when java transmits data to redis: jdk/string/jackson) //Jackson serializer Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); jackson2JsonRedisSerializer.setObjectMapper(om); //String serializer StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // The key is serialized by String template.setKeySerializer(stringRedisSerializer); // The key of hash is also serialized by String template.setHashKeySerializer(stringRedisSerializer); // value is serialized by jackson // user[name = Zhang San] - > } template.setValueSerializer(jackson2JsonRedisSerializer); // The value serialization method of hash is jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
4.4 design a test case
@SpringBootTest(classes = PortalApp.class) @RunWith(SpringRunner.class) public class TestRedisTemplate { @Resource private RedisTemplate<String,Object> redisTemplate; @Test public void testString(){ // Gets an operand of type String ValueOperations<String, Object> stringObjectValueOperations = redisTemplate.opsForValue(); // Declare a string type of data with an expiration time //TimeUnit can be specified as hour / minute / second / millisecond / microsecond // stringObjectValueOperations.set("name", "Zhang San", 30, TimeUnit.MINUTES); // Benchmarking nx operations in redis /* Boolean aBoolean = stringObjectValueOperations.setIfAbsent("name", "aaa"); System.out.println("aBoolean:"+aBoolean);*/ // Addition with NX function /*Boolean aBoolean = stringObjectValueOperations.setIfAbsent("name", "Zhang San ", 30, TimeUnit.MINUTES); System.out.println("aBoolean:"+aBoolean);*/ Object value = stringObjectValueOperations.get("name"); System.out.println("value:"+value); // Execute the deletion method of String (general) Boolean name = redisTemplate.delete("name"); } @Test public void testHash(){ // HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash(); // boundXXX: determines the scope of the operation. When declared, the target object of the operation is directly determined according to the key parameters, // Key: is the key of redis value // hk: is a field of value of hash type // hv: is the value value corresponding to the field in the hash BoundHashOperations<String, Object, Object> userOperations = redisTemplate.boundHashOps("user"); /*userOperations.put("name","Lin Qingxia "); userOperations.put("age",20); userOperations.put("gender","Female ");*/ Map<String,Object> map = new HashMap<>(); map.put("name","Zhou Huimin"); map.put("age",21); map.put("gender","female"); userOperations.putAll(map); userOperations.delete("age"); // Method for obtaining field of hash Object name = userOperations.get("name"); } @After public void after(){ // Release the connection with the redis service by unbinding // Method exists in a transaction, which will be automatically released (@ Transactional) after the operation is completed RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } }
4.5 difference between opsforvalue and boundValueOps
There are five common types of redis operations in Spring, which correspond to the following:
redisTemplate.opsForValue();
redisTemplate.opsForHash();
redisTemplate.opsForList();
redisTemplate.opsForSet();
redisTemplate.opsForZSet();
redisTemplate.boundValueOps("key");
redisTemplate.boundHashOps("key");
redisTemplate.boundListOps("key");
redisTemplate.boundSetOps("key");
redisTemplate.boundZSetOps("key");
Note: opsFor's method is to take out a certain type of redis processing channel, while the bound method is to bind a specified key first, and then carry out subsequent operations through this key. If a method frequently operates on a key in the actual development scenario, the bound method should be used
4.6 close the connection with redis
In the springboot project or ssm project, you must add @ Transactional annotation on the method of operating the redisTemplate object, otherwise the redis connection will not be closed automatically. When a large number of requests come in, redis does not release the previous connection in time, resulting in the connection crash.
**Error status: * * could not get a resource connection from the pool
**Error status: * * Could not release connection to pool
If the transaction annotation is not added, the following code is required for manual release
RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
5. Realize commodity details cachingthinking
Query from the cache first. If there is one in the cache, it will be returned directly. If there is no one, it will query the database, put it into the cache, and then return
Selection of data structure: string type, goods: 1001
5.1 design GoodsService
public interface GoodsService { /** * Query the goods in the cache * @ param ID * @ return */ public Goods queryGoodsByCacheId(Long id) throws CustomerException;}
5.2 design GoodsServiceImpl
@Service("goodsService") @Transactional(rollbackFor = Throwable.class) public class GoodsServiceImpl implements GoodsService { @Resource private RedisTemplate<String,Object> redisTemplate; @Resource private GoodsMapper goodsMapper; // Specifies the prefix of the item key private static final String GOODS_CACHE="goods:"; // Specifies the expiration time constant in hours private static final Integer GOODS_CACHE_EXPIRE=2; @Override public Goods queryGoodsByCacheId(Long id) throws BusinessException { // Confirm that the operation type is string, and the name of the key: GOODS_CACHE+id(goods:1) BoundValueOperations<String, Object> goodsOps = redisTemplate.boundValueOps(GOODS_CACHE + id); //1. Find it from the cache first, and return it directly if any Object jsonObject = goodsOps.get(); // The obtained product information should be in json format //Use the string related method: isEmpty to determine whether the string is null / null /* public static boolean isEmpty(@Nullable Object str) { return str == null || "".equals(str); }*/ boolean empty = StringUtils.isEmpty(jsonObject); if(empty){ //2. If not, search from the database, put it into the cache, and then return //Query by id and only query the goods on the shelf (status) Goods goods = new Goods(); goods.setId(id); goods.setStatus(1); Goods selectedGoods = goodsMapper.selectOne(goods); // When the commodity does not exist in the database, a business exception is thrown if(selectedGoods == null){ throw new BusinessException("There is no product corresponding to this number"); } //Cache found //Convert item object to json //Set expiration time String objectParseString = JsonUtils.objectToJson(selectedGoods); goodsOps.set(objectParseString,GOODS_CACHE_EXPIRE, TimeUnit.HOURS); return selectedGoods; }else{ // Use the conversion method of JsonUtils to convert the json string into a Goods object Goods goods = JsonUtils.jsonToObject(jsonObject.toString(), Goods.class); return goods; } } }
5.3 design GoodsController
5.4 solution to cache double write inconsistency
5.4.1 primary programme
Problem: modify the database first, and then delete the cache. If the cache deletion fails, there will be new data in the database and old data in the cache, and the data will be inconsistent.
Solution:
Delete the cache first and then modify the database. If deleting the cache succeeds and modifying the database fails, the database is old data and the cache is empty. The data will not be inconsistent because there is no cache when reading. Read the old data in the database and then update it to the cache.
In short, when updating data, first delete the cached data, then update the database, and then update the latest data to the cache when it needs to be queried
![](Redis%E7%9B%B8%E5%85%B3%E5%91%BD%E4%BB%A4%E5%8F%8A%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%E4%BB%8B%E7%BB%8D.assets/1363214-20190928104103320-652143063.png)
Operation analysis:
- Editing commodities occurs in the background, that is, in the admin module. When we edit commodity information in admin, we cannot call the GoodsService method in the portal
- The admin module is also not suitable for adding redis. In this way, every time the admin executes, the redis connection will be opened, which loses the significance of eshop as an aggregation project
Conclusion:
You need to initiate communication with the portal module in the admin module, that is, "remote call"
method:
The HttpClient of apache is used and the tool class is encapsulated
public class HttpClientUtil { public static String doGet(String url, Map<String, String> param) { // Create Httpclient object CloseableHttpClient httpclient = HttpClients.createDefault(); String resultString = ""; CloseableHttpResponse response = null; try { // Create uri URIBuilder builder = new URIBuilder(url); if (param != null) { for (String key : param.keySet()) { builder.addParameter(key, param.get(key)); } } URI uri = builder.build(); // Create http GET request HttpGet httpGet = new HttpGet(uri); // Execute request response = httpclient.execute(httpGet); // Judge whether the return status is 200 if (response.getStatusLine().getStatusCode() == 200) { resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (response != null) { response.close(); } httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return resultString; } public static String doGet(String url) { return doGet(url, null); } public static String doPost(String url, Map<String, String> param) { // Create Httpclient object CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // Create Http Post request HttpPost httpPost = new HttpPost(url); // Create parameter list if (param != null) { List<NameValuePair> paramList = new ArrayList<NameValuePair>(); for (String key : param.keySet()) { paramList.add(new BasicNameValuePair(key, param.get(key))); } // Simulation form UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList); httpPost.setEntity(entity); } // Execute http request response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return resultString; } public static String doPost(String url) { return doPost(url, null); } public static String doPostJson(String url, String json) { // Create Httpclient object CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // Create Http Post request HttpPost httpPost = new HttpPost(url); // Create request content StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); httpPost.setEntity(entity); // Execute http request response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return resultString; } }
Use spring's own encapsulated httpClient: RestTemplate
public class TestHttpClient { @Test public void testPostWithoutParam() throws Exception { // Use Spring's own resttemplate resttemplate resttemplate = new resttemplate(); URI uri = new URI(" http://localhost:8081/goods/update "); String result = restTemplate.postForObject(uri, null, String.class); System.out.println(result); } @ Test public void testPostWithParam() throws Exception { RestTemplate restTemplate = new RestTemplate(); URI uri = new URI(" http://localhost:8080/goods/deleteCache "); / / parameter multivaluemap < string, Object > parammap = new linkedmultivaluemap < > (); parammap.add (" Id "," 1 ") ; String result = restTemplate.postForObject(uri, paramMap, String.class); System.out.println(result); } @Test public void testPostWithForm() throws Exception { RestTemplate restTemplate = new RestTemplate(); URI uri = new URI(" http://localhost:8080/user/insert ") ; / / parameter multivaluemap < string, Object > parammap = new linkedmultivaluemap < > (); parammap.add ("name", "Yang"); parammap.add ("phone", "132"); string result = resttemplate.postforobject (URI, parammap, string. Class); system.out.println (result);} @ test public void testpostwithbody() throws exception { RestTemplate restTemplate = new RestTemplate(); URI uri = new URI(" http://localhost:8080/user/add "); / / header httpheaders = new httpheaders(); httpheaders.setcontenttype (mediatype. Application_json); / / parameter map < string, Object > param = new HashMap < > (); param.put (" name "," Yang ") ; param.put("phone", "136"); HttpEntity<Map<String, Object>> httpEntity = new HttpEntity<>(param,httpHeaders); String result = restTemplate.postForObject(uri, httpEntity, String.class); System.out.println(result); }}
It can be encapsulated as an HttpClientUtil
Add admin module
Add GoodsController
Note that the GoodsController at this time is the interface responsible for providing background operation commodity information and operating mysql
Add GoodsService and GoodsServiceImpl
Provide the update method in GoodsServiceImpl, and remotely call the delete cache operation of the portal module through the HttpClient tool
If the call to delete the cache fails during commodity modification, the modified business will be affected. Modifying goods is a background business, and deleting cache is the business of portal goods details. The modified business cannot be affected by the failure of deleting cache call. Therefore, we need to create a new thread to execute the business of deleting the cache, but considering the performance, we need to use the thread pool, and then consider the retry mechanism of deletion failure. Therefore, the code is very cumbersome, and there will be calls to delete the cache in many places. Therefore, the code encapsulating the call can be extracted separately into a middleware. So message oriented middleware appears. Later, we will solve it through message queue
5.4.2 analysis of inconsistent data cache under concurrency
Question:
When the first request data is changed, the cache is deleted first, and then the database is to be modified. At this time, there is no time to modify it;
The second request comes to read the cache and finds that the cache is empty. Query the database and find the old data before modification and put it in the cache;
The third request reads the data in the cache (at this time, the first request has completed the database modification operation).
As a result, the data in the database and cache are different
Problem analysis:
This problem can occur only when the same data is read and written concurrently. If the concurrency is very low, especially the read concurrency is very low, and the number of visits per day is less than 10000, there are few inconsistent scenarios; However, if there are hundreds of millions of traffic every day and tens of thousands of concurrent reads per second, the above database + cache inconsistency may occur as long as there are data update requests per second
terms of settlement:
Later, we will solve it through message queue
6. Realize second kill business6.1 design GoodsService
6.2 design GoodsServiceImpl
public JsonResult seckill(){ // When accessing resources, first determine whether there is a lock through setnx //In order to release the lock more stably during the lock resource process, the expiration time in redis can be used for 10 seconds // However, the operation to generate the key (setIfAbsent) and the operation to set the expiration time expire cannot form an atomic operation // Solution: form the above two operations into atomic level operations (at least spring boot 2.2.4 and Tomcat 9.0.24) // When assigning a value to a key, provide the UUID through java and take the UUID as the value of the lock // Possibility of lock failure: the first user who grabs the lock does not complete the service and does not release it (assuming it takes 15 seconds). In this process, the redis system has released the lock, resulting in the second user getting the lock resource, // At this time, the first user deletes the lock, so that the third user can add the lock... The lock "disappears" // Objective: when a user locks, he can only unlock it himself // If you want higher availability, you can also extend the expiration time of the lock when it is about to expire (through the timed task Timer) // Redisson is a powerful lock function // If deepening fails, the second kill fails // Judge whether there is stock. If there is stock - 1 (the user id is added to the successful spike user queue) // If stock > 0 indicates inventory, the user can complete the second kill: inventory - 1 // In order to release the lock correctly no matter whether there is a problem in the business during operation, it is also necessary to ensure timely release through exception handling to prevent deadlock sending try{ }finally{ // Release lock resource // Through judgment, the uuid generated in the current business is used as the value of the lock. If it is the same as the value of the lock in the redis server, it means that it is the same operator and the lock can be released } }
6.2 realize the second kill request in GoodsController
@RestController@RequestMapping("/goods") public class GoodsController { @Resource private RedisTemplate<String,Object> template = null; // jedis is encapsulated in the bottom layer //private StringRedisTemplate stringRedisTemplate = null; @PetMapping("/seckill") public JsonResult seckill(){ } }7. Single sign on sso based on jpa+redis
concept
Single sign on (SSO) is a one-time authentication login of users. When the user is Identity authentication server After the last login, you can gain access to other associated systems and application software in the single sign on system. At the same time, this implementation does not require the administrator to modify the user's login status or other information, which means that in multiple application systems, users can access all mutually trusted application systems with only one login. This method reduces the time consumption caused by login and assists user management. It is a popular login method at present
mechanism
When the user accesses the application system 1 for the first time, because he has not logged in, he will be guided to the authentication system to log in; According to the login information provided by the user, the authentication system performs identity verification. If it passes the verification, it should return an authentication credential - ticket to the user; When users visit other applications, they will bring this ticket as their authentication credentials. After receiving the request, the application system will send the ticket to the authentication system for verification to check the legitimacy of the ticket. If the verification is passed, the user can access application system 2 and application system 3 without logging in again
Main functions
System sharing
A unified authentication system is one of the prerequisites of SSO. The main function of the authentication system is to compare the user's login information with the user information base to authenticate the user; After successful authentication, the authentication system should generate a unified authentication mark (ticket) and return it to the user. In addition, the authentication system should also verify the ticket to judge its effectiveness.
Information identification
To realize the function of SSO, let users only Sign in Once, the application system must be able to identify the logged in user. The application system should be able to identify and extract the ticket, and automatically judge whether the current user has logged in through communication with the authentication system Single sign on The function of.
In addition:
1. A single user information database is not necessary, and many systems can not store all user information Centralized storage , user information should be allowed to be placed in different storage. In fact, as long as the unified authentication system and the generation and verification of ticket s are unified, single sign on can be realized no matter where the user information is stored.
2. A unified authentication system does not mean that there is only a single authentication server
When the user is accessing application system 1, the first Authentication server After authentication, the ticket generated by the server is obtained. When he accesses the application system 2, the authentication server 2 can recognize that the ticket is generated by the first server, exchange authentication information through the standard communication protocol between the authentication servers, and still complete the SSO function.
Implementation steps
Create a new sso project and level with eshop
Create a new database sso
pom file
Detailed implementation steps of SSO
-
concept
Single sign on (SSO) is a one-time authentication login of users. When the user is Identity authentication server After the last login, you can gain access to other associated systems and application software in the single sign on system. At the same time, this implementation does not require the administrator to modify the user's login status or other information, which means that in multiple application systems, users can access all mutually trusted application systems with only one login. This method reduces the time consumption caused by login and assists user management. It is a popular login method at present
2. Mechanism
When the user accesses the application system 1 for the first time, because he has not logged in, he will be guided to the authentication system to log in; According to the login information provided by the user, the authentication system performs identity verification. If it passes the verification, it should return an authentication credential - ticket to the user; When users visit other applications, they will bring this ticket as their authentication credentials. After receiving the request, the application system will send the ticket to the authentication system for verification to check the legitimacy of the ticket. If the verification is passed, the user can access application system 2 and application system 3 without logging in again
3. Main functions
System sharingA unified authentication system is one of the prerequisites of SSO. The main function of the authentication system is to compare the user's login information with the user information base to authenticate the user; After successful authentication, the authentication system should generate a unified authentication mark (ticket) and return it to the user. In addition, the authentication system should also verify the ticket to judge its effectiveness.
Information identificationTo realize the function of SSO, let users only Sign in Once, the application system must be able to identify the logged in user. The application system should be able to identify and extract the ticket, and automatically judge whether the current user has logged in through communication with the authentication system Single sign on The function of.
In addition:
1. A single user information database is not necessary, and many systems can not store all user information Centralized storage , user information should be allowed to be placed in different storage. In fact, as long as the unified authentication system and the generation and verification of ticket s are unified, single sign on can be realized no matter where the user information is stored.
2. A unified authentication system does not mean that there is only a single authentication server
When the user is accessing application system 1, the first Authentication server After authentication, the ticket generated by the server is obtained. When he accesses the application system 2, the authentication server 2 can recognize that the ticket is generated by the first server, exchange authentication information through the standard communication protocol between the authentication servers, and still complete the SSO function.
4. Implementation steps
4.1 create a new database ssojpa operation can not add a data table, = = automatically created during the first operation==
4.2 create a new sso project and level with eshop 4.2.1 pom<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.gxa</groupId> <artifactId>sso</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <!--core/portal/admin...All sub modules need to be used parent Base dependent version of--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> </parent> <!--The version of the customized component--> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <swagger3.version>3.0.0</swagger3.version> <swagger3-ui.version>1.9.6</swagger3-ui.version> </properties> <dependencies> <!--unit testing --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <!-- springboot web Place in parent project,core Medium operation json need web Provided jackson rely on BaseService It also needs to be extracted separately Spring Administration --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--springboot integration test--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--to configure springboot-jdbc rely on,Built in transaction manager--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--mysql Connection drive--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <!--springboot2.2 Default supported mysql Drive is 8.0.19--> <!-- <version>5.1.48</version>--> </dependency> <!--jpa Related dependencies of--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--springboot integration swagger3 Dependence of--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>$</version> </dependency> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>$</version> </dependency> <!--jedis of redis Operation client--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <!-- spring-boot-starter-data-redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>$</source> <target>$</target> </configuration> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> <!--Exclude test cases when packaging--> <configuration> <skipTests>true</skipTests> </configuration> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> <!--Configure main startup class--> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.gxa.sso.MainApplication</mainClass> </configuration> <executions> <execution> <goals> <!--You can package dependent packages into generated packages Jar Package--> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </pluginManagement> <resources> <!--It can still be recognized through configuration mapper In the directory xml file--> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <!--Solve the problem that static resources / yml configuration files cannot be loaded -- > <resource> <directory>src/main/resources</directory> <includes> <include>**/*.*</include> </includes> </resource> </resources> </build> </project>4.2.2 edit application-dev.yml
server: tomcat: # Specify character set uri-encoding: UTF-8 # Specify port port: 8080 spring: # Configure data sources datasource: type: com.zaxxer.hikari.HikariDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/sso?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC username: root password: root jpa: hibernate: ddl-auto: update # Create for the first time and update after redis: host: 139.198.183.226 port: 6379 password: '!123456Bl' jedis: pool: min-idle: 1000 max-idle: -1 database: 0
Differences of several attribute values of hibernate.ddl-auto of pa
create:
Every time you load hibernate, you will delete the last generated table, and then regenerate a new table according to your model class. Even if there is no change twice, you should do so. This is an important reason for the loss of database table data.
create-drop :
Each time hibernate is loaded, a table is generated according to the model class, but the table is automatically deleted as soon as sessionFactory is closed.
update:
The most commonly used attribute. When hibernate is loaded for the first time, the table structure will be automatically established according to the model class (provided that the database is established first). When hibernate is loaded later, the table structure will be automatically updated according to the model class. Even if the table structure is changed, the rows in the table still exist and the previous rows will not be deleted. It should be noted that when deployed to the server, the table structure will not be established immediately. It will not be established until the application runs for the first time.
validate :
Each time hibernate is loaded, verify that the database table structure is created. It will only be compared with the tables in the database. No new table will be created, but new values will be inserted.
spring: profiles: active: dev4.2.4 design entity class entity.User
@Data @AllArgsConstructor @NoArgsConstructor @Table(name = "tb_user") @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "user_id") private Long id; @Column(name = "user_name") private String name; @Column(name = "user_phone") private String phone; @Column(name = "user_password") private String password; @Column(name = "salt") private String salt; /** * Login type: 1. Mobile Web 2.pc web 3. Mobile app */ @Column(name="login_type") private Integer loginType; /** * User status 1. Normal 2. Disabled */ @Column(name="user_status") private Integer status; @Column(name="last_login_time") private Date lastLoginTime; /** * Refresh token on mobile terminal */ private String token; }4.2.5 designing UserRepository
//@RepositoryDefinition(domainClass = User.class,idClass = Long.class) public interface UserRepository extends CrudRepository<User,Long> { User findByName(String name); List<User> findByNameStartsWith(String name); // where phone in (?,?) List<User> findByPhoneIn(String...phones); // where name like '%?%' limit ?,? Page<User> findByNameContains(String name, Pageable pageable); //hql //select * from tb_user where name=? and phone=? @Query("select user from User user where name=?1 and phone =?2") User selectByHql(String name,String phone); @Query("select user from User user where name=:name and phone =:phone") User selectByHql2(@Param("name") String name,@Param("phone") String phone); @Query(nativeQuery = true,value = "select count(*)from tb_user") Integer getCount(); }4.2.6 design test cases
@RunWith(SpringRunner.class) @SpringBootTest(classes = MainApplication.class) public class TestUserRepository { @Resource UserRepository userRepository; @Test public void testSave(){ User user = new User(); user.setName("zhang wuji"); user.setPhone("133"); // User save = userRepository.save(user); // System.out.println(save); } @Test public void testUpdate(){ User user = new User(); user.setId(2L); user.setName("zhang wuji"); user.setPhone("131"); // User save = userRepository.save(user); } @Test public void testFindByName(){ // User user = userRepository.findById(1L).get(); User user = userRepository.findByName("zhang wuji"); System.out.println(user); } @Test public void testDeleteByName(){ User user = new User(); user.setId(1L); // userRepository.delete(user); } @Test public void testFindByNameStartingWith(){ List<User> users = userRepository.findByNameStartsWith("Zhang"); System.out.println(users); } @Test public void testFindByPhoneIn(){ List<User> byPhoneIn = userRepository.findByPhoneIn("133","131"); System.out.println(byPhoneIn); } @Test public void testFindByNameContains(){ PageRequest of = PageRequest.of(0, 2); Page<User> pageInfo = userRepository.findByNameContains("Zhang", of); System.out.println(pageInfo); } @Test public void testHql(){ User user = userRepository.selectByHql("Zhang Sanfeng", "132"); System.out.println(user); } @Test public void testHql2(){ User user = userRepository.selectByHql2("Zhang Sanfeng", "132"); System.out.println(user); } @Test public void testCount(){ Integer count = userRepository.getCount(); System.out.println(count); } }4.3 registration
The registration process remains unchanged
4.4 sending mobile phone verification code 4.4.1 Alibaba's dependence on sms sdk<dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-dysmsapi</artifactId> <version>1.1.0</version> </dependency>4.4.2 tools
public class SendSms { //Product Name: cloud communication SMS API product, developers do not need to replace it static final String product = "Dysmsapi"; //Product domain name, developers do not need to replace static final String domain = "dysmsapi.aliyuncs.com"; // TODO needs to be replaced with the developer's own AK (find it on alicloud access console) static final String accessKeyId = "your accessKeyId"; static final String accessKeySecret = "your accessKeySecret""; // autograph static final String sign = "your signName"; public static SendSmsResponse sendSms(String mobile, String templateCode, String templateParam) throws ClientException { //Self adjustable timeout System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); //Initialize acsClient. Regionization is not supported temporarily IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret); DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain); IAcsClient acsClient = new DefaultAcsClient(profile); //Assembly request object - see console - document for details SendSmsRequest request = new SendSmsRequest(); //Required: mobile phone number to be sent request.setPhoneNumbers(mobile); //Required: SMS signature - can be found in SMS console request.setSignName(sign); //Required: SMS template - can be found in SMS console request.setTemplateCode(templateCode); //Optional: the variables in the template replace the JSON string. For example, when the template content is "Dear $, and your verification code is $", the value here is request.setTemplateParam(templateParam); //hint exceptions may be thrown here. Pay attention to catch SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); return sendSmsResponse; } /** * Generate random 6-digit SMS verification code * @return */ private static String getMsgCode() { int n = 6; StringBuilder code = new StringBuilder(); Random ran = new Random(); for (int i = 0; i < n; i++) { code.append(Integer.valueOf(ran.nextInt(10)).toString()); } return code.toString(); } }
explain
Replace your accessKeyId and your accessKeySecret in the code with the requested or existing access_key and access_secret;
Replace your phoneNumber with the phone number you want to receive SMS;
your signName replaces the requested signature name;
Replace your templateCode with the code displayed on the console. In the code, the SMS verification code code is a variable, and the value can be generated and replaced by user-defined rules, which can be randomly generated 6-digit or other digit numbers or letters.
4.4.3 writing a servicepublic interface SmsService { public void sendLoginSms(String phone) throws CustomerException;}
@Servicepublic class SmsServiceImpl implements SmsService { @Resource private RedisTemplate redisTemplate; // Set the key stored in redis as a constant public static final String LOGIN_SMS="login_sms:"; @Override public void sendLoginSms(String phone) throws CustomerException { //One click login can send SMS with or without an account //Sending login is not allowed without registered account //You can't register by sending a registration message //1. Generate verification code (call with static method of tool class) //The generated verification code is printed on the console for cheating //2. The server stores the verification code and sets the expiration time to 30 minutes //3. Call the tool class to send SMS to the user // String templateParam = "{\"getCode\":\""+msgCode+"\", \"storeName \ ": \" Taobao direct store \ ", \" address \ ": \" Alibaba Shanghai Branch \ ", \" detail\":\" warehouse pieces \ "}"; SendSms.sendSms(phone,code,templateParam); // 4. Data returned by SMS interface //SendSmsResponse response = sendSms(mobile,templateCode,templateParam); // System.out.println("data returned by SMS interface -------------------"); //System.out.println("Code=" + response.getCode()); // OK: successful // System.out.println("Message=" + response.getMessage()); // System.out.println("RequestId=" + response.getRequestId()); // System.out.println("BizId=" + response.getBizId()); } }4.4.4 writing controller
@RequestMapping("/sms") @RestController @Api(description = "SMS related interface documents") public class SmsController { @Resource private SmsService smsService; @RequestMapping(value = "/login") @ApiOperation(value = "Send one click login SMS",notes = "Send one click login SMS",httpMethod = "POST") public JsonResult sendLoginSms(@RequestParam("phone") String phone) { if(!phone.matches("^[1](([3][0-9])|([4][5,7,9])|([5][0-9])|([6][6])|([7][3,5,6,7,8])|([8][0-9])|([9][8,9]))[0-9]$")){ throw new BusinessException("Illegal mobile phone number format"); } smsService.sendLoginSms(phone); } }4.5 one click login 4.5.1 writing a service
public interface UserService { Map<String,String> login(String phone,String code) ; User queryUserByToken(String token); }
@Service public class UserServiceImpl implements UserService { @Resource private RedisTemplete redisTemplete; @Resource private UserRepository userRepository; /** * request can be injected / assembled directly in spring and springboot */ @Resource private HttpServletRequest request; //Set token prefix public static final String LOGIN="==key==!@#$%^"; @Override public Map<String, String> login(String phone, String code) throws CustomerException { //One click login //1. Obtain the verification code stored in the redis server //The verification code is invalid. If it is empty, an exception will be thrown // Verify the verification code. If it is inconsistent, an exception will be thrown //After verification, find the user information from the mysql database //If the user does not exist, register anonymously in the anonymous format: "user:" + phone, and specify the user status //If the user exists but the status is not 1, it indicates that it is frozen and an exception is thrown //If the user exists, get the last token. If it is not empty, remove the last login information in redis //Generate a token, store login information, and use the processed UUID String token=UUID.randomUUID().toString().replace("-",""); //Process login // Set ip, set token, and update mysql // redis adds token information and expiration time redisTemplete.set(LOGIN+token,JsonUtils.objectToJson(user),60*60*2,TimeUnit.SECEND); Map<String,String> result=new HashMap<>(); result.put("token",token); return result; } @Override public User queryUserByToken(String token) throws BusinessException { String result = redisTemplete.get(LOGIN + token); if (StringUtils.isEmpty(result)) { throw new BusinessException("invalid token"); } //To update the expiration time redisTemplete.expire(LOGIN+token,60*60*2); return JsonUtils.jsonToPojo(result,User.class); }
e.getCode());
//OK: successful
// System.out.println("Message=" + response.getMessage());
// System.out.println("RequestId=" + response.getRequestId());
// System.out.println("BizId=" + response.getBizId());
}
}
##### 4.4.4 writing controller ```java @RequestMapping("/sms") @RestController @Api(description = "SMS related interface documents") public class SmsController { @Resource private SmsService smsService; @RequestMapping(value = "/login") @ApiOperation(value = "Send one click login SMS",notes = "Send one click login SMS",httpMethod = "POST") public JsonResult sendLoginSms(@RequestParam("phone") String phone) { if(!phone.matches("^[1](([3][0-9])|([4][5,7,9])|([5][0-9])|([6][6])|([7][3,5,6,7,8])|([8][0-9])|([9][8,9]))[0-9]$")){ throw new BusinessException("Illegal mobile phone number format"); } smsService.sendLoginSms(phone); } }4.5 one click login 4.5.1 writing a service
public interface UserService { Map<String,String> login(String phone,String code) ; User queryUserByToken(String token); }
@Service public class UserServiceImpl implements UserService { @Resource private RedisTemplete redisTemplete; @Resource private UserRepository userRepository; /** * request can be injected / assembled directly in spring and springboot */ @Resource private HttpServletRequest request; //Set token prefix public static final String LOGIN="==key==!@#$%^"; @Override public Map<String, String> login(String phone, String code) throws CustomerException { //One click login //1. Obtain the verification code stored in the redis server //The verification code is invalid. If it is empty, an exception will be thrown // Verify the verification code. If it is inconsistent, an exception will be thrown //After verification, find the user information from the mysql database //If the user does not exist, register anonymously in the anonymous format: "user:" + phone, and specify the user status //If the user exists but the status is not 1, it indicates that it is frozen and an exception is thrown //If the user exists, get the last token. If it is not empty, remove the last login information in redis //Generate a token, store login information, and use the processed UUID String token=UUID.randomUUID().toString().replace("-",""); //Process login // Set ip, set token, and update mysql // redis adds token information and expiration time redisTemplete.set(LOGIN+token,JsonUtils.objectToJson(user),60*60*2,TimeUnit.SECEND); Map<String,String> result=new HashMap<>(); result.put("token",token); return result; } @Override public User queryUserByToken(String token) throws BusinessException { String result = redisTemplete.get(LOGIN + token); if (StringUtils.isEmpty(result)) { throw new BusinessException("invalid token"); } //To update the expiration time redisTemplete.expire(LOGIN+token,60*60*2); return JsonUtils.jsonToPojo(result,User.class); }