working on metrics
This commit is contained in:
parent
1e8cb87e18
commit
77a5cec9c0
15 changed files with 777 additions and 495 deletions
|
@ -26,7 +26,7 @@
|
||||||
<entry key="angel_route">
|
<entry key="angel_route">
|
||||||
<value>
|
<value>
|
||||||
<list>
|
<list>
|
||||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/angel_route-1.0.6/lib" />
|
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/angel_route-1.0.7/lib" />
|
||||||
</list>
|
</list>
|
||||||
</value>
|
</value>
|
||||||
</entry>
|
</entry>
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
<entry key="body_parser">
|
<entry key="body_parser">
|
||||||
<value>
|
<value>
|
||||||
<list>
|
<list>
|
||||||
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/body_parser-1.0.2/lib" />
|
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/body_parser-1.0.3/lib" />
|
||||||
</list>
|
</list>
|
||||||
</value>
|
</value>
|
||||||
</entry>
|
</entry>
|
||||||
|
@ -435,11 +435,11 @@
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/analyzer-0.30.0+4/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/analyzer-0.30.0+4/lib" />
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/angel_http_exception-1.0.0/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/angel_http_exception-1.0.0/lib" />
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/angel_model-1.0.0/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/angel_model-1.0.0/lib" />
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/angel_route-1.0.6/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/angel_route-1.0.7/lib" />
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/args-1.0.1/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/args-1.0.1/lib" />
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-1.13.3/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-1.13.3/lib" />
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/barback-0.15.2+13/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/barback-0.15.2+13/lib" />
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/body_parser-1.0.2/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/body_parser-1.0.3/lib" />
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-1.0.2/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-1.0.2/lib" />
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.1/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.1/lib" />
|
||||||
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/cli_util-0.1.2+1/lib" />
|
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/cli_util-0.1.2+1/lib" />
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="performance::hello" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
|
<configuration default="false" name="performance::hello (DEV)" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
|
||||||
<option name="checkedMode" value="false" />
|
|
||||||
<option name="filePath" value="$PROJECT_DIR$/performance/hello/main.dart" />
|
<option name="filePath" value="$PROJECT_DIR$/performance/hello/main.dart" />
|
||||||
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||||
<method />
|
<method />
|
11
.idea/runConfigurations/performance__hello__PRODUCTION_.xml
Normal file
11
.idea/runConfigurations/performance__hello__PRODUCTION_.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="performance::hello (PRODUCTION)" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
|
||||||
|
<option name="checkedMode" value="false" />
|
||||||
|
<option name="envs">
|
||||||
|
<entry key="ANGEL_ENV" value="production" />
|
||||||
|
</option>
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/performance/hello/main.dart" />
|
||||||
|
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
File diff suppressed because it is too large
Load diff
2
lib/metrics.dart
Normal file
2
lib/metrics.dart
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export 'src/stats/metric_server.dart';
|
||||||
|
export 'src/stats/stats.dart';
|
|
@ -9,6 +9,9 @@ typedef Future<String> ViewGenerator(String path, [Map data]);
|
||||||
|
|
||||||
/// Base class for Angel servers. Do not bother extending this.
|
/// Base class for Angel servers. Do not bother extending this.
|
||||||
class AngelBase extends Routable {
|
class AngelBase extends Routable {
|
||||||
|
static ViewGenerator noViewEngineConfigured =(String view, [Map data]) async =>
|
||||||
|
"No view engine has been configured yet.";
|
||||||
|
|
||||||
Container _container = new Container();
|
Container _container = new Container();
|
||||||
|
|
||||||
final Map configuration = {};
|
final Map configuration = {};
|
||||||
|
@ -28,6 +31,12 @@ class AngelBase extends Routable {
|
||||||
/// A function that renders views.
|
/// A function that renders views.
|
||||||
///
|
///
|
||||||
/// Called by [ResponseContext]@`render`.
|
/// Called by [ResponseContext]@`render`.
|
||||||
ViewGenerator viewGenerator = (String view, [Map data]) async =>
|
ViewGenerator viewGenerator = noViewEngineConfigured;
|
||||||
"No view engine has been configured yet.";
|
|
||||||
|
/// Closes this instance, rendering it **COMPLETELY DEFUNCT**.
|
||||||
|
Future close() async {
|
||||||
|
await super.close();
|
||||||
|
_container = null;
|
||||||
|
viewGenerator = noViewEngineConfigured;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -686,6 +686,7 @@ class HookedServiceEventDispatcher {
|
||||||
|
|
||||||
void _close() {
|
void _close() {
|
||||||
_ctrl.forEach((c) => c.close());
|
_ctrl.forEach((c) => c.close());
|
||||||
|
listeners.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fires an event, and returns it once it is either canceled, or all listeners have run.
|
/// Fires an event, and returns it once it is either canceled, or all listeners have run.
|
||||||
|
|
|
@ -3,7 +3,6 @@ library angel_framework.http.request_context;
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:mirrors';
|
import 'dart:mirrors';
|
||||||
import 'package:angel_http_exception/angel_http_exception.dart';
|
|
||||||
import 'package:body_parser/body_parser.dart';
|
import 'package:body_parser/body_parser.dart';
|
||||||
import 'package:charcode/charcode.dart';
|
import 'package:charcode/charcode.dart';
|
||||||
import 'metadata.dart';
|
import 'metadata.dart';
|
||||||
|
@ -214,6 +213,11 @@ class RequestContext {
|
||||||
|
|
||||||
/// Shorthand to add to [_injections].
|
/// Shorthand to add to [_injections].
|
||||||
void inject(type, value) {
|
void inject(type, value) {
|
||||||
|
if (!app.isProduction && type is Type) {
|
||||||
|
if (!reflect(value).type.isAssignableTo(reflectType(type)))
|
||||||
|
throw new ArgumentError('Cannot inject $value (${value.runtimeType}) as an instance of $type.');
|
||||||
|
}
|
||||||
|
|
||||||
_injections[type] = value;
|
_injections[type] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,4 +282,19 @@ class RequestContext {
|
||||||
return _body = await parseBody(io,
|
return _body = await parseBody(io,
|
||||||
storeOriginalBuffer: app.storeOriginalBuffer == true);
|
storeOriginalBuffer: app.storeOriginalBuffer == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disposes of all resources.
|
||||||
|
Future close() async {
|
||||||
|
_body = null;
|
||||||
|
_acceptsAllCache = null;
|
||||||
|
_acceptHeaderCache = null;
|
||||||
|
_io = null;
|
||||||
|
_override = _path = null;
|
||||||
|
_contentType = null;
|
||||||
|
_provisionalQuery?.clear();
|
||||||
|
properties.clear();
|
||||||
|
_injections.clear();
|
||||||
|
serviceParams.clear();
|
||||||
|
params.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ library angel_framework.http.response_context;
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:angel_route/angel_route.dart';
|
import 'package:angel_route/angel_route.dart';
|
||||||
import 'package:json_god/json_god.dart' as god;
|
import 'package:json_god/json_god.dart' as god;
|
||||||
import 'package:mime/mime.dart';
|
import 'package:mime/mime.dart';
|
||||||
|
@ -158,11 +157,24 @@ class ResponseContext implements StreamSink<List<int>>, StringSink {
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disposes of all resources.
|
||||||
|
Future dispose() async {
|
||||||
|
await close();
|
||||||
|
properties.clear();
|
||||||
|
encoders.clear();
|
||||||
|
_buffer.clear();
|
||||||
|
cookies.clear();
|
||||||
|
app = null;
|
||||||
|
_headers.clear();
|
||||||
|
serializer = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Prevents further request handlers from running on the response, except for response finalizers.
|
/// Prevents further request handlers from running on the response, except for response finalizers.
|
||||||
///
|
///
|
||||||
/// To disable response finalizers, see [willCloseItself].
|
/// To disable response finalizers, see [willCloseItself].
|
||||||
void end() {
|
void end() {
|
||||||
_isOpen = false;
|
_isOpen = false;
|
||||||
|
if (_done?.isCompleted == false) _done.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes JSON to the response.
|
/// Serializes JSON to the response.
|
||||||
|
@ -434,14 +446,16 @@ class ResponseContext implements StreamSink<List<int>>, StringSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _LockableBytesBuilder extends BytesBuilder {
|
abstract class _LockableBytesBuilder extends BytesBuilder {
|
||||||
factory _LockableBytesBuilder() => new _LockableBytesBuilderImpl();
|
factory _LockableBytesBuilder() {
|
||||||
|
return new _LockableBytesBuilderImpl();
|
||||||
|
}
|
||||||
|
|
||||||
void _lock();
|
void _lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LockableBytesBuilderImpl implements _LockableBytesBuilder {
|
class _LockableBytesBuilderImpl implements _LockableBytesBuilder {
|
||||||
|
final BytesBuilder _buf = new BytesBuilder(copy: false);
|
||||||
bool _closed = false;
|
bool _closed = false;
|
||||||
Uint8List _data = new Uint8List(0);
|
|
||||||
|
|
||||||
StateError _deny() =>
|
StateError _deny() =>
|
||||||
new StateError('Cannot modified a closed response\'s buffer.');
|
new StateError('Cannot modified a closed response\'s buffer.');
|
||||||
|
@ -455,70 +469,37 @@ class _LockableBytesBuilderImpl implements _LockableBytesBuilder {
|
||||||
void add(List<int> bytes) {
|
void add(List<int> bytes) {
|
||||||
if (_closed)
|
if (_closed)
|
||||||
throw _deny();
|
throw _deny();
|
||||||
else if (bytes.isNotEmpty) {
|
else _buf.add(bytes);
|
||||||
int len = _data.length + bytes.length;
|
|
||||||
var d = new Uint8List(len);
|
|
||||||
|
|
||||||
for (int i = 0; i < _data.length; i++) {
|
|
||||||
d[i] = _data[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < bytes.length; i++) {
|
|
||||||
d[i + _data.length] = bytes[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
_data = d;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void addByte(int byte) {
|
void addByte(int byte) {
|
||||||
if (_closed)
|
if (_closed)
|
||||||
throw _deny();
|
throw _deny();
|
||||||
else {
|
else _buf.addByte(byte);
|
||||||
int len = _data.length + 1;
|
|
||||||
var d = new Uint8List(len);
|
|
||||||
|
|
||||||
for (int i = 0; i < _data.length; i++) {
|
|
||||||
d[i] = _data[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
d[_data.length] = byte;
|
|
||||||
_data = d;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void clear() {
|
void clear() {
|
||||||
if (_closed)
|
_buf.clear();
|
||||||
throw _deny();
|
|
||||||
else {
|
|
||||||
for (int i = 0; i < _data.length; i++) _data[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get isEmpty => _data.isEmpty;
|
bool get isEmpty => _buf.isEmpty;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get isNotEmpty => _data.isNotEmpty;
|
bool get isNotEmpty => _buf.isNotEmpty;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get length => _data.length;
|
int get length => _buf.length;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<int> takeBytes() {
|
List<int> takeBytes() {
|
||||||
if (_closed)
|
return _buf.takeBytes();
|
||||||
return toBytes();
|
|
||||||
else {
|
|
||||||
var r = new Uint8List.fromList(_data);
|
|
||||||
clear();
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<int> toBytes() {
|
List<int> toBytes() {
|
||||||
return _data;
|
return _buf.toBytes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,13 @@ class Routable extends Router {
|
||||||
|
|
||||||
Routable() : super();
|
Routable() : super();
|
||||||
|
|
||||||
|
Future close() async {
|
||||||
|
_services.clear();
|
||||||
|
configuration.clear();
|
||||||
|
requestMiddleware.clear();
|
||||||
|
_onService.close();
|
||||||
|
}
|
||||||
|
|
||||||
/// Additional filters to be run on designated requests.
|
/// Additional filters to be run on designated requests.
|
||||||
@override
|
@override
|
||||||
final Map<String, RequestHandler> requestMiddleware = {};
|
final Map<String, RequestHandler> requestMiddleware = {};
|
||||||
|
|
|
@ -30,11 +30,12 @@ typedef Future AngelConfigurer(Angel app);
|
||||||
/// A powerful real-time/REST/MVC server class.
|
/// A powerful real-time/REST/MVC server class.
|
||||||
class Angel extends AngelBase {
|
class Angel extends AngelBase {
|
||||||
final List<Angel> _children = [];
|
final List<Angel> _children = [];
|
||||||
final Map<String, Tuple3<MiddlewarePipeline, Map, Match>> _handlerCache = {};
|
final Map<String, Tuple3<List, Map, Match>> _handlerCache = {};
|
||||||
|
|
||||||
Router _flattened;
|
Router _flattened;
|
||||||
bool _isProduction;
|
bool _isProduction = false;
|
||||||
Angel _parent;
|
Angel _parent;
|
||||||
|
StreamSubscription<HttpRequest> _sub;
|
||||||
ServerGenerator _serverGenerator = HttpServer.bind;
|
ServerGenerator _serverGenerator = HttpServer.bind;
|
||||||
|
|
||||||
/// A global Map of converters that can transform responses bodies.
|
/// A global Map of converters that can transform responses bodies.
|
||||||
|
@ -74,10 +75,7 @@ class Angel extends AngelBase {
|
||||||
/// This value is memoized the first time you call it, so do not change environment
|
/// This value is memoized the first time you call it, so do not change environment
|
||||||
/// configuration at runtime!
|
/// configuration at runtime!
|
||||||
bool get isProduction {
|
bool get isProduction {
|
||||||
if (_isProduction != null)
|
return _isProduction ??= (Platform.environment['ANGEL_ENV'] == 'production');
|
||||||
return _isProduction;
|
|
||||||
else
|
|
||||||
return _isProduction = Platform.environment['ANGEL_ENV'] == 'production';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The function used to bind this instance to an HTTP server.
|
/// The function used to bind this instance to an HTTP server.
|
||||||
|
@ -158,7 +156,8 @@ class Angel extends AngelBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
optimizeForProduction();
|
optimizeForProduction();
|
||||||
return httpServer..listen(handleRequest);
|
_sub = httpServer.listen(handleRequest);
|
||||||
|
return httpServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -196,9 +195,13 @@ class Angel extends AngelBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shuts down the server, and closes any open [StreamController]s.
|
/// Shuts down the server, and closes any open [StreamController]s.
|
||||||
|
///
|
||||||
|
/// The server will be **COMPLETE DEFUNCT** after this operation!
|
||||||
Future<HttpServer> close() async {
|
Future<HttpServer> close() async {
|
||||||
HttpServer server;
|
HttpServer server;
|
||||||
|
|
||||||
|
_sub?.cancel();
|
||||||
|
|
||||||
if (httpServer != null) {
|
if (httpServer != null) {
|
||||||
server = httpServer;
|
server = httpServer;
|
||||||
await httpServer.close(force: true);
|
await httpServer.close(force: true);
|
||||||
|
@ -210,6 +213,20 @@ class Angel extends AngelBase {
|
||||||
|
|
||||||
for (var plugin in shutdownHooks) await plugin(this);
|
for (var plugin in shutdownHooks) await plugin(this);
|
||||||
|
|
||||||
|
await super.close();
|
||||||
|
_preContained.clear();
|
||||||
|
_handlerCache.clear();
|
||||||
|
_injections.clear();
|
||||||
|
encoders.clear();
|
||||||
|
_serializer = god.serialize;
|
||||||
|
_children.clear();
|
||||||
|
_parent = null;
|
||||||
|
logger = null;
|
||||||
|
startupHooks.clear();
|
||||||
|
shutdownHooks.clear();
|
||||||
|
responseFinalizers.clear();
|
||||||
|
_flattened = null;
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,9 +308,12 @@ class Angel extends AngelBase {
|
||||||
/// Runs some [handler]. Returns `true` if request execution should continue.
|
/// Runs some [handler]. Returns `true` if request execution should continue.
|
||||||
Future<bool> executeHandler(
|
Future<bool> executeHandler(
|
||||||
handler, RequestContext req, ResponseContext res) async {
|
handler, RequestContext req, ResponseContext res) async {
|
||||||
|
if (handler == null) return false;
|
||||||
var result = await getHandlerResult(handler, req, res);
|
var result = await getHandlerResult(handler, req, res);
|
||||||
|
|
||||||
if (result is bool) {
|
if (result == null)
|
||||||
|
return false;
|
||||||
|
else if (result is bool) {
|
||||||
return result;
|
return result;
|
||||||
} else if (result != null) {
|
} else if (result != null) {
|
||||||
res.serialize(result,
|
res.serialize(result,
|
||||||
|
@ -384,30 +404,36 @@ class Angel extends AngelBase {
|
||||||
|
|
||||||
if (requestedUrl.isEmpty) requestedUrl = '/';
|
if (requestedUrl.isEmpty) requestedUrl = '/';
|
||||||
|
|
||||||
var tuple = _handlerCache.putIfAbsent('${req.method}:$requestedUrl', () {
|
Tuple3<List, Map, Match> resolveTuple() {
|
||||||
Router r = isProduction ? (_flattened ??= flatten(this)) : this;
|
Router r = isProduction ? (_flattened ??= flatten(this)) : this;
|
||||||
var resolved =
|
var resolved =
|
||||||
r.resolveAll(requestedUrl, requestedUrl, method: req.method);
|
r.resolveAll(requestedUrl, requestedUrl, method: req.method);
|
||||||
|
|
||||||
return new Tuple3(
|
return new Tuple3(
|
||||||
new MiddlewarePipeline(resolved),
|
new MiddlewarePipeline(resolved).handlers,
|
||||||
resolved.fold<Map>({}, (out, r) => out..addAll(r.allParams)),
|
resolved.fold<Map>({}, (out, r) => out..addAll(r.allParams)),
|
||||||
resolved.isEmpty ? null : resolved.first.route.match(requestedUrl),
|
resolved.isEmpty ? null : resolved.first.route.match(requestedUrl),
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
var tuple = isProduction
|
||||||
|
? _handlerCache.putIfAbsent(
|
||||||
|
'${req.method}:$requestedUrl', resolveTuple)
|
||||||
|
: resolveTuple();
|
||||||
|
|
||||||
req.inject(Zone, zone);
|
req.inject(Zone, zone);
|
||||||
req.inject(ZoneSpecification, zoneSpec);
|
req.inject(ZoneSpecification, zoneSpec);
|
||||||
req.inject(MiddlewarePipeline, tuple.item1);
|
|
||||||
req.params.addAll(tuple.item2);
|
req.params.addAll(tuple.item2);
|
||||||
req.inject(Match, tuple.item3);
|
req.inject(Match, tuple.item3);
|
||||||
req.inject(Stopwatch, new Stopwatch()..start());
|
|
||||||
|
|
||||||
var pipeline = tuple.item1.handlers;
|
if (logger != null) req.inject(Stopwatch, new Stopwatch()..start());
|
||||||
|
|
||||||
|
var pipeline = tuple.item1;
|
||||||
|
|
||||||
for (var handler in pipeline) {
|
for (var handler in pipeline) {
|
||||||
try {
|
try {
|
||||||
if (!await executeHandler(handler, req, res)) break;
|
if (handler == null || !await executeHandler(handler, req, res))
|
||||||
|
break;
|
||||||
} on AngelHttpException catch (e, st) {
|
} on AngelHttpException catch (e, st) {
|
||||||
e.stackTrace ??= st;
|
e.stackTrace ??= st;
|
||||||
return await handleAngelHttpException(e, st, req, res, request);
|
return await handleAngelHttpException(e, st, req, res, request);
|
||||||
|
@ -436,6 +462,11 @@ class Angel extends AngelBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleAngelHttpException(e, stackTrace, req, res, request);
|
return handleAngelHttpException(e, stackTrace, req, res, request);
|
||||||
|
}).whenComplete(() {
|
||||||
|
scheduleMicrotask(() {
|
||||||
|
req.close();
|
||||||
|
res.dispose();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
155
lib/src/stats/metric_server.dart
Normal file
155
lib/src/stats/metric_server.dart
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import '../http/http.dart';
|
||||||
|
import 'stats.dart';
|
||||||
|
|
||||||
|
/// A variant of an [Angel] server that records performance metrics.
|
||||||
|
class AngelMetrics extends Angel {
|
||||||
|
Angel _inner;
|
||||||
|
HttpServer _server;
|
||||||
|
StreamSubscription<HttpRequest> _sub;
|
||||||
|
|
||||||
|
AngelMetrics() : super() {
|
||||||
|
var zoneBuilder = createZoneForRequest;
|
||||||
|
createZoneForRequest = (request, req, res) async {
|
||||||
|
var spec = await zoneBuilder(request, req, res);
|
||||||
|
return new ZoneSpecification.from(
|
||||||
|
spec,
|
||||||
|
run: (Zone self, ZoneDelegate parent, Zone zone, f()) {
|
||||||
|
var sw = new Stopwatch();
|
||||||
|
//print('--- ${req.method} ${req.uri}: $f');
|
||||||
|
sw.start();
|
||||||
|
|
||||||
|
void whenDone() {
|
||||||
|
sw.stop();
|
||||||
|
var ms = sw.elapsedMilliseconds;
|
||||||
|
parent.print(
|
||||||
|
zone, '--- ${req.method} ${req.uri} DONE after ${ms}ms: $f');
|
||||||
|
}
|
||||||
|
|
||||||
|
var r = parent.run(zone, f);
|
||||||
|
|
||||||
|
if (r is Future) {
|
||||||
|
return r.then((x) {
|
||||||
|
whenDone();
|
||||||
|
return x;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
whenDone();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
logger = new Logger('angel_metrics')
|
||||||
|
..onRecord.listen((rec) {
|
||||||
|
print(rec);
|
||||||
|
|
||||||
|
if (rec.error != null) {
|
||||||
|
print(rec.error);
|
||||||
|
print(rec.stackTrace);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
factory AngelMetrics.custom(ServerGenerator serverGenerator) {
|
||||||
|
return new AngelMetrics().._inner = new Angel.custom(serverGenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
HttpServer get httpServer => _server ?? super.httpServer;
|
||||||
|
|
||||||
|
final AngelMetricsStats stats = new AngelMetricsStats._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<HttpServer> startServer([address, int port]) async {
|
||||||
|
if (_inner == null) return await super.startServer(address, port);
|
||||||
|
|
||||||
|
var host = address ?? InternetAddress.LOOPBACK_IP_V4;
|
||||||
|
_server = await _inner.serverGenerator(host, port ?? 0);
|
||||||
|
|
||||||
|
for (var configurer in startupHooks) {
|
||||||
|
await configure(configurer);
|
||||||
|
}
|
||||||
|
|
||||||
|
optimizeForProduction();
|
||||||
|
_sub = _server.listen(handleRequest);
|
||||||
|
return _server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<HttpServer> close() async {
|
||||||
|
_sub?.cancel();
|
||||||
|
await _inner.close();
|
||||||
|
return await super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<RequestContext> createRequestContext(HttpRequest request) {
|
||||||
|
return stats.createRequestContext
|
||||||
|
.run<RequestContext>(() => super.createRequestContext(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ResponseContext> createResponseContext(HttpResponse response,
|
||||||
|
[RequestContext correspondingRequest]) {
|
||||||
|
return stats.createResponseContext.run<ResponseContext>(
|
||||||
|
() => super.createResponseContext(response, correspondingRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future handleRequest(HttpRequest request) {
|
||||||
|
return stats.handleRequest.run(() => super.handleRequest(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> executeHandler(
|
||||||
|
handler, RequestContext req, ResponseContext res) {
|
||||||
|
return stats.executeHandler
|
||||||
|
.run<bool>(() => super.executeHandler(handler, req, res));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future getHandlerResult(handler, RequestContext req, ResponseContext res) {
|
||||||
|
return stats.getHandlerResult
|
||||||
|
.run(() => super.getHandlerResult(handler, req, res));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future runContained(
|
||||||
|
Function handler, RequestContext req, ResponseContext res) {
|
||||||
|
return stats.runContained.run(() => super.runContained(handler, req, res));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future sendResponse(
|
||||||
|
HttpRequest request, RequestContext req, ResponseContext res,
|
||||||
|
{bool ignoreFinalizers: false}) {
|
||||||
|
return stats.sendResponse.run(() => super.sendResponse(request, req, res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AngelMetricsStats {
|
||||||
|
AngelMetricsStats._() {
|
||||||
|
all = [
|
||||||
|
createRequestContext,
|
||||||
|
createResponseContext,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
final Stats createRequestContext = new Stats('createRequestContext');
|
||||||
|
final Stats createResponseContext = new Stats('createResponseContext');
|
||||||
|
final Stats handleRequest = new Stats('handleRequest');
|
||||||
|
final Stats executeHandler = new Stats('executeHandler');
|
||||||
|
final Stats getHandlerResult = new Stats('getHandlerResult');
|
||||||
|
final Stats runContained = new Stats('runContained');
|
||||||
|
final Stats sendResponse = new Stats('sendResponse');
|
||||||
|
|
||||||
|
List<Stats> all;
|
||||||
|
|
||||||
|
void log() {
|
||||||
|
all.forEach((s) => s.log());
|
||||||
|
}
|
||||||
|
}
|
49
lib/src/stats/stats.dart
Normal file
49
lib/src/stats/stats.dart
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/// Computes averages progressively.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
class Stats {
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
int _total = 0, _count = 0;
|
||||||
|
double _average = 0.0;
|
||||||
|
|
||||||
|
Stats(this.name);
|
||||||
|
|
||||||
|
double get average => _average ?? (_total / _count);
|
||||||
|
|
||||||
|
void log() {
|
||||||
|
print('$name: $average avg.');
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(int value) {
|
||||||
|
_average = null;
|
||||||
|
_total += value;
|
||||||
|
_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<T> run<T>(FutureOr<T> f()) {
|
||||||
|
var sw = new Stopwatch();
|
||||||
|
//print('--- $name START');
|
||||||
|
sw.start();
|
||||||
|
|
||||||
|
void whenDone() {
|
||||||
|
sw.stop();
|
||||||
|
var ms = sw.elapsedMilliseconds;
|
||||||
|
add(ms);
|
||||||
|
print('--- $name DONE after ${ms}ms');
|
||||||
|
}
|
||||||
|
|
||||||
|
var r = f();
|
||||||
|
|
||||||
|
if (r is Future) {
|
||||||
|
return (r as Future).then((x) {
|
||||||
|
whenDone();
|
||||||
|
return x;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
whenDone();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,24 +4,20 @@ library performance.hello;
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:angel_framework/metrics.dart';
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
for (int i = 0; i < Platform.numberOfProcessors - 1; i++)
|
for (int i = 0; i < Platform.numberOfProcessors - 1; i++) {
|
||||||
Isolate.spawn(start, i + 1);
|
Isolate.spawn(start, i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
start(0);
|
start(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void start(int id) {
|
void start(int id) {
|
||||||
var app = new Angel.custom(startShared)
|
var app = new AngelMetrics.custom(startShared)
|
||||||
..lazyParseBodies = true
|
..lazyParseBodies = true
|
||||||
..get('/', (req, res) => res.write('Hello, world!'))
|
..get('/', (req, res) => res.write('Hello, world!'));
|
||||||
..optimizeForProduction(force: true)
|
|
||||||
..logger = (new Logger('streaming_test')
|
|
||||||
..onRecord.listen((rec) {
|
|
||||||
print(rec);
|
|
||||||
if (rec.stackTrace != null) print(rec.stackTrace);
|
|
||||||
}));
|
|
||||||
|
|
||||||
var oldHandler = app.errorHandler;
|
var oldHandler = app.errorHandler;
|
||||||
app.errorHandler = (e, req, res) {
|
app.errorHandler = (e, req, res) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ description: A high-powered HTTP server with DI, routing and more.
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/angel_framework
|
homepage: https://github.com/angel-dart/angel_framework
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=1.19.0"
|
sdk: ">=1.19.0 <2.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
angel_http_exception: ^1.0.0
|
angel_http_exception: ^1.0.0
|
||||||
angel_model: ^1.0.0
|
angel_model: ^1.0.0
|
||||||
|
|
Loading…
Reference in a new issue