I suggest reading it together with the next article
Data structure + infrastructure
The data structure
The DB part is handled by Spring-data-jPA +mysql, with Lombok’s participation
@mappedsuperclass @data @noargsconstructor @allargsconstructor public class BaseEntity {// public BaseEntity @id // public primary key @generatedValue private Long ID; @column (updatable = false) // Not allowed to change @creationTIMESTAMP // Automatically assigned when created private Date createTime; @updatetimestamp // Automatically changes when changing private Date updateTime; }Copy the code
@entity // Identifies this as a JPA database Entity class @table @data // Lombok getter toString @toString (callSuper = true) // overwrite toString containing fields of the parent class @slf4j // Slf4j log@builder //biulder constructor @noargsconstructor // AllArgsConstructor // full parameter constructor @JsonIgnoreProperties({"hibernateLazyInitializer", "Handler "}) public class RedPacketInfo extends BaseEntity implements Serializable {// Private String redPacket_ID; // ID private int total_amount; Private int total_packet; Private int remaining_amount; Private int remaining_packet; Private String user_id; // user ID}Copy the code
@entity // Identifies this as a JPA database Entity class @table @data // Lombok getter toString @toString (callSuper = true) // overwrite toString containing fields of the parent class @slf4j // Slf4j log@builder //biulder constructor @noargsconstructor // AllArgsConstructor // full parameter constructor @JsonIgnoreProperties({"hibernateLazyInitializer", "Handler "}) public class RedPacketRecord extends BaseEntity implements Serializable {// Private int degree; private String red_packet_id; private String user_id; }Copy the code
REDIS data structure
REDIS stores three parts of information for a red envelope:
KEY: red packet ID+_TAL_PACKET VALUE: remaining number of red packets
2. KEY: red envelope ID+_TOTAL_AMOUNT VALUE: remaining red envelope amount
3. KEY: red envelope ID+_lock VALUE: distributed lock of red envelope
Work with the REDIS base method
private static final TimeUnit SECONDS = TimeUnit.SECONDS; private static final long DEFAULT_TOMEOUT = 5; private static final int SLEEPTIME = 50; /** * public Boolean getLock(String lockKey, String lockKey, String lockKey, String lockKey) String value, long timeout, TimeUnit unit){ boolean lock = false; while (! Lock) {/ / set the key own timeout lock. = redisTemplate opsForValue () setIfAbsent (lockKey, value, a timeout, the unit); If (lock) {return lock; if (lock) {return lock; } try {// pause for 50ms and re-loop thread. sleep(SLEEPTIME); } catch (InterruptedException e) { e.printStackTrace(); } } return lock; } /** * public Boolean getLock(String lockKey){return getLock(lockKey,String.valueOf(new Date().getTime()),DEFAULT_TOMEOUT,SECONDS); }Copy the code
** @param key * @return */ public String get(String key) {return redisTemplate.opsForValue().get(key); } public void set(String key, String key, String key, String key, String key, String key, String key) String value) { redisTemplate.opsForValue().set(key, value); }Copy the code
DAO
public interface RedPacketInfoRepository extends JpaRepository<RedPacketInfo, Long> {
@Query("select o from RedPacketInfo o where o.red_packet_id=:redPacketId")
public RedPacketInfo findByRedPacketId(@Param("redPacketId") String redPacketId);
}
Copy the code
public interface RedPacketRecordRepository extends JpaRepository<RedPacketRecord,Long> {
}
Copy the code
configuration
@component@enableasync // EnableAsync annotations, Public class RedPacketConfig implements ApplicationRunner {// redPacketService; @Override public void run(ApplicationArguments args) throws Exception { String userId = "001"; RedPacketService. HandOut (userId, 10000, 20); } / * * * introduced random component * @ return * / @ Bean public RandomValuePropertySource RandomValuePropertySource () {return new RandomValuePropertySource("RedPackeRandom"); }}Copy the code
Send a red envelope
Sending red packets usually has no special need to handle high concurrency points
/** * @param total_amount * @param total_amount Public RedPacketInfo handOut(String userId,int total_amount,int tal_packet){@param tal_packet * @return */ public RedPacketInfo handOut(String userId,int total_amount,int tal_packet){ RedPacketInfo redPacketInfo = new RedPacketInfo(); redPacketInfo.setRed_packet_id(genRedPacketId(userId)); redPacketInfo.setTotal_amount(total_amount); redPacketInfo.setTotal_packet(tal_packet); redPacketInfo.setRemaining_amount(total_amount); redPacketInfo.setRemaining_packet(tal_packet); redPacketInfo.setUser_id(userId); redPacketInfoRepository.save(redPacketInfo); redisUtil.set(redPacketInfo.getRed_packet_id()+TAL_PACKET, tal_packet+""); redisUtil.set(redPacketInfo.getRed_packet_id()+TOTAL_AMOUNT, total_amount+""); return redPacketInfo; }Copy the code
Private String genRedPacketId(String userId){String redpacketId = userId+"_"+new Date().getTime()+"_"+redisUtil.incrBy("redpacketid",1); return redpacketId; }Copy the code
Grab a red envelope
See the code comment
/** * @param userId * @param redPacketId * @return */ public GrabResult grab(String userId, String redPacketId){ Date begin = new Date(); String MSG = "The red envelope has been stolen!" ; boolean resultFlag = false; Double amountdb = 0.00; Try {// The process of grabbing red packets must be atomic, GetLock (redPacketId+"_lock")) {RedPacketRecord RedPacketRecord = new RedPacketRecord().builder().red_packet_id(redPacketId) .user_id(userId).build(); // If there are no red packets, If (integer.parseint (redisUtil. Get (redPacketId + TAL_PACKET)) <= 0) {}else remaining_amount = Integer.parseInt(redisUtil.get(redPacketId + TOTAL_AMOUNT)); Int remaining_packet = integ.parseint (redisUtil. Get (redPacketId + TAL_PACKET)); Remaining_amount /remaining_packet*2 int amount = remaining_amount; remaining_amount = remaining_amount; if (remaining_packet ! = 1) { int maxAmount = remaining_amount / remaining_packet * 2; amount = Integer.parseInt(randomValuePropertySource.getProperty("random.int[0," + maxAmount + "]").toString()); } redisUtil. IncrBy (redPacketId + TAL_PACKET, -1); redisUtil.incrByFloat(redPacketId + TOTAL_AMOUNT, -amount); / / to return results redPacketRecord. SetAmount (amount). Amountdb = amount / 100.00; MSG = "Congratulations on getting the red envelope" + amountdb + "$!" ; resultFlag = true; / / asynchronous bookkeeping try {redPacketCallBackService. Callback (userId, redPacketId, Integer.parseInt(redisUtil.get(redPacketId + TAL_PACKET)), Integer.parseInt(redisUtil.get(redPacketId + TOTAL_AMOUNT)), amount); } catch (Exception e) { log.error(e.getMessage(), e); }}}}finally {// unLock redis distributed lock redisutil. unLock(redPacketId+"_lock"); } Date end = new Date(); System.out.println(MSG +", "+redisUtil. Get (redPacketId + TAL_PACKET)+", "+ (end. GetTime () - the begin. GetTime ()) +" ms "); return new GrabResult().builder().msg(msg).resultFlag(resultFlag).amount(amountdb).red_packet_id(redPacketId).user_id(userId).build (a); }Copy the code
Asynchronous enter an item in an account
/** * @program: redis * @description: write information * @author: x-Pacific Zhang * @create: 2019-04-30 11:36 **/ @Service public class RedPacketCallBackService { @Autowired private RedPacketInfoRepository redPacketInfoRepository; @Autowired private RedPacketRecordRepository redPacketRecordRepository; */ @async@transactional (Propagation = Propagation.REQUIRES_NEW) public void callback(String) UserId,String redPacketId,int Remaining_packet,int Remaining_AMOUNT,int amount) throws Exception {// Verify RedPacketInfo redPacketInfo = redPacketInfoRepository.findByRedPacketId(redPacketId); if(redPacketInfo.getRemaining_packet() <= 0 || redPacketInfo.getRemaining_amount() < amount){ throw new Exception(" Red envelope balance error, failed to grab red envelope this time!" ); } / / update the first red envelope information table redPacketInfo. SetRemaining_packet (remaining_packet); redPacketInfo.setRemaining_amount(remaining_amount); redPacketInfoRepository.save(redPacketInfo); RedPacketRecord = new RedPacketRecord().builder() .user_id(userId).red_packet_id(redPacketId).amount(amount).build(); redPacketRecordRepository.save(redPacketRecord); }}Copy the code
Test snatching red envelopes
@Test public void testConcurrent(){ String redPacketId = "001_1556677154968_19"; // System.out.println(redPacketInfoRepository.findByRedPacketId("001_1556619425512_5")); Date begin = new Date(); for(int i = 0; i < 200; i++) { Thread thread = new Thread(() -> { String userId = "user_" + randomValuePropertySource.getProperty("random.int(10000)").toString(); redPacketService.grab(userId, redPacketId); }); thread.start(); } Date end = new Date(); System.out.println(" total consumption: "+(end.getTime() -begin.gettime ()))); try { Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); }}Copy the code