├── env.list ├── src ├── main │ ├── resources │ │ ├── META-INF │ │ │ └── app.properties │ │ ├── application-dev.properties │ │ ├── application-prod.properties │ │ ├── application.properties │ │ ├── rule-template.drl │ │ ├── log4j2_dev.xml │ │ └── log4j2_prod.xml │ └── java │ │ └── com │ │ └── netease │ │ └── push │ │ ├── rule │ │ ├── Tsdb.java │ │ ├── Action.java │ │ ├── Kafka.java │ │ ├── RuleMetadata.java │ │ ├── Event.java │ │ ├── Condition.java │ │ ├── Rule.java │ │ ├── RuleEngineManager.java │ │ └── DynamicRuleParser.java │ │ ├── message │ │ ├── deserializers │ │ │ ├── Deserializer.java │ │ │ ├── Key.java │ │ │ └── TokenDeserializer.java │ │ └── OnlineStatus.java │ │ ├── kafka │ │ ├── Producer.java │ │ ├── KafkaProperties.java │ │ └── Consumer.java │ │ ├── RuleEngine.java │ │ ├── destination │ │ └── DestinationManager.java │ │ └── utils │ │ └── MonitorControl.java └── test │ └── java │ └── com │ └── netease │ └── push │ ├── RuleEngineTest.java │ ├── rule │ ├── ActionTest.java │ ├── RuleMetadataTest.java │ └── DynamicRuleParserTest.java │ └── kafka │ └── KafkaPropertiesTest.java ├── Dockerfile ├── startup.sh ├── doc └── rule.md ├── .gitignore ├── README.md └── pom.xml /env.list: -------------------------------------------------------------------------------- 1 | ENV=dev 2 | DEV_META=http://10.242.83.109:8080/ 3 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/app.properties: -------------------------------------------------------------------------------- 1 | app.id=rule-engine -------------------------------------------------------------------------------- /src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | logging.config = classpath:log4j2_dev.xml -------------------------------------------------------------------------------- /src/main/resources/application-prod.properties: -------------------------------------------------------------------------------- 1 | logging.config = classpath:log4j2_prod.xml -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=rule-engine 2 | spring.main.web-environment=false 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | VOLUME /tmp 3 | ADD startup.sh / 4 | ARG JAR_FILE 5 | COPY ${JAR_FILE} /app.jar 6 | ENTRYPOINT ["/bin/sh", "/startup.sh"] -------------------------------------------------------------------------------- /startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo `date` 3 | echo `pwd` 4 | echo `ls` 5 | echo $ENV 6 | echo $DEV_META 7 | echo $PRO_MET 8 | echo `java -Denv=$ENV -Ddev_meta=$DEV_META -Dpro_meta=$PRO_META -jar /app.jar` 9 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/rule/Tsdb.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Created by hzliuzebo on 2018/10/18. 7 | */ 8 | @Data 9 | public class Tsdb { 10 | private String host; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/rule/Action.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Created by hzliuzebo on 2018/10/18. 7 | */ 8 | @Data 9 | public class Action { 10 | private Tsdb tsdb; 11 | 12 | private Kafka kafka; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/rule/Kafka.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Created by hzliuzebo on 2018/10/26. 7 | */ 8 | @Data 9 | public class Kafka { 10 | private String brokers; 11 | 12 | private String topic; 13 | } 14 | -------------------------------------------------------------------------------- /doc/rule.md: -------------------------------------------------------------------------------- 1 | ### 规则格式 2 | 3 | ```json 4 | { 5 | "action": { 6 | "kafka": { 7 | "brokers":"10.242.83.109:9092", 8 | "topic":"test" 9 | } 10 | }, 11 | "npnsIotSqlVersion":"2018-10-18", 12 | "ruleDisabled":true, 13 | "sql":"select * from newsOnlineStatus" 14 | } 15 | ``` 16 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/rule/RuleMetadata.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | import lombok.Data; 4 | 5 | 6 | /** 7 | * Created by hzliuzebo on 2018/10/18. 8 | */ 9 | @Data 10 | public class RuleMetadata { 11 | private String sql; 12 | 13 | private Boolean ruleDisabled; 14 | 15 | private String npnsIotSqlVersion; 16 | 17 | private Action action; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/message/deserializers/Deserializer.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.message.deserializers; 2 | 3 | 4 | /** 5 | * Created by hzliuzebo on 2018/9/20. 6 | */ 7 | public interface Deserializer { 8 | /** 9 | * deserialize message to KafkaMessage 10 | * @param message 11 | * @return 12 | */ 13 | public T deserialize(Class destinationClass, String message); 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/com/netease/push/RuleEngineTest.java: -------------------------------------------------------------------------------- 1 | package com.netease.push; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Unit test for simple RuleEngine. 9 | */ 10 | public class RuleEngineTest 11 | { 12 | /** 13 | * Rigorous Test :-) 14 | */ 15 | @Test 16 | public void shouldAnswerWithTrue() 17 | { 18 | assertTrue( true ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/rule-template.drl: -------------------------------------------------------------------------------- 1 | template header 2 | 3 | rule 4 | eventType 5 | 6 | package com.netease.push; 7 | 8 | global com.netease.push.destination.DestinationManager destinationManager; 9 | global com.netease.push.rule.Action action; 10 | 11 | template "filter" 12 | 13 | rule "filter_@{row.rowNumber}" 14 | when 15 | m: @{eventType}(@{rule}) 16 | then 17 | m.setAction(action); 18 | destinationManager.processMessage(m); 19 | end 20 | 21 | end template -------------------------------------------------------------------------------- /src/main/java/com/netease/push/message/deserializers/Key.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.message.deserializers; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Created by hzliuzebo on 2018/9/20. 10 | */ 11 | @Target(ElementType.FIELD) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface Key { 14 | int value() default 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/rule/Event.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | /** 6 | * Created by hzliuzebo on 2018/10/22. 7 | */ 8 | public abstract class Event { 9 | @JSONField(serialize=false) 10 | private Action action; 11 | 12 | public void setAction(Action action) { 13 | this.action = action; 14 | } 15 | 16 | public Action getAction() { 17 | return action; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/message/OnlineStatus.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.message; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | import com.netease.push.message.deserializers.Key; 5 | import com.netease.push.rule.Action; 6 | import com.netease.push.rule.Event; 7 | import lombok.Data; 8 | 9 | /** 10 | * Created by hzliuzebo on 2018/10/22. 11 | */ 12 | @Data 13 | public class OnlineStatus extends Event { 14 | 15 | @JSONField(serialize=false) 16 | public String topic; 17 | @Key(0) 18 | public String deviceId; 19 | @Key(1) 20 | public String onlineStatus; 21 | @Key(2) 22 | public String eventTime; 23 | @Key(3) 24 | public String domain; 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/netease/push/rule/ActionTest.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | import org.junit.Test; 7 | 8 | 9 | /** 10 | * Created by hzliuzebo on 2018/10/18. 11 | */ 12 | public class ActionTest { 13 | private static Logger logger = LogManager.getLogger(ActionTest.class); 14 | @Test 15 | public void shouldTestSuccess() { 16 | Action action = new Action(); 17 | // Kafka kafka = new Kafka(); 18 | // kafka.setBrokers("localhost:9092"); 19 | // kafka.setTopic("test"); 20 | // action.setKafka(kafka); 21 | logger.info("action json string : " + JSON.toJSONString(action)); 22 | logger.info("action : " + action.toString()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/netease/push/kafka/KafkaPropertiesTest.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.kafka; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | /** 9 | * Created by hzliuzebo on 2018/10/26. 10 | */ 11 | @RunWith(SpringRunner.class) 12 | @SpringBootTest 13 | public class KafkaPropertiesTest { 14 | @Test 15 | private void TestKafkaProperties() { 16 | KafkaProperties kafkaProperties = new KafkaProperties(); 17 | kafkaProperties.setBrokerAddress("10.242.83.109:9092"); 18 | kafkaProperties.setAutoCommit(true); 19 | kafkaProperties.setGroup("rule-engine"); 20 | kafkaProperties.setMaxPollCount(1000); 21 | kafkaProperties.setMaxPollInterval(10); 22 | kafkaProperties.setTopics("newsOnlineStatus"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | target/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # IntelliJ 37 | *.iml 38 | .idea/ 39 | 40 | # Keystore files 41 | # Uncomment the following line if you do not want to check your keystore files in. 42 | #*.jks 43 | 44 | # External native build folder generated in Android Studio 2.2 and later 45 | .externalNativeBuild 46 | 47 | # Google Services (e.g. APIs or Firebase) 48 | google-services.json 49 | 50 | # Freeline 51 | freeline.py 52 | freeline/ 53 | freeline_project_description.json 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### rule engine 2 | 3 | #### kafka config 4 | 5 | kafka consumer config 6 | ```json 7 | { 8 | "brokerAddress": "localhost:9092", 9 | "group": "rule-engine", 10 | "topics": "topic", 11 | "autoCommit": true, 12 | "maxPollCount": 1000, 13 | "maxPollInterval": 2000, 14 | "maxQps": 1000 15 | } 16 | ``` 17 | 18 | #### rule config 19 | ```json 20 | { 21 | "action": { 22 | "kafka": { 23 | "brokers": "localhost:9092", 24 | "topic": "dest-topic" 25 | } 26 | }, 27 | "npnsIotSqlVersion": "2018-10-18", 28 | "ruleDisabled": true, 29 | "sql": "select * from topic" 30 | } 31 | ``` 32 | 33 | > kafka consumer topic should be equal to sql database, as the above example, the topic should be "topic" 34 | 35 | #### compile jar 36 | 37 | mvn package -Dmaven.test.skip=true 38 | 39 | #### build docker image 40 | mvn package dockerfile:build -Dmaven.test.skip=true 41 | 42 | #### run jar 43 | java -Denv=DEV -Ddev_meta=http://localhost:8080/ -jar ./target/push-rule-engine-1.0-SNAPSHOT.jar 44 | 45 | -------------------------------------------------------------------------------- /src/test/java/com/netease/push/rule/RuleMetadataTest.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by hzliuzebo on 2018/10/18. 13 | */ 14 | public class RuleMetadataTest { 15 | private static Logger logger = LogManager.getLogger(RuleMetadataTest.class); 16 | 17 | @Test 18 | public void shouldTestSuccess() { 19 | RuleMetadata ruleMetadata = new RuleMetadata(); 20 | logger.info("test set sql."); 21 | ruleMetadata.setSql("select * from device.info"); 22 | ruleMetadata.setRuleDisabled(true); 23 | ruleMetadata.setNpnsIotSqlVersion("2018-10-18"); 24 | 25 | Action action = new Action(); 26 | Kafka kafka = new Kafka(); 27 | kafka.setBrokers("localhost:9092"); 28 | kafka.setTopic("test"); 29 | action.setKafka(kafka); 30 | ruleMetadata.setAction(action); 31 | logger.info("rule metadata : " + JSON.toJSONString(ruleMetadata)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/kafka/Producer.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.kafka; 2 | 3 | import org.apache.kafka.clients.producer.ProducerConfig; 4 | import org.apache.kafka.common.serialization.StringSerializer; 5 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 6 | import org.springframework.kafka.core.KafkaTemplate; 7 | import org.springframework.kafka.core.ProducerFactory; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | @Component 14 | public class Producer { 15 | 16 | private KafkaTemplate kafkaTemplate; 17 | 18 | public void start(KafkaProperties properties) { 19 | Map map = new HashMap<>(); 20 | map.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, properties.getBrokerAddress()); 21 | map.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 22 | map.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 23 | ProducerFactory producerFactory = new DefaultKafkaProducerFactory(map); 24 | kafkaTemplate = new KafkaTemplate<>(producerFactory); 25 | } 26 | 27 | public void sendMessage(String topic, String message){ 28 | kafkaTemplate.send(topic, message); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/netease/push/rule/DynamicRuleParserTest.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | import com.netease.push.message.OnlineStatus; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.context.junit4.SpringRunner; 9 | 10 | import java.util.Date; 11 | 12 | /** 13 | * Created by hzliuzebo on 2018/10/22. 14 | */ 15 | @RunWith(SpringRunner.class) 16 | @SpringBootTest 17 | public class DynamicRuleParserTest { 18 | @Autowired 19 | private DynamicRuleParser dynamicRuleParser; 20 | 21 | @Test 22 | public void shoudlTestDynamicRuleParserSuccess() { 23 | RuleMetadata ruleMetadata = new RuleMetadata(); 24 | ruleMetadata.setSql("select * from test"); 25 | ruleMetadata.setRuleDisabled(true); 26 | ruleMetadata.setNpnsIotSqlVersion("2018-10-22"); 27 | Action action = new Action(); 28 | // Kafka kafka = new Kafka(); 29 | // kafka.setBrokers("localhost:9092"); 30 | // kafka.setTopic("test"); 31 | // action.setKafka(kafka); 32 | ruleMetadata.setAction(action); 33 | dynamicRuleParser.update(ruleMetadata); 34 | OnlineStatus onlineStatus = new OnlineStatus(); 35 | onlineStatus.setTopic("test"); 36 | onlineStatus.setDeviceId("deviceId"); 37 | onlineStatus.setOnlineStatus(1+""); 38 | onlineStatus.setEventTime(new Date().getTime()+""); 39 | onlineStatus.setDomain("news.163.com"); 40 | dynamicRuleParser.evaluate(onlineStatus); 41 | onlineStatus.setTopic("two"); 42 | dynamicRuleParser.evaluate(onlineStatus); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/kafka/KafkaProperties.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.kafka; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public class KafkaProperties { 7 | 8 | private static Logger logger = LoggerFactory.getLogger(KafkaProperties.class); 9 | 10 | private String brokerAddress; 11 | private String group; 12 | private String topics; 13 | private Boolean autoCommit; 14 | private Integer maxPollInterval; 15 | private Integer maxPollCount; 16 | 17 | public String getGroup() { 18 | return group; 19 | } 20 | 21 | public void setGroup(String group) { 22 | this.group = group; 23 | } 24 | 25 | public String getBrokerAddress() { 26 | return this.brokerAddress; 27 | } 28 | 29 | public void setBrokerAddress(String brokerAddress) { 30 | this.brokerAddress = brokerAddress; 31 | } 32 | 33 | public void show() { 34 | logger.info("broker address : [{}], group : [{}], topic : [{}]", 35 | this.getBrokerAddress(), this.getGroup(), this.getTopics()); 36 | } 37 | 38 | public String getTopics() { 39 | return topics; 40 | } 41 | 42 | public void setTopics(String topics) { 43 | this.topics = topics; 44 | } 45 | 46 | public Boolean getAutoCommit() { 47 | return autoCommit; 48 | } 49 | 50 | public void setAutoCommit(Boolean autoCommit) { 51 | this.autoCommit = autoCommit; 52 | } 53 | 54 | public Integer getMaxPollInterval() { 55 | return maxPollInterval; 56 | } 57 | 58 | public void setMaxPollInterval(Integer maxPollInterval) { 59 | this.maxPollInterval = maxPollInterval; 60 | } 61 | 62 | public Integer getMaxPollCount() { 63 | return maxPollCount; 64 | } 65 | 66 | public void setMaxPollCount(Integer maxPollCount) { 67 | this.maxPollCount = maxPollCount; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/rule/Condition.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | /** 4 | * Created by hzliuzebo on 2018/10/22. 5 | */ 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class Condition { 11 | 12 | private String field; 13 | private Object value; 14 | private Condition.Operator operator; 15 | 16 | public String getField() { 17 | return field; 18 | } 19 | 20 | public void setField(String field) { 21 | this.field = field; 22 | } 23 | 24 | public Object getValue() { 25 | return value; 26 | } 27 | 28 | public void setValue(Object value) { 29 | this.value = value; 30 | } 31 | 32 | public Condition.Operator getOperator() { 33 | return operator; 34 | } 35 | 36 | public void setOperator(Condition.Operator operator) { 37 | this.operator = operator; 38 | } 39 | 40 | public static enum Operator { 41 | NOT_EQUAL_TO("NOT_EQUAL_TO"), 42 | EQUAL_TO("EQUAL_TO"), 43 | GREATER_THAN("GREATER_THAN"), 44 | LESS_THAN("LESS_THAN"), 45 | GREATER_THAN_OR_EQUAL_TO("GREATER_THAN_OR_EQUAL_TO"), 46 | LESS_THAN_OR_EQUAL_TO("LESS_THAN_OR_EQUAL_TO"); 47 | private final String value; 48 | private static Map constants = new HashMap(); 49 | 50 | static { 51 | for (Condition.Operator c : values()) { 52 | constants.put(c.value, c); 53 | } 54 | } 55 | 56 | private Operator(String value) { 57 | this.value = value; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return this.value; 63 | } 64 | 65 | public static Condition.Operator fromValue(String value) { 66 | Condition.Operator constant = constants.get(value); 67 | if (constant == null) { 68 | throw new IllegalArgumentException(value); 69 | } else { 70 | return constant; 71 | } 72 | } 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/RuleEngine.java: -------------------------------------------------------------------------------- 1 | package com.netease.push; 2 | 3 | import com.netease.push.rule.RuleEngineManager; 4 | import com.netease.push.utils.MonitorControl; 5 | import org.apache.log4j.LogManager; 6 | import org.apache.log4j.Logger; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.CommandLineRunner; 9 | import org.springframework.boot.SpringApplication; 10 | import org.springframework.boot.autoconfigure.SpringBootApplication; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * Hello world! 16 | * 17 | */ 18 | @SpringBootApplication 19 | public class RuleEngine implements CommandLineRunner { 20 | private static Logger logger = LogManager.getLogger(RuleEngine.class); 21 | 22 | @Autowired 23 | private RuleEngineManager ruleEngineManager; 24 | 25 | public static void main( String[] args ) { 26 | // do not need start web server 27 | // ConfigurableApplicationContext context = new SpringApplicationBuilder(PushApnsApplication.class).web(false).run(args); 28 | SpringApplication.run(RuleEngine.class, args); 29 | try { 30 | waitForShutdown(9090); 31 | } catch (IOException e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | 36 | @Override 37 | public void run(String... args) throws Exception { 38 | ruleEngineManager.start(); 39 | registerShutdownHook(); 40 | } 41 | 42 | private void registerShutdownHook() { 43 | final class MonitorShutdown extends Thread { 44 | private MonitorShutdown() { 45 | super("PushMonitorShutdown"); 46 | } 47 | 48 | public void run() { 49 | logger.info("push monitor agent application exit."); 50 | if (ruleEngineManager.isStarted()) { 51 | ruleEngineManager.stop(); 52 | } 53 | } 54 | } 55 | Runtime.getRuntime().addShutdownHook(new MonitorShutdown()); 56 | } 57 | 58 | private static void waitForShutdown(int port) throws IOException { 59 | new MonitorControl().start(port); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/destination/DestinationManager.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.destination; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.netease.push.kafka.KafkaProperties; 5 | import com.netease.push.kafka.Producer; 6 | import com.netease.push.message.OnlineStatus; 7 | import com.netease.push.rule.Action; 8 | import com.netease.push.rule.Event; 9 | import com.netease.push.rule.Kafka; 10 | import com.netease.push.rule.Tsdb; 11 | import org.apache.log4j.LogManager; 12 | import org.apache.log4j.Logger; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | /** 19 | * Created by hzliuzebo on 2018/10/22. 20 | */ 21 | @Component 22 | public class DestinationManager { 23 | private static Logger logger = LogManager.getLogger(DestinationManager.class); 24 | 25 | private Map kafkaProducerMap = new HashMap<>(); 26 | 27 | public void processMessage(Event event) { 28 | OnlineStatus onlineStatus = (OnlineStatus)event; 29 | if (onlineStatus != null) { 30 | Action action = onlineStatus.getAction(); 31 | logger.info("action : " + JSON.toJSONString(action)); 32 | Kafka kafka = action.getKafka(); 33 | if (kafka != null) { 34 | // send message to kafka 35 | String key = kafka.getBrokers(); 36 | if (kafkaProducerMap.get(key) == null) { 37 | Producer producer = new Producer(); 38 | KafkaProperties kafkaProperties = new KafkaProperties(); 39 | kafkaProperties.setBrokerAddress(kafka.getBrokers()); 40 | producer.start(kafkaProperties); 41 | kafkaProducerMap.put(key, producer); 42 | } 43 | kafkaProducerMap.get(key).sendMessage(kafka.getTopic(), JSON.toJSONString(onlineStatus)); 44 | } 45 | 46 | Tsdb tsdb = action.getTsdb(); 47 | if (tsdb != null) { 48 | // send message to tsdb 49 | logger.info("tsdb is not supported now."); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/message/deserializers/TokenDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.message.deserializers; 2 | 3 | 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.ParameterizedType; 6 | import java.lang.reflect.Type; 7 | 8 | /** 9 | * Created by hzliuzebo on 2018/9/20. 10 | */ 11 | public class TokenDeserializer implements Deserializer{ 12 | @Override 13 | public T deserialize(Class destinationClass, String message) { 14 | T result = (T) deserializeInternal(destinationClass, message); 15 | return result; 16 | } 17 | 18 | public Object deserializeInternal(Type valueType, String message) { 19 | // resolve a parameterized type to a class 20 | Class valueClass = valueType instanceof Class ? (Class) valueType : null; 21 | if (valueType instanceof ParameterizedType) { 22 | valueClass = (Class) ((ParameterizedType) valueType).getRawType(); 23 | } 24 | // Void means skip 25 | if (valueClass == Void.class) { 26 | return null; 27 | } 28 | 29 | Object newInstance = TokenDeserializer.newInstance(valueClass); 30 | if (newInstance == null) { 31 | return null; 32 | } 33 | String[] items = message.split("\n"); 34 | // String[] items = message.split(";"); 35 | int itemsCount = items.length; 36 | for (Field field : valueClass.getDeclaredFields()) { 37 | Key key = field.getAnnotation(Key.class); 38 | if (key==null) { 39 | continue; 40 | } 41 | int index = key.value(); 42 | if (index > itemsCount) { 43 | continue; 44 | } 45 | try { 46 | field.set(newInstance, items[index]); 47 | } catch (IllegalAccessException e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | return newInstance; 52 | } 53 | 54 | public static T newInstance(Class clazz) { 55 | try { 56 | return clazz.newInstance(); 57 | } catch (InstantiationException e) { 58 | e.printStackTrace(); 59 | return null; 60 | } catch (IllegalAccessException e) { 61 | e.printStackTrace(); 62 | return null; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/rule/Rule.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | /** 4 | * Created by hzliuzebo on 2018/10/22. 5 | */ 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class Rule { 11 | 12 | private List conditions; 13 | private Rule.eventType eventType; 14 | 15 | public List getConditions() { 16 | return conditions; 17 | } 18 | 19 | public void setConditions(List conditions) { 20 | this.conditions = conditions; 21 | } 22 | 23 | public Rule.eventType getEventType() { 24 | return eventType; 25 | } 26 | 27 | public void setEventType(Rule.eventType eventType) { 28 | this.eventType = eventType; 29 | } 30 | 31 | @Override 32 | public String toString(){ 33 | StringBuilder statementBuilder = new StringBuilder(); 34 | 35 | for (Condition condition : getConditions()) { 36 | 37 | String operator = null; 38 | 39 | switch (condition.getOperator()) { 40 | case EQUAL_TO: 41 | operator = "=="; 42 | break; 43 | case NOT_EQUAL_TO: 44 | operator = "!="; 45 | break; 46 | case GREATER_THAN: 47 | operator = ">"; 48 | break; 49 | case LESS_THAN: 50 | operator = "<"; 51 | break; 52 | case GREATER_THAN_OR_EQUAL_TO: 53 | operator = ">="; 54 | break; 55 | case LESS_THAN_OR_EQUAL_TO: 56 | operator = "<="; 57 | break; 58 | } 59 | 60 | statementBuilder.append(condition.getField()).append(" ").append(operator).append(" "); 61 | 62 | if (condition.getValue() instanceof String) { 63 | statementBuilder.append("'").append(condition.getValue()).append("'"); 64 | } else { 65 | statementBuilder.append(condition.getValue()); 66 | } 67 | 68 | statementBuilder.append(" && "); 69 | } 70 | 71 | String statement = statementBuilder.toString(); 72 | 73 | // remove trailing && 74 | return statement.substring(0, statement.length() - 4); 75 | } 76 | 77 | public static enum eventType { 78 | ORDER("ORDER"), 79 | INVOICE("INVOICE"); 80 | private final String value; 81 | private static Map constants = new HashMap(); 82 | 83 | static { 84 | for (Rule.eventType c: values()) { 85 | constants.put(c.value, c); 86 | } 87 | } 88 | 89 | private eventType(String value) { 90 | this.value = value; 91 | } 92 | 93 | public static Rule.eventType fromValue(String value) { 94 | Rule.eventType constant = constants.get(value); 95 | if (constant == null) { 96 | throw new IllegalArgumentException(value); 97 | } else { 98 | return constant; 99 | } 100 | } 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/rule/RuleEngineManager.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.ctrip.framework.apollo.Config; 5 | import com.ctrip.framework.apollo.ConfigChangeListener; 6 | import com.ctrip.framework.apollo.ConfigService; 7 | import com.ctrip.framework.apollo.model.ConfigChange; 8 | import com.ctrip.framework.apollo.model.ConfigChangeEvent; 9 | import com.netease.push.kafka.Consumer; 10 | import com.netease.push.kafka.KafkaProperties; 11 | import org.apache.log4j.LogManager; 12 | import org.apache.log4j.Logger; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.annotation.Configurable; 15 | import org.springframework.stereotype.Service; 16 | 17 | /** 18 | * Created by hzliuzebo on 2018/10/26. 19 | */ 20 | @Service 21 | @Configurable 22 | public class RuleEngineManager implements ConfigChangeListener{ 23 | private static Logger logger = LogManager.getLogger(RuleEngineManager.class); 24 | 25 | @Autowired 26 | private DynamicRuleParser dynamicRuleParser; 27 | 28 | @Autowired 29 | private Consumer consumer; 30 | 31 | private boolean isStarted = false; 32 | private Config config; 33 | 34 | public RuleEngineManager() { 35 | this.config = ConfigService.getAppConfig(); 36 | this.config.addChangeListener(this); 37 | } 38 | 39 | public boolean isStarted() { 40 | return isStarted; 41 | } 42 | 43 | public void start() { 44 | updateRule(); 45 | startKafkaConsumer(); 46 | } 47 | 48 | public void stop() { 49 | stopKafkaConsumer(); 50 | } 51 | 52 | private void updateRule() { 53 | String ruleConfig = config.getProperty("rule", ""); 54 | logger.info("rule config : " + ruleConfig); 55 | RuleMetadata ruleMetadata = JSON.parseObject(ruleConfig, RuleMetadata.class); 56 | if (ruleMetadata == null) { 57 | logger.error("get rule config error, config : " + ruleConfig); 58 | return; 59 | } 60 | dynamicRuleParser.update(ruleMetadata); 61 | } 62 | 63 | private void startKafkaConsumer() { 64 | String kafkaConfig = config.getProperty("kafka", ""); 65 | KafkaProperties properties; 66 | if (kafkaConfig.equalsIgnoreCase("")) { 67 | // default properties 68 | properties = new KafkaProperties(); 69 | properties.setBrokerAddress("10.160.114.6:9092"); 70 | properties.setAutoCommit(true); 71 | properties.setGroup("online-status-news"); 72 | properties.setMaxPollCount(100); 73 | properties.setMaxPollInterval(2000); 74 | properties.setTopics("newsOnlineStatus"); 75 | } else { 76 | properties = JSON.parseObject(kafkaConfig, KafkaProperties.class); 77 | } 78 | logger.info("kafka config : " + JSON.toJSONString(properties)); 79 | consumer.start(properties); 80 | } 81 | 82 | private void stopKafkaConsumer() { 83 | consumer.stop(); 84 | } 85 | 86 | @Override 87 | public void onChange(ConfigChangeEvent configChangeEvent) { 88 | for (String key : configChangeEvent.changedKeys()) { 89 | ConfigChange change = configChangeEvent.getChange(key); 90 | logger.info("Change - key: " + change.getPropertyName() + " , oldValue: " + 91 | change.getOldValue() + " , newValue: " + 92 | change.getNewValue() + " , changeType: " + change.getChangeType()); 93 | if (key.equals("kafka")) { 94 | stopKafkaConsumer(); 95 | startKafkaConsumer(); 96 | } 97 | if (key.equals("rule")) { 98 | RuleMetadata ruleMetadata = JSON.parseObject(change.getNewValue(), RuleMetadata.class); 99 | if (ruleMetadata != null) { 100 | updateRule(); 101 | } 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/kafka/Consumer.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.kafka; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.ctrip.framework.apollo.Config; 5 | import com.ctrip.framework.apollo.ConfigChangeListener; 6 | import com.ctrip.framework.apollo.ConfigService; 7 | import com.ctrip.framework.apollo.model.ConfigChange; 8 | import com.ctrip.framework.apollo.model.ConfigChangeEvent; 9 | import com.netease.push.message.OnlineStatus; 10 | import com.netease.push.message.deserializers.Deserializer; 11 | import com.netease.push.message.deserializers.TokenDeserializer; 12 | import com.netease.push.rule.DynamicRuleParser; 13 | import org.apache.kafka.clients.consumer.ConsumerConfig; 14 | import org.apache.kafka.clients.consumer.ConsumerRecord; 15 | import org.apache.kafka.common.serialization.StringDeserializer; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | import org.springframework.kafka.core.ConsumerFactory; 20 | import org.springframework.kafka.core.DefaultKafkaConsumerFactory; 21 | import org.springframework.kafka.listener.KafkaMessageListenerContainer; 22 | import org.springframework.kafka.listener.MessageListener; 23 | import org.springframework.kafka.listener.config.ContainerProperties; 24 | import org.springframework.stereotype.Component; 25 | 26 | import java.util.HashMap; 27 | import java.util.Map; 28 | 29 | @Component 30 | public class Consumer implements MessageListener, ConfigChangeListener { 31 | 32 | private final static Logger logger = LoggerFactory.getLogger(Consumer.class); 33 | 34 | @Autowired 35 | private DynamicRuleParser ruleParser; 36 | 37 | private Consumer(){ 38 | Config config = ConfigService.getAppConfig(); 39 | config.addChangeListener(this); 40 | } 41 | 42 | private Deserializer deserializer = new TokenDeserializer(); 43 | private KafkaMessageListenerContainer container; 44 | 45 | public void start(KafkaProperties properties) { 46 | Map map = new HashMap<>(); 47 | map.put(ConsumerConfig.GROUP_ID_CONFIG, properties.getGroup()); 48 | map.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, properties.getBrokerAddress()); 49 | map.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, properties.getAutoCommit()); 50 | map.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, properties.getMaxPollInterval()); 51 | map.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, properties.getMaxPollCount()); 52 | map.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 53 | map.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 54 | ConsumerFactory consumerFactory = new DefaultKafkaConsumerFactory<>(map); 55 | ContainerProperties containerProperties = new ContainerProperties(properties.getTopics().split(",")); 56 | containerProperties.setMessageListener(this); 57 | container = new KafkaMessageListenerContainer<>(consumerFactory, containerProperties); 58 | container.start(); 59 | } 60 | 61 | public void stop() { 62 | container.stop(); 63 | container = null; 64 | } 65 | 66 | @Override 67 | public void onMessage(ConsumerRecord data) { 68 | String message = data.value(); 69 | if (message == null) { 70 | return; 71 | } 72 | logger.info("message = " + message); 73 | OnlineStatus onlineStatus = deserializer.deserialize(OnlineStatus.class, message); 74 | onlineStatus.setTopic(data.topic()); 75 | ruleParser.evaluate(onlineStatus); 76 | } 77 | 78 | @Override 79 | public void onChange(ConfigChangeEvent configChangeEvent) { 80 | for (String key: configChangeEvent.changedKeys()) { 81 | ConfigChange change = configChangeEvent.getChange(key); 82 | if (key.equalsIgnoreCase("kafka")) { 83 | KafkaProperties kafkaProperties = JSON.parseObject(change.getNewValue(), KafkaProperties.class); 84 | if (kafkaProperties!=null) { 85 | stop(); 86 | start(kafkaProperties); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/rule/DynamicRuleParser.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.rule; 2 | 3 | import com.netease.push.destination.DestinationManager; 4 | import com.netease.push.message.OnlineStatus; 5 | import net.sf.jsqlparser.JSQLParserException; 6 | import net.sf.jsqlparser.parser.CCJSqlParserUtil; 7 | import net.sf.jsqlparser.statement.Statement; 8 | import net.sf.jsqlparser.statement.select.Select; 9 | import net.sf.jsqlparser.util.TablesNamesFinder; 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | import org.drools.template.ObjectDataCompiler; 13 | import org.kie.api.KieServices; 14 | import org.kie.api.builder.KieFileSystem; 15 | import org.kie.api.runtime.KieContainer; 16 | import org.kie.api.runtime.StatelessKieSession; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.stereotype.Component; 19 | 20 | import java.util.Arrays; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | /** 26 | * Created by hzliuzebo on 2018/10/22. 27 | */ 28 | @Component 29 | public class DynamicRuleParser { 30 | private static Logger logger = LogManager.getLogger(DynamicRuleParser.class); 31 | 32 | @Autowired 33 | private DestinationManager destinationManager; 34 | 35 | private String topic; 36 | private String drl; 37 | private StatelessKieSession statelessKieSession; 38 | private RuleMetadata ruleMetadata; 39 | 40 | private String applyRuleTemplate(Event event, Rule rule) throws Exception { 41 | Map data = new HashMap(); 42 | ObjectDataCompiler objectDataCompiler = new ObjectDataCompiler(); 43 | 44 | data.put("rule", rule); 45 | data.put("eventType", event.getClass().getName()); 46 | 47 | return objectDataCompiler.compile(Arrays.asList(data), Thread.currentThread().getContextClassLoader().getResourceAsStream("rule-template.drl")); 48 | } 49 | 50 | public void update(RuleMetadata ruleMetadata) { 51 | String sql = ruleMetadata.getSql(); 52 | // first parse sql 53 | try { 54 | Statement statement = CCJSqlParserUtil.parse(sql); 55 | Select selectStatement = (Select)statement; 56 | TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); 57 | List tableList = tablesNamesFinder.getTableList(selectStatement); 58 | // just use the first table name which is kafka topic 59 | this.topic = tableList.get(0); 60 | Rule topicRule = new Rule(); 61 | 62 | Condition topicCondition = new Condition(); 63 | topicCondition.setField("topic"); 64 | topicCondition.setOperator(Condition.Operator.EQUAL_TO); 65 | topicCondition.setValue(this.topic); 66 | 67 | // In reality, you would have multiple rules for different types of events. 68 | // The eventType property would be used to find rules relevant to the event 69 | topicRule.setEventType(Rule.eventType.ORDER); 70 | 71 | topicRule.setConditions(Arrays.asList(topicCondition)); 72 | 73 | try { 74 | this.drl = applyRuleTemplate(new OnlineStatus(), topicRule); 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | } 78 | } catch (JSQLParserException e) { 79 | e.printStackTrace(); 80 | return; 81 | } 82 | 83 | // second dynamic generate rule 84 | KieServices kieServices = KieServices.Factory.get(); 85 | KieFileSystem kieFileSystem = kieServices.newKieFileSystem(); 86 | kieFileSystem.write("src/main/resources/rule.drl", drl); 87 | kieServices.newKieBuilder(kieFileSystem).buildAll(); 88 | 89 | KieContainer kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId()); 90 | statelessKieSession = kieContainer.getKieBase().newStatelessKieSession(); 91 | 92 | statelessKieSession.getGlobals().set("destinationManager", destinationManager); 93 | statelessKieSession.getGlobals().set("action", ruleMetadata.getAction()); 94 | } 95 | 96 | public void evaluate(Event event) { 97 | statelessKieSession.execute(event); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/netease/push/utils/MonitorControl.java: -------------------------------------------------------------------------------- 1 | package com.netease.push.utils; 2 | 3 | /** 4 | * Created by hzliuzebo on 2018/10/26. 5 | */ 6 | import com.google.common.io.Closeables; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.*; 11 | import java.net.ServerSocket; 12 | import java.net.Socket; 13 | 14 | /** 15 | * A simple blocking control server that processes user-sent command 16 | */ 17 | public class MonitorControl { 18 | private static final Logger log = LoggerFactory.getLogger(MonitorControl.class); 19 | 20 | public void start(int port) throws IOException { 21 | ServerSocket serverSocket; 22 | try { 23 | serverSocket = new ServerSocket(port); 24 | log.info("Suro control service started at port " + port); 25 | } catch (IOException e) { 26 | throw new IOException(String.format("Can't start server socket at port %d for Suro's control service: %s", port, e.getMessage()), e); 27 | } 28 | 29 | try{ 30 | while (true) { 31 | Socket clientSocket = null; 32 | try{ 33 | clientSocket = listen(port, serverSocket); 34 | Command cmd = processCommand(clientSocket); 35 | if(cmd == Command.EXIT) { 36 | return; 37 | } 38 | }finally { 39 | closeAndIgnoreError(clientSocket); 40 | } 41 | } 42 | }finally { 43 | closeAndIgnoreError(serverSocket); 44 | log.info("Suro's control service exited"); 45 | } 46 | } 47 | 48 | private Socket listen(int port, ServerSocket serverSocket) throws IOException { 49 | try { 50 | return serverSocket.accept(); 51 | }catch(IOException e) { 52 | throw new IOException(String.format("Error when Suro control was accepting user connection at port %d: %s", port, e.getMessage()), e); 53 | } 54 | } 55 | 56 | /** 57 | * Processes user command. For now it supports only "exit", case insensitive. 58 | * @param clientSocket The client socket after connection is established between a client and this server 59 | * 60 | * @return the last processed command 61 | */ 62 | private Command processCommand(Socket clientSocket) { 63 | BufferedReader in = null; 64 | BufferedWriter out = null; 65 | try{ 66 | in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 67 | out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); 68 | String s; 69 | while ((s = in.readLine()) != null) { 70 | if("exit".equalsIgnoreCase(s.trim())) { 71 | respond(out, "ok"); 72 | 73 | return Command.EXIT; 74 | }else { 75 | respond(out, String.format("Unknown command '%s'", s)); 76 | } 77 | } 78 | }catch (IOException e) { 79 | log.warn(String.format("Failed to accept user command at port %d: %s", clientSocket.getPort(), e.getMessage()), e); 80 | }finally { 81 | try{ 82 | Closeables.close(in, true); 83 | Closeables.close(out, true); 84 | } catch(IOException ignored) { 85 | 86 | } 87 | 88 | } 89 | 90 | return null; 91 | } 92 | 93 | // Implemented this method for both Socket and ServerSocket because we want to 94 | // make Suro compilable and runnable under Java 6. 95 | private static void closeAndIgnoreError(Socket socket) { 96 | if(socket == null) return; 97 | 98 | try{ 99 | socket.close(); 100 | }catch(IOException e) { 101 | log.warn(String.format("Failed to close the client socket on port %d: %s. Exception ignored.", socket.getPort(), e.getMessage()), e); 102 | } 103 | } 104 | 105 | private static void closeAndIgnoreError(ServerSocket socket) { 106 | if(socket == null) return; 107 | 108 | try{ 109 | socket.close(); 110 | }catch(IOException e) { 111 | log.warn(String.format("Failed to close the server socket on port %d: %s. Exception ignored.", socket.getLocalPort(), e.getMessage()), e); 112 | } 113 | } 114 | 115 | /** 116 | * Writes line-based response. 117 | * @param out the channel used to write back response 118 | * @param response A string that ends with a new line 119 | * @throws IOException 120 | */ 121 | private void respond(BufferedWriter out, String response) throws IOException { 122 | out.append(response); 123 | out.append("\n"); 124 | out.flush(); 125 | } 126 | 127 | private static enum Command { 128 | EXIT("exit") 129 | ; 130 | 131 | private final String name; 132 | private Command(String name) { 133 | this.name = name; 134 | } 135 | 136 | public String getName() { 137 | return name; 138 | } 139 | } 140 | 141 | public static void main(String[] args) throws Exception { 142 | MonitorControl control = new MonitorControl(); 143 | control.start(9090); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.netease.push 8 | rule-engine 9 | 1.0-SNAPSHOT 10 | 11 | rule-engine 12 | 13 | http://www.example.com 14 | 15 | 16 | UTF-8 17 | 1.7 18 | 1.7 19 | push 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-parent 25 | 1.5.4.RELEASE 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | ch.qos.logback 35 | logback-classic 36 | 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-test 42 | test 43 | 44 | 45 | org.springframework.kafka 46 | spring-kafka 47 | 48 | 49 | org.springframework.kafka 50 | spring-kafka-test 51 | test 52 | 53 | 54 | org.drools 55 | drools-templates 56 | 6.2.0.Final 57 | 58 | 59 | junit 60 | junit 61 | 4.12 62 | test 63 | 64 | 65 | org.apache.logging.log4j 66 | log4j-slf4j-impl 67 | 2.10.0 68 | 69 | 70 | org.apache.logging.log4j 71 | log4j-api 72 | 2.10.0 73 | 74 | 75 | org.apache.logging.log4j 76 | log4j-core 77 | 2.10.0 78 | 79 | 80 | org.projectlombok 81 | lombok 82 | 1.16.16 83 | 84 | 85 | com.alibaba 86 | fastjson 87 | 1.2.49 88 | 89 | 90 | com.ctrip.framework.apollo 91 | apollo-client 92 | 1.0.0 93 | 94 | 95 | com.github.jsqlparser 96 | jsqlparser 97 | 1.2 98 | 99 | 100 | org.springframework 101 | spring-aspects 102 | 4.2.8.RELEASE 103 | 104 | 105 | 106 | 107 | 108 | 109 | org.apache.maven.plugins 110 | maven-compiler-plugin 111 | 112 | 1.7 113 | 1.7 114 | 115 | 116 | 117 | com.spotify 118 | dockerfile-maven-plugin 119 | 1.3.6 120 | 121 | ${docker.image.prefix}/${project.artifactId} 122 | ${project.version} 123 | 124 | target/${project.artifactId}-${project.version}.jar 125 | 126 | 127 | 128 | 129 | org.springframework.boot 130 | spring-boot-maven-plugin 131 | 132 | com.netease.push.RuleEngine 133 | 134 | 135 | 136 | 137 | repackage 138 | 139 | 140 | 141 | 142 | 143 | 144 | maven-clean-plugin 145 | 3.0.0 146 | 147 | 148 | 149 | maven-resources-plugin 150 | 3.0.2 151 | 152 | 153 | maven-compiler-plugin 154 | 3.7.0 155 | 156 | 157 | maven-surefire-plugin 158 | 2.20.1 159 | 160 | 161 | maven-jar-plugin 162 | 3.0.2 163 | 164 | 165 | maven-install-plugin 166 | 2.5.2 167 | 168 | 169 | maven-deploy-plugin 170 | 2.8.2 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /src/main/resources/log4j2_dev.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | target/logs/ 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 45 | 46 | 47 | 49 | 50 | %5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 62 | 64 | 65 | 66 | 67 | 68 | 70 | 71 | %5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n 72 | 73 | 74 | 75 | 76 | 78 | 80 | 81 | 82 | 83 | 85 | 87 | 88 | 89 | 90 | 91 | 93 | 94 | %5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n 95 | 96 | 97 | 98 | 99 | 100 | 102 | 104 | 105 | 106 | 107 | 108 | 110 | 111 | %5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n 112 | 113 | 114 | 115 | 116 | 117 | 119 | 121 | 122 | 123 | 124 | 125 | 127 | 128 | %5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n 129 | 130 | 131 | 132 | 133 | 134 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /src/main/resources/log4j2_prod.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | target/logs/ 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 45 | 46 | 47 | 49 | 50 | %5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 62 | 64 | 65 | 66 | 67 | 68 | 70 | 71 | %5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n 72 | 73 | 74 | 75 | 76 | 78 | 80 | 81 | 82 | 83 | 85 | 87 | 88 | 89 | 90 | 91 | 93 | 94 | %5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n 95 | 96 | 97 | 98 | 99 | 100 | 102 | 104 | 105 | 106 | 107 | 108 | 110 | 111 | %5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n 112 | 113 | 114 | 115 | 116 | 117 | 119 | 121 | 122 | 123 | 124 | 125 | 127 | 128 | %5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n 129 | 130 | 131 | 132 | 133 | 134 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | --------------------------------------------------------------------------------