Angular.JS与Apache KAFKA的集成问题

时间:2018-06-12 07:25:15

标签: angularjs notifications apache-kafka spring-kafka

目前我的应用程序在Spring上运行,AngularJS用于后端和前端。 我已经设置了一个Kafka Listener来使用事件,Stomp.js和SockJS建立一个持久连接,将消息发送给客户端。

我能够向websocket发送消息,但是通过kafka,我无法在客户端(AngularJS)上阅读消息

客户端

(function() {
  'use strict';

  function toObject(str) {
    return str;
  }
  function DealNotificationService($state, desktopNotificationService, config, authService, notificationService) {
    var service = {}, subscriptions = [];

    function onMessage(message) {
      desktopNotificationService.showNotification('websocket', {
        body: message,
        icon: 'img/alert-warning.png',
        onClick: function notificationClicked() {
          $state.transitionTo('home.blotter');
        },
        autoClose: 1000 * 60 * 60 // 1 hour
      }, function onShow(error) {
          if (error) console.log('unable to show notification', error);
        }
      );
    }

    function subscribeToTopic(topic) {
      return notificationService.subscribe(topic, onMessage, toObject, service)
      .then(function(subscriptionId) {
        return subscriptions.push(subscriptionId);
      });
    }

    function subscribeToTopics(topics) {
      return topics.forEach(function(topic) {
        subscribeToTopic(topic);
      });
    }

    function requestNotificationPermission() {
      if (Notification && Notification.permission !== 'granted') {
        desktopNotificationService.showNotification('Deal Alert', {
          body: "Subscribed to Deal Alerts",
          icon: 'null',
          onClick: null,
          autoClose: 1000
        }, function onShow(error) {
            if (error) console.log('unable to show notification', error);
          }
        );
      }
    }

    service.listen = function() {
      config.init().then(authService.getNotificationTopics).then(subscribeToTopics).then(requestNotificationPermission);
    };

    service.stop = function() {
      subscriptions.forEach(function(s) {
        notificationService.unsubscribe(s);
      });
      subscriptions.length = 0;
    };

    return service;
  }
  DealNotificationService.$inject = ['$state', 'webNotification', 'FxoConfig', 'AuthorizationService', 'NotificationService'];

  var deps = ['angular-web-notification', 'fxo.security.authorization',
      'fxo.core.services.notification', 'fxo.core.services.config'];
  angular.module('fxo.admin.notification', deps)
    .service('DealNotificationService', DealNotificationService);
})();

客户端模块

(function() {
  function NotificationService($q, config, _) {
    var service = {}, client;

    var defaultConverter = {
      toString: function toString(msg) {
        return JSON.stringify(msg);
      },
      toObject: function toObject(str) {
        return JSON.parse(str);
      }
    };

    function StompClient(wsConfig) {
      var socket, stomp, me = this;
      this.endPoint = wsConfig.parameters['admin.notificationEndPoint'];
      this.subscriptions = [];

      //callback function when message is received
      function onMessage(callback, converter, thisArg) {
        return function onMessage(message) {
          callback.call(thisArg, converter.call(thisArg, message.body));
        };
      }

      function connected() {
        return stomp && stomp.connected;
      }

      this.connect = function() {
        var connectPromise = null;
        return $q(function(resolve) {
          if (connected()) {
            resolve(me);
          }
          else {
            /* globals Stomp */
            /* globals SockJS */
            if (connectPromise) {
              //if already attempting to connect, return the same promise
              resolve(connectPromise);
            }
            else {
              console.log("Opening socket connection for notifications");
              //create a web socket connection with the server
              var prefix = "";
              if ("true" == wsConfig.parameters['bypassWebsealFlag']) {
                prefix += wsConfig.parameters['ownHostName'];
              }
              socket = new SockJS(prefix+me.endPoint);
              stomp = Stomp.over(socket);
              connectPromise = stomp.connect({}, function() {
                connectPromise = null;
                resolve(me);
              });
            }
          }
        });
      };
      this.subscribe = function(topicName, callback, converter, thisArg) {
        //if the client is connected, iterate through all pending subscriptions and subscribe
        if (connected()) {
          var strToObject = converter || defaultConverter.toObject;
          var subscription = stomp.subscribe(topicName, onMessage(callback, strToObject, thisArg));
          this.subscriptions.push(subscription);
          return subscription.id;
        } else {
          throw new Error("Web socket not connected. Please connect first");
        }
      };
      this.disconnect = function() {
        console.log("Web socket disconnecting");
        //unsubscribe all existing subscriptions
        me.subscriptions.forEach(function(subscription) {
          subscription.unsubscribe();
        });
        me.subscriptions.length = 0;

        //disconnect the web socket
        stomp.disconnect();
      };
      this.unsubscribe = function(subscriptionId) {
        var s = _.find(me.subscriptions, {id: subscriptionId});
        if (s) {
          s.unsubscribe();
          _.remove(me.subscriptions, s);
        }
      };
    }

    /**
     * subscribe to the topic and calls the callback function whenever a message is received
     */
    service.subscribe = function sub(topicName, callback, converter, thisArg) {
      //return a promise that returns the subscription object
      var defer = $q.defer();

      //create a new client only if one has not been created yet
      config.init().then(function() {
        if (!client) client = new StompClient(config);
        client.connect().then(function() {
          console.log("Web socket connected");
          defer.resolve(client.subscribe(topicName, callback, converter, thisArg));
        });
      });
      return defer.promise;
    };

    service.unsubscribe = function(subscriptionId) {
      client.unsubscribe(subscriptionId);
    };

    return service;
  }

  NotificationService.$inject = ['$q', 'FxoConfig', 'FxoUtils'];

  angular.module('fxo.core.services.notification', [
      'fxo.core.services.config',
      'fxo.core.services.admin',
      'fxo.core.utils'
    ])
    .factory('NotificationService', NotificationService)
  ;
})();

