1.概述

众所周知, Spring Integration具有用于与外部系统交互的大量连接器。 Twitter也不例外,而且很长一段时间以来,因为Spring Social一直是一个开箱即用的解决方案,Spring Integration利用该解决方案来连接到社交网络。

1.1Spring社交EOL

不幸的是, Spring Social已经到了使用寿命 ,该项目现在处于维护模式。 Spring团队决定不进一步开发Spring Social的原因是,使API绑定与社交网络的API保持同步变得很繁琐。

除此之外,Spring Framework 5发布后,开发人员希望利用其响应式编程模型,这将要求团队在现有的响应式社交绑定旁边重新实现一个响应式Spring Social绑定。

现在建议开发人员实现自己的绑定或使用专用库之一连接社交网络。

1.2 Spring Integration的Twitter模块已移至扩展

Spring Social现在处于维护模式,这迫使Spring Integration团队将Twitter支持模块从主项目移至扩展。 由于Spring Social不会接收更新,因此它将基于早期的Spring Framework版本构建。 这将导致类路径冲突,也将阻碍Spring Integration的开发。

因此, 从Spring Integration 5.1开始,Twitter模块可作为扩展使用 。

1.3有哪些替代方案?

Twitter4J是Yamas Yusuke开发和维护的Twitter API的非官方Java库。 官方的HBC库(由Twitter构建)是一个Java HTTP Client,用于使用Twitter的Streaming API。 自2016年以来,后者从未见过重大更新,而Twitter4J正在定期更新。

也可以选择实现自己的API绑定。 在使用RestTemplate的基于Spring的项目中,绝对是一个选择,并且这是进行REST调用的简便方法。

本指南以流模式使用Twitter4J,可以将其集成到Spring Integration消息流中。

1.4 Twitter流如何工作?

简而言之, 您的应用打开了一个与Twitter API的单一连接,只要发生新匹配,就会通过该连接发送新结果 。 相反,另一种方法是通过向REST API重复发送请求来批量传送数据。

流提供了一种低延迟的传递机制 ,该机制可以支持非常高的吞吐量,而不必处理速率限制。

2.示例项目

该示例项目展示了Twitter的Streaming API到Spring Integration消息流的集成,可在GitHub上找到 : https : //github.com/springuni/springuni-examples/tree/master/spring-integration/twitter-streaming 。

Maven依赖

由于Spring Social现在是EOL,因此我们不会在此基础上继续发展。 我们引入的只是spring-integration-core和twitter4j-stream 。

<dependencies>
    <dependency>
      <groupId>org.springframework.integration</groupId>
      <artifactId>spring-integration-core</artifactId>
    </dependency>

    <dependency>
      <groupId>org.twitter4j</groupId>
      <artifactId>twitter4j-stream</artifactId>
      <version>4.0.1</version>
    </dependency>
  </dependencies>

该项目还使用了Lombok和Spring Boot测试支持,但是这些是可选的。

Spring Integration的可听消息源

Spring Integration提供了对实现入站消息组件的支持。 它们分为轮询和监听行为 。

最初依赖于Inbound Twitter Channel Adapter建立在Spring Social之上,现在已移至扩展, 它是轮询用户 。 也就是说,您必须提供一个轮询器配置才能使用它。 另一方面,Twitter实施速率限制,以管理应用程序获取更新的频率。 使用旧的Twitter Channel适配器时,您应该考虑速率限制,以便您配置的轮询间隔符合Twitter策略。

另一方面, 侦听入站组件更简单,通常只需要实现MessageProducerSupport 。 这样的侦听组件看起来像这样。

public class MyMessageProducer extends MessageProducerSupport {

  public MyMessageProducer(MessageChannel outputChannel) {
    // Defining an output channel is required
    setOutputChannel(outputChannel);
  }

  @Override
  protected void onInit() {
    super.onInit();
    // Custom initialization - if applicable - comes here
  }

  @Override
  public void doStart() {
    // Lifecycle method for starting receiving messages
  }

  @Override
  public void doStop() {
    // Lifecycle method for stopping receiving messages
  }

  private void receiveMessage() {
    // Receive data from upstream service
    SomeData data = ...;

    // Convert it to a message as appropriate and send it out
    this.sendMessage(MessageBuilder.withPayload(data).build());
  }

}

只有两个必需的元素:

  • 必须定义输出消息通道
  • 每当组件收到消息时,都必须调用sendMessage

(可选)您可能希望控制组件的初始化并管理其生命周期。

由于Twitter的Streaming API本质上是消息驱动的,因此监听行为自然很合适。 让我们看看如何在这样的上下文中合并Twitter4J。

使用Twitter4J连接到Twitter Streaming API

Twitter4J管理连接处理的细微差别,并从Twitter的Streaming API接收更新。 我们需要做的就是获取一个TwitterStream实例,附加一个侦听器并定义过滤。

实例化

Twitter4J网站上的流示例表明,应通过TwitterStreamFactory创建一个TwitterStream实例。 这完全有道理,但是在Spring应用程序上下文中,我们希望它成为托管bean。

Spring的FactoryBean工具是包含创建单例TwitterStream实例的详细信息的简单FactoryBean方法。

public class TwitterStreamFactory extends AbstractFactoryBean<TwitterStream> {

  @Override
  public Class<?> getObjectType() {
    return TwitterStream.class;
  }

  @Override
  protected TwitterStream createInstance() {
    return new twitter4j.TwitterStreamFactory().getInstance();
  }

