Netflix Mantis簡介 - Baeldung
Mantis是一個用於構建流處理應用程式(作業)的平臺。它提供了一種簡便的方法來管理作業的部署和生命週期。此外,它有助於這些作業之間的資源分配,發現和通訊。
因此,開發人員可以始終專注於實際的業務邏輯,同時始終獲得強大且可擴充套件的平臺的支援,以執行其高容量,低延遲,無阻塞的應用程式。
螳螂的工作包括三個不同的部分:
- source,負責從外部源檢索所述資料
- 一個或多個階段stages,負責處理傳入的事件流
- sink收集處理過的資料
現在讓我們探索它們。
加入 mantis-runtime jackson-databind 依賴:
<dependency> <groupId>io.mantisrx</groupId> <artifactId>mantis-runtime</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> |
現在,為了設定工作的資料來源,讓我們實現Mantis Source介面:
public class RandomLogSource implements Source<String> { @Override public Observable<Observable<String>> call(Context context, Index index) { return Observable.just( Observable .interval(250, TimeUnit.MILLISECONDS) .map(this::createRandomLogEvent)); } private String createRandomLogEvent(Long tick) { // generate a random log entry string ... } } |
如我們所見,它只是每秒多次生成隨機日誌條目。
建立一個Mantis作業
該作業只是從我們的RandomLogSource收集日誌事件。稍後,我們將新增組和聚合轉換以獲得更復雜和有趣的結果。
首先,讓我們建立一個LogEvent實體:
public class LogEvent implements JsonType { private Long index; private String level; private String message; // ... } |
然後,讓我們新增我們的TransformLogStage。可實現ScalarComputation介面並拆分日誌條目以構建LogEvent。此外,它還會過濾出任何錯誤的格式化字串:
public class TransformLogStage implements ScalarComputation<String, LogEvent> { @Override public Observable<LogEvent> call(Context context, Observable<String> logEntry) { return logEntry .map(log -> log.split("#")) .filter(parts -> parts.length == 3) .map(LogEvent::new); } } |
執行作業
public class LogCollectingJob extends MantisJobProvider<LogEvent> { @Override public Job<LogEvent> getJobInstance() { return MantisJob .source(new RandomLogSource()) .stage(new TransformLogStage(), new ScalarToScalar.Config<>()) .sink(Sinks.eagerSubscribe(Sinks.sse(LogEvent::toJsonString))) .metadata(new Metadata.Builder().build()) .create(); } } |
它擴充套件了MantisJobProvider。首先,它從我們的RandomLogSource獲取資料,並將TransformLogStage應用於獲取的資料。最後,它將處理後的資料傳送到內建的接收器,該接收器熱切地透過SSE訂閱和傳遞資料。
現在,讓我們將作業配置為在啟動時本地執行:
@SpringBootApplication public class MantisApplication implements CommandLineRunner { // ... @Override public void run(String... args) { LocalJobExecutorNetworked.execute(new LogCollectingJob().getJobInstance()); } } |
讓我們執行該應用程式。我們將看到類似以下的日誌訊息:
Serving modern HTTP SSE server sink on port: 86XX |
現在讓我們使用curl連線到接收器:
$ curl localhost:86XX data: {"index":86,"level":"WARN","message":"login attempt"} data: {"index":87,"level":"ERROR","message":"user created"} data: {"index":88,"level":"INFO","message":"user created"} data: {"index":89,"level":"INFO","message":"login attempt"} data: {"index":90,"level":"INFO","message":"user created"} data: {"index":91,"level":"ERROR","message":"user created"} data: {"index":92,"level":"WARN","message":"login attempt"} data: {"index":93,"level":"INFO","message":"user created"} ... |
配置接收器sink
到目前為止,我們已經使用內建接收器來收集處理過的資料。讓我們看看是否可以透過提供自定義接收器為我們的場景增加更多的靈活性。
例如,如果我們想按訊息過濾日誌怎麼辦?
讓我們建立一個實現Sink <LogEvent>介面的LogSink:
public class LogSink implements Sink<LogEvent> { @Override public void call(Context context, PortRequest portRequest, Observable<LogEvent> logEventObservable) { SelfDocumentingSink<LogEvent> sink = new ServerSentEventsSink.Builder<LogEvent>() .withEncoder(LogEvent::toJsonString) .withPredicate(filterByLogMessage()) .build(); logEventObservable.subscribe(); sink.call(context, portRequest, logEventObservable); } private Predicate<LogEvent> filterByLogMessage() { return new Predicate<>("filter by message", parameters -> { if (parameters != null && parameters.containsKey("filter")) { return logEvent -> logEvent.getMessage().contains(parameters.get("filter").get(0)); } return logEvent -> true; }); } } |
在此接收器實現中,我們配置了一個謂詞,該謂詞使用filter引數僅檢索包含filter引數中設定的文字的日誌:
$ curl localhost:8874?filter=login data: {"index":93,"level":"ERROR","message":"login attempt"} data: {"index":95,"level":"INFO","message":"login attempt"} data: {"index":97,"level":"ERROR","message":"login attempt"} ... |
注意Mantis還提供了一種強大的查詢語言MQL,可用於以SQL方式查詢,轉換和分析流資料。
階段Stage連結
現在,讓我們假設有興趣知道在給定的時間間隔內有多少個ERROR,WARN或INFO日誌條目。為此,我們將在工作中再增加兩個階段並將它們連結在一起。
- 分組
首先,讓我們建立一個GroupLogStage。
此階段是ToGroupComputation實現,該實現從現有TransformLogStage接收LogEvent流資料。之後,它將按日誌級別對條目進行分組並將其傳送到下一個階段:
public class GroupLogStage implements ToGroupComputation<LogEvent, String, LogEvent> { @Override public Observable<MantisGroup<String, LogEvent>> call(Context context, Observable<LogEvent> logEvent) { return logEvent.map(log -> new MantisGroup<>(log.getLevel(), log)); } public static ScalarToGroup.Config<LogEvent, String, LogEvent> config(){ return new ScalarToGroup.Config<LogEvent, String, LogEvent>() .description("Group event data by level") .codec(JacksonCodecs.pojo(LogEvent.class)) .concurrentInput(); } } |
我們還透過提供描述,用於序列化輸出的編解碼器,建立了一個自定義階段配置,並允許透過使用 parallelInput()同時執行此階段的呼叫方法。
需要注意的一件事是,此階段是水平可伸縮的。意味著我們可以根據需要執行該階段的多個例項。還值得一提的是,當在Mantis叢集中部署時,此階段會將資料傳送到下一階段,以便屬於特定組的所有事件將落入下一階段的同一工作人員。
- 彙總
在繼續進行下一步之前,我們首先新增一個LogAggregate實體:
public class LogAggregate implements JsonType { private final Integer count; private final String level; } |
現在,讓我們建立鏈中的最後一個階段。
此階段實現GroupToScalarComputation並將日誌組流轉換為標量LogAggregate。透過計算每種型別的日誌在流中出現多少次來完成此操作。此外,它還有一個LogAggregationDuration引數,可用於控制聚合視窗的大小:
public class CountLogStage implements GroupToScalarComputation<String, LogEvent, LogAggregate> { private int duration; @Override public void init(Context context) { duration = (int)context.getParameters().get("LogAggregationDuration", 1000); } @Override public Observable<LogAggregate> call(Context context, Observable<MantisGroup<String, LogEvent>> mantisGroup) { return mantisGroup .window(duration, TimeUnit.MILLISECONDS) .flatMap(o -> o.groupBy(MantisGroup::getKeyValue) .flatMap(group -> group.reduce(0, (count, value) -> count = count + 1) .map((count) -> new LogAggregate(count, group.getKey())) )); } public static GroupToScalar.Config<String, LogEvent, LogAggregate> config(){ return new GroupToScalar.Config<String, LogEvent, LogAggregate>() .description("sum events for a log level") .codec(JacksonCodecs.pojo(LogAggregate.class)) .withParameters(getParameters()); } public static List<ParameterDefinition<?>> getParameters() { List<ParameterDefinition<?>> params = new ArrayList<>(); params.add(new IntParameter() .name("LogAggregationDuration") .description("window size for aggregation in milliseconds") .validator(Validators.range(100, 10000)) .defaultValue(5000) .build()); return params; } } |
- 配置並執行作業
public class LogAggregationJob extends MantisJobProvider<LogAggregate> { @Override public Job<LogAggregate> getJobInstance() { return MantisJob .source(new RandomLogSource()) .stage(new TransformLogStage(), TransformLogStage.stageConfig()) .stage(new GroupLogStage(), GroupLogStage.config()) .stage(new CountLogStage(), CountLogStage.config()) .sink(Sinks.eagerSubscribe(Sinks.sse(LogAggregate::toJsonString))) .metadata(new Metadata.Builder().build()) .create(); } } |
一旦執行該應用程式並執行我們的新作業,就可以看到每隔幾秒鐘檢索一次的日誌計數:
$ curl localhost:8133 data: {"count":3,"level":"ERROR"} data: {"count":13,"level":"INFO"} data: {"count":4,"level":"WARN"} data: {"count":8,"level":"ERROR"} data: {"count":5,"level":"INFO"} data: {"count":7,"level":"WARN"} ... |
綜上所述,在本文中,我們已經瞭解了Netflix Mantis是什麼以及它的用途。此外,我們研究了主要概念,使用它們來構建作業,並探索了針對不同場景的自定義配置。
與往常一樣,完整的程式碼可以在GitHub上找到
相關文章
- Evrete 規則引擎簡介 | baeldungVR
- 阿里巴巴哨兵Sentinel簡介 | Baeldung阿里
- Apache Kafka資料模型概念簡介 - BaeldungApacheKafka模型
- 非同步程式設計測試Awaitlity簡介| Baeldung非同步程式設計AI
- Netflix開源Mantis:基於微服務的運維監控平臺微服務運維
- Netflix客戶端:Netflix for Mac客戶端Mac
- 簡介
- Axon框架指南 - Baeldung框架
- Jira使用簡介 HP ALM使用簡介
- BookKeeper 介紹(1)--簡介
- TunePat Netflix Video Downloader——Netflix視訊工具IDE
- Netflix播放器:Clicker for Netflix mac版播放器Mac
- loadsh簡介
- Knative 簡介
- Javascript 簡介JavaScript
- JanusGraph -- 簡介
- Linux簡介Linux
- CSS 簡介CSS
- 反射簡介反射
- CSS簡介CSS
- JUC簡介
- sass簡介
- APIGateway 簡介APIGateway
- Feign簡介
- Django簡介Django
- Virgilio 簡介
- 簡介JSXJS
- LVM : 簡介LVM
- Linux——簡介Linux
- Apache簡介Apache
- JAVA簡介Java
- NATS簡介
- Mybatis簡介MyBatis
- pwa簡介
- SVG簡介SVG
- kafka 簡介Kafka
- AOP簡介
- MySQLInnodbPurge簡介MySql