How do I get the server ip/port for a ktor service? - ktor

I'm looking to write a Ktor feature that should announce the service on a local network using DNS-SD/mDNS. I would like to be able to automatically start the announcing on ktor application start and stop it on ktor application stopped. I've written code that does this using ApplicationStarted and ApplicationStopped event. This code works.
However, I can find no way of getting what IP address/port from ktor other than reading the configuration.
Is there any way of listening for/listing the connectors that ktor is currently using?

You can access connectors through the BaseApplicationEngine instance:
val server = embeddedServer(Netty, 9091) {}
println(server.environment.connectors)
or by casting environment to ApplicationEngineEnvironment:
fun Application.module(testing: Boolean = false) {
(environment as ApplicationEngineEnvironment).connectors.forEach { connector ->
println("${connector.host}:${connector.port}")
}
}

Related

Apache ActiveMQ AMQP Spring Boot AWS

I have an ActiveMQ AWS service with protocol AMQP. AWS returns to me:
failover:(amqp+ssl://b-ca138bd4-e6c4-4596-8329-f11bebf40111-1.mq.us-east-1.amazonaws.com:5671,amqp+ssl://b-ca138bd4-e6c4-4596-8329-f11bebf40111-2.mq.us-east-1.amazonaws.com:5671)
I am trying to implement using Spring Boot the connection with that endpoint, but I have many problems. I have tried with many ways, but I can't connect to the ActiveMQ using Spring.
I have tried:
Creating many configuration Beans, like:
#Bean
fun connectionFactory(): ConnectionFactory {
val activeMQConnectionFactory = ActiveMQConnectionFactory()
activeMQConnectionFactory.brokerURL = "amqp+ssl://b-ca138bd4-e6c4-4596-8329-f11bebf40111-1.mq.us-east-1.amazonaws.com:5671"
activeMQConnectionFactory.trustedPackages = listOf("com.rappi.scaffolding")
return activeMQConnectionFactory
}
and using many dependencies like:
implementation("org.apache.activemq:activemq-spring:5.17.0")
implementation("org.springframework:spring-jms")
and
implementation("org.springframework.boot:spring-boot-starter-artemis")
But is not possible for me establish the connection. At this moment I am seeing this error:
Reason: java.io.IOException: Transport scheme NOT recognized: [amqp+ssl]
There are some example in Java or Kotlin or guide to connect me with AWS using AMQP protocol? I didn't find any in Google.
I have read that using QPid, but it not works for me.
I have found many examples using Rabbit, but not Apache ActiveMQ protocol amqp+ssl.
Finally It works using the Bean:
#Bean
fun connectionFactory(): ConnectionFactory {
return JmsConnectionFactory(
"failover:(amqps://b-ca138bd4-e6c4-4596-8329-f11bebf40111-1.mq.us-east-1.amazonaws.com:5671,amqps://b-ca138bd4-e6c4-4596-8329-f11bebf40111-2.mq.us-east-1.amazonaws.com:5671)").apply {
this.username = user
this.password = passwordAQ
}
There are many things wrong with your code and configuration.
First, the URL you're using for your client is incorrect. The amqp+ssl scheme is not valid for any client. That's the scheme used to define the connector in the ActiveMQ broker configuration.
Second, your dependencies are wrong. As far as the client goes you just need:
implementation("org.apache.qpid:qpid-jms-client:1.6.0")
Of course, if you're using Spring you'll need all the related Spring dependencies, but as far as the client itself goes this is all you need.
Third, your code is wrong. You should be using something like this:
#Bean
fun connectionFactory(): ConnectionFactory {
return new org.apache.qpid.jms.JmsConnectionFactory("failover:(amqps://b-ca138bd4-e6c4-4596-8329-f11bebf40111-1.mq.us-east-1.amazonaws.com:5671,amqps://b-ca138bd4-e6c4-4596-8329-f11bebf40111-2.mq.us-east-1.amazonaws.com:5671)");
}

Karate-gatling: set local addresses

I have simple karate feature file for testing an API, and I want to use this feature file for load testing. Thus I am using Gatling to execute the karate feature file: https://github.com/intuit/karate/tree/master/karate-gatling
However, when I fire up multiple users, I want to submit the requests with different ip alias that I have configured.
Using Gatling, I could use localAddress to bind to the socket
val protocol = http.localAddresses(ip)
But in Karate-Gatling, karateProtocol is used instead
val protocol = karateProtocol()
And the readme states that "Karate is responsible for making HTTP requests while Gatling is only measuring the timings and managing threads".
This means that the HTTP requests and localAddress bindings cannot be changed through Gatling, but I am wondering if there is a workaround through Karate so that different ip alias can be used for different requests.
This sounds like a feature request is needed to tell the HTTP client (Apache for e.g.) to use a local-address.
It would be great if you can help contribute and test, one way to set the localAddress would be in the code here:
RequestConfig.Builder configBuilder = RequestConfig.custom()
.setCookieSpec(LenientCookieSpec.KARATE)
.setConnectTimeout(config.getConnectTimeout())
.setSocketTimeout(config.getReadTimeout());
String localIp = "1.2.3.4";
try {
InetAddress localAddress = InetAddress.getByName(localIp);
configBuilder.setLocalAddress(localAddress);
} catch (Exception e) {
context.logger.error("failed to resolve local address: {}", localIp);
}

"URL was not normalized" with Spring Boot 2 and Kotlin

In my current project we deploy several Spring Boot 1.5.4.RELEASE microservices in Openshift with Kubernetes. We have configured an Apache Proxy Balancer:
From To
/msa/microname1 -> /
/msa/microname2 -> /
...
Recently we introduced Spring Boot 2 and we developed a new microservice with Kotlin. We configured the balancer the same way considering that urls like /health and /info are placed under /actuator path.
Now when we consume any endpoint of this new microservice (/health or any of our endpoints) we are having an error like this:
org.springframework.security.web.firewall.RequestRejectedException:
The request was rejected because the URL was not normalized ...
The path that we intercept in our microservice has an extra slash in the beginning: //<path_to_resource>
When I use the microservice url I get the resource without problems, but when using the proxy balancer mapping we have the issue described above.
We checked our proxy balancer and it is configured the same way than the others.
Is is any extra configuration on Spring Boot 2 we have to consider?
Can this be a problem related to Kotlin?
Update
As a tweak we have configured the DefaultHttpFirewall to allow url enconded slash, but this does not fixes the issue with the double slash. It only masks the problem.
#Bean
fun allowUrlEncodedSlash(): HttpFirewall {
var firewall: DefaultHttpFirewall = DefaultHttpFirewall()
firewall.setAllowUrlEncodedSlash(true)
return firewall
}
override fun configure(web: WebSecurity) {
web.httpFirewall(allowUrlEncodedSlash())
}
Check this answer: https://stackoverflow.com/a/48644226/10451721
It seems to be the same issue as yours but in Java instead of Kotlin.
Spring doesn't like // in URLs by default.
I have converted the Java in the linked answer for you:
#Bean
fun allowUrlEncodedSlashHttpFirewall(): HttpFirewall {
val firewall = StrictHttpFirewall()
firewall.setAllowUrlEncodedSlash(true)
return firewall
}
Solved by adding context to the application in our balancer.
Now all our endpoints have a context in our microservice, defined in application.yml.
From To
/msa/microname1 -> /
/msa/microname2 -> /
/msa/Kotlinname/kt -> /kt

Automate RabbitMQ consumer testing

I have a .net micro-service receiving messages using RabbitMQ client, I need to test the following:
1- consumer is successfully connected to rabbitMq host.
2- consumer is listening to queue.
3- consumer is receiving messages successfully.
To achieve the above, I have created a sample application that sends messages and I am debugging consumer to be sure that it is receiving messages.
How can I automate this test? hence include it in my micro-service CI.
I am thinking to include my sample app in my CI so I can fire a message then run a consumer unit test that waits a specific time then passes if the message received, but this seems like a wrong practice to me because the test will not start until a few seconds the message is fired.
Another way I am thinking of is firing the sample application from the unit test itself, but if the sample app fails to work that would make it the service fault.
Is there any best practices for integration testing of micro-services connecting through RabbitMQ?
I have built many such tests. I have thrown up some basic code on
GitHub here with .NET Core 2.0.
You will need a RabbitMQ cluster for these automated tests. Each test starts by eliminating the queue to ensure that no messages already exist. Pre existing messages from another test will break the current test.
I have a simple helper to delete the queue. In my applications, they always declare their own queues, but if that is not your case then you'll have to create the queue again and any bindings to any exchanges.
public class QueueDestroyer
{
public static void DeleteQueue(string queueName, string virtualHost)
{
var connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "localhost";
connectionFactory.UserName = "guest";
connectionFactory.Password = "guest";
connectionFactory.VirtualHost = virtualHost;
var connection = connectionFactory.CreateConnection();
var channel = connection.CreateModel();
channel.QueueDelete(queueName);
connection.Close();
}
}
I have created a very simple consumer example that represents your microservice. It runs in a Task until cancellation.
public class Consumer
{
private IMessageProcessor _messageProcessor;
private Task _consumerTask;
public Consumer(IMessageProcessor messageProcessor)
{
_messageProcessor = messageProcessor;
}
public void Consume(CancellationToken token, string queueName)
{
_consumerTask = Task.Run(() =>
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: queueName,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
_messageProcessor.ProcessMessage(message);
};
channel.BasicConsume(queue: queueName,
autoAck: false,
consumer: consumer);
while (!token.IsCancellationRequested)
Thread.Sleep(1000);
}
}
});
}
public void WaitForCompletion()
{
_consumerTask.Wait();
}
}
The consumer has an IMessageProcessor interface that will do the work of processing the message. In my integration test I created a fake. You would probably use your preferred mocking framework for this.
The test publisher publishes a message to the queue.
public class TestPublisher
{
public void Publish(string queueName, string message)
{
var factory = new ConnectionFactory() { HostName = "localhost", UserName="guest", Password="guest" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "",
routingKey: queueName,
basicProperties: null,
body: body);
}
}
}
My example test looks like this:
[Fact]
public void If_SendMessageToQueue_ThenConsumerReceiv4es()
{
// ARRANGE
QueueDestroyer.DeleteQueue("queueX", "/");
var cts = new CancellationTokenSource();
var fake = new FakeProcessor();
var myMicroService = new Consumer(fake);
// ACT
myMicroService.Consume(cts.Token, "queueX");
var producer = new TestPublisher();
producer.Publish("queueX", "hello");
Thread.Sleep(1000); // make sure the consumer will have received the message
cts.Cancel();
// ASSERT
Assert.Equal(1, fake.Messages.Count);
Assert.Equal("hello", fake.Messages[0]);
}
My fake is this:
public class FakeProcessor : IMessageProcessor
{
public List<string> Messages { get; set; }
public FakeProcessor()
{
Messages = new List<string>();
}
public void ProcessMessage(string message)
{
Messages.Add(message);
}
}
Additional advice is:
If you can append randomized text to your queue and exchange names on each test run then do so to avoid concurrent tests interfering with each other
I have some helpers in the code for declaring queues, exchanges and bindings also, if your applications don't do that.
Write a connection killer class that will force close connections and check your applications still work and can recover. I have code for that, but not in .NET Core. Just ask me for it and I can modify it to run in .NET Core.
In general, I think you should avoid including other microservices in your integration tests. If you send a message from one service to another and expect a message back for example, then create a fake consumer that can mock the expected behaviour. If you receive messages from other services then create fake publishers in your integration test project.
I was successfully doing such kind of test. You need test instance of RabbitMQ, test exchange to send messages to and test queue to connect to receive messages.
Do not mock everything!
But, with test consumer, producer and test instance of rabbitMQ there is no actual production code in that test.
use test rabbitMQ instance and real aplication
In order to have meaniningfull test I would use test RabbitMQ instance, exchange and queue, but leave real application (producer and consumer).
I would implement following scenario
when test application does something that test message to rabbitMQ
then number of received messages in rabbitMQ is increased then
application does something that it should do upon receiving messages
Steps 1 and 3 are application-specific. Your application sends messages to rabbitMQ based on some external event (HTTP message received? timer event?). You could reproduce such condition in your test, so application will send message (to test rabbitMQ instance).
Same story for verifying application action upon receiving message. Application should do something observable upon receiving messages.
If application makes HTTP call- then you can mock that HTTP endpoint and verify received messages. If application saves messages to the database- you could pool database to look for your message.
use rabbitMQ monitoring API
Step 2 can be implemented using RabbitMQ monitoring API (there are methods to see number of messages received and consumed from queue https://www.rabbitmq.com/monitoring.html#rabbitmq-metrics)
consider using spring boot to have health checks
If you are java-based and then using Spring Boot will significantly simpify your problem. You will automatically get health check for your rabbitMQ connection!
See https://spring.io/guides/gs/messaging-rabbitmq/ for tutorial how to connect to RabbitMQ using Spring boot.
Spring boot application exposes health information (using HTTP endpoint /health) for every attached external resource (database, messaging, jms, etc)
See https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html#_auto_configured_healthindicators for details.
If connection to rabbitMQ is down then health check (done by org.springframework.boot.actuate.amqp.RabbitHealthIndicator) will return HTTP code 4xx and meaninfull json message in JSON body.
You do not have to do anything particular to have that health check- just using org.springframework.boot:spring-boot-starter-amqp as maven/gradle dependency is enough.
CI test- from src/test directory
I have written such test (that connect to external test instance of RabbitMQ) using integration tests, in src/test directory. If using Spring Boot it is easiest to do that using test profile, and having details of connection to test RabbitMQ instance in application-test.properties (production could use production profile, and application-production.properties file with production instance of RabbitMQ).
In simplest case (just verify connection to rabbitMQ) all you need is to start application normally and validate /health endpoint.
In this case I would do following CI steps
one that builds (gradle build)
one that run unit tests (tests without any external dependenices)
one that run integration tests
CI test- external
Above described approach could also be done for application deployed to test environment (and connected to test rabbitMQ instance). As soon as application starts, you can check /health endpoint to make sure it is connected to rabbitMQ instance.
If you make your application send message to rabbitMQ, then you could observe rabbbitMQ metrics (using rabbitMQ monitoring API) and observe external effects of message being consumed by application.
For such test you need to start and deploy your application from CI befor starting tests.
for that scenario I would do following CI steps
step that that builds app
steps that run all tests in src/test directory (unit, integration)
step that deploys app to test environment, or starts dockerized application
step that runs external tests
for dockerized environment, step that stops docker containers
Consider dockerized enevironment
For external test you could run your application along with test RabbitMQ instance in Docker. You will need two docker containers.
one with application
one with rabbitMQ . There is official docker image for rabbitmq https://hub.docker.com/_/rabbitmq/ and it is really easy to use
To run those two images, it is most reasonable to write docker-compose file.

MobileFirst - JavaScript Adapter calling Java Adapter via REST endpoint

This question is related to - [How to invoke Java adapter from HTTP adapter? ][1]
I have 2 adapters in my MobileFirst 7.1 project :
A Java Adapter that is exposing Rest Endpoint.
A JavaScript adapter will call the Java Adapter via Rest Endpoint
To be exact, this is what I call in the JS adapter :
function JSAdapterCalltoJavaAdapter() {
var input = {
method : 'get',
returnedContentType : 'xml',
path : "adapter/JavaAdapterRestPath"
};
return WL.Server.invokeHttp(input);
}
We have run JMeter load test for 800 Threads on Java Adapter, there is no issue. However, when we run load Test on JS Adapter, the MobileFirst server stop responding, and does not accept incoming request from JS Adapter. The new requests timeout and the MobileFirst console become unresponsive. When we stop the load test, the server gradually recovered.
I have configured the following params adapter.xml :
<connectionTimeoutInMilliseconds>, <socketTimeoutInMilliseconds>, and <maxConcurrentConnectionsPerNode>
It seems like there is threading issue when using JS adapter to call Java adapter under load.
Sounds like you need to open a PMR (support ticket) if you are encountering a threading issue in the MobileFirst Server so that the support/dev team could help you. If you have an actual programming question, ask it.