我有一个示例Hello World REST API,我编写了一些简单的Kafka生产者代码。
现在我希望我的Kafka Producer每次调用REST API时都会向主题发送消息,即有人进入 www.mywebpage.com/entry-api/test 我希望我的制作人发送消息到主题。
最简单的方法是每次创建新的生产者实例并发送一条消息,但这似乎只是简单的愚蠢。所以我想出必须有一种方法将这个Kafka生产者实例注入Jersey资源,以便生成器将使用服务器创建,关闭的一个服务器将关闭。
我已经读过我可以用依赖注入做到这一点,但我迷失了实际应该做的以及它是如何工作的。
我非常感谢一些指导方针,我应该如何更改我的代码,以便注入Kafka生产者。
以下代码。
这是我的主要课程:
package org.apache.kafka;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
public class MainApp {
public static void main(String[] args) throws Exception {
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
Server jettyServer = new Server(9090);
jettyServer.setHandler(context);
ServletHolder jerseyServlet = context.addServlet(
org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
// Tells the Jersey Servlet which REST service/class to load.
jerseyServlet.setInitParameter(
"jersey.config.server.provider.classnames",
EntryApi.class.getCanonicalName());
try {
jettyServer.start();
jettyServer.join();
} finally {
jettyServer.destroy();
}
}
}
这是资源:
package org.apache.kafka;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/entry-api")
public class EntryApi {
@GET
@Path("test")
@Produces(MediaType.TEXT_PLAIN)
public void test() {
//HERE I WANT TO SEND MESSAGE WITH KAFKA PRODUCER
}
}
我也有卡夫卡制片人写的:
package org.apache.kafka;
import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import java.util.Properties;
import java.util.concurrent.Future;
public class SampleProducer {
private KafkaProducer<String, String> producer;
public SampleProducer() {
Properties prodProp = new Properties();
prodProp.put("bootstrap.servers", "sandbox.hortonworks.com:6667");
prodProp.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
prodProp.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
prodProp.put("request.required.acks", "1");
this.producer = new KafkaProducer<String, String>(prodProp);
}
public void sendMsgToTopic(String inMessage) {
StringBuilder msg = new StringBuilder(inMessage);
ProducerRecord<String, String> msgData = new ProducerRecord<String, String>("testTopic", msg.toString());
Future<RecordMetadata> rs = producer.send(msgData, new Callback() {
public void onCompletion(RecordMetadata rM, Exception e) {
System.out.println("Received ack for partition=" + rM.partition() + " offset=" + rM.offset());
}
});
try {
RecordMetadata rM = (RecordMetadata) rs.get();
msg.append(" partition=" + rM.partition() + " offset=" + rM.offset());
} catch (Exception e) {
System.out.println(e);
}
}
public void closeProducer() {
this.producer.close();
}
}
修改
好的,所以我使用Jersey 2.x在沙盒上工作(感谢 peeskillet )。
事情是,我必须使它与Jersey 1.18一起工作(这是要求:()
再次,感谢 peeskillet 我已经得到了类似的东西:
@Provider
public class KafkaProducerProvider implements InjectableProvider<Inject, Type>{
@Override
public ComponentScope getScope() {
return ComponentScope.Singleton;
}
@Override
public Injectable<SampleProducer> getInjectable(ComponentContext ic, Inject a, Type c) {
if (c.equals(SampleProducer.class)) {
return new Injectable<SampleProducer>() {
@Override
public SampleProducer getValue() {
Properties prodProp = new Properties();
prodProp.put("bootstrap.servers", "sandbox.hortonworks.com:6667");
prodProp.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
prodProp.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
prodProp.put("request.required.acks", "1");
return new SampleProducer(prodProp);
}
};
}
答案 0 :(得分:0)
所以经过一些游戏后,看起来关闭单一服务并不像我想象的那么容易。我以为我们可以用@PreDestroy
注释一个方法,但我想它并不那么容易
注射没问题。这真的是你所需要的一切
public class InjectionFeature implements Feature {
@Override
public boolean configure(FeatureContext context) {
context.register(new AbstractBinder(){
@Override
protected void configure() {
bind(SampleProducer.class)
.to(SampleProducer.class).in(Singleton.class);
}
});
return true;
}
}
您还可以使用相同的configure
方法绑定您想要的任何其他服务。然后你只需要将这个功能添加到类
jerseyServlet.setInitParameter(
"jersey.config.server.provider.classnames",
EntryApi.class.getCanonicalName() + ","
+ InjectionFeature.class.getCanonicalName());
您现在应该可以将它注入资源类
@Path("/entry-api")
public class EntryApi {
@Inject
private SampleProducer producer;
问题在于关闭,如果你想在关机前清理任何东西。我能找到的唯一方法是实现ApplicationEventListener
public class ApplicationListener implements ApplicationEventListener {
@Inject
private ServiceLocator locator;
@Override
public void onEvent(ApplicationEvent ae) {
switch (ae.getType()) {
case DESTROY_FINISHED: {
SampleProducer producer = locator.getService(SampleProducer.class);
producer.destroy();
}
}
}
@Override
public RequestEventListener onRequest(RequestEvent re) {
return null;
}
}
在这里,您正在查找SampleProducer
并自行关闭它。
只需像上面的功能一样注册监听器。
另见: