1
0
mirror of https://github.com/yldio/copilot.git synced 2024-12-29 05:10:05 +02:00

metrics-service spike

This commit is contained in:
Sérgio Ramos 2016-11-04 17:09:58 +00:00
parent 2945190dbb
commit 3c1780342d
36 changed files with 727 additions and 0 deletions

View File

@ -0,0 +1,3 @@
/node_modules
/npm-debug.log
.idea

View File

@ -0,0 +1,16 @@
{
"name": "graphql",
"version": "1.0.0",
"scripts": {
"start": "node src/index.js"
},
"main": "src/index.js",
"private": true,
"dependencies": {
"graphql": "^0.7.2",
"hapi": "^15.2.0",
"hapi-graphql": "^1.0.1",
"inert": "^4.0.2",
"require-dir": "^0.3.1"
}
}

View File

@ -0,0 +1,29 @@
const requireDir = require('require-dir');
const plugins = require('./plugins');
const routes = requireDir('./routes');
const Hapi = require('hapi');
const path = require('path');
const fs = require('fs');
const server = new Hapi.Server();
server.connection({
host: 'localhost',
port: 8000
});
server.register(plugins, (err) => {
if (err) {
throw err;
}
Object.keys(routes).forEach((name) => {
routes[name](server);
});
server.start((err) => {
server.connections.forEach((conn) => {
console.log(`started at: ${conn.info.uri}`);
});
});
});

View File

@ -0,0 +1,15 @@
module.exports = [
require('inert'), {
register: require('hapi-graphql'),
options: {
query: {
pretty: true,
graphiql: true,
schema: require('./schema')
},
route: {
path: '/graphql'
}
}
}
];

View File

@ -0,0 +1,11 @@
const path = require('path');
module.exports = (server) => {
server.route({
method: 'GET',
path: '/',
handler: (request, reply) => {
reply.file(path.join(__dirname, 'index.html'));
}
});
};

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="root"></div>
<script>
const client = fetch('http://localhost:8000/graphql', {
method: 'POST',
body: `
subscription {
events(container:"1") {
value,
when
}
}
`
});
client.then((client) => {
debugger;
});
</script>
</body>
</html>

View File

@ -0,0 +1,18 @@
const Pkg = require('../../package.json');
const internals = {
response: {
version: Pkg.version
}
};
module.exports = (server) => {
server.route({
method: 'GET',
path: '/ops/version',
config: {
description: 'Returns the version of the server',
handler: (request, reply) => reply(internals.response)
}
});
};

View File

@ -0,0 +1,61 @@
const graphql = require('graphql');
const {
GraphQLID,
GraphQLSchema,
GraphQLObjectType,
GraphQLString
} = graphql;
const EventType = new GraphQLObjectType({
name: 'EventType',
fields: {
value: {
type: GraphQLString
},
when: {
type: GraphQLString
}
}
});
const EventSubscription = {
type: EventType,
args: {
container: {
type: GraphQLID
}
},
start: function() {
console.log('start', arguments);
},
stop: function() {
console.log('stop', arguments);
},
resolve: function() {
console.log('resolve', arguments);
}
};
const subscription = new GraphQLObjectType({
name: 'RootSubscriptionType',
fields: {
events: EventSubscription
}
});
module.exports = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Q',
fields: {
a: { type: GraphQLString },
}
}),
mutation: new GraphQLObjectType({
name: 'M',
fields: {
c: { type: GraphQLString },
}
}),
subscription
});

View File

@ -0,0 +1,3 @@
/node_modules
/npm-debug.log
.idea

View File

@ -0,0 +1,14 @@
{
"name": "http1",
"version": "1.0.0",
"scripts": {
"start": "node src/index.js"
},
"main": "src/index.js",
"private": true,
"dependencies": {
"hapi": "^15.2.0",
"inert": "^4.0.2",
"require-dir": "^0.3.1"
}
}

View File

@ -0,0 +1,29 @@
const requireDir = require('require-dir');
const plugins = require('./plugins');
const routes = requireDir('./routes');
const Hapi = require('hapi');
const path = require('path');
const fs = require('fs');
const server = new Hapi.Server();
server.connection({
host: 'localhost',
port: 8000
});
Object.keys(routes).forEach((name) => {
routes[name](server);
});
server.register(plugins, (err) => {
if (err) {
throw err;
}
server.start((err) => {
server.connections.forEach((conn) => {
console.log(`started at: ${conn.info.uri}`);
});
});
});

