Failed to resolve service with Spring Cloud LoadBalancer - spring

I followed this guide to implement a simple client-side load balancer in a Spring Boot application. Below is the code:
#RestController
class MyController(
val loadBalancedWebClientBuilder: WebClient.Builder
) {
private fun makeRequest() {
loadBalancedWebClientBuilder.build().post()
.uri(URI("http://my-service/endpoint"))
.body(BodyInserters.fromValue("{ \"data\" = \"data\" }"))
.retrieve()
.bodyToMono(String::class.java)
.block()
}
}
Here is the WebClient configuration:
#Configuration
#LoadBalancerClient(name = "my-service", configuration = [LoadBalancerConfig::class])
class WebClientConfig {
#LoadBalanced
#Bean
fun webClientBuilder(): WebClient.Builder {
return WebClient.builder()
}
}
And the load balancer configuration:
class LoadBalancerConfig {
#Bean
#Primary
fun serviceInstanceListSupplier(): ServiceInstanceListSupplier {
return ServiceInstanceListSupplerImpl("my-service")
}
}
class ServiceInstanceListSupplerImpl(private val serviceId: String) : ServiceInstanceListSupplier {
override fun getServiceId(): String {
return serviceId
}
override fun get(): Flux<List<ServiceInstance>> {
return Flux.just(listOf("http://localhost").mapIndexed { index, s ->
DefaultServiceInstance(index.toString(), serviceId, s, 8088, true) })
}
}
Finally, in the configuration file I have the below:
spring:
cloud:
discovery:
client:
simple:
instances:
my-service[0].uri: http://localhost:8088
When running the above, the following exception gets thrown:
org.springframework.web.reactive.function.client.WebClientRequestException: Failed to resolve 'my-service' after 4 queries ; nested exception is java.net.UnknownHostException: Failed to resolve 'my-service' after 4 queries
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:141) ~[spring-webflux-5.3.24.jar:5.3.24]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ Request to POST http://my-service/endpoint [DefaultWebClient]
Original Stack Trace:
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:141) ~[spring-webflux-5.3.24.jar:5.3.24]
at reactor.core.publisher.MonoErrorSupplied.subscribe(MonoErrorSupplied.java:55) ~[reactor-core-3.4.25.jar:3.4.25]
at reactor.core.publisher.Mono.subscribe(Mono.java:4490) ~[reactor-core-3.4.25.jar:3.4.25]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103) ~[reactor-core-3.4.25.jar:3.4.25]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) ~[reactor-core-3.4.25.jar:3.4.25]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) ~[reactor-core-3.4.25.jar:3.4.25]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) ~[reactor-core-3.4.25.jar:3.4.25]
at reactor.core.publisher.MonoNext$NextSubscriber.onError(MonoNext.java:93) ~[reactor-core-3.4.25.jar:3.4.25]
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onError(MonoFlatMapMany.java:204) ~[reactor-core-3.4.25.jar:3.4.25]
at reactor.core.publisher.SerializedSubscriber.onError(SerializedSubscriber.java:124) ~[reactor-core-3.4.25.jar:3.4.25]
...
Any clue what might be wrong?

Related

How to create custom error response in Spring Integration using java DSL

