How to pass binary data between microservices in Spring Cloud? - spring

I'd like to ask. How to pass binary data between microservices in Spring Cloud?
Should (Can) I use #FeignClient or #RibbonClient ? How it should be? I've already read that #FeignClient is not deal with this issue What else? OkHttp?
Thx in advance

Spring Cloud integrates with some http clients, like you mentioned. Ribbon has some non-http clients/transports built in, but I haven't used that and AFIAK, netflix doesn't use them either.
You can use the Spring Cloud LoadBalancerClient interface directly. It gives you access to a host and port, then you can use any client you want.
public class MyClass {
#Autowired
private LoadBalancerClient loadBalancer;
public void doStuff() {
ServiceInstance instance = loadBalancer.choose("myService");
String host = instance.getHost();
int port = instance.getPort();
// ... do something with the host and port
}
}
I also did a sample integration with OkHttp.

Related

How to specify Ribbon Configuration for a specific Feign client in a Spring Boot project having multiple Feign clients?

I have a spring boot project which is having two Feign Client's.
First Feign client (i.e. ServiceAProxy.class below), which will work on Service Discovery using Eureka or Consul.
#FeignClient(name="service-A")
public interface ServiceAProxy{
#RequestMapping(method= RequestMethod.GET)
String getResponse();
}
Second Feign client( i.e ServiceBProxy.class ), needs to have a set of server list which will be picked from Spring Cloud Configuration server or Apache zookeeper config server and will be passed to the ribbon client of ServiceBProxy.class. For that, I'm trying to add a Ribbon Client Configuration that will modify the ribbon server list. Below is the code for the second feign client.
#FeignClient(name="service-B")
#RibbonClient(name="service-B",configuration= ServiceBProxy.LocalRibbonClientConfig.class)
public interface ServiceBProxy{
#RequestMapping(method = RequestMethod.GET)
String invalidateUsers();
#Configuration
class LocalRibbonClientConfig{
#Value("${server-host1}") // fetched from config
private String host1;
#Value("${server-host2}") // fetched from config
private String host2;
#Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(
new Server(host1,8080),
new Server(host2,8081)
);
}
}
}
Now, when I run the code, ServiceBProxy.class works as expected and picks the list of servers that was specified in the LocalRibbonClientConfig.class.
But the problem is with the ServiceAProxy.class which was suppose to work on the basis of service discovery also starts to use the LocalRibbonClientConfig.
How can I only allow ServiceBProxy.class to use the custom ribbon configurations and other feign clients in the project to work as per their default behaviour.
Please guide on this.

How to setup local dev environment properties of Spring Boot based on docker compose with testcontainers?

I'm developing a service that has many dependencies like Redis, PubSub, S3, ServiceC and ServiceD. When I hit an endpoint in development, e.g. /data/4, a http request to ServiceC is performed.
So to that to work, something that mocks ServiceC must run. In my case it's wiremock (since I cant run ServiceC directly, as it also has many dependencies). All of these MockServices are in a docker-compose-dev file.
Now to my question: How can I run the docker-compose with testcontainers, get the assigned ports, and set the correct properties to the WebClient has the right mock url + port?
What lifecycle hook can I use to run before spring boot starts, and also can configure the properties?
One downside would be an increased boot time in the dev mode though, but I can't assign fixed ports in docker compose file, because they might be used on the developer's machine. So makes no sense to ship url defaults for the service urls on localhost.
Testcontainers supports starting Docker Compose out-of-the-box using the DockerComposeContainer class. While creating an instance of this class you have to expose all your containers:
#Testcontainers
public class DockerComposeTest {
#Container
public static DockerComposeContainer<?> environment =
new DockerComposeContainer<>(new File("docker-compose.yml"))
.withExposedService("database_1", 5432, Wait.forListeningPort())
.withExposedService("keycloak_1", 8080,
Wait.forHttp("/auth").forStatusCode(200)
.withStartupTimeout(Duration.ofSeconds(30)));
#Test
void dockerComposeTest() {
System.out.println(environment.getServicePort("database_1", 5432));
System.out.println(environment.getServicePort("keycloak_1", 8080));
}
}
The example above maps a PostgreSQL database and Keycloak to a random ephemeral port on your machine. Right after the wait checks pass, your test will run and you can access the actual port with .getServicePort().
A possible solution for the WebClient configuration is to use a ApplicationContextInitializer as you somehow have to define the Spring Boot property before the container starts:
public class PropertyInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
// static access to environment variable of the test
TestPropertyValues
.of("your_web_client_base_url=http://localhost:" + environment.getServicePort("serviceC", 8080) + "/api")
.applyTo(configurableApplicationContext);
}
}
You can then register the initializer for your integration tests with:
#Testcontainers
#ContextConfiguration(initializers = {PropertyInitializer.class})
class DockerComposeTest {
}
As an alternative, you could also try to solely mock all HTTP communication to external services with WireMock.

