Redis related commands and usage scenarios

1. Redis related commands and usage ...
1.1 string
1.2 hset
1.3 list
1.4 set
1.5 sortedset
2.1 counters (atomic, single thread, microsecond level)
2.2 cache mall home page data
2.3 shopping cart: hset hdel hlen hincrby hgetall
2.4 rush purchase, purchase restriction, issuance of coupons, activation codes, etc
2.5 commodity screening
2.6 storage objects (infrequently changing): using json
2.7 storage object (frequent change): hset hincrby
2.8 using list to realize stack / queue / blocking queue
2.9 micro-blog news and official account message (MQ: Message Queue)
2.10 high heat data access acceleration
2.11 friends circle praise
2.12 using set to implement attention model
2.13 distributed lock (inventory of goods in shopping)
2.14 network lottery
2.15 hot search ranking
4.1 adding dependencies
4.2 setting redis connection in YML
4.3 design configuration class (define serializer)
4.4 design a test case
4.5 difference between opsforvalue and boundValueOps
4.6 close the connection with redis
5.1 design GoodsService
5.2 design GoodsServiceImpl
5.3 design GoodsController
5.4 solution to cache double write inconsistency
6.1 design GoodsService
6.2 design GoodsServiceImpl
6.2 realize the second kill request in GoodsController
concept
mechanism
Main functions
Implementation steps
Detailed implementation steps of SSO
1. Redis related commands and usage

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 OK
Scenario: 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 range
Scenario: 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) 0
2. 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 caching

thinking

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

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 business

6.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

  1. 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 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.

4. Implementation steps

4.1 create a new database sso

jpa 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.

4.2.3 edit application.yml
spring: profiles: active: dev
4.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 service
public 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); }

12 October 2021, 20:56 | Views: 5855

Add new comment

For adding a comment, please log in
or create account

0 comments