Jingdong Shopping cart solution

“This is the 10th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

I. Analysis of various scenarios of JD shopping cart

Step 1: Log in your JINGdong account and check the items in your shopping cart. (If there is 1 item in the shopping cart)

Step 2: Log out, add item B to the shopping cart without login, close the browser, and open it again.

Step 3: Log in to your JINGdong account again. (The items added to the cart when logging out have been merged with the items in the cart when logging in)

Note: Recently I found that the shopping cart of JINGdong Mall already needs login to add to the shopping cart.

Two, high concurrency jingdong shopping cart technology implementation

  1. Add items to cart in login state
Graph LR logged in - > | 1. Add a shopping cart | shopping cart shopping cart service - > | 2. Write redis | redis shopping cart service - > | 3. Asynchronous write db | message middleware MQ message middleware MQ - > write | | the db

In the case of high concurrency, the logged-in user adds a shopping cart, the shopping cart service stores the data in Redis first, then sends a message to the messaging middleware, and finally writes the data from the consuming middleware to the database.

This high concurrency ensures that all query operations will fall on the REIDS, resulting in smooth persistence of data to db.

  1. Add an item to cart when not logged in
Graph LR not logged in - > | 1. Add a shopping cart | shopping cart shopping cart service - > | 2. Write redis | redis

When an unlogged user adds a shopping cart, the shopping cart service generates a unique ID: cartId for the user and takes the cartId as the key. The data of the shopping cart is saved into Redis (valid for 3 months). Finally, the shopping cart service writes the cartId back into the cookies of the user

This section does not save the database because there is no corresponding user. In the future, every time the user adds an item, they just take this cartId with them,

  1. Then login state merge
Graph state of LR login again - > | 1. Login combination after shopping cart request | shopping cart shopping cart service - > | 2. Merge into the db | db shopping cart service - > | 3. Refresh | Redis

After the user logs in successfully, the client sends the cartId and userId in the cookies to the shopping cart system, and the shopping cart system merges the data not logged in by Redis into the userId database through cartId.

Finally, refresh the redis data added to the login userId and delete the redis data that is not logged in to the carId.

Three, shopping cart redis classic scene

Add 2 items to cart using hash data, key= Cart :user: user ID hash= item ID, quantity

127.0. 01.:6379> hset cart:user:1000 101 1
(integer) 1
127.0. 01.:6379> hset cart:user:1000 102 1
(integer) 1
127.0. 01.:6379> hgetall cart:user:1000
1) "101"
2) "1"
3) "102"
4) "1"
Copy the code

Modify the shopping cart data to add a quantity for an item

127.0. 01.:6379> hincrby cart:user:1000 101 1
(integer) 2
127.0. 01.:6379> hincrby cart:user:1000 102 10
(integer) 11
127.0. 01.:6379> hgetall cart:user:1000
1) "101"
2) "2"
3) "102"
4) "11"
Copy the code

Count the number of items in your shopping cart

127.0. 01.:6379> hlen cart:user:1000
(integer) 2
Copy the code

Delete an item from the cart

127.0. 01.:6379> hdel cart:user:1000 102
(integer) 1
127.0. 01.:6379> hgetall cart:user:1000
1) "101"
2) "2"
Copy the code

Four, case actual combat

SpringBoot+Redis+Cookies to achieve high concurrency shopping cart

The shopping cart entity class is not logged in@Data
public class CookieCart {
     Id / / commodities
    private Long productId;
    // Quantity of goods
    private int amount;

}
Copy the code

Log in to the shopping cart entity class

@Data
public class Cart {

    private Long userId;
    Id / / commodities
    private Long productId;
    // Quantity of goods
    private int amount;

}
Copy the code

