我正在尝试在Dart中编写一个可以并行处理多个请求的HTTP服务器。到目前为止,我没有成功实现“并行”部分。
这是我最初尝试的内容:
import 'dart:io';
main() {
HttpServer.bind(InternetAddress.ANY_IP_V4, 8080).then((HttpServer server) {
server.listen((HttpRequest request) {
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
while (stopwatch.elapsedMilliseconds < 1000) { /* do nothing */ }
request.response.statusCode = HttpStatus.OK;
request.response.write(stopwatch.elapsedMilliseconds.toString());
request.response.close().catchError(print);
});
});
}
在每个请求上,它忙于工作一秒钟,然后完成。我让它以这种方式处理请求,以便它的时间可以预测,因此我可以很容易地在Windows任务管理器中看到请求的效果(CPU核心跳到100%使用)。
我可以说这并不是并行处理请求,因为:
如果我将几个浏览器标签加载到http://example:8080/
然后全部刷新,则标签会依次加载,每个标签之间约1秒钟。
如果我使用负载测试工具wrk进行这些设置... wrk -d 10 -c 8 -t 8 http://example:8080/
...它会在我给出的10秒内完成5到8个请求。如果服务器使用我的所有8个核心,我希望一个数字接近80个请求。
当我在wrk测试期间打开Windows任务管理器时,我发现我的核心中只有一个接近100%使用率,其余核心几乎空闲。
所以,然后我尝试使用隔离,希望为每个请求手动生成一个新的隔离/线程:
import 'dart:io';
import 'dart:isolate';
main() {
HttpServer.bind(InternetAddress.ANY_IP_V4, 8080).then((HttpServer server) {
server.listen((HttpRequest request) {
spawnFunction(handleRequest).send(request);
});
});
}
handleRequest() {
port.receive((HttpRequest request, SendPort sender) {
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
while (stopwatch.elapsedMilliseconds < 1000) { /* do nothing */ }
request.response.statusCode = HttpStatus.OK;
request.response.write(stopwatch.elapsedMilliseconds.toString());
request.response.close().catchError(print);
});
}
这根本不起作用。它不喜欢我正在尝试将HttpRequest作为消息发送到隔离区。这是错误:
#0 _SendPortImpl._sendInternal (dart:isolate-patch/isolate_patch.dart:122:3)
#1 _SendPortImpl._sendNow (dart:isolate-patch/isolate_patch.dart:95:18)
#2 _SendPortImpl.send (dart:isolate-patch/isolate_patch.dart:91:18)
#3 main.<anonymous closure>.<anonymous closure> (file:///C:/Development/dartbenchmark/simple2.dart:7:40)
#4 _StreamSubscriptionImpl._sendData (dart:async/stream_impl.dart:475:12)
#5 _StreamImpl._sendData.<anonymous closure> (dart:async/stream_impl.dart:251:29)
#6 _SingleStreamImpl._forEachSubscriber (dart:async/stream_impl.dart:335:11)
#7 _StreamImpl._sendData (dart:async/stream_impl.dart:249:23)
#8 _StreamImpl._add (dart:async/stream_impl.dart:51:16)
#9 StreamController.add (dart:async/stream_controller.dart:10:35)
#10 _HttpServer._handleRequest (http_impl.dart:1261:20)
#11 _HttpConnection._HttpConnection.<anonymous closure> (http_impl.dart:1188:33)
#12 _StreamSubscriptionImpl._sendData (dart:async/stream_impl.dart:475:12)
#13 _StreamImpl._sendData.<anonymous closure> (dart:async/stream_impl.dart:251:29)
#14 _SingleStreamImpl._forEachSubscriber (dart:async/stream_impl.dart:335:11)
#15 _StreamImpl._sendData (dart:async/stream_impl.dart:249:23)
#16 _StreamImpl._add (dart:async/stream_impl.dart:51:16)
#17 StreamController.add (dart:async/stream_controller.dart:10:35)
#18 _HttpParser._doParse (http_parser.dart:415:26)
#19 _HttpParser._parse (http_parser.dart:161:15)
#20 _HttpParser._onData._onData (http_parser.dart:509:11)
#21 _StreamSubscriptionImpl._sendData (dart:async/stream_impl.dart:475:12)
#22 _StreamImpl._sendData.<anonymous closure> (dart:async/stream_impl.dart:251:29)
#23 _SingleStreamImpl._forEachSubscriber (dart:async/stream_impl.dart:335:11)
#24 _StreamImpl._sendData (dart:async/stream_impl.dart:249:23)
#25 _StreamImpl._add (dart:async/stream_impl.dart:51:16)
#26 StreamController.add (dart:async/stream_controller.dart:10:35)
#27 _Socket._onData._onData (dart:io-patch/socket_patch.dart:726:42)
#28 _StreamSubscriptionImpl._sendData (dart:async/stream_impl.dart:475:12)
#29 _StreamImpl._sendData.<anonymous closure> (dart:async/stream_impl.dart:251:29)
#30 _SingleStreamImpl._forEachSubscriber (dart:async/stream_impl.dart:335:11)
#31 _StreamImpl._sendData (dart:async/stream_impl.dart:249:23)
#32 _StreamImpl._add (dart:async/stream_impl.dart:51:16)
#33 StreamController.add (dart:async/stream_controller.dart:10:35)
#34 _RawSocket._RawSocket.<anonymous closure> (dart:io-patch/socket_patch.dart:452:52)
#35 _NativeSocket.multiplex (dart:io-patch/socket_patch.dart:253:18)
#36 _NativeSocket.connectToEventHandler.<anonymous closure> (dart:io-patch/socket_patch.dart:338:54)
#37 _ReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:81:92)
Unhandled exception:
Illegal argument(s): Illegal argument in isolate message : (object is a closure)
#0 _throwDelayed.<anonymous closure> (dart:async/stream_impl.dart:22:5)
#1 _asyncRunCallback._asyncRunCallback (dart:async/event_loop.dart:15:17)
#2 _asyncRunCallback._asyncRunCallback (dart:async/event_loop.dart:25:9)
#3 Timer.run.<anonymous closure> (dart:async/timer.dart:17:21)
#4 Timer.run.<anonymous closure> (dart:async/timer.dart:25:13)
#5 Timer.Timer.<anonymous closure> (dart:async-patch/timer_patch.dart:9:15)
#6 _Timer._createTimerHandler._handleTimeout (timer_impl.dart:99:28)
#7 _Timer._createTimerHandler._handleTimeout (timer_impl.dart:107:7)
#8 _Timer._createTimerHandler.<anonymous closure> (timer_impl.dart:115:23)
#9 _ReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:81:92)
使用的版本:
是否可以使用Dart并行处理这些请求,并使用我的所有计算机可用内核?
答案 0 :(得分:9)
我写了一个名为dart-isoserver的库来做这件事。它现在严重腐烂,但你可以看到这种方法。
https://code.google.com/p/dart-isoserver/
我所做的是通过隔离端口代理HttpRequest和HttpResponse,因为您无法直接发送它们。它有用,但有一些警告:
spawnFunction()
现在有一个未捕获的异常处理程序参数,所以这有点可修复,但spawnUri()没有。 dart-isoserver使用spawnUri()来实现热加载,因此必须将其删除。关于您的第一个代码示例的说明。正如你所注意到的,这肯定不会并行运行,因为Dart是单线程的。同一个隔离中的Dart代码不会同时运行。
答案 1 :(得分:6)
即使使用当前的HttpServer限制,也可以通过在Apache或Nginx等反向代理服务器后面运行多个服务器进程来利用多个核心。在Dart中,您还可以派生子进程来拆分计算密集型任务。
一个好的起点是阅读有关扩展node.js的内容,因为这也使用单进程每个进程架构。
编辑:答案现已过时,现在可以在隔离区之间共享请求,允许Dart进程使用多个核心。
请参阅ServerSocket.bind(shared)的文档。
“可选参数shared指定是否可以从同一个Dart进程绑定到同一地址,端口和v6Only组合。如果shared为true且执行了其他绑定,则传入连接将在该组之间分配ServerSockets。使用它的一种方法是使用多个隔离区来分配传入的连接。“
答案 2 :(得分:2)
您需要:
shared: true
这是一个准系统,最小的Dart服务器,可将传入的请求分布在6个隔离中:
import 'dart:io';
import 'dart:isolate';
void main() async {
for (var i = 1; i < 6; i++) {
Isolate.spawn(_startServer, []);
}
// Bind one server in current Isolate
_startServer();
print('Serving at http://localhost:8080/');
await ProcessSignal.sigterm.watch().first;
}
void _startServer([List args]) async {
final server = await HttpServer.bind(
InternetAddress.loopbackIPv4,
8080,
shared: true, // This is the magic sauce
);
await for (final request in server) {
_handleRequest(request);
}
}
void _handleRequest(HttpRequest request) async {
// Fake delay
await Future.delayed(const Duration(seconds: 2));
request.response.write('Hello, world!');
await request.response.close();
}