How to configure spring-boot application to use custom HostnameVerifier?

I have a system of several spring-boot 2.1.x applications, communicating over https. The hostnames are generated at runtime, according to a naming schema, with variable suffixes - and the IP addresses are also generated at runtime, in an OpenShift environment. The idea is that the certificate CNs don't match the runtime hostnames.
The http client that I am using in these applications is netty.
I want to be able to use my custom implementation of the HostnameVerifier interface - or any other similar method, but I don't know how to get a handle on the netty client configuration from Spring environment:
public class TrustAllHostnames implements HostnameVerifier {
#Override
public boolean verify(String hostname, SSLSession sslSession) {
// incomplete
return true;
}
}

How to call external non REST API from a spring cloud micro-service that use eureka to call other internal micro-service?

In our project we use Spring cloud + Eureka as service registry.
When we use the ribbon client to call internal micro-services, all URL are resolved via Eureka ... that's a problem to call external URLs.
As external API are old fashioned usage of Feign doesn't seem to be good choice.
What's the best way to call an external URL from such a service ?
Thanks in advance
Patrice
One way working:
Use two configurations.
Declare your RestTemplate Bean to call external services like this:
#Primary
#Qualifier("withoutEureka")
#Bean
public RestTemplate restTemplate(){
...
}
Inject this reference in your client this way
#Bean
public MyClientForExtCall myClientForExtCall(#Qualifier("withoutEureka")RestTemplate restTemplate)
In the other configuration use the restTemplate as usual, but don't forget to use another qualifier
#LoadBalanced
#Bean
#Qualifier("withEureka")
public RestTemplate loadBalancedEureka(){
...
}
#Bean
public MyClientForInternal myClientForInternal(#Qualifier("withoutEureka")RestTemplate restTemplate)
Patrice
You can use Ribbon without Eureka. For external APIs where you cannot configure in Eureka to abstract the discover. You can hard code their URLs in client and configure server list. The Ribbon client defaults to a configured server list, and you can supply the configuration like this:
stores:
ribbon:
listOfServers: example.com, google.com

How to use Spring Cloud with ElasticMQ

We want to use Spring Cloud for AWS SQS, but it seems to me that it only allows us to specify region. Can we fake it so that it uses ElasticMQ (on localhost:9320 for instance)? I didn't find an easy way to do this without editing hosts file and putting certificates on localhost
I found a way after some research.
You should set an endpoint after AmazonSQS instance is injected in order to override the already set endpoint, as so:
#Autowired
public void setAmazonSqs(AmazonSQS amazonSqs)
{
this.amazonSqs = amazonSqs;
// use elasticMQ if spring default profile is used; no active profiles
if (environment.getActiveProfiles().length == 0)
{
amazonSqs.setEndpoint("http://localhost:9320");
}
}
it is up to you if you're going to use QueueMessagingTemplate, anyway you should modify the injected AmazonSQS instance.

Resources