question 2: when doing local testing, we start all...
# http4k
u
question 2: when doing local testing, we start alll our microservices on local host with different ports. To make the services correctly calling each other we keep a map of the ports and set all the hostname to localhost. So far so good... now we are toying with the idea of not starting the servers (sockets and all), but just connecting the httpHandlers between them. What we need is a way to select the right one using some abstraction (what currently is the port). Is there some work done in this direction somewhere?
s
We have some patterns we tend to use, but nothing encoded in http4k itself. The first trick is to use one client per server (i.e. if a class talks to two different services, inject two
HttpHandlers
one for each dependency). That way you can simply inject the correct server instead of using a real client. The second trick is to use a
SystemUnderTest
abstraction that knows about each service and can wire things together accordingly and provide the right implementation
The last trick (and that applies to prod code as well) is to have a
ServiceRegistry
which can serve a similar purpose to
SystemUnderTest
and can be used to provide the correct clients at service startup. eg
serviceRegistry.clientFor(ServiceA)
where
ServiceA
implements a
Service
interface that can provide the correct configuration depending on the context (
in-memory
, vs
local integration
vs
real
)
d
Repeat of what Ivan has written, but I’ll leave it here : On our current project we’ve created a simple “service discovery” interface, which the services register themselves into with their name when they “start”. You can also look up service httphandlers based on name from the same object. For prod, this object provides a real Http client with a SetBaseHostFrom to set the server/port. For testing, it just looks up the preRegistered app from a simple map. You obvs have to start the apps in the correct order for this to work.
u
interesting!
it seems definitely something in the direction we want to go. follow up: one consideration is that sometime we want to use a browser to verify the system started from inside the IDE. Currently using real http servers is not a problem. If we switch to this system it won't work. Possible solutions: 1) leave the current setup as a possibility, 2) keep everything "socketless" but create a http-proxy for the UI endpoints
d
we do exactly this as well! we wire all of the apps up, then pull out the "gateways" from the service discovery and just wrap them in servers and start em up
which gives us a single running process with a bunch of services (and maybe fakes as well) running inside - a bit like a k8s cluster - i jokingly refer to it as the "H4K cluster" pattern because we have service discovery objects acting as an ingress and an egress (where we register external fakes)
👍 1
there's probably a conference talk in the there somewhere, or maybe I'll formalise it in the lib.
it just goes to show once again how powerful and reusable the SaaF concept is
❤️ 3
u
Thinking about it... Quarkus. Io would make it even better! Unfortunately the changes they did to support vert.x made much harder to use http4k inside it.
n
You definitely need to write this up. It’s such a useful technique.
d
@Uberto Barbini could you elaborate on both of those?
u
Premise: By design Quarkus.io is based on the idea of a single http server, in dev mode everytime you send a request it will reload and hotswap all changed classes from sources. It's surprisingly fast and solid. Now, it used to be quite simple to add http4k to quarkus.io, just changing the main servlet like this: https://github.com/uberto/mandel-quarkus/blob/master/src/main/kotlin/com/ubertob/mandel/MandelServer.kt but at some point they changed the default Quarkus main using some kind of Vert.x "crap" instead of a HttpServlet and there is no more an easy way, afaik. What should be possible is to write a Quarkus,io extension for http4k, in this way you have the control of the process and you can define the interface for the users even better. I played with that a bit in Dec, it doesn't seem hard but I didn't have the time to finish it. If we go on this idea of H4K minikube... 🙂 it may be worth resurrect the idea!
d
@Uberto Barbini am happy to make that an official http4k module if you want to get it working! 🙂
u
Is there something generic enough you can share about the H4K cluster? If we start using it then I can finish the Quarkus.io job. I basically dropped it when KotlinConf rejected my proposal 😉
d
It's literally just a couple of interfaces which allow you to register then lookup HttpHandlers based on an Identity. Let me bang together a quick example
After going around in circles for a few hours, it boiled down to this. What do we think? https://gist.github.com/daviddenton/63942d3bafb4c64445ed949bec8819ec
(which is annoying because there's nothing to it..)
It wires them together directly - not sure how it might look if we wanted to introduce real HTTP clients into the mix. Will play more to see..
u
mmmh this seems heading exactly in the same direction we were thinking...
it's not completely clear to me the difference between InternalService and ExternalServices in this example
d
Well, fundamentally there is no real difference - they are just 2 different "sets" of services - one of which will be our services and the other which is the set of 3rd party external systems.
u
ah ok, this is also as we called them 🙂 but in this example they seems used differently...
d
So then you can run, for example, all your services in memory, talking to a "cluster" of real services
u
what we need is something like provideHttpClient(service): HttpHandler that will load the service hostname/port in the handler and it can work in both way (socket and socketless)
d
or you can swap out that external cluster for your cluster of fakes
👍 1
you probably don't want to have the HttpHandler itself know about the Discovery.
eg.
Copy code
.install(Proxy.ID) { discovery -> Proxy(discovery.lookup(App.ID)) }
vs
Copy code
.install(Proxy.ID) { discovery -> Proxy(discovery) }
probably makes it harder to test
Also, the concept as written only operates at the level of HttpHandlers, and not Http4kServers
u
I suppose this cluster should also abstract the server, I mean I don't really see much value to deploy this cluster in prod, it's for local testing so it should start a "ingress" httpServer and let all the rest of httpHandler stay socketless
ok next step could be let H4K cluster speak directly to K8S or CloudFoundry 😄