将KafkaProducer集成到Jersey REST

时间:2015-09-15 11:35:33

标签: java rest jersey apache-kafka

我有一个示例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);
                }

            };
        }

1 个答案:

答案 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并自行关闭它。 只需像上面的功能一样注册监听器。

另见: