头图

Vivo Global Mall Time Machine-A powerful tool for guaranteeing large-scale promotional activities

vivo互联网技术
中文

1. Background

The official website mall operates during the Double 11, Double 12 and other big promotion periods. The classmates will carefully design many promotional activities that give users benefits. When the promotion activities become more and more patterns, it will involve a lot of operational configuration work (such as specifying the validity period of the event, Specify the start and stop status of the event, specify the products involved in the event, etc.).

If for some reason, some of the configurations are not configured as expected, it is discovered that the configuration is not configured correctly at the moment of the big promotion, so there is a high probability that a lot of orders will be lost, and there may also be mismatched discounts leading to some that should not be enjoyed. The discounts are also enjoyed by users, which may cause relatively large losses to the mall. Therefore, in order to minimize the probability of the occurrence of these situations, we wondered whether we could provide a capability to allow operating students to work in important e-commerce universities. Before the promotion officially starts, check in advance whether all expected offers are configured correctly.

2. Conception

I want the operating students to check whether the big promotion offers are normal, and at the same time, I hope that no extra work will be added. How to do it?

Taking into account the particularity of the e-commerce business, the various big promotion discounts configured will ultimately be mainly reflected in the discounted price, so we consider to implement it from this perspective.

On the core links of e-commerce, there are mainly business details page, shopping cart, order confirmation, and order submission. Then you only need to see the discounted price in advance in these scenarios to judge the big price. Whether the promotion promotion is configured correctly.

The key question now is how to achieve "in advance" see? The preamble promotional series of articles we introduced construction denominated center of , pricing center unified computing power shut all of the price, so we just let denominated center can provide "advance" ability can be.

The pricing center calculates the discount price. Normally, it will only calculate the various discounts that the product can enjoy at the current time in real time, and inform the upstream business party of the final discount price, so we can enable the pricing center to calculate the discount price at "a certain point in time in the future" , And when the pricing center calculates the discount price, one of the key information relied on is "current time" , so we only need to change the so-called "current time" to "tampering" to a certain point in the future To achieve what we call the purpose of crossing.

There is also a very important point of concern, but also the ability to cross the premise that they can not affect the normal trading online, that can not make normal ordinary users "advance" see the future price.

So how can we achieve the operation experience without affecting normal users? We consider adopting a whitelist mechanism, so that the so-called traversal experience can only be carried out for users who have logged in and whose user id is in the whitelist.

After determining the general idea, there are still some issues that need to be confirmed:

Do you only need the mall shopping process for the complete experience of traversal?

If you need to experience all the atmosphere of the entire official website during the big promotion, there may be more changes involved, such as the big promotion promotion page, the exclusive aggregation product page, and the simplified version only focuses on the entire shopping order process.

Does the entire traversal process need to actually create an order?

After traveling through time, the user's order time and the time of confirming the order are the same, so all the discounts on the order confirmation page and the final price are truly WYSIWYG, and you can get all the discount information without actually placing an order.

Therefore, when submitting an order, it is recommended to directly block and remind the user "You are currently in time and space, please return to the reality to place an order". The order is not actually created, and there will be no subsequent chain operations of writing resources. At the same time, many unnecessary changes will be reduced in this case.

Do I need to make a special mark for the user special coupons received during the traversal process?

a) If the coupons received during the traversal process are specially marked, after exiting the time machine, after the coupon is actually available, it should be recommended not to use it to prevent the occupation of ordinary user resources, and it is not recommended to increase discounts in this case The number of coupons issued.

b) The coupons received during the traversal process are not specially marked, so after exiting the time machine, using the coupons is no different from other coupons normally received. This situation is considered to occupy the resources of ordinary users, so it is recommended to increase the coupons accordingly. The number of coupons issued.

Plan a requires the coupon system to make relevant adaptation changes, but there is no pollution or occupation of real online resources; plan b does not require any changes, but it will occupy a very small amount of real resources. If the operator feels that the problem is not big, it is recommended to use plan b , From the project point of view, the cost is minimal.

Three, realize

3.1 Core flow chart

According to the aforementioned conceived plan, the following mall passes through the core shopping process:

3.2 Key points of transformation

The key points of the transformation can be seen from the above flow chart:

  • Maintenance of whitelist information
  • Get the "current time"

3.2.1 Whitelist information maintenance

In order to facilitate the subsequent sharing of user time information, we store this information (openId: travelTime) in the configuration center, and provide a corresponding management console to facilitate the setting of the user and the time of travel.

3.2.2 Get "Current Time"

The entire upstream and downstream associated system may need to obtain "current time" , and "current time" needs to be able to obtain the configured whitelist information and current user information. Obviously, in order to reduce code changes as much as possible for each business system, acquiring "current time" suitable for a public module, and each business system depends on this public module to automatically have the expected "current time" .

Therefore, the link relationship of the entire business system after integrating the time machine module is as follows:

3.2.3 Time Machine Module

From the foregoing, we can derive the main capabilities that need to be included in the time machine module (vivo-xxx-time-travel):

  • a) Traverse user whitelist information
  • b) Get the "current time"
  • c) Read and set the context openId

The implementation of a and b is relatively simple. You only need to access the company's configuration center normally and obtain "current time" according to the specified openId. The more troublesome thing is to get "current time" for this user at openId information.

The previous interface calls between various business systems may not require user openId information, but now the traversing users are designated whitelist users, so the user openId information detected by the ingress link must be passed all the way down to the downstream. Business system.

Solution 1: calls the coupling openId information between the various business systems of the 1612d8d302df2c, and all business systems need to be remodeled. Obviously, this solution is relatively primitive and very unfriendly to all business parties, so it is not recommended to use it. Solution 2: Since each of our back-end business systems uses dubbo for interface calls, we can use dubbo's custom business filter based on the spi plug-in mechanism to transparently transmit openId as additional information when the additional interface is called. (If it is in other interface calling methods, it is also recommended to adopt similar principle processing methods)

