How to use multiple isolates to serve requests - dart

How can a Dart server be set up to use all available cores for in coming requests (e.g. by using multiple isolates to serve requests)?

Use the shared: true argument of HttpServer.bind. Example:
import 'dart:io';
import 'dart:isolate';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:args/args.dart' show ArgParser;
main(List<String> args) {
var parser = new ArgParser()
..addOption('address', abbr: 'a', defaultsTo: '0.0.0.0')
..addOption('port', abbr: 'p', defaultsTo: '9393')
..addOption('isolates', abbr: 'i', defaultsTo: '3');
var arguments = parser.parse(args);
var nbOfIsolates = int.parse(arguments['isolates']);
for (int i = 1; i < nbOfIsolates; i++) {
Isolate.spawn(_startShelfServer, [arguments['address'], int.parse(arguments['port'])]);
}
_startShelfServer([arguments['address'], int.parse(arguments['port'])]);
}
_startShelfServer(List args) async {
String address = args[0];
int port = args[1];
var helloWorldHandler = (shelf.Request request) => new shelf.Response.ok("Hello World - from isolate ${Isolate.current.hashCode}");
var handler = const shelf.Pipeline()
.addHandler(helloWorldHandler);
var server = await HttpServer.bind(address, port, shared: true);
await shelf_io.serveRequests(server, handler);
print('Serving at http://${server.address.host}:${server.port} - isolate: ${Isolate.current.hashCode}');
}

Related

Can this HttpServer handle production-level concurrent requests? [duplicate]

How can a Dart server be set up to use all available cores for in coming requests (e.g. by using multiple isolates to serve requests)?
Use the shared: true argument of HttpServer.bind. Example:
import 'dart:io';
import 'dart:isolate';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:args/args.dart' show ArgParser;
main(List<String> args) {
var parser = new ArgParser()
..addOption('address', abbr: 'a', defaultsTo: '0.0.0.0')
..addOption('port', abbr: 'p', defaultsTo: '9393')
..addOption('isolates', abbr: 'i', defaultsTo: '3');
var arguments = parser.parse(args);
var nbOfIsolates = int.parse(arguments['isolates']);
for (int i = 1; i < nbOfIsolates; i++) {
Isolate.spawn(_startShelfServer, [arguments['address'], int.parse(arguments['port'])]);
}
_startShelfServer([arguments['address'], int.parse(arguments['port'])]);
}
_startShelfServer(List args) async {
String address = args[0];
int port = args[1];
var helloWorldHandler = (shelf.Request request) => new shelf.Response.ok("Hello World - from isolate ${Isolate.current.hashCode}");
var handler = const shelf.Pipeline()
.addHandler(helloWorldHandler);
var server = await HttpServer.bind(address, port, shared: true);
await shelf_io.serveRequests(server, handler);
print('Serving at http://${server.address.host}:${server.port} - isolate: ${Isolate.current.hashCode}');
}

market tickers in dart programming language

I was trying to fetch market tickers from Google Finance but I am getting the same value over time. Below is my code, any fix? Its giving same output $2,254.84 again and again.
import 'dart:io' as io;
import 'dart:convert';
import 'package:puppeteer/puppeteer.dart' as pp;
void main() async {
var launch = await pp.puppeteer.launch(
executablePath: "/usr/bin/brave-browser",
);
print(await launch.version);
var stocks_nasdaq = ["GOOG", "MSFT", "APPL", "TESLA"];
var page = await launch.newPage();
while (true) {
for (var asset in stocks_nasdaq) {
await page.goto("https://www.google.com/finance/quote/$asset:NASDAQ");
var kk = ["c_p", "24h", "p_year"].iterator;
var aa = ((await page.$$eval("div.P6K39c",
'function (el) { var eee=[];for(var a=0;a<el.length;a++){eee[a]=el[a].innerHTML; } return eee; }')));
for (var a in aa) {
if (a.contains("\$")) {
kk.moveNext();
print({
asset: {kk.current: a}
});
}
}
io.sleep(Duration(seconds: 10));
}
}
}

How to get filename in multipart post request using Dart/Aqueduct

