I wonder if Ktor will be implementing <micrometer....
# ktor
d
I wonder if Ktor will be implementing micrometer.io metrics, vert.x seemed to do it just for the popularity of Prometheus on Kubernetes https://vertx.io/blog/eclipse-vert-x-metrics-now-with-micrometer-io/
👍 2
d
So your suggestion here would be to switch from dropwizard metrics in the Ktor metrics feature to the micrometer one? So you have more flexibility to select which metrics framework to use?
d
Yes, I think it's very important in microservices, and dropwizard is limiting... also I think it should be possible to access the instrumentation framework for custom application instrumentation, is that currently possible?
d
Okay to the first part. Maybe you can create an issue in github, ideally a proposal PR actually doing it. For the second part. What do you want to do? From the ktor perspective, you can use anything from the JVM. Ktor itself is decoupled so you can intercept any part and do the source level instrumentation you would need. But maybe you are referring to something else. So any example would be appreciated here to have some context.
d
For the second thing, how would I reuse the JMXReporter in my own code? Save a global variable of it, use DI, or does the application object expose a common interface for it?
Say I need a gauge for some specialized task in my controller..
d
as far as I remember you provided the reporter, so you should have the instance (but maybe I don’t remember well)
for the application call there is a
attributes
field that serves as a small instance container, though it is per call/request, I have to check if there is one for the application itself
d
I almost finished a basic implementation that does almost the same as the current one using micrometer, but I have a problem with how to expose a metrics port/route (or should I leave the user of the feature to do it...)? See configuration: http://micrometer.io/docs/registry/prometheus
d
I guess would be nice to optionally expose the url, what’s the problem?
d
Should I do that from the plugin? Should I just provide a pre-configured route to put into
routing
? Also, in my case, I prefer having a seperate port to expose internally for the purpose... that would mean creating another embeddedServer just for that? Or should I be using what it says in the configuration system over there? I know there was a discussion about one Ktor instance listening to two ports.. and it wasn't so simple...
d
let me try using two http ports
doesn’t it work for you?
or do you mean using the application.conf?
still I guess you can use the same port and limit by interface or ip, or protect using authentication or something
you can for example allow to provide a callback giving a callback to register the route so you can wrap it with the authentication
d
I mean dynamically when the Feature is installed... that would be the ideal... I guess the user would still need to have the multiple connector config for this to work though...
No need for authentication if the metrics scraper is running internally in a Kubernetes or Swarm cluster...
d
maybe some people will want to use without docker and without several ports
but still, in the link I put, there is a sample on how to check the port that was used
so maybe you can put in the configuration:
availableOnPort: Int? = null
if null, no checks are done, and with the port, it checks that the request is from that port, if it is not the case, you can return a 404
d
But how could that be linked to the user's
routing
?
Is there a routing interceptor for features or something?
d
I guess you can just use
routing
from an application instance
as far as I know there is a singleton
Routing
instance per application
and then
Route
instance nodes
the shutdown feature “registers” a route for shutdown
but maybe it don’t even use the routing feature
because you can just create a plain interceptor
d
It's a pity there's no shortcut
embeddedServer
to provide a list of ports, and a
port(...)
function in routing...
d
well, why can’t you just call the embeddedServer and create an application for just this?
maybe you can even split it in two features collaborating
to make the basic one simpler
d
I think it's a common use-case to have external/internal ports open with only one behind a proxy to the outside world... Actually I don't even see how the routing will distinguish between the ports... it looks like either one will resolve to all urls... I was thinking of seperating into multiple features like in ContentDisposition, since each backend has it's own deps and initialization... My current use case is Prometheus, so I'm working on the base and this flavor at the same time now...
Copy code
if (call.request.local.port == 8080) {
local.port is the port bound
in the link I sent you it shows an example to configure two connectors, one with a public exposed bind, and other with a private one and differentiate them
d
Oh... I guess I could clean that up with an extension function... (for my other use case...)
d
but still, what prevents you to call embeddedServer in your feature?
registering a new port, getting a totally different Application instante, and configuring a route there?
d
How much overhead would it be?
d
don’t know but once running should be negligible right?
d
Also it needs to have one installed Feature instance shared in all the embeddedServers needing it...
d
do you mean memory overhead?
do you have several embeddedServer/applications on the same JVM?
and using docker?
d
If running 20 instances w/ k8s or more... memory or other resources come to be meted...
d
well but they are different JVMs right?
different docker instances
d
On same JVM, internal, external + metrics * 20 or more...
Each instance another operating system... w/ another JVM
d
well if you have several embeddedServers in the same machine, maybe you can use a global static object, to keep track and just instantiate one embeddedServer
but that’s a bit strange
it is usually to have a single application with its own JVM for each docker instance
still you know best your use-cases
the options I see are those
d
Yes there is one JVM for each docker instance, w/ one embeddedServer for each port being exposed... So the option is just to let the user use the multiple-connector setup and see what is done in ShutDownUrl and check the port for the matching one declared in the configs. Ok I guess I'll try it that way, thanks!
👍 1
d
And the other option is to call the embeddedServer. About the overhead of that. You can check by yourself, but my guess is that it wouldn’t be too much in terms of cpu. In terms of memory a bit more, but maybe not too much either. Where too much depends on your case 🙂 don’t have exact numbers
d
I guess that's what the metrics are for 😉
😛 1
Just a little detail, I'm not too clear what the difference is between the
pipeline.environment.monitor
(
Routing.RoutingCallStarted
,
Routing.RoutingCallFinished
and
pipeline.insertPhaseBefore(ApplicationCallPipeline.Infrastructure, phase)
intercepted callbacks. I know the latter receives the call and the former has the routes, but in terms of timing is there any difference? Also routes can't be retrieved from the former? The current implementation has both and marks events from both...
d
I guess with the phase, there could be additional interceptors before/after I guess
d
I'm getting funny paths...
/(authenticate \"default\")/apk-manager/apk/(method:GET)
, I'd like to seperate the method into a seperate tag and not have the authenticate there... will I have to parse this with a regex or have to go through all the the different route parts to compose my own urls (instead of toString)?
d
I would duplicate the toString (as an extension method) and adjust for your case
d
Copy code
override fun toString() = when {
        parent == null -> "/"
        parent.parent == null -> "/$selector"
        else -> "$parent/$selector"
    }
Is the current one, it wouldn't be so bad if
parent
was just the path... but there's (authenticate:... there...
d
Probably because Authenticate is a Route node and it just dumps the tree
maybe you can check in your method that it is not a Authentication node
d
It looks like it's calling retroactively each parent's toString... it is in authentication node..
I wonder if I could get a list of all the path nodes somehow
then I guess I could filter them out by type
d
Well, it is a node tree, you can do it
d
I'm a bit unclear about the difference between
parent
and
selector
...
It seems like there's tons of data classes deriving from selector, so that's what I need to test... but the node tree is a Route...
Btw this is my code so far (it works, but needs a few adjustments configs and builders..): https://gist.github.com/dave08/7df0ca9c4d8ce38571fe2d3d5fb4c347
👍 1
d
Well parent is just the parent node in the Route tree, the selector is an associated class to the Route node, that is in charge of selecting if the route matches. I guess that instead of doing inheritance of a Route, the selecting part is done by composition
looks good
d
It might not end up backwards compatible for stat names, since micrometer does things a bit differently... w/ work and name mappers it could get similar but it would be a pity to lose some other features (like tags for the systems that support them...)
d
probably not a big deal, I guess
d
Also, I'm considering copying metrics exposed by other commonly used systems...
So all I need is to retrieve all the `Route`s and test the selector type?
d
probably, didn’t check the code itself, but would make sense
what about something like this?
Copy code
override fun toString() = when {
    parent == null -> "/"
    else -> 
	    if (selector is Blah) {
	    	"$parent"
	    } else {
	    	"$parent/$selector".trim('/')
	    }
}
d
How would that work? I can override the Route's toString() using an extension function?
d
well, no, I just copied it, but forgot to change the declaration, you have to make it a extension method/property and instead of calling the implicit to string call that one for parent
Copy code
val Route.cleanPath get() = when {
    parent == null -> "/"
    else -> 
	    if (selector is Blah) {
	    	"${parent.cleanPath}"
	    } else {
	    	"${parent.cleanPath}/$selector".trim('/')
	    }
}
d
My goodness! There's no hierarchy! I'd have to check for a whole bunch of types... I could exclude authentication though...
d
start excluding it
we can always create an empty interface like “NotImportantSelector” (a better name would be nice) so people can implement it, and update the Authentication selector to implement it
d
It looks like what you did works.. I guess I just have to clean out more things and get the http method seperately to put it in another tag... and test more complex urls. Thanks!
👏 1