Let's take a look at some core code implementations in the time machine module: (filters executed when the current business system is used as a consumer)

The filter executed when the current business system is the consumer

/**
 * 当前业务系统作为消费方时执行的过滤器
 */
@Activate(group = Constants.CONSUMER)
public class BizConsumerFilter implements Filter {
 
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        if (invocation instanceof RpcInvocation) {
            String openId = invocation.getAttachment("tc_xxx_travel_openId");
            if (openId == null && TimeTravelUtil.getContextOpenId() != null) {
                // 作为消费方在发起调用前,如果缺失openId,则设置上下文的openId
                ((RpcInvocation) invocation).setAttachment(openIdAttachmentKey, TimeTravelUtil.getContextOpenId());
            }
        }
        return invoker.invoke(invocation);
    }
}

current business system as a filter executed by the service provider;

/**
 * 当前业务系统作为服务提供方时执行的过滤器
 */
@Activate(group = Constants.PROVIDER)
public class BizProviderFilter implements Filter {
 
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        if (invocation instanceof RpcInvocation) {
            String openId = invocation.getAttachment("tc_xxx_travel_openId");
            if (openId != null) {// 作为下游服务提供方,获取上游系统设置的上下文openId
                TimeTravelUtil.setContextOpenId(openId);
            }
        }
        try {
            return invoker.invoke(invocation);
        } finally {
            TimeTravelUtil.removeContextOpenId();
        }
    }
}

through time acquisition tools;

/**
 * 穿越时间获取工具类
 */
public final class TimeTravelUtil {
 
    private static final ThreadLocal<TimeTravelInfo> currentUserTimeTravelInfoThreadLocal = new ThreadLocal<>();
    private static final ThreadLocal<String> contextOpenId = new ThreadLocal<>();
 
    public static void setContextOpenId(String openId) {
        contextOpenId.set(openId);
        setUserTravelInfoIfExists(openId);
    }
 
    public static String getContextOpenId() {
        return contextOpenId.get();
    }
 
    public static void removeContextOpenId() {
        contextOpenId.remove();
        removeUserTimeTravelInfo();
    }
 
    /**
     * 设置当前上下文用户穿越信息,如果存在的话
     * @param openId
     */
    public static void setUserTravelInfoIfExists(String openId) {
        // TimeTravellersConfig 会接入配置中心,承载所有白名单穿越用户信息配置,并将每一个穿越用户信息转换为TimeTravelInfo
        TimeTravelInfo userTimeTravelInfo = TimeTravellersConfig.getUserTimeTravelInfo(openId);
        if (userTimeTravelInfo.isInTravel()) {
            currentUserTimeTravelInfoThreadLocal.set(userTimeTravelInfo);
        }
    }
 
    /**
     * 移除当前上下文用户穿越信息
     */
    public static void removeUserTimeTravelInfo() {
        currentUserTimeTravelInfoThreadLocal.remove();
    }
 
    /**
     * 当前链路上下文是否处于穿越中
     * @return
     */
    public static boolean isInTimeTravel() {
        return currentUserTimeTravelInfoThreadLocal.get() != null;
    }
 
    /**
     * 获取「当前」时间,单位:毫秒。
     * 若当前是穿越中,则返回设置的穿越时间,否则返回实际系统时间
     * @return
     */
    public static long getNow() {
        TimeTravelInfo travelInfo = currentUserTimeTravelInfoThreadLocal.get();
        return travelInfo != null ? travelInfo.getTravelTime() : System.currentTimeMillis();
    }
 
}

User traversal information

/**
 * 用户穿越信息
 */
public class TimeTravelInfo {
    /**
     * 当前是否处于穿越中
     */
    private boolean isInTravel = false;
 
    /**
     * 当前穿越时间点,仅在isInTravel=true时有效
     */
    private Long travelTime;
 
    public boolean isInTravel() {
        return isInTravel;
    }
 
    public void setInTravel(boolean inTravel) {
        isInTravel = inTravel;
    }
 
    public Long getTravelTime() {
        return travelTime;
    }
 
    public void setTravelTime(Long travelTime) {
        this.travelTime = travelTime;
    }
}

After the business system relies on this vivo-xxx-time-travel module, wherever the current time needs to be obtained, change the original System.currentTimeMillis() to TimeTravelUtil.getNow().

3.4 problem

A more important problem encountered in the process of time machine capacity building is that when the context transfers openId information, there will be cross-thread transfer loss.

If the bottom layer is the Java thread pool to directly implement asynchronous calls, then the context copy and transfer can be realized by intercepting the thread pool. Our internal full-link system has already processed the thread context information through the relevant proxy technology. If you use Hystrix asynchronous call, you can look at the article the author of another article devoted to " Hystrix how to solve ThreadLocal information loss ."

Four, finally

The time machine-related capabilities introduced in this article are mainly used in the official website mall, but are not limited to e-commerce scenarios. The time machine module is not coupled with a specific business when it is designed, so it can also be applied to other business scenarios or have some Reference meaning.

In addition, in the e-commerce scenario in this article, the focus is on whether the discounted price is normal. It basically involves the read operation. If some scenarios require complete business function operations after traversing, such as actually placing an order, then some write operations will be involved. At this time, you can use the related capabilities of the shadow library to complete a complete journey through operation.

Author: vivo official website mall development team-Wei Fuping
阅读 537

vivo 互联网技术
分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。
2.5k 声望
10k 粉丝
0 条评论
2.5k 声望
10k 粉丝
文章目录
宣传栏