- add round trip trace message from client and back to tell us where the bottleneck

might be when using multi-user whiteboard.
This commit is contained in:
Richard Alam 2017-08-16 09:44:27 -07:00
parent dd5c703700
commit ec314b8105
12 changed files with 231 additions and 69 deletions

View File

@ -0,0 +1,26 @@
package org.bigbluebutton.core.apps.whiteboard
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
trait ClientToServerLatencyTracerMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def handleClientToServerLatencyTracerMsg(msg: ClientToServerLatencyTracerMsg): Unit = {
def broadcastEvent(): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(ServerToClientLatencyTracerMsg.NAME, routing)
val header = BbbClientMsgHeader(ServerToClientLatencyTracerMsg.NAME, props.meetingProp.intId, msg.header.userId)
val body = ServerToClientLatencyTracerMsgBody(msg.body.timestamp, msg.body.prettyTimestamp, msg.body.tzOffset, msg.body.senderId)
val event = ServerToClientLatencyTracerMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
broadcastEvent()
}
}

View File

@ -166,6 +166,9 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[SendWhiteboardAnnotationPubMsg](envelope, jsonNode)
case GetWhiteboardAnnotationsReqMsg.NAME =>
routeGenericMsg[GetWhiteboardAnnotationsReqMsg](envelope, jsonNode)
case ClientToServerLatencyTracerMsg.NAME =>
log.info("-- trace --" + jsonNode.toString)
routeGenericMsg[ClientToServerLatencyTracerMsg](envelope, jsonNode)
// Presentation
case SetCurrentPresentationPubMsg.NAME =>

View File