Business class

    /** * not logged in * add shopping cart */
    @PostMapping(value = "/addCart")
    public void addCart(CookieCart obj) {
        // Get the cardId of a cookie
        String cartId=this.getCookiesCartId();
        String key=COOKIE_KEY+cartId;
        Boolean hasKey = redisTemplate.opsForHash().getOperations().hasKey(key);
        / / there
        if(hasKey){
            // Save key as shopping cart, hash key as item ID, and value as quantity
            this.redisTemplate.opsForHash().put(key, obj.getProductId().toString(),obj.getAmount());
        }else{
            this.redisTemplate.opsForHash().put(key, obj.getProductId().toString(), obj.getAmount());
            this.redisTemplate.expire(key,90, TimeUnit.DAYS); }}/** * Not logged in * Change cart quantity */
    @PostMapping(value = "/updateCart")
    public void updateCart(CookieCart obj) {
        String cartId=this.getCookiesCartId();
        String key=COOKIE_KEY+cartId;
        this.redisTemplate.opsForHash().put(key, obj.getProductId().toString(),obj.getAmount());
    }
    /** * not logged in * delete cart item */
    @PostMapping(value = "/delCart")
    public void delCart(Long productId) {
        String cartId=this.getCookiesCartId();
        String key=COOKIE_KEY+cartId;
        this.redisTemplate.opsForHash().delete(key, productId.toString());
    }
    /** * not logged in * query a user's shopping cart */
    @PostMapping(value = "/findAll")
    public CartPage findAll(a) {
        String cartId=this.getCookiesCartId();
        String key=COOKIE_KEY+cartId;

        CartPage<CookieCart> cartPage=new CartPage();
        // Query the total number of shopping carts for the user
        long size=this.redisTemplate.opsForHash().size(key);
        cartPage.setCount((int)size);

        // Query all items in the shopping cart
        Map<String,Integer> map= this.redisTemplate.opsForHash().entries(key);
        List<CookieCart> cartList=new ArrayList<>();
        for (Map.Entry<String,Integer> entry:map.entrySet()){
            CookieCart cart=new CookieCart();
            cart.setProductId(Long.parseLong(entry.getKey()));
            cart.setAmount(entry.getValue());
            cartList.add(cart);
        }
        cartPage.setCartList(cartList);
        return cartPage;
    }

    /** * Login * merge shopping cart * merge shopping cart from cookie to the login user's shopping cart */
    @PostMapping(value = "/mergeCart")
    public void mergeCart(Long userId) {
        // Step 1: Extract the shopping cart data from the cookie of the user who is not logged in
        String cartId=this.getCookiesCartId();
        String keycookie=COOKIE_KEY+cartId;
        Map<String,Integer> map= this.redisTemplate.opsForHash().entries(keycookie);

        // Step 2: merge the shopping cart from the cookie into the shopping cart of the logged-in user
        String keyuser = "cart:user:" + userId;
        this.redisTemplate.opsForHash().putAll(keyuser,map);

        // Step 3: Delete the shopping cart data from the cookies of users not logged in by Redis
        this.redisTemplate.delete(keycookie);

        // Step 4: Delete the cartid of the cookies of the unlogged user
        Cookie cookie=new Cookie("cartId".null);
        cookie.setMaxAge(0);
        response.addCookie(cookie);
    }
    
    
    /** * Get cookies */
    public  String getCookiesCartId(a){
        // Step 1: Check whether the cookies have cartid
        Cookie[] cookies =  request.getCookies();
        if(cookies ! =null) {for(Cookie cookie : cookies){
                if(cookie.getName().equals("cartId")) {returncookie.getValue(); }}}// Step 2: cookies do not have cartid. Generate global ID directly and set it in cookie
        // Generate globally unique IDS
        long id=this.idGenerator.incrementId();
        // Set to cookies
        Cookie cookie=new Cookie("cartId",String.valueOf(id));
        response.addCookie(cookie);
        return id+""; }}Copy the code

Id generator

@Service
public class IdGenerator {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final String ID_KEY = "id:generator:cart";

    /** * generate globally unique id */
    public Long incrementId(a) {
        long n=this.stringRedisTemplate.opsForValue().increment(ID_KEY);
        returnn; }}Copy the code

Redis distributed cache family

  • Redis Distributed Cache (1) – Redis Installation (Linux and Docker)
  • Redis Distributed cache (ii) – RDB and AOF
  • SpringBoot integrates Mybatis-Plus,Redis and Swagger
  • Redis distributed cache (4) – SpringCache integrates redis
  • Redis Distributed Cache (5) — Common command (String)
  • Redis Distributed Cache (6) — Article read volume PV Solution (String)
  • Redis Distributed Cache (7) — Distributed Global ID Solution (String)
  • Redis Distributed Cache (8) — Highly Concurrent atomic Operations (Redis+Lua)
  • Redis Distributed Cache (9) — Hacker Anti-brush Attack Solution (String)
  • Redis Distributed Cache (10) common Commands (Hash)
  • Redis Distributed Cache (11) store Java Bean object Hash
  • Redis Distributed Cache (12) – Short Link Solution (Hash)
  • The article continues to be updated…