View File

@ -0,0 +1,3 @@
module.exports = [
require('inert')
];

View File

@ -0,0 +1,11 @@
const path = require('path');
module.exports = (server) => {
server.route({
method: 'GET',
path: '/',
handler: (request, reply) => {
reply.file(path.join(__dirname, 'index.html'));
}
});
};

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
var source = new EventSource(`${document.location.origin}/stats`);
source.onmessage = function(e) {
document.body.innerHTML += "Message: " + e.data + '<br />';
};
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,21 @@
let messageId = 0;
module.exports = (server) => {
server.route({
method: 'GET',
path: '/stats',
handler: (request, reply) => {
request.raw.res.setHeader('Content-Type', 'text/event-stream');
const intervalId = setInterval(() => {
messageId += 1;
const str = JSON.stringify({
msg: messageId
});
request.raw.res.write(`data:${str}\n\n`);
}, 100);
}
});
};

View File

@ -0,0 +1,18 @@
const Pkg = require('../../package.json');
const internals = {
response: {
version: Pkg.version
}
};
module.exports = (server) => {
server.route({
method: 'GET',
path: '/ops/version',
config: {
description: 'Returns the version of the server',
handler: (request, reply) => reply(internals.response)
}
});
};

View File

@ -0,0 +1,3 @@
/node_modules
/npm-debug.log
.idea

View File

@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFtTCCA52gAwIBAgIJAK4ScT3ylJVTMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTYxMTAzMTQyNDIyWhcNMTcxMTAzMTQyNDIyWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEA6gusYDdyFH9e6iNRT7epiZrGh7R9nwD930d1UM37cZiF5/6oKKY2rP/N
pe9qnhC+k1LV2ItEZieEun1k4c+ayFSknWtbC+2tKaX5iNVIeqTKP+f0sG+FRpZk
VMPZF4eAKze+BjFZDPoFFkvhKC9MOxEyleSR8cCzSmyhVlrgBTRWhhcSMn2BPh41
lnUplVP/43lHUWfgs3Et5EDJihzHDjfw0jo2AZ5KtqlYxQSAMt9Z5UsvYQSRUKvD
U7YUqHMUGo5F9wZCrcYyn/KbViiiIygX0A6uHU+0ajUyf2EnR1iJsdZ5oLgG6jyk
gCwQcNstXi4JVvthXi5HPmHs+i5H1aAOnMy0t21ssARYiZUq3rSmJ2mPXzJw8SYC
SLxCEo3WxBXTDOzzne7kzNl6TC0IHTfGDa80wIa3qitK7PaLQaVrGaFy2ITwt76B
414+txdM1JINW4/P1S9jv59zkwYKaoFVGdCvnt33XtAlj7C6sManZLzgxvtHhj9i
q//GjWswea2hPTezWcpzxJRVXDzVfJGu/lfoGTnZXSVRSQ5EbKQYbVRL3zI5x0hj
MPx+w8vDV88qJgywbBZkRwLaunjAwwPdpuLUwsObc5WuwOMobe+G3ycYqHswQpjc
6Mw1158nO4Kdx8DJUq7qyRDk3g7kkvp75cbF1xU4fxNzB3VJD6sCAwEAAaOBpzCB
pDAdBgNVHQ4EFgQUyt+QUohMXTlCx4nOaozvYR3wbSowdQYDVR0jBG4wbIAUyt+Q
UohMXTlCx4nOaozvYR3wbSqhSaRHMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpT
b21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGSCCQCu
EnE98pSVUzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQBAZ4Wvcnoi
dAZ3HMBODZTw9E6NZLKgl4aVQiktkCOBOwzJKxkzCrrrr+YQXYx4rHPi85FBevt/
NkSsm/Ux+am5iNLjMOW3kDIt1VYYQ6Wn4iPEPUeWzCeyHCOfbG3gOsjDWC1GmWaY
WIBi3Zh+7txvrzyhRdNwoS+pAZOzbljCisSDmt1an48tpojI/ZqLD+oGg0LEMuik
sBWl227KxyW9DEwLcK9K3I0zhR3NKqWYt47XYvVDq+CqiVI7+/RMwnX/gtIjjGGS
5gLdD7mS+Fjig0LtCasDqKYraNQfTAyW3afYITeU4QAKukpJXwwGil3SEP2PxG/y
GBz2x6hgQZHB+8VzeA4Zxu5SmLOetJ3yxHRi8NWbiaIn/J+wdRLMjXZ0jTvv86mE
VB9X9e0ltvCH/o/VrCOVWHuUXP0zNRawQqYB1qRZ7I7Z/Aes0TQTcQOD/RYUnsfK
Mk9XhEpp9IQerufd4e0wlUq6BBA2sN6mkgy57Zsix+LAjU15z0hDRE2xWWNmbakY
VBgUTQ8KObAMaSbzUwTildtbsDhtI6pYr8y096NjFiWqkon6R8dveZBfvv5vwoxc
h2HTw85eGHyBDcWUe7c7+D+Tp7feNVOeeWtvF7LSRnmTlzqoHdMQ5ns9DxaoUu1Y
kbIEE8WnLU0m72Vq5Jzvode8VP3tZpxyvw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,17 @@
{
"name": "http2",
"version": "1.0.0",
"scripts": {
"start": "node src/index.js"
},
"main": "src/index.js",
"private": true,
"dependencies": {
"bunyan": "^1.8.4",
"hapi": "^15.2.0",
"http2": "^3.3.6",
"inert": "^4.0.2",
"require-dir": "^0.3.1",
"string-to-stream": "^1.1.0"
}
}

