zoukankan      html  css  js  c++  java
  • 使用centraldogma 作为easy-rules spring boot starter的规则存储

    centraldogma 前边有介绍过,同时也说明过部署以及使用,以下是基于centraldogma 扩展easy-rules 的spring boot starter
    方便快捷的支持rule 的修改以及实时更新,代码已经push github了,可以参考使用

    开发流程

    借鉴了以前easy-rules spring boot starter的开发,只是调整添加了对于centraldogma的支持

    • 核心代码结构
     
    ├── README.md
    ├── pom.xml
    └── src
        ├── main
        ├── java
        └── com
        └── github
        └── rongfengliang
        ├── DefaultRuleEngineListener.java
        ├── DefaultRulesListener.java
        ├── EasyRulesAutoConfiguration.java
        ├── EasyRulesEngineConfiguration.java
        ├── EasyRulesEntities.java
        ├── RuleExpressionType.java
        ├── RulesConfig.java
        ├── RulesContentType.java
        ├── SimpleBeanResovler.java
        ├── SpringBeanUtil.java
        └── rulelocator
        ├── RulesLocator.java
        └── impl
        └── FileRulesLocator.java
        └── resources
        └── META-INF
        └── spring.factories
        └── test
            └── java
                └── com
                    └── github
                        └── rongfengliang
                            └── CentraldogmaStringReader.java
     
     
     
    • 代码说明
      pom.xml
     
    <?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.github.rongfengliang</groupId>
        <artifactId>easy-rules-centraldogma-spring-boot-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <encoding>UTF-8</encoding>
            <java.version>1.8</java.version>
            <spring-boot.version>2.2.3.RELEASE</spring-boot.version>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <easy-rules>4.1.0</easy-rules>
            <centraldogma-client>0.51.1</centraldogma-client>
            <slf4j-simple>1.7.32</slf4j-simple>
            <lombok.version>1.18.20</lombok.version>
        </properties>
        <scm>
            <developerConnection>scm:git:https://github.com/rongfengliang/easy-rules-centraldogma-spring-boot-starer.git</developerConnection>
          <tag>HEAD</tag>
      </scm>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
                <version>${spring-boot.version}</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-core</artifactId>
                <version>${easy-rules}</version>
            </dependency>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-mvel</artifactId>
                <version>${easy-rules}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
                <optional>true</optional>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>com.linecorp.centraldogma</groupId>
                <artifactId>centraldogma-client-spring-boot-starter</artifactId>
                <version>${centraldogma-client}</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
                <version>${spring-boot.version}</version>
            </dependency>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-spel</artifactId>
                <version>${easy-rules}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>${spring-boot.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>${slf4j-simple}</version>
                <optional>true</optional>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <distributionManagement>
            <repository>
                <id>sonatype-releases</id>
                <name>sonatype Release Repository</name>
                <url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
            </repository>
            <snapshotRepository>
                <id>sonatype-snapshots</id>
                <name>sonatype Snapshot Repository</name>
                <url>https://oss.sonatype.org/content/repositories/snapshots</url>
            </snapshotRepository>
        </distributionManagement>
     
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-release-plugin</artifactId>
                    <version>3.0.0-M4</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                    <version>3.2.0</version>
                    <configuration>
                        <attach>true</attach>
                    </configuration>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>jar-no-fork</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
     
     

    配置入口

    package com.github.rongfengliang;
     
     
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.linecorp.centraldogma.client.CentralDogma;
    import com.linecorp.centraldogma.client.Watcher;
    import com.linecorp.centraldogma.common.Entry;
    import com.linecorp.centraldogma.common.Query;
    import com.linecorp.centraldogma.common.Revision;
    import org.jeasy.rules.api.*;
    import org.jeasy.rules.core.DefaultRulesEngine;
    import org.jeasy.rules.spel.SpELRuleFactory;
    import org.jeasy.rules.support.reader.JsonRuleDefinitionReader;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Scope;
    import org.springframework.expression.BeanResolver;
     
    import java.io.StringReader;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.concurrent.CompletableFuture;
    import java.util.function.Consumer;
     
    @Configuration
    @EnableConfigurationProperties(EasyRulesEngineConfiguration.class)
    public class EasyRulesAutoConfiguration {
        private Logger log = LoggerFactory.getLogger(DefaultRulesListener.class);
        private final EasyRulesEngineConfiguration properties; // 配置
        private Map<String, Rules> easyRules; // 基于map 存储bean 中间状态数据
        private EasyRulesEntities easyRulesEntities; //  基于map 存储bean 中间状态数据
        EasyRulesAutoConfiguration(EasyRulesEngineConfiguration properties) {
            this.properties = properties;
            this.easyRules = null;
            this.easyRulesEntities =new EasyRulesEntities();
        }
     
        @Bean
        @ConditionalOnMissingBean
        public RuleListener defaultRulesListener() {
            return new DefaultRulesListener();
        }
     
        @Bean
        @ConditionalOnMissingBean
        public RulesEngineListener defaultRuleEngineListener() {
            return new DefaultRuleEngineListener();
        }
     
        @Bean
        @ConditionalOnMissingBean
        public BeanResolver defaultedResolver(SpringBeanUtil springBeanUtil) {
            return new SimpleBeanResovler(SpringBeanUtil.getApplicationContext());
        }
     
        @Bean
        @ConditionalOnMissingBean
        public SpringBeanUtil springBeanUtil() {
            return new SpringBeanUtil();
        }
     
        @Bean 
        @ConditionalOnClass(value = {CentralDogma.class, ObjectMapper.class}) // 需要依赖jackson以及CentralDogma bean,此bean 主要实现加载之后的配置加载以及变动数据的实时更新,同时转换为对应的easy-rules
        public CommandLineRunner commandLineRunner(CentralDogma dogma, ObjectMapper objectMapper, BeanResolver beanResolver) {
            log.info("project:{}, repo:{},filename:{}", properties.getProject(), properties.getRepo(), properties.getConfName());
            Watcher watcher = dogma.fileWatcher(properties.getProject(), properties.getRepo(), Query.ofText(properties.getConfName()));
            watcher.watch((revision, value) -> {
                log.info("Updated to {} at {}", value, revision);
                try {
                    List<RulesConfig> rulesConfigs = objectMapper.readValue(value.toString(), new TypeReference<List<RulesConfig>>() {
                    });
                    easyRulesEntities.setRulesConfigs(rulesConfigs);
                    this.easyRules = rulesContent2EasyRules(beanResolver);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                    log.error("json convert  error", e);
                }
            });
            return args -> {
                log.info("init CentralDogma load easy rules conf");
            };
        }
     
        /**
         * centralDogmaRules with prototype for fetch new conf rules
         * bean name is <b>centralDogmaRules</b>
         *
         * @param beanResolver
         * @param centralDogma
         * @return
         * @throws Exception
         */
        @Bean(name = "centralDogmaRules")
        @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        @ConditionalOnClass(value = {CentralDogma.class, ObjectMapper.class}) // 为了安全以及实时变动支持,基于了原型模式
        public Map<String, Rules> centralDogmaRules(BeanResolver beanResolver, CentralDogma centralDogma, ObjectMapper objectMapper) throws Exception {
            Map<String, Rules> rules = new HashMap<>();
            if (Objects.isNull(easyRules)) {
                CompletableFuture<Entry<String>> future =
                        centralDogma.getFile(properties.getProject(), properties.getRepo(), Revision.HEAD, Query.ofText(properties.getConfName()));
                log.info("load rule  content", future.join().content());
                List<RulesConfig> rulesConfigs = objectMapper.readValue(future.join().content(), new TypeReference<List<RulesConfig>>() {
                });
                easyRulesEntities.setRulesConfigs(rulesConfigs);
                return getStringRulesMap(beanResolver, rules);
            } else {
                return this.easyRules;
            }
     
        }
     
        /**
         * common method for Map<String, Rules> convert
         * @param beanResolver
         * @param rules
         * @return
         */
        private Map<String, Rules> getStringRulesMap(BeanResolver beanResolver, Map<String, Rules> rules) {
            easyRulesEntities.getRulesConfigs().forEach(new Consumer<RulesConfig>() {
                @Override
                public void accept(RulesConfig rulesConfig) {
                    log.info("load rule conf to easy rules engine:{}", rulesConfig.getRulesContent());
                    StringReader stringReader = new StringReader(rulesConfig.getRulesContent().toPrettyString());
                    SpELRuleFactory jsonRuleFactory = new SpELRuleFactory(new JsonRuleDefinitionReader(), beanResolver);
                    Rules jsonRules = null;
                    try {
                        jsonRules = jsonRuleFactory.createRules(stringReader);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    rules.put(rulesConfig.getRulesId(), jsonRules);
                }
            });
            return rules;
        }
     
        /**
         * convert rulesContent2EasyRules
         *
         * @param beanResolver
         * @return
         */
        private Map<String, Rules> rulesContent2EasyRules(BeanResolver beanResolver) {
            Map<String, Rules> rules = new HashMap<>();
            return getStringRulesMap(beanResolver, rules);
        }
        /**
         * 获取配置额规则列表
         *
         * @param beanResolver spring beanResolver
         * @return Map<String, Rules>
         * @throws Exception
         */
        /**
         * 为了安全使用原型模式
         *
         * @param defaultRulesListener
         * @param defaultRuleEngineListener
         * @return RulesEngine
         */
        @Bean
        @ConditionalOnMissingBean(RulesEngine.class)
        @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        public RulesEngine rulesEngine(RuleListener defaultRulesListener, RulesEngineListener defaultRuleEngineListener) {
            log.info("create rule Engine");
            RulesEngineParameters parameters = new RulesEngineParameters();
            if (this.properties.getPriorityThreshold() > 0) {
                parameters.setPriorityThreshold(this.properties.getPriorityThreshold());
            }
            if (this.properties.isSkipOnFirstAppliedRule()) {
                parameters.setSkipOnFirstAppliedRule(this.properties.isSkipOnFirstAppliedRule());
            }
            if (this.properties.isSkipOnFirstFailedRule()) {
                parameters.setSkipOnFirstFailedRule(this.properties.isSkipOnFirstFailedRule());
            }
            if (this.properties.isSkipOnFirstNonTriggeredRule()) {
                parameters.setSkipOnFirstNonTriggeredRule(this.properties.isSkipOnFirstNonTriggeredRule());
            }
            DefaultRulesEngine rulesEngine = new DefaultRulesEngine(parameters);
            rulesEngine.registerRuleListener(defaultRulesListener);
            rulesEngine.registerRulesEngineListener(defaultRuleEngineListener);
            return rulesEngine;
        }
     
    }
     
     

    EasyRulesEngineConfiguration 配置主要包含了一些通用参数的支持

    package com.github.rongfengliang;
     
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
     
     
    @Data
    @ConfigurationProperties(prefix = "easyrules")
    public class EasyRulesEngineConfiguration {
        private boolean skipOnFirstAppliedRule;
        private boolean skipOnFirstNonTriggeredRule;
        private boolean skipOnFirstFailedRule;
        private int priorityThreshold;
        private String project;
        private String repo;
        private String contentType;
        private String confName;
    }
     
     

    规则存储实体定义RulesConfig(因为数据结构的多变,使用了jsonnode,实现灵活扩展)

    package com.github.rongfengliang;
     
     
    import com.fasterxml.jackson.databind.JsonNode;
    import lombok.Data;
     
    /**
     * @author dalong
     * rules文件配置信息
     */
     
    @Data
    public class RulesConfig {
        private String rulesId;
        private JsonNode rulesContent;
    }
     
     

    自定义beanresovler SimpleBeanResovler 主要是spel 需要

    package com.github.rongfengliang;
     
    import org.springframework.context.ApplicationContext;
    import org.springframework.expression.BeanResolver;
    import org.springframework.expression.EvaluationContext;
     
    /**
     * @author dalong
     * SimpleBeanResovler
     */
    public class SimpleBeanResovler implements BeanResolver {
        private ApplicationContext applicationContext;
     
        public SimpleBeanResovler(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
        }
     
        @Override
        public Object resolve(EvaluationContext context, String beanName) {
            return applicationContext.getBean(beanName);
        }
    }
     

    SpringBeanUtil bean 工具类,主要简化原型的处理

    package com.github.rongfengliang;
     
    import org.jeasy.rules.api.Rules;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
     
    import java.util.Map;
     
    public class SpringBeanUtil implements ApplicationContextAware {
        /**
         * 上下文对象实例
         */
        private static ApplicationContext applicationContext;
     
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
     
        public static Map<String, Rules> centralDogmaRules(){
            return SpringBeanUtil.getBean("centralDogmaRules",Map.class);
        }
     
        /**
         * 获取applicationContext
         *
         * @return
         */
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
     
        /**
         * 通过name获取 Bean.
         *
         * @param name
         * @return
         */
        public static Object getBean(String name) {
            return getApplicationContext().getBean(name);
        }
     
        /**
         * 通过class获取Bean.
         *
         * @param clazz
         * @param <T>
         * @return
         */
        public static <T> T getBean(Class<T> clazz) {
            return getApplicationContext().getBean(clazz);
        }
     
        /**
         * 通过name,以及Clazz返回指定的Bean
         *
         * @param name
         * @param clazz
         * @param <T>
         * @return
         */
        public static <T> T getBean(String name, Class<T> clazz) {
            return getApplicationContext().getBean(name, clazz);
        }
    }

    说明

    一以上是具体的代码部分,关于使用,后边会有介绍

    参考资料

    https://github.com/rongfengliang/easy-rules-centraldogma-spring-boot-starter
    https://github.com/line/centraldogma

  • 相关阅读:
    Template-网页模板:百科
    POJ 1743 Musical Theme(后缀数组)
    android ProgressBar 样式讲解
    opencv显示鼠标所在位置的rgb值
    JSU 2013 Summer Individual Ranking Contest
    spring mvc 中文乱码 post与get的方法解决
    iphone/ipad实现自定义的开关UISwitch(continuous,clipsToBounds,userInteractionEnabled属性)
    hdu 2093
    poj 1180 斜率优化dp
    太空飞行计划问题
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/15168860.html
Copyright © 2011-2022 走看看