  @Override
  protected void destroyInstance(TwitterStream twitterStream) {
    twitterStream.shutdown();
  }

}

尽管我们也可以将其公开为普通的bean,而不用由FactoryBean创建FactoryBean ,但这不会适当地将其关闭。

附加侦听器并定义过滤

这将是我们自定义MessageProducer实现的责任。

@Slf4j
public class TwitterMessageProducer extends MessageProducerSupport {

  private final TwitterStream twitterStream;

  private List<Long> follows;
  private List<String> terms;

  private StatusListener statusListener;
  private FilterQuery filterQuery;

  public TwitterMessageProducer(TwitterStream twitterStream, MessageChannel outputChannel) {
    this.twitterStream = twitterStream;
    setOutputChannel(outputChannel);
  }

  @Override
  protected void onInit() {
    super.onInit();

    statusListener = new StatusListener();

    long[] followsArray = null;

    if (!CollectionUtils.isEmpty(follows)) {
      followsArray = new long[follows.size()];
      for (int i = 0; i < follows.size(); i++) {
        followsArray[i] = follows.get(i);
      }
    }

    String[] termsArray = null;
    if (!CollectionUtils.isEmpty(terms)) {
      termsArray = terms.toArray(new String[0]);
    }

    filterQuery = new FilterQuery(0, followsArray, termsArray);
  }

  @Override
  public void doStart() {
    twitterStream.addListener(statusListener);
    twitterStream.filter(filterQuery);
  }

  @Override
  public void doStop() {
    twitterStream.cleanUp();
    twitterStream.clearListeners();
  }

  public void setFollows(List<Long> follows) {
    this.follows = follows;
  }

  public void setTerms(List<String> terms) {
    this.terms = terms;
  }

  StatusListener getStatusListener() {
    return statusListener;
  }

  FilterQuery getFilterQuery() {
    return filterQuery;
  }

  class StatusListener extends StatusAdapter {

    @Override
    public void onStatus(Status status) {
      sendMessage(MessageBuilder.withPayload(status).build());
    }

    @Override
    public void onException(Exception ex) {
      log.error(ex.getMessage(), ex);
    }

    @Override
    public void onStallWarning(StallWarning warning) {
      log.warn(warning.toString());
    }

  }
}

MessageProducerSupport和TwitterStream的管理界面提供的生命周期方法可以很好地配合使用。 这也将使我们能够在需要时在运行时停止和启动组件。

Java配置

尽管Spring可以自动装配组件,但我还是更喜欢通过手动配置来控制依赖关系。

@Slf4j
@Configuration
public class TwitterConfig {

  @Bean
  TwitterStreamFactory twitterStreamFactory() {
    return new TwitterStreamFactory();
  }

  @Bean
  TwitterStream twitterStream(TwitterStreamFactory twitterStreamFactory) {
    return twitterStreamFactory.getInstance();
  }

  @Bean
  MessageChannel outputChannel() {
    return MessageChannels.direct().get();
  }

  @Bean
  TwitterMessageProducer twitterMessageProducer(
      TwitterStream twitterStream, MessageChannel outputChannel) {

    TwitterMessageProducer twitterMessageProducer =
        new TwitterMessageProducer(twitterStream, outputChannel);

    twitterMessageProducer.setTerms(Arrays.asList("java", "microservices", "spring"));

    return twitterMessageProducer;
  }

  @Bean
  IntegrationFlow twitterFlow(MessageChannel outputChannel) {
    return IntegrationFlows.from(outputChannel)
        .transform(Status::getText)
        .handle(m -> log.info(m.getPayload().toString()))
        .get();
  }

}

这里的重要部分是我们的自定义消息生成器如何与消息流集成。 基本上,除了在生产者的输出通道中列出消息之外,我们不需要执行任何其他操作。

测试中

只有Chuck Norris在生产中测试代码。 但是,像您和我这样的普通凡人,我们确实会编写测试用例。

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestConfig.class)
public class TwitterMessageProducerTest {

  @MockBean
  private TwitterStream twitterStream;

  @Autowired
  private PollableChannel outputChannel;

  @Autowired
  private TwitterMessageProducer twitterMessageProducer;

  @Test
  public void shouldBeInitialized() {
    StatusListener statusListener = twitterMessageProducer.getStatusListener();
    verify(twitterStream).addListener(statusListener);

    FilterQuery filterQuery = twitterMessageProducer.getFilterQuery();
    verify(twitterStream).filter(filterQuery);
  }

  @Test
  public void shouldReceiveStatus() {
    StatusListener statusListener = twitterMessageProducer.getStatusListener();

    Status status = mock(Status.class);
    statusListener.onStatus(status);

    Message<?> statusMessage = outputChannel.receive();
    assertSame(status, statusMessage.getPayload());
  }

  @Import(TwitterConfig.class)
  static class TestConfig {

    @Bean
    MessageChannel outputChannel() {
      return MessageChannels.queue(1).get();
    }

  }

}

我喜欢Twitter4J的设计,因为它利用了界面。 该库的大多数重要部分都作为普通接口公开。 TwitterStream也不例外。 也就是说,在测试用例中可以轻松地将其嘲笑。

六,结论

  • Spring Social现在已经停产了 -它不会收到新功能
  • Spring Integration的Twitter模块可作为扩展使用 -已从主项目中移出。
  • Twitter入站通道适配器是一个轮询用户 –选择轮询间隔时必须处理速率限制
  • Twitter的Streaming API符合入站通道适配器的监听行为

翻译自: https://www.javacodegeeks.com/2018/12/streaming-api-spring-integration.html