I have following simple proxy integration flow. The main task of which is to take request from the proxy send it to the actual endpoint, get the respond and send it back to the client.
#SpringBootApplication
#EnableIntegration
public class IntegrationApp {
#Value("${narko.pin}")
private String pinUrl;
public static void main(String[] args) {
SpringApplication.run(MinzdravApplication.class, args);
}
#Bean
public DirectChannel requestPinChannel() {
return new DirectChannel();
}
#Bean
public DirectChannel replyPinChannel() {
return new DirectChannel();
}
#Bean
public IntegrationFlow httpProxyFlowPin() throws Exception {
return IntegrationFlows
.from(Http.inboundGateway("/narko/api/patient/by-pinpp")
.requestChannel(requestPinChannel()).replyChannel(replyPinChannel())
.mappedRequestHeaders("activityid")
.errorChannel("httpProxyErrorFlow.input")
)
.wireTap(sf->sf.handle(new InwardMessageHandler()))
.enrichHeaders(h -> h.header("Content-Type", "application/json"))
.handle(Http.outboundGateway(pinUrl).charset("utf-8")
.expectedResponseType(String.class))
.channel(replyPinChannel())
.get();
}
#Bean
public IntegrationFlow httpProxyErrorFlow() {
return f -> f
.transform(Throwable::getCause)
.<HttpClientErrorException>handle((p, h) ->
new RuntimeException("custom exception"));
}
}
When the api at the outbound gateway is down. I have following error:
{
"timestamp": "2022-08-10T12:51:58.561+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/narko/api/patient/by-pinpp"
}
And I have following exceptions on logs:
java.lang.ClassCastException: class org.springframework.web.client.ResourceAccessException cannot be cast to class org.springframework.web.client.HttpClientErrorException (org.springframework.web.client.ResourceAccessException and org.springframework.web.client.HttpClientErrorException are in unnamed module of loader 'app')
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.integration.handler.LambdaMessageProcessor.processMessage(LambdaMessageProcessor.java:104) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.handler.ServiceActivatingHandler.handleRequestMessage(ServiceActivatingHandler.java:105) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:136) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:56) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:115) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:133) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:106) ~[spring-integration-core-5.5.10.jar:5.5.10]
How can I create custom exception response?
Any navigation or hint is appreciated.
First of all you don't need that .replyChannel(replyPinChannel() and .channel(replyPinChannel()). An inbound gateway sends a message with a replyChannel header, the last replying endpoint in the flow, not founding its outputChannel, will consult with that replyChannel.
Secondly, your solution about an error handler is OK, but you see yourself in the stacktrace that you just don't cast to the proper type: the ResourceAccessException is not an instance of HttpClientErrorException. Consider to expect a RestClientException instead which is a super for both ResourceAccessException and HttpClientErrorException.

Producer Kafka throws deserialization exception

I have one topic and Producer/Consumer:
Dependencies (Spring Initializr)
Producer (apache kafka)
Consumer (apache kafka stream, cloud stream)
Producer:
KafkaProducerConfig
#Configuration
public class KafkaProducerConfig {
#Bean
public KafkaTemplate<String, Person> kafkaTemplate(){
return new KafkaTemplate<>(producerFactory());
}
#Bean
public ProducerFactory<String, Person> producerFactory(){
Map<String, Object> configs = new HashMap<>();
configs.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");
configs.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
return new DefaultKafkaProducerFactory<>(configs);
}
}
Controller:
#RestController
public class KafkaProducerApplication {
private KafkaTemplate<String, Person> kafkaTemplate;
public KafkaProducerApplication(KafkaTemplate<String, Person> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
#GetMapping("/persons")
public Mono<List<Person>> findAll(){
var personList = Mono.just(Arrays.asList(new Person("Name1", 15),
new Person("Name2", 10)));
personList.subscribe(dataList -> kafkaTemplate.send("topic_test_spring", dataList.get(0)));
return personList;
}
}
It works correctly when accessing the endpoint and does not throw any exception in the IntelliJ console.
Consumer:
spring:
cloud:
stream:
function:
definition: personService
bindings:
personService-in-0:
destination: topic_test_spring
kafka:
bindings:
personService-in-0:
consumer:
configuration:
value:
deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
binders:
brokers:
- localhost:9091
- localhost:9092
kafka:
consumer:
properties:
spring:
json:
trusted:
packages: "*"
PersonKafkaConsumer
#Configuration
public class PersonKafkaConsumer {
#Bean
public Consumer<KStream<String, Person>> personService(){
return kstream -> kstream.foreach((key, person) -> {
System.out.println(person.getName());
});
}
}
Here I get the exception when run the project.
org.apache.kafka.streams.errors.StreamsException: Deserialization exception handler is set to fail upon a deserialization error. If you would rather have the streaming pipeline continue after a deserialization error, please set the default.deserialization.exception.handler appropriately. Caused by: java.lang.IllegalArgumentException: The class 'com.example.producer.model.Person' is not in the trusted packages: [java.util, java.lang, com.nttdata.bootcamp.yanki.model, com.nttdata.bootcamp.yanki.model.]. If you believe this class is safe to deserialize, please provide its name. If the serialization is only done by a trusted source, you can also enable trust all (). org.apache.kafka.streams.errors.StreamsException: Deserialization exception handler is set to fail upon a deserialization error. If you would rather have the streaming pipeline continue after a deserialization error, please set the default.deserialization.exception.handler appropriately.
The package indicated in the exception refers to the entity's package but in the producer. The producer's properties file has no configuration.

LoadBalanced WebClient together with Eureka WebClient enabled

In a reactive microservice I'm registering to Eureka and using a #LoadBalanced WebClient to get a response from an instance. Registering in Eureka alone works, but once I add the #LoadBalanced WebClient I get following error.
2021-05-22 14:31:14.835 INFO 1852 --- [ main] com.netflix.discovery.DiscoveryClient : Getting all instance registry info from the eureka server
2021-05-22 14:31:14.985 ERROR 1852 --- [ main] scoveryClientServiceInstanceListSupplier : Exception occurred while retrieving instances for service 127.0.0.1
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'scopedTarget.eurekaClient': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:274) ~[spring-beans-5.3.6.jar:5.3.6]
I assume it is related to the configuration eureka.client.webclient.enabled=true.
The Application
That's my application and the crucial parts of its configuration.
application.yml
eureka:
client:
serviceUrl:
defaultZone: ${vcap.services.eureka-service.credentials.uri:http://127.0.0.1:8761}/eureka/
webclient:
enabled: true
ConsumerApplication.java
#SpringBootApplication
public class ConsumerApplication {
public Mono<ServerResponse> handler(ServerRequest request) {
return webClientBuilder()
.baseUrl("http://producer")
.build()
.get()
.retrieve()
.bodyToMono(String.class)
.onErrorResume(e -> Mono.just("Error " + e.getMessage()))
.flatMap(r -> ok().bodyValue(Map.of("Producer says", r)));
}
#Bean
#LoadBalanced
public WebClient.Builder webClientBuilder(){
return WebClient.builder();
}
#Bean
RouterFunction<ServerResponse> routes() {
return route()
.GET("", this::handler)
.build();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
Eureka WebClient disabled
If instead eureka.client.webclient.enabled=false is used, everything works perfectly fine. However, I don't think this should be the solution.
DiscoveryClientOptionalArgsConfiguration : Eureka HTTP Client uses RestTemplate.
How would I go about using a #LoadBalanced WebClient together with eureka.client.webclient.enabled=true?
Had the same problem, this is how I fixed it.
#Bean
#LoadBalanced
public WebClient.Builder lbWebClient() {
return WebClient.builder();
}
#Bean
#Primary
public WebClient.Builder webClient() {
return WebClient.builder();
}
Use #Qualifier later on to used the loadbalanced one.

Spring Cloud Gateway - Unable to find GatewayFilterFactory with name [Filter_Name]

I have a spring cloud gateway application. I am trying to setup a gateway filter. The Spring Boot version is 2.3.4.RELEASE Here are the dependencies:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation platform(SpringBootPlugin.BOM_COORDINATES)
implementation platform('org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR8')
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-sleuth'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
}
Here is the configutraion for the gateway client
server:
port: 8081
spring:
cloud:
gateway:
routes:
- id: onboard_redirect
uri: http://localhost:8080/api/v1/onboard
predicates:
- Path=/api/v1/onboard
filters:
- name: MyLogging
args:
baseMessage: My Custom Message
preLogger: true
postLogger: true
Here is my filter class:
#Component
public class MyLoggingGatewayFilterFactory extends AbstractGatewayFilterFactory<MyLoggingGatewayFilterFactory.Config> {
final Logger logger =
LoggerFactory.getLogger(MyLoggingGatewayFilterFactory.class);
public MyLoggingGatewayFilterFactory() {
super(Config.class);
}
#Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// Pre-processing
if (config.preLogger) {
logger.info("Pre GatewayFilter logging: "
+ config.baseMessage);
}
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
// Post-processing
if (config.postLogger) {
logger.info("Post GatewayFilter logging: "
+ config.baseMessage);
}
}));
};
}
public static class Config {
public String baseMessage;
public boolean preLogger;
public boolean postLogger;
}
}
Everything works without configuring the filter but when I configure the filter I get follwing error:
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalArgumentException: Unable to find GatewayFilterFactory with name MyLogging
Caused by: java.lang.IllegalArgumentException: Unable to find GatewayFilterFactory with name MyLogging
what I am doing wrong here?
The filter class is MyLoggingGatewayFilterFactory, not MyLogging as you set in your properties.
Try to the following modification in your application.yml file:
filters:
- name: MyLoggingGatewayFilterFactory
Add this dependency in the application.properties file.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-
resilience4j</artifactId>
</dependency>

Hystrix Feign Retry for Timeout not working

I have a Feign Configuration and Hystrix Commands in my project.
below is Feign Config
#Configuration
public class FeignRetryConfig {
#Primary
#Bean
public Feign.Builder feignBuilder(Retryer nephosFeignRetryer) {
return HystrixFeign.builder()
.errorDecoder(new FeignErrorDecoder())
.retryer(nephosFeignRetryer);
}
// retry set to 3 times
#Bean
public Retryer nephosFeignRetryer() {
return new Retryer.Default(10, SECONDS.toMillis(5), 5);
}
#Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
and below is my ErrorDecoder:
public class FeignErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultErrorDecoder = new Default();
#Override
public Exception decode(String methodKey, Response response) {
Exception exception = defaultErrorDecoder.decode(methodKey, response);
if (response.status() == 500) {
log.error(String.format("##### Got %s response from %s #######", response.status(),
methodKey));
return new RetryableException(
exception.getMessage(),
exception,
null
);
}
return exception;
}
}
and below is my client:
#FeignClient(name = "TEST-CONFIG", configuration = FeignRetryConfig.class, fallbackFactory =
XYZClientFallbackFactory.class)
public interface TestClient {
#RequestMapping(value = "/test", method = RequestMethod.GET, consumes =
MediaType.APPLICATION_JSON_VALUE)
Observable<String> test();
}
SO from TEST-CONFIG I am throwing IOException ( 500 Error ) to Test, but it does not make any retry. below is my error:
com.netflix.hystrix.exception.HystrixRuntimeException: TestClient#test() failed and fallback failed.
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:815)
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:790)
at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1451)
at com.netflix.hystrix.AbstractCommand$FallbackHookApplication$1.onError(AbstractCommand.java:1376)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: feign.RetryableException: status 500 reading TestClient#test(); content:
{"status":500,"erroritems":[{"code":"RuntimeException","message":"org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection"}]}
at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:301)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:297)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
... 30 common frames omitted
Caused by: feign.FeignException: status 500 reading TestClient#test(); content:
{"status":500,"erroritems":[{"code":"RuntimeException","message":"org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection"}]}
at feign.FeignException.errorStatus(FeignException.java:62)
at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:91)
Can Somebody Help Please, What am I Missing ?
I guess you have hystrix enabled. Try setting
feign.hystrix.enabled: false
and see if it works then; if so it would prove your configuration to be ok. There is a post on hystrix and retrying that suggests that this does not go well together. If you want to keep hystrix enabled (and why should you not), perhaps it is worth looking at spring-retry to circumvent the problem.

Resources