目前我的应用程序在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);
}
}