我需要为流处理实现一个非阻塞的生产者/消费者服务,该服务公开了HTTP API。我需要使用一个黑盒可执行文件,该文件会吐出无限行的JSON编码事件数据:
Linux-https://s3-us-west-1.amazonaws.com/bp-interview-artifacts/generator-linux-amd64
Mac OS X-https://s3-us-west-1.amazonaws.com/bp-interview-artifacts/generator-macosx-amd64
我创建了一个 Spring Boot 应用程序,它可以编译并运行,但是没有任何反应: EXE 文件可以运行,但是该应用程序不执行任何操作,它会继续监听并没有其他的。我在做什么错了?
我在IDEA IntelliJ中将应用程序作为Java应用程序运行。
此外,我认为我的应用程序可以更好,例如: 1.正确实现读/写分离。 2. MutableInt类型的增量不是线程安全的,我想要没有它的另一种方法。 3.结果打印(序列化)不标准 4.输入解析通常包含错误处理(catch Exception e) 5.通常,方法的命名并不总是反映功能。
我是这个流媒体应用程序的新手,我将感谢所有提示,提示和助手。 我正在考虑将RxJava用于他的应用程序,使用RabbitMQ是更好的做法还是最好的做法?
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.beniregev</groupId>
<artifactId>bigpanda-home-exercise</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>bigpanda-home-exercise</name>
<description>Big Panda home exercise</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
资源/application.properties
stream.generator.exe.path=D:\\JavaProjects\\IdeaProjects\\bigpanda-home-exercise\\generator-windows-amd64.exe
StreamEvent.java
package com.beniregev.bigpandahomeexercise.models;
public class StreamEvent {
private String type;
private String data;
private String timestamp;
/**
*
* @return {@code Event-Type} as {@link String}.
*/
public String getType() {
return type;
}
/**
*
* @param type The event-type to set
*/
public void setType(String type) {
this.type = type;
}
/**
*
* @return {@code data} property of the Event
*/
public String getData() {
return data;
}
/**
*
* @param dataThe value to set {@code data} property
*/
public void setData(String data) {
this.data = data;
}
/**
*
* @return {@code timestamp} property of the Event
*/
public String getTimestamp() {
return timestamp;
}
/**
*
* @param timestampthe timestamp value to set
*/
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}
CountersService.java
package com.beniregev.bigpandahomeexercise.services;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class CountersService {
private Map<String, MutableInt> words = new HashMap<String, MutableInt>();
private Map<String, MutableInt> types = new HashMap<String, MutableInt>();
public void countWords(String word) {
MutableInt count = words.get(word);
if (count == null) {
words.put(word, new MutableInt());
}
else {
count.increment();
}
}
public void countTypes(String type) {
MutableInt count = types.get(type);
if (count == null) {
types.put(type, new MutableInt());
}
else {
count.increment();
}
}
private String printMap(Map<String, MutableInt> counters) {
StringBuffer strb = new StringBuffer();
counters.entrySet().forEach(entry -> {strb.append(entry.getKey() + " -> " + entry.getValue().get() +", ");});
return strb.toString().substring(0, strb.length()-2);
}
public String printEventsTypesCounter() {
return printMap(types);
}
public String printWordsCounter() {
return printMap(words);
}
class MutableInt {
int value = 1;
public void increment () { ++value; }
public int get () { return value; }
}
}
StreamProcessService.java
package com.beniregev.bigpandahomeexercise.services;
import com.beniregev.bigpandahomeexercise.models.StreamEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
@Component
public class StreamProcessService {
@Value("${stream.generator.exe.path}")
String path;
@Autowired
CountersService countService;
final ObjectMapper jsonMapper = new ObjectMapper();
@PostConstruct
public void init() {
try {
Process process = Runtime.getRuntime()
.exec(path);
EventStream eventStream =
new EventStream(process.getInputStream(), streamEvent -> {
if(streamEvent != null) {
countService.countTypes(streamEvent.getType());
countService.countWords(streamEvent.getData());
}
});
Executors.newSingleThreadExecutor().submit(eventStream);
} catch(Exception e) {
e.printStackTrace();
}
}
private class EventStream implements Runnable {
private InputStream inputStream;
private Consumer<StreamEvent> consumer;
public EventStream(InputStream inputStream, Consumer<StreamEvent> consumer) {
this.inputStream = inputStream;
this.consumer = consumer;
}
@Override
public void run() {
new BufferedReader(new InputStreamReader(inputStream)).lines()
.map(s -> {
StreamEvent streamEvent = null;
try {
streamEvent = jsonMapper.readValue(s, StreamEvent.class);
}catch(Exception e) {
//bad JSON String. Skip it
}
return streamEvent;
}).forEach( consumer);
}
}
}
DisplayOutputController.java
package com.beniregev.bigpandahomeexercise.controllers;
import com.beniregev.bigpandahomeexercise.services.CountersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController("DisplayOutputController")
public class DisplayOutputController {
@Autowired
CountersService countService;
@GetMapping("/test")
public String test() {
return "<body><p><b>Test</body>";
}
@GetMapping("/countEventsTypes")
public String countTypes(){
return countService.printEventsTypesCounter();
}
@GetMapping("/countWords")
public String countWords(){
return countService.printWordsCounter();
}
}
BigpandaHomeExerciseApplication.java
package com.beniregev.bigpandahomeexercise;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan({"com.beniregev.bigpandahomeexercise"})
public class BigpandaHomeExerciseApplication {
public static void main(String[] args) {
SpringApplication.run(BigpandaHomeExerciseApplication.class, args);
}
}