I am trying to upload image from client (flutter) to server (Aqueduct.io) using MultipartRequest.
It's working, but currently file names are assigned the current time, how can I pass the filename from a client and parse it on a server side?
Client code:
final String imageName = nameController.text.replaceAll(" ", "");
var postUri = Uri.parse("http://***:8888/media");
var request = new http.MultipartRequest("POST", postUri);
request.files.add(new http.MultipartFile.fromBytes('file', image,
filename: imageName, contentType: MediaType('image', 'jpeg')));
request.send().then((response) {
if (response.statusCode == 200) print("Uploaded!");
});
}
Server code:
import 'dart:async';
import 'dart:io';
import 'package:aqueduct/aqueduct.dart';
import 'package:mime/mime.dart';
import 'package:http_server/http_server.dart';
class MediaController extends ResourceController {
MediaController() {
acceptedContentTypes = [ContentType("multipart", "form-data")];
}
#Operation.post()
Future<Response> postMultipartForm() async {
final transformer = MimeMultipartTransformer(
request.raw.headers.contentType.parameters["boundary"]);
final bodyStream =
Stream.fromIterable([await request.body.decode<List<int>>()]);
final parts = await transformer.bind(bodyStream).toList();
for (var part in parts) {
final HttpMultipartFormData multipart = HttpMultipartFormData.parse(part);
final content = multipart.cast<List<int>>();
final filePath =
"public/" + DateTime.now().millisecondsSinceEpoch.toString() + ".jpg"; // <---current filename implementation
final IOSink sink = File(filePath).openWrite();
await for (List<int> item in content) {
sink.add(item);
}
await sink.flush();
await sink.close();
}
return Response.ok({});
}
}
Okay, I have the asnwer
import 'dart:async';
import 'dart:io';
import 'package:aqueduct/aqueduct.dart';
import 'package:mime/mime.dart';
import 'package:http_server/http_server.dart';
class MediaController extends ResourceController {
MediaController() {
acceptedContentTypes = [ContentType("multipart", "form-data")];
}
#Operation.post()
Future<Response> postMultipartForm() async {
final transformer = MimeMultipartTransformer(
request.raw.headers.contentType.parameters["boundary"]);
final bodyStream =
Stream.fromIterable([await request.body.decode<List<int>>()]);
final parts = await transformer.bind(bodyStream).toList();
for (var part in parts) {
final HttpMultipartFormData multipart = HttpMultipartFormData.parse(part);
List<String> tokens = part.headers['content-disposition'].split(";");
String filename;
for (var i = 0; i < tokens.length; i++) {
if (tokens[i].contains('filename')) {
filename = tokens[i]
.substring(tokens[i].indexOf("=") + 2, tokens[i].length - 1);
}
}
print('file $filename.jpg uploaded');
final content = multipart.cast<List<int>>();
final filePath =
// "public/" + DateTime.now().millisecondsSinceEpoch.toString() + ".jpg";
'public/$filename.jpg';
final IOSink sink = File(filePath).openWrite();
await for (List<int> item in content) {
sink.add(item);
}
await sink.flush();
await sink.close();
}
return Response.ok({});
}
}

How to use results from different isolates in the main isolate?