@ -3,6 +3,7 @@ package org.bigbluebutton.core.running
import java.io.{ PrintWriter, StringWriter }
import org.bigbluebutton.core.apps.users._
import org.bigbluebutton.core.apps.whiteboard.ClientToServerLatencyTracerMsgHdlr
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x }
import org.bigbluebutton.core.util.TimeUtil
//import java.util.concurrent.TimeUnit
@ -78,7 +79,8 @@ class MeetingActor(
with SendTimeRemainingUpdateHdlr
with SendBreakoutTimeRemainingMsgHdlr
with ChangeLockSettingsInMeetingCmdMsgHdlr
with SyncGetMeetingInfoRespMsgHdlr {
with SyncGetMeetingInfoRespMsgHdlr
with ClientToServerLatencyTracerMsgHdlr {
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case e: Exception => {
@ -207,6 +209,7 @@ class MeetingActor(
case m: GetWhiteboardAccessReqMsg => handleGetWhiteboardAccessReqMsg(m)
case m: SendWhiteboardAnnotationPubMsg => handleSendWhiteboardAnnotationPubMsg(m)
case m: GetWhiteboardAnnotationsReqMsg => handleGetWhiteboardAnnotationsReqMsg(m)
case m: ClientToServerLatencyTracerMsg => handleClientToServerLatencyTracerMsg(m)
// Poll
case m: StartPollReqMsg => handleStartPollReqMsg(m)

View File

@ -22,6 +22,11 @@ class AnalyticsActor extends Actor with ActorLogging {
log.info(TAG + json)
}
def traceMessage(msg: BbbCommonEnvCoreMsg): Unit = {
val json = JsonUtil.toJson(msg)
log.info(" -- trace -- " + json)
}
def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
msg.core match {
@ -76,6 +81,9 @@ class AnalyticsActor extends Actor with ActorLogging {
case m: SetCurrentPresentationPubMsg => logMessage(msg)
case m: SetCurrentPresentationEvtMsg => logMessage(msg)
case m: ClientToServerLatencyTracerMsg => traceMessage(msg)
case m: ServerToClientLatencyTracerMsg => traceMessage(msg)
case _ => // ignore message
}
}

View File

@ -79,7 +79,7 @@ class UserActor(val userId: String,
def handleMsgFromClientMsg(msg: MsgFromClientMsg):Unit = {
log.debug("Received MsgFromClientMsg " + msg)
def convertToJsonNode(json: String): Option[JsonNode] = {
JsonUtil.toJsonNode(json) match {
@ -97,6 +97,10 @@ class UserActor(val userId: String,
val routing = Routing.addMsgFromClientRouting(msgFromClient.header.meetingId, msgFromClient.header.userId)
val envelope = new BbbCoreEnvelope(msgFromClient.header.name, routing)
if (msgFromClient.header.name == "ClientToServerLatencyTracerMsg") {
log.info("-- trace -- " + msg.json)
}
for {
jsonNode <- convertToJsonNode(msg.json)
} yield {

View File

@ -3,6 +3,15 @@ package org.bigbluebutton.common2.msgs
case class AnnotationVO(id: String, status: String, annotationType: String,
annotationInfo: scala.collection.immutable.Map[String, Any], wbId: String, userId: String, position: Int)
object ClientToServerLatencyTracerMsg { val NAME = "ClientToServerLatencyTracerMsg" }
case class ClientToServerLatencyTracerMsg(header: BbbClientMsgHeader, body: ClientToServerLatencyTracerMsgBody) extends StandardMsg
case class ClientToServerLatencyTracerMsgBody(timestamp: Long, prettyTimestamp: String, tzOffset: Long, senderId: String)
object ServerToClientLatencyTracerMsg { val NAME = "ServerToClientLatencyTracerMsg" }
case class ServerToClientLatencyTracerMsg(header: BbbClientMsgHeader, body: ServerToClientLatencyTracerMsgBody) extends BbbCoreMsg
case class ServerToClientLatencyTracerMsgBody(timestamp: Long, prettyTimestamp: String, tzOffset: Long, senderId: String)
object ClearWhiteboardEvtMsg {
val NAME = "ClearWhiteboardEvtMsg"
}

View File

@ -59,6 +59,8 @@ public class ConnectionInvokerService implements IConnectionInvokerService {
private final long SEND_TIMEOUT = 5000000000L; // 5s
private Long lastMsgLengthLog = System.currentTimeMillis();
public ConnectionInvokerService() {
messages = new LinkedBlockingQueue<ClientMessage>();
}
@ -74,6 +76,10 @@ public class ConnectionInvokerService implements IConnectionInvokerService {
while (sendMessages) {
ClientMessage message;
try {
if (System.currentTimeMillis() - lastMsgLengthLog > 60000) {
lastMsgLengthLog = System.currentTimeMillis();
log.info("Message queue length = " + messages.size());
}
message = messages.take();
if (log.isTraceEnabled()) {
log.trace("Took message from queue: " + message.getMessageName());
@ -169,6 +175,10 @@ public class ConnectionInvokerService implements IConnectionInvokerService {
log.trace("Handle direct message: " + msg.messageName + " msg=" + msg.json);
}
if ("ServerToClientLatencyTracerMsg".equals(msg.messageName)) {
log.info("-- trace -- " + msg.json);
}
final String connId = msg.connId;
Runnable sender = new Runnable() {
public void run() {

View File

@ -0,0 +1,14 @@
package org.bigbluebutton.core.events
{
import flash.events.Event;
public class RoundTripLatencyReceivedEvent extends Event
{
public static const ROUND_TRIP_LATENCY_RECEIVED: String = "round trip latency received event";
public function RoundTripLatencyReceivedEvent()
{
super(ROUND_TRIP_LATENCY_RECEIVED, false, false);
}
}
}

View File

@ -82,6 +82,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<mate:Listener type="{BBBEvent.REMOVE_GUEST_FROM_LIST}" method="removeGuestWindow" />
<mate:Listener type="{BBBEvent.WAITING_FOR_MODERATOR_ACCEPTANCE}" method="openWaitWindow" />
<mate:Listener type="{BBBEvent.RECONNECT_DISCONNECTED_EVENT}" method="closeWaitWindow"/>
<mate:Listener type="{RoundTripLatencyReceivedEvent.ROUND_TRIP_LATENCY_RECEIVED}" method="handleRoundTripLatencyReceivedEvent"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
@ -119,7 +121,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.events.LockControlEvent;
import org.bigbluebutton.core.events.NewGuestWaitingEvent;
import org.bigbluebutton.core.events.RoundTripLatencyReceivedEvent;
import org.bigbluebutton.core.events.SwitchedLayoutEvent;
import org.bigbluebutton.core.model.LiveMeeting;
import org.bigbluebutton.core.vo.LockSettingsVO;
import org.bigbluebutton.main.events.AppVersionEvent;
import org.bigbluebutton.main.events.BBBEvent;
@ -735,6 +739,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
PopUpUtil.createModalPopUp(mdiCanvas, AudioSelectionWindow, true);
}
private function handleRoundTripLatencyReceivedEvent(event: RoundTripLatencyReceivedEvent): void {
rttLabel.text = "RTT = " + LiveMeeting.inst().whiteboardModel.latencyInSec + "s";
}
private function handleWebRTCMediaRequestEvent(event:WebRTCMediaEvent):void {
var options:PhoneOptions = new PhoneOptions();
if (!options.showMicrophoneHint) return;
@ -1052,6 +1060,11 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
id="copyrightLabel2" truncateToFit="true"
selectable="true" paddingRight="10"/>
<mx:Spacer width="100%" id="spacer" />
<mx:Label
id="rttLabel"
text="RTT = 0ms"
visible="true"
includeInLayout="true" />
<mx:Label
id="lblWebRTC"
text="{'[ '+ResourceUtil.getInstance().getString('bbb.mainshell.notification.webrtc')+' ]'}"

View File

@ -27,6 +27,7 @@ package org.bigbluebutton.modules.whiteboard.models
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.events.RoundTripLatencyReceivedEvent;
import org.bigbluebutton.modules.whiteboard.commands.GetWhiteboardShapesCommand;
import org.bigbluebutton.modules.whiteboard.events.WhiteboardAccessEvent;
import org.bigbluebutton.modules.whiteboard.events.WhiteboardCursorEvent;
@ -39,7 +40,36 @@ package org.bigbluebutton.modules.whiteboard.models
private var _multiUser:Boolean = false;
public var _dispatcher:Dispatcher = new Dispatcher();
private var _dispatcher:Dispatcher = new Dispatcher();
private var _lastTraceSentOn: Date = new Date();
private var _lastTraceReceivedOn: Date = new Date();
private var _roundTripTime: Number = 0;
public function sentLastTrace(date: Date):void {
_lastTraceSentOn = date;
}
public function get lastTraceSentOn(): Date {
return _lastTraceSentOn;
}
public function set lastTraceReceivedTimestamp(ts: Number): void {
var tsDate: Date = new Date(ts);
var now: Date = new Date();
_roundTripTime = (now.time - tsDate.time) / 1000;
_dispatcher.dispatchEvent(new RoundTripLatencyReceivedEvent());
}
public function get latencyInSec(): Number {
if (_lastTraceReceivedOn.time < _lastTraceSentOn.time) {
var now: Date = new Date();
return (now.time - _lastTraceSentOn.time) / 1000;
} else {
return (_lastTraceReceivedOn.time - _lastTraceSentOn.time) / 1000;
}
}
private function getWhiteboard(id:String, requestHistory:Boolean=true):Whiteboard {

View File

@ -58,6 +58,9 @@ package org.bigbluebutton.modules.whiteboard.services
case "SendCursorPositionEvtMsg":
handleSendCursorPositionEvtMsg(message);
break;
case "ServerToClientLatencyTracerMsg":
handleServerToClientLatencyTracerMsg(message);
break;
default:
// LogUtil.warn("Cannot handle message [" + messageName + "]");
}
@ -116,5 +119,12 @@ package org.bigbluebutton.modules.whiteboard.services
LiveMeeting.inst().whiteboardModel.updateCursorPosition(userId, xPercent, yPercent);
}
private function handleServerToClientLatencyTracerMsg(message:Object):void {
var userId:String = message.body.senderId as String;
var timestamp:Number = message.body.timestamp as Number;
LiveMeeting.inst().whiteboardModel.lastTraceReceivedTimestamp = timestamp;
}
}
}

View File

@ -23,6 +23,7 @@ package org.bigbluebutton.modules.whiteboard.services
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.managers.ConnectionManager;
import org.bigbluebutton.core.model.LiveMeeting;
import org.bigbluebutton.modules.whiteboard.events.WhiteboardAccessEvent;
import org.bigbluebutton.modules.whiteboard.events.WhiteboardDrawEvent;
@ -175,6 +176,37 @@ package org.bigbluebutton.modules.whiteboard.services
},
JSON.stringify(message)
);
sendClientToServerLatencyTracerMsg();
}
public function sendClientToServerLatencyTracerMsg():void {
var lastTraceSentOn: Date = LiveMeeting.inst().whiteboardModel.lastTraceSentOn;
var now: Date = new Date();
if (now.time - lastTraceSentOn.time > 60000) {
var prettyDate: String = now.toString();
var ts: Number = now.time;
var tzOffset: Number = now.timezoneOffset;
LiveMeeting.inst().whiteboardModel.sentLastTrace(now);
var message:Object = {
header: {name: "ClientToServerLatencyTracerMsg", meetingId: UsersUtil.getInternalMeetingID(), userId: UsersUtil.getMyUserID()},
body: {timestamp: ts, prettyTimestamp: prettyDate, tzOffset: tzOffset, senderId: UsersUtil.getMyUserID()}
};
var _nc:ConnectionManager = BBB.initConnectionManager();
_nc.sendMessage2x(
function(result:String):void { // On successful result
//LOGGER.debug(result);
},
function(status:String):void { // status - On error occurred
//LOGGER.error(status);
},
JSON.stringify(message)
);
}
}
}
}