通过 websockets 处理错误是一个设计决策
Error handling over websockets a design dessision
我目前正在构建一个具有两个明确用例的网络应用程序。
- 传统客户端向服务器请求数据。
- 客户端在服务器开始向客户端推送数据后向服务器请求流。
目前我正在使用通过 websocket 传递的 json 消息来实现 1 和 2。然而,事实证明这很难,因为我需要手动编写大量错误处理代码,因为客户端不等待响应。它只是发送消息,希望它能在某个时候得到回复。
我在前端使用 Js 和 React,在后端使用 Clojure。
关于这个我有两个问题。
- 鉴于当前的设计,有哪些替代方案可以通过 websocket 进行错误处理?
- 使用 UC1 的 rest 和 UC2 的 websockets 拆分两个 UC 是否更聪明,然后我可以在前端使用类似
fetch
的东西进行 rest 调用。
更新。
当前的问题是不知道如何通过 websockets 构建异步发送功能可以匹配发送消息和响应消息。
这是在 socket.io 上执行 request/response 的方案。您可以通过普通的 webSocket 执行此操作,但您必须自己构建更多的基础结构。可以在客户端和服务器中使用相同的库:
function initRequestResponseSocket(socket, requestHandler) {
var cntr = 0;
var openResponses = {};
// send a request
socket.sendRequestResponse = function(data, fn) {
// put this data in a wrapper object that contains the request id
// save the callback function for this id
var id = cntr++;
openResponses[id] = fn;
socket.emit('requestMsg', {id: id, data: data});
}
// process a response message that comes back from a request
socket.on('responseMsg', function(wrapper) {
var id = wrapper.id, fn;
if (typeof id === "number" && typeof openResponses[id] === "function") {
fn = openResponses[id];
delete openResponses[id];
fn(wrapper.data);
}
});
// process a requestMsg
socket.on('requestMsg', function(wrapper) {
if (requestHandler && wrapper.id) {
requestHandler(wrapper.data, function(responseToSend) {
socket.emit('responseMsg', {id: wrapper.id, data; responseToSend});
});
}
});
}
这通过将发送的每条消息包装在包含唯一 id 值的包装器对象中来实现。然后,当另一端发送响应时,它包含相同的 id 值。然后可以将该 id 值与该特定消息的特定回调响应处理程序相匹配。它可以从客户端到服务器或从服务器到客户端的两种方式工作。
您可以通过在两端的 socket.io 套接字连接上调用一次 initRequestResponseSocket(socket, requestHandler)
来使用它。如果您希望接收请求,则传递一个 requestHandler 函数,每次有请求时该函数都会被调用。如果您只是发送请求和接收响应,那么您不必在连接的那一端传入 requestHandler。
要发送消息并等待回复,您可以这样做:
socket.sendRequestResponse(data, function(err, response) {
if (!err) {
// response is here
}
});
如果您正在接收请求并发回响应,那么您可以这样做:
initRequestResponseSocket(socket, function(data, respondCallback) {
// process the data here
// send response
respondCallback(null, yourResponseData);
});
至于错误处理,您可以监视连接丢失,并且可以在此代码中设置超时,这样如果在一定时间内没有收到响应,您就会收到错误消息返回。
下面是上述代码的扩展版本,它实现了对某个时间段内未出现的响应的超时:
function initRequestResponseSocket(socket, requestHandler, timeout) {
var cntr = 0;
var openResponses = {};
// send a request
socket.sendRequestResponse = function(data, fn) {
// put this data in a wrapper object that contains the request id
// save the callback function for this id
var id = cntr++;
openResponses[id] = {fn: fn};
socket.emit('requestMsg', {id: id, data: data});
if (timeout) {
openResponses[id].timer = setTimeout(function() {
delete openResponses[id];
if (fn) {
fn("timeout");
}
}, timeout);
}
}
// process a response message that comes back from a request
socket.on('responseMsg', function(wrapper) {
var id = wrapper.id, requestInfo;
if (typeof id === "number" && typeof openResponse[id] === "object") {
requestInfo = openResponses[id];
delete openResponses[id];
if (requestInfo) {
if (requestInfo.timer) {
clearTimeout(requestInfo.timer);
}
if (requestInfo.fn) {
requestInfo.fn(null, wrapper.data);
}
}
}
});
// process a requestMsg
socket.on('requestMsg', function(wrapper) {
if (requestHandler && wrapper.id) {
requestHandler(wrapper.data, function(responseToSend) {
socket.emit('responseMsg', {id: wrapper.id, data; responseToSend});
});
}
});
}
在你的问题和你的设计中有一些有趣的事情,我更愿意忽略实现细节并查看高级架构。
您声明您正在寻找一个请求数据的客户端和一个响应一些数据流的服务器。这里需要注意两点:
- HTTP 1.1 具有发送流响应的选项(分块传输编码)。如果您的用例只是发送流响应,这可能更适合您。当您例如想要将消息推送到未响应某种请求的客户端(有时称为 服务器端事件 )。
- 与 HTTP 不同,Websockets 本身并不实现某种请求-响应循环。您可以通过实现自己的机制来使用协议,例如子协议 WAMP 正在执行。
正如您所发现的那样,实现自己的机制会带来一些缺陷,而这正是 HTTP 具有明显优势的地方。鉴于您的问题中所述的要求,我会选择 HTTP 流式传输方法,而不是实施您自己的 request/response 机制。
我目前正在构建一个具有两个明确用例的网络应用程序。
- 传统客户端向服务器请求数据。
- 客户端在服务器开始向客户端推送数据后向服务器请求流。
目前我正在使用通过 websocket 传递的 json 消息来实现 1 和 2。然而,事实证明这很难,因为我需要手动编写大量错误处理代码,因为客户端不等待响应。它只是发送消息,希望它能在某个时候得到回复。 我在前端使用 Js 和 React,在后端使用 Clojure。
关于这个我有两个问题。
- 鉴于当前的设计,有哪些替代方案可以通过 websocket 进行错误处理?
- 使用 UC1 的 rest 和 UC2 的 websockets 拆分两个 UC 是否更聪明,然后我可以在前端使用类似
fetch
的东西进行 rest 调用。
更新。 当前的问题是不知道如何通过 websockets 构建异步发送功能可以匹配发送消息和响应消息。
这是在 socket.io 上执行 request/response 的方案。您可以通过普通的 webSocket 执行此操作,但您必须自己构建更多的基础结构。可以在客户端和服务器中使用相同的库:
function initRequestResponseSocket(socket, requestHandler) {
var cntr = 0;
var openResponses = {};
// send a request
socket.sendRequestResponse = function(data, fn) {
// put this data in a wrapper object that contains the request id
// save the callback function for this id
var id = cntr++;
openResponses[id] = fn;
socket.emit('requestMsg', {id: id, data: data});
}
// process a response message that comes back from a request
socket.on('responseMsg', function(wrapper) {
var id = wrapper.id, fn;
if (typeof id === "number" && typeof openResponses[id] === "function") {
fn = openResponses[id];
delete openResponses[id];
fn(wrapper.data);
}
});
// process a requestMsg
socket.on('requestMsg', function(wrapper) {
if (requestHandler && wrapper.id) {
requestHandler(wrapper.data, function(responseToSend) {
socket.emit('responseMsg', {id: wrapper.id, data; responseToSend});
});
}
});
}
这通过将发送的每条消息包装在包含唯一 id 值的包装器对象中来实现。然后,当另一端发送响应时,它包含相同的 id 值。然后可以将该 id 值与该特定消息的特定回调响应处理程序相匹配。它可以从客户端到服务器或从服务器到客户端的两种方式工作。
您可以通过在两端的 socket.io 套接字连接上调用一次 initRequestResponseSocket(socket, requestHandler)
来使用它。如果您希望接收请求,则传递一个 requestHandler 函数,每次有请求时该函数都会被调用。如果您只是发送请求和接收响应,那么您不必在连接的那一端传入 requestHandler。
要发送消息并等待回复,您可以这样做:
socket.sendRequestResponse(data, function(err, response) {
if (!err) {
// response is here
}
});
如果您正在接收请求并发回响应,那么您可以这样做:
initRequestResponseSocket(socket, function(data, respondCallback) {
// process the data here
// send response
respondCallback(null, yourResponseData);
});
至于错误处理,您可以监视连接丢失,并且可以在此代码中设置超时,这样如果在一定时间内没有收到响应,您就会收到错误消息返回。
下面是上述代码的扩展版本,它实现了对某个时间段内未出现的响应的超时:
function initRequestResponseSocket(socket, requestHandler, timeout) {
var cntr = 0;
var openResponses = {};
// send a request
socket.sendRequestResponse = function(data, fn) {
// put this data in a wrapper object that contains the request id
// save the callback function for this id
var id = cntr++;
openResponses[id] = {fn: fn};
socket.emit('requestMsg', {id: id, data: data});
if (timeout) {
openResponses[id].timer = setTimeout(function() {
delete openResponses[id];
if (fn) {
fn("timeout");
}
}, timeout);
}
}
// process a response message that comes back from a request
socket.on('responseMsg', function(wrapper) {
var id = wrapper.id, requestInfo;
if (typeof id === "number" && typeof openResponse[id] === "object") {
requestInfo = openResponses[id];
delete openResponses[id];
if (requestInfo) {
if (requestInfo.timer) {
clearTimeout(requestInfo.timer);
}
if (requestInfo.fn) {
requestInfo.fn(null, wrapper.data);
}
}
}
});
// process a requestMsg
socket.on('requestMsg', function(wrapper) {
if (requestHandler && wrapper.id) {
requestHandler(wrapper.data, function(responseToSend) {
socket.emit('responseMsg', {id: wrapper.id, data; responseToSend});
});
}
});
}
在你的问题和你的设计中有一些有趣的事情,我更愿意忽略实现细节并查看高级架构。
您声明您正在寻找一个请求数据的客户端和一个响应一些数据流的服务器。这里需要注意两点:
- HTTP 1.1 具有发送流响应的选项(分块传输编码)。如果您的用例只是发送流响应,这可能更适合您。当您例如想要将消息推送到未响应某种请求的客户端(有时称为 服务器端事件 )。
- 与 HTTP 不同,Websockets 本身并不实现某种请求-响应循环。您可以通过实现自己的机制来使用协议,例如子协议 WAMP 正在执行。
正如您所发现的那样,实现自己的机制会带来一些缺陷,而这正是 HTTP 具有明显优势的地方。鉴于您的问题中所述的要求,我会选择 HTTP 流式传输方法,而不是实施您自己的 request/response 机制。