I'm really new to Dart and also to programming. I'm trying to develop a a command-line program in Dart using isolates. My intention is to compare it's performance againt the same program, but written in Java with threads.
The Dart program looks like this so far:
main.dart
import "dart:async";
import "dart:isolate";
main() {
var rPort1 = new ReceivePort();
var rPort2 = new ReceivePort();
var p1 = 0;
rPort1.listen((partial) {
print("p1 ${partial}");
p1 = partial;
rPort1.close();
});
var p2 = 0;
rPort2.listen((partial) {
print("p2 ${partial}");
p2 = partial;
rPort2.close();
});
Isolate.spawnUri(new Uri.file("MyIsolate.dart"), [arg0, ...], rPort1.sendPort);
Isolate.spawnUri(new Uri.file("MyIsolate.dart"), [arg0, ...], rPort2.sendPort);
var p3 = p1 + p2;
print("p3 ${p3}");
}
myIsolate.dart
import "dart:async";
import "dart:isolate";
main(args, SendPort port) {
var partial = 0;
// ... do stuff ...
// args are used and partial is updated
port.send(partial);
}
The output looks like this:
p3 0
p1 -0.1168096561671553
p2 0.023709338284264223
As you can see the return values of each isolate comes after the main isolate has finished it's execution. What I want is to use the result of the isolates to further calculation in the main.
I don't know what I'm missing. I'm sure is something very stupid, but I cannot move forward on this problem. In Java is simple to get the result value of each thread, but in Dart I can't figure how to do this in isolates.
Any ideas?
You have to wait until all streams (from your ports) are complete. One of ways for doing that is something like that:
import "dart:async";
import "dart:isolate";
main() {
var rPort1 = new ReceivePort();
var rPort2 = new ReceivePort();
// Defining completers which would complete when Streams are finished
Completer c1 = new Completer();
Completer c2 = new Completer();
var p1 = 0;
rPort1.listen((partial) {
print("p1 ${partial}");
p1 = partial;
rPort1.close();
}, onDone: ()=>c1.complete()); // Notice onDone callback here
var p2 = 0;
rPort2.listen((partial) {
print("p2 ${partial}");
p2 = partial;
rPort2.close();
}, onDone: ()=>c2.complete()); // And here
Isolate.spawnUri(new Uri.file("my_isolate.dart"), [0], rPort1.sendPort);
Isolate.spawnUri(new Uri.file("my_isolate.dart"), [0], rPort2.sendPort);
// Waiting for both streams to complete before summing our results
Future.wait([c1.future,c2.future]).then((_){
var p3 = p1 + p2;
print("p3 ${p3}");
});
}
For your task, if you're waiting for exact values, you may define only Futures for these values you need, and complete them without waiting for your isolates (and their streams) to finish.
For doing this, just move c*.complete(<value>) to corresponding listen() callback. Something like that (not tested):
rPort1.listen((partial) {
print("p1 ${partial}");
c1.complete(partial);
rPort1.close();
});
rPort2.listen((partial) {
print("p2 ${partial}");
c2.complete(partial);
rPort2.close();
});
...
Future.wait([c1.future,c2.future]).then((result){
var p3 = result[0] + result[1];
print("p3 ${p3}");
});
If you want to wait for something in Dart, that something should be a future. You can convert a stream or port event to a future in many different ways. If in doubt, you can always use a Completer to create a future from any other event.
In this case it can be done easier because you just want one event from each stream, and you can use Stream.first (or Stream.last or Stream.single) for that.
import "dart:async";
import "dart:isolate";
main() {
var rPort1 = new ReceivePort();
var rPort2 = new ReceivePort();
Future.wait([
Isolate.spawnUri(new Uri.file("my_isolate.dart"), ["0"], rPort1.sendPort)
.then((_) => rPort1.first,
onError: (_) => rPort1.close()),
Isolate.spawnUri(new Uri.file("my_isolate.dart"), ["0"], rPort2.sendPort)
.then((_) => rPort2.first,
onError: (_) => rPort2.close()),
]).then((ps) {
// Waiting for both streams to complete before summing our results
var p3 = ps[0] + ps[1];
print("p3 ${p3}");
});
}
Here I also wait for the spawnUri return Future because it may contain an error if your isolate didn't spawn correctly.
You can also use some of the helper functions in the isolate package.
import "dart:async";
import "dart:isolate";
import "package:isolate/isolate.dart";
main() async {
// A SingleResponseChannel has a send-port and a result future,
// and completes the future with the first port event.
// Warning: Only closed when event is sent on port!
// Consider setting a time-out on the channel.
var c1 = new SingleResponseChannel();
var c2 = new SingleResponseChannel();
Isolate.spawnUri(new Uri.file("my_isolate.dart"), ["0"], c1.port);
Isolate.spawnUri(new Uri.file("my_isolate.dart"), ["0"], c2.port);
var p3 = await c1.result + await c2.result;
print("p3 ${p3}");
}

How can I use shelf_web_socket to listen for http and ws requests on the same port