服务器端(Kafka侦听器类)

package com.fxo.portal.ticketing.message.listener;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.fxo.portal.kernel.spring.kafka.listener.MessageListener;
import com.fxo.portal.ticketing.service.impl.DeltaHedgeFailureNotificationService;

@Component("deltaHedgeFailureMessageListener")
public class DeltaHedgeFailureMessageListener implements MessageListener<Integer, String> {

  private static final Logger logger = LoggerFactory.getLogger(DeltaHedgeFailureMessageListener.class);

  @Autowired
  private DeltaHedgeFailureNotificationService<String> deltaHedgeFailureNotificationService;

  @Override
  public void onMessage(ConsumerRecord<Integer, String> data) {
    if(data != null) {
      logger.debug("Recieved notification payload: '{}'", data.toString());
      deltaHedgeFailureNotificationService.asyncSend(data.value());
    }
  }
}

服务器端(通知接口类)

package com.fxo.portal.ticketing.service.impl;

import com.fxo.portal.kernel.notification.NotificationService;

public interface DeltaHedgeFailureNotificationService<T> extends NotificationService<T> {

  void asyncSend(T data);

}

Kafka的通知实施

package com.fxo.portal.ticketing.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.fxo.portal.config.ConfigService;
import com.fxo.portal.kernel.notification.NotificationServiceImpl;

@Service("deltaHedgeFailureNotificationService")
public class DeltaHedgeFailureNotificationServiceImpl extends NotificationServiceImpl<String> 
    implements DeltaHedgeFailureNotificationService<String> {

  @Autowired private ConfigService configService;
  private static String destination = null;
  private static final String DEAL_NOTIFICATION_TOPIC = "admin.dealNotificationTopic";
  private static final Logger logger = LoggerFactory.getLogger(DeltaHedgeFailureNotificationServiceImpl.class);

  @Override
  public String getDestination() {
    if (destination == null) destination = configService.getConfig(DEAL_NOTIFICATION_TOPIC);
    return destination;
  }

  @Override
  public void asyncSend(String data) {
//      Enable async support in servlet and enable this async method
//      super.asyncSend(data);
    send(data);
  }
}

通知实施

package com.fxo.portal.kernel.notification;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Async;

import java.util.Map;

public abstract class NotificationServiceImpl<T> implements NotificationService<T> {

  @Override
  public void send(T data, Map<String, Object> headers) {
    String destination = getDestination();
    logger.debug("Sending message to: {}", destination);
    this.template.convertAndSend(destination, data, headers);
  }
  @Override
  public void send(T data) {
    send(data, null);
  }

  @Override
  @Async
  public void asyncSend(T data) {
    logger.debug("Notifying async: '{}'", data);
    send(data);
  }

  @Override
  @Async
  public void asyncSend(T data, Map<String, Object> headers) {
    logger.debug("Notifying async");
    send(data, headers);
  }

  @Autowired private SimpMessagingTemplate template;
  private static final Logger logger = LoggerFactory.getLogger(NotificationServiceImpl.class);
}

KAFKA测试类发送消息

package com.fxo.portal.ticketing;

import java.text.DecimalFormat;
import java.util.Locale;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;

import com.fxo.portal.kernel.spring.kafka.KafkaTemplate;

@Component
public class PublishMessageTest {

  private static final Logger logger = LoggerFactory.getLogger(PublishMessageTest.class);

  private ScheduledExecutorService execService = null;

  @Autowired
  private KafkaTemplate<Integer, String> kafkaTemplate;

  @Qualifier("notificationMessageSource")
  @Autowired private MessageSource messageSource;

  private static final DecimalFormat numberFormatter = new DecimalFormat("#,###.00");

  @Value("${topic.deltahedge.notification}")
  private String deltahedgeNotificationTopic;

  public PublishMessageTest() {
    super();
    this.execService = Executors.newSingleThreadScheduledExecutor();
      initialize();
  }

  public void initialize() {
      this.execService.scheduleAtFixedRate(new Runnable() {

      @Override
      public void run() {
          String message = messageSource.getMessage(
                  "deal.notification.deltaHedge",
                  new Object[] {"Hello", "I'm new here", numberFormatter.format(1.11), "TICKET-" + System.currentTimeMillis()},
                  Locale.US);
          String test_message = "hello i'm from kafka";
        kafkaTemplate.send(deltahedgeNotificationTopic, test_message);
          logger.debug("Sent message to kafka: '{}'", test_message);

      }

      }, 1, 1, TimeUnit.MINUTES);
    }


}

0 个答案:

没有答案