背景

在前面的 Lattice – 业务叠加产品 例子中,我们启用的简单模式,在启动Lattice中,我们有下面这个代码:

   public static void main(String[] args) {
        Lattice.getInstance().setSimpleMode(true);
        .......
    }

这行代码的作用,是用于Lattice在启动时,自动的进行业务配置的生成并加载到内存中。自动生成配置的代码位置: org.hiforce.lattice.runtime.Lattice#autoBuildBusinessConfig,见下:

    private void autoBuildBusinessConfig() {
        List<ProductConfig> productConfigs = getAllRegisteredProducts().stream()
                .map(this::buildProductConfig)
                .collect(Collectors.toList());
        for (BusinessSpec businessSpec : getAllRegisteredBusinesses()) {
            List<PriorityConfig> priorityConfigs = businessSpec.getRealizations().stream()
                    .flatMap(p -> autoBuildPriorityConfig(businessSpec, p).stream())
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            BusinessConfig businessConfig = BusinessConfig.builder()
                    .bizCode(businessSpec.getCode())
                    .installedProducts(productConfigs)
                    .priorityConfigs(priorityConfigs)
                    .build();
            autoMakeupPriorityConfig(businessConfig, getAllRegisteredProducts());
            businessConfigs.add(businessConfig);
        }
    }

一般而言,在开发时用简单模式是比较方便的,但是存在一些隐患:

  1. 因为在AppStore中,最终会沉淀成百上千个产品插件,并不是每个产品插件,业务都需要安装;自动配置是默认业务需要安装所有产品插件。这会导致一次业务调用中,需要遍历所有产品,并判断产品是否生效,这个会非常耗时;
  2. 扩展点调用中,如果有多个定制逻辑实现,在FIRST这种Reduce策略下,会对产品优先级非常敏感。 而自动装配的产品,是按照产品自己制定的优先级(默认是500)进行升序排列的(数字越小,优先级越高)。 对于优先级相同情况下,最终排序的结果会和JVM的类加载顺序有关。如果,产品在设计时,没有做好充分的排他性设计,这对于业务执行的确定性,存在重大隐患;
  3. 如果AppStore中的产品插件,如果是同一个团队提供的,相对比较容易通过 @Product注解中显性申明优先级可以解决。但在多团队合作、生态合作都能参与贡献产品插件情况下,就比较容易产生优先级冲突
  4. 即使产品做了显性的优先级申明,但实际复杂业务运行中,会发现 A 业务和 B业务对于已安装的两个产品的优先级要求会截然相反,那么这种自动配置方式,就不适用了

所以,在生产环境上,为了业务的确定性,我们可以用代码方式、或者从数据库中读取配置方式,进行业务配置的加载。 这个业务配置,未来是可以做成可视化。 所有人,都能清楚的看到业务的定义如何,这个业务安装了哪些产品插件。 安装的产品在哪些点上会有冲突,这个冲突的优先级是在呢么样的。这对于业务知识的传递、问题排查、生态构建会有极大的帮助。

Step 1: 构建一个无业务配置的调用过程

 public class LoadBusinessConfigSample {

    public static void main(String[] args) {
        Lattice.getInstance().start();
        System.out.println("---------------------------------------");
        LatticeOverlayProductSample.doBusiness("groupBy");
        System.out.println("---------------------------------------");
    }
}

因为没有启动简单模式 ,所以上述代码的调用结果如下:

---------------------------------------
Lattice extension invoke error. Due to the business config is null. bizCode: [business.b]
---------------------------------------

Step 2: 为 business.b 构建配置

    private static BusinessConfig buildBusinessBConfig() {
        return BusinessConfigBuilder.builder()
                .bizCode(BusinessB.CODE)
                .build();
    }

通过BusinessConfig的Builder构造器,我们可以为业务身份为 "business.b” 的业务创建一份配置。

Step 3: 为 business.b 安装 “团购产品”

我们继续修改上面的代码,通过ProductConfigBuilder为业务安装产品插件,如下:

    private static BusinessConfig buildBusinessBConfig() {
        return BusinessConfigBuilder.builder()
                .bizCode(BusinessB.CODE)
                .install(GroupBuyProduct.CODE)
                .build();
    }

Step 4: 为冲突的扩展点指定优先级(即调用顺序)

在我们的样例中,“自定义商品单价”扩展点,是存在多份业务定制实现的,是有冲突的。这里,我们可以指定优先级,让产品的优先级更高。这样,但产品处于生效状态下,那么产品的定制逻辑会优先被调用。如下:

public class LoadBusinessConfigSample {

    public static void main(String[] args) {
        Lattice.getInstance().start();

        Lattice.getInstance().addBusinessConfig(buildBusinessBConfig());//register business configuration.

        System.out.println("---------------------------------------");
        LatticeOverlayProductSample.doBusiness("groupBy");
        System.out.println("---------------------------------------");
    }

    private static BusinessConfig buildBusinessBConfig() {
        return BusinessConfigBuilder.builder()
                .bizCode(BusinessB.CODE)
                .install(GroupBuyProduct.CODE)
                .extension(
                        PriorityConfigBuilder.builder()
                                .extCode(EXT_ORDER_LINE_CUSTOM_UNIT_PRICE)
                                .priority(GroupBuyProduct.CODE, PRODUCT)
                                .priority(BusinessB.CODE, BUSINESS)
                                .build())
                .build();
    }
}

至此,自主注册一份业务配置的样例就可以执行了,执行结果可以发现是产品优先,结果如下:

---------------------------------------
GroupBuyProduct effect status:true
[Business B] overlay product unit price: 700
---------------------------------------

当然,读者也可以把上面优先级注册地方的顺序改一下,改成 业务的优先级 在 产品的优先级之前,如下:

    private static BusinessConfig buildBusinessBConfig() {
        //优先级调整一下看看效果!
        return BusinessConfigBuilder.builder()
                .bizCode(BusinessB.CODE)
                .install(GroupBuyProduct.CODE)
                .extension(
                        PriorityConfigBuilder.builder()
                                .extCode(EXT_ORDER_LINE_CUSTOM_UNIT_PRICE)
                                .priority(BusinessB.CODE, BUSINESS) 
                                .priority(GroupBuyProduct.CODE, PRODUCT)
                                .build())
                .build();
    }

再次执行本样例,执行结果会变成:

---------------------------------------
GroupBuyProduct effect status:true
[Business B] overlay product unit price: 1000
---------------------------------------

样例代码

样例代码可以通过访问: https://github.com/hiforce/lattice-sample/tree/main/lattice-business-config 获取