https://pub.dartlang.org/packages/shelf_web_socket shows this example
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_web_socket/shelf_web_socket.dart';
void main() {
var handler = webSocketHandler((webSocket) {
webSocket.listen((message) {
webSocket.add("echo $message");
});
});
shelf_io.serve(handler, 'localhost', 8080).then((server) {
print('Serving at ws://${server.address.host}:${server.port}');
});
}
I would like to know how to combine this with my HTTP server initialization
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as sIo;
import 'package:shelf_auth/shelf_auth.dart' as sAuth;
import 'package:shelf_auth/src/authentication.dart' as sAuth2;
import 'package:option/option.dart';
import 'package:shelf_web_socket/shelf_web_socket.dart' as sWs;
...
var authMiddleware = sAuth.authenticate(
[new MyAuthenticator()],
sessionHandler: new sAuth.JwtSessionHandler('bla', 'blub', new UserLookup()),
allowHttp: true,
allowAnonymousAccess: false);
var handler = const shelf.Pipeline()
.addMiddleware(shelf.logRequests())
.addMiddleware(authMiddleware)
.addHandler(_handleHttpRequest);
// var wsHandler = sWs.webSocketHandler(_handleWebSocketConnect);
sIo.serve(handler, '0.0.0.0', servePort).then((server) {
_log.finest('Serving at http://${server.address.host}:${server.port}');
});
What needs to be done so that wsHandler gets called for WebSocket connects and handler keeps handling HTTP requests (if possible on the same port) and if possible uses the configured authentication and session management.
I tried it on a different port but with the authentication/session middleware (no idea if this is supposed to be used together)
var authMiddleware = sAuth.authenticate(
[new MyAuthenticator()],
sessionHandler: new sAuth.JwtSessionHandler('bla', 'blub', new UserLookup()),
allowHttp: true,
allowAnonymousAccess: false);
var handler = const shelf.Pipeline()
.addMiddleware(shelf.logRequests())
.addMiddleware(authMiddleware)
.addHandler(_handleHttpRequest);
sIo.serve(handler, '0.0.0.0', servePort).then((server) {
_log.finest('Serving at http://${server.address.host}:${server.port}');
});
var wsHandler = const shelf.Pipeline()
.addMiddleware(shelf.logRequests())
.addMiddleware(authMiddleware)
.addHandler(sWs.webSocketHandler(_handleWebSocketConnect));
sIo.serve(wsHandler, '0.0.0.0', servePort + 1).then((server) {
_log.finest('Serving at ws://${server.address.host}:${server.port}');
});
and got
Illegal argument(s): webSocketHandler may only be used with a server that supports request hijacking.
At the moment your root handler is the http handler. You'll need to set up a handler that conditionally sends requests to the ws handler or another handler for your http requests. Eg
/ws -> your ws handler
/rest -> your other handler
The easiest way to do that is to use a router like shelf_route.
However someone recently tried this and hit a bug in shelf that stopped this working. Which as you noted below is fixed but not merged.
Once the issue is fixed you should be able to do
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_route/shelf_route.dart' as route;
import 'package:shelf_web_socket/shelf_web_socket.dart' as sWs;
import 'package:shelf_auth/shelf_auth.dart' as sAuth;
import 'dart:async';
import 'package:option/option.dart';
import 'package:shelf_exception_response/exception_response.dart';
void main(List<String> arguments) {
var authMiddleware = sAuth.authenticate(
[new MyAuthenticator()],
sessionHandler: new sAuth.JwtSessionHandler('bla', 'blub', new UserLookup()),
allowHttp: true,
allowAnonymousAccess: false);
var router = (route.router()
..get('/rest', _handleHttpRequest)
..get('/ws', sWs.webSocketHandler(_handleWebSocketConnect)));
var handler = const shelf.Pipeline()
.addMiddleware(exceptionResponse())
.addMiddleware(shelf.logRequests())
.addMiddleware(authMiddleware)
.addHandler(router.handler);
route.printRoutes(router);
io.serve(handler, '127.0.0.1', 8080).then((server) {
print('Serving at http://${server.address.host}:${server.port}');
});
}
Until the issue is fixed you can replace the router.handler with
var hackyRouterHandler = (shelf.Request request) {
var path = request.url.path;
if (path.startsWith('/rest')) {
return _handleHttpRequest(request);
}
else if (path.startsWith('/ws')) {
return sWs.webSocketHandler(_handleWebSocketConnect)(request);
}
else {
throw new NotFoundException();
}
};

Resources