View File

@ -0,0 +1,40 @@
const http2 = require('http2');
const requireDir = require('require-dir');
const plugins = require('./plugins');
const routes = requireDir('./routes');
const Hapi = require('hapi');
const path = require('path');
const fs = require('fs');
const server = new Hapi.Server();
server.connection({
listener: http2.createServer({
key: fs.readFileSync(path.join(__dirname, '../key.pem')),
cert: fs.readFileSync(path.join(__dirname, '../cert.pem')),
log: require('bunyan').createLogger({
name: 'server',
stream: process.stdout,
serializers: require('http2/lib/http').serializers
})
}),
host: 'localhost',
port: 8000,
tls: true
});
Object.keys(routes).forEach((name) => {
routes[name](server);
});
server.register(plugins, (err) => {
if (err) {
throw err;
}
server.start((err) => {
server.connections.forEach((conn) => {
console.log(`started at: ${conn.info.uri}`);
});
});
});

View File

@ -0,0 +1,3 @@
module.exports = [
require('inert')
];

View File

@ -0,0 +1,11 @@
const path = require('path');
module.exports = (server) => {
server.route({
method: 'GET',
path: '/',
handler: (request, reply) => {
reply.file(path.join(__dirname, 'index.html'));
}
});
};

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
var source = new EventSource(`${document.location.origin}/stats`);
source.onmessage = function(e) {
document.body.innerHTML += "SSE notification: " + e.data + '<br />';
// fetch resource via XHR... from cache!
var xhr = new XMLHttpRequest();
xhr.open('GET', e.data);
xhr.onload = function() {
document.body.innerHTML += "Message: " + this.response + '<br />';
};
xhr.send();
};
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,38 @@
const str = require('string-to-stream');
let messageId = 0;
module.exports = (server) => {
server.route({
method: 'GET',
path: '/stats',
handler: (request, reply) => {
request.raw.res.setHeader('Content-Type', 'text/event-stream');
const intervalId = setInterval(() => {
const resourcePath = `/resource/${messageId}`;
if (!request.raw.res.push) {
clearInterval(intervalId);
return;
}
const stream = request.raw.res.push(resourcePath, {
status: 200,
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
str(JSON.stringify({
msg: messageId
})).pipe(stream);
request.raw.res.write(`data:${resourcePath}\n\n`);
messageId += 1;
}, 100);
}
});
};

View File

@ -0,0 +1,18 @@
const Pkg = require('../../package.json');
const internals = {
response: {
version: Pkg.version
}
};
module.exports = (server) => {
server.route({
method: 'GET',
path: '/ops/version',
config: {
description: 'Returns the version of the server',
handler: (request, reply) => reply(internals.response)
}
});
};

View File

@ -0,0 +1,30 @@
# metrics-service
> A service which produces random streams of data (firehose) of interpretable metric information for various subsystems that can or will be pulled out from a container. These are currently defined in [RFD-0037](https://github.com/joyent/rfd/blob/master/rfd/0037/README.md#default-metric-keys).
This spike's purpose was to find the best pattern to expose such metrics service with mock data.
## http1
+ very simple API to push data from the server to the client
+ plain HTTP
- requires a connection to each container - because it's not bidirectional, we can't subscribe to more containers on-demand
## http2
+ very simple API to push data from the server to the client
+ multiplexing
- push doesn't really help us, because it's only useful for static resources
## graphql
+ common interface between all data resources
+ filter received data
- api not finalised yet
- api not documented
## ws (with [nes](https://github.com/hapijs/nes))
+ strong integration with [Hapi](https://github.com/hapijs/hapi)
+ allow to subscribe and unsubscribe on-demand on a single connection
- some networks might require fallback to pooling

3
spikes/metrics-service/ws/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/node_modules
/npm-debug.log
.idea

View File

@ -0,0 +1,16 @@
{
"name": "ws",
"version": "1.0.0",
"scripts": {
"start": "node src/index.js"
},
"main": "src/index.js",
"private": true,
"dependencies": {
"component-emitter": "^1.2.1",
"hapi": "^15.2.0",
"inert": "^4.0.2",
"nes": "^6.3.1",
"require-dir": "^0.3.1"
}
}

View File

@ -0,0 +1,29 @@
const requireDir = require('require-dir');
const plugins = require('./plugins');
const routes = requireDir('./routes');
const Hapi = require('hapi');
const path = require('path');
const fs = require('fs');
const server = new Hapi.Server();
server.connection({
host: 'localhost',
port: 8000
});
server.register(plugins, (err) => {
if (err) {
throw err;
}
Object.keys(routes).forEach((name) => {
routes[name](server);
});
server.start((err) => {
server.connections.forEach((conn) => {
console.log(`started at: ${conn.info.uri}`);
});
});
});

View File

@ -0,0 +1,30 @@
const Emitter = require('component-emitter');
const cdm = {};
module.exports = (server) => ({
on: (id) => {
if (cdm[id]) {
cdm[id].sockets +=1;
return;
}
let messageId = 0;
const interval = setInterval(() => {
server.publish(`/stats/${id}`, {
id: messageId += 1
});
}, 500);
cdm[id] = {
interval,
sockets: 1
};
},
off: (id) => {
if (!(cdm[id].sockets -= 1)) {
clearInterval(cdm[id].interval);
}
}
});

View File

@ -0,0 +1,4 @@
module.exports = [
require('inert'),
require('nes')
];

View File

@ -0,0 +1,11 @@
const path = require('path');
module.exports = (server) => {
server.route({
method: 'GET',
path: '/',
handler: (request, reply) => {
reply.file(path.join(__dirname, 'index.html'));
}
});
};

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/nes@6.3.1"></script>
</head>
<body>
<div id="root"></div>
<script>
const client = new window.nes.Client('ws://localhost:8000');
client.connect((err) => {
if (err) {
throw err;
}
console.log('connected');
client.subscribe('/stats/5', (update, flag) => {
console.log(update, flag);
}, (err) => {
if (err) {
throw err;
}
console.log('subscribed');
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,18 @@
const Metric = require('../metric');
module.exports = (server) => {
const metric = Metric(server);
server.subscription('/stats/{id}', {
onSubscribe: (socket, path, params, next) => {
console.log('onSubscribe');
metric.on(params.id);
next();
},
onUnsubscribe: (socket, path, params, next) => {
console.log('onUnsubscribe');
metric.off(params.id);
next();
}
});
};

View File

@ -0,0 +1,18 @@
const Pkg = require('../../package.json');
const internals = {
response: {
version: Pkg.version
}
};
module.exports = (server) => {
server.route({
method: 'GET',
path: '/ops/version',
config: {
description: 'Returns the version of the server',
handler: (request, reply) => reply(internals.response)
}
});
};