Feign get stuck inside Asnyc Method In Spring Boot - spring-boot

I am using feing client inside a async method and this method returns Future.
But while i am running service, feign get stuck and I never see response log from feign client. Code block get stuck there and service stop to available . Code blocks below:
#Async
public Future<ReportResponse> getId(String id) {
ReportResponse response = client.getReport(id);
log.info("resp for " + response);
return new AsyncResult<>(ReportResponse);
}
List<Future<ReportResponse>> futuresList = new ArrayList<>();
for (String id : Ids) {
futuresList.add(reportService.getId(id));
}
for (Future<ReportResponse> future :
futuresList) {
try {
ReportResponse reponse = future.get();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
#FeignClient(name = "service", url = "${url}", configuration = FeignClientProperties.FeignClientConfiguration.class)
public interface Client {
#GetMapping(value = "/{id}"
ReportResponse getId(#PathVariable String id);
}
Dependency i used:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Can anyone help me with this?
Thanks.

Related

WebFlux subscribe() method getting stuck

I am developing a Microservice application in SpringBoot. I am using Spring Cloud gateway there,now since Spring Cloud Gateway uses WebFlux module so,I want to extract username and password inside ServerAuthenticationConverter. But unfortunately flow is getting stuck on subscribe() method.
#Component
public class MyConverter implements ServerAuthenticationConverter {
#Override
public Mono<Authentication> convert(ServerWebExchange exchange) {
String token = exchange.getRequest().getHeaders().getFirst("token");
Map<String,String> credentialMap = new HashMap<>();
if(StringUtils.containsIgnoreCase(exchange.getRequest().getPath().toString(),"/login")){
exchange.getFormData().subscribe(data -> {
for(Map.Entry<String,List<String>> mapEntry : data.entrySet()) {
for (String value : mapEntry.getValue()) {
credentialMap.put(mapEntry.getKey(),value);
log.info("key=" + mapEntry.getKey() + "|value=" + mapEntry.getValue());
}
}
});
User user = new User(credentialMap.get("username"),credentialMap.get("password"));
return Mono.justOrEmpty(new UsernamePasswordAuthenticationToken(user,credentialMap.get("password"), List.of(new SimpleGrantedAuthority("ADMIN"))));
}
else{
if(StringUtils.isNotBlank(token)){
if(StringUtils.contains(token,"Bearer")){
return Mono.justOrEmpty(new MyToken(AuthorityUtils.NO_AUTHORITIES,token.substring(7)));
}else{
return Mono.justOrEmpty(new MyToken(AuthorityUtils.NO_AUTHORITIES,token));
}
}
}
}
throw new IllegalArgumentException("Invalid Access");
}
}
But after printing log statement within subscribe method program flow is getting halted,no exception.
I think subscribe() method is causing some thread level issue.Can someone figureout the problem????

Spring boot webflux TEXT_EVENT_STREAM_VALUE is not working

I'm using spring-boot with the dependency spring-boot-starter-webflux,
i want to get one data per second with browser,
when i use spring-boot version 2.1.4,the code is working,
but 2.1.5 or greater version not working,
i will get all of the data at 10 sesonds later,not one data per second
I want to get the reason,or others i should do
I find spring-boot update the dependency of netty in 2.1.5,
so if i add the dependency in my pom.xml with
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
<version>0.8.8.RELEASE</version>
</dependency>
it working
#RestController
#RequestMapping("/demo")
public class DemoController {
// just get a string per second
#GetMapping(value = "",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getMsg(){
return Flux.fromStream(new Random().ints(10).mapToObj(intStream ->
{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "this is data "+intStream;
}));
}
}
I believe this will achieve what it is you are going for.
#GetMapping(value = "",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getMsg(){
return Flux.fromStream(new Random()
.ints(10)
.mapToObj(value -> "this is data " + value))
.delayElements(Duration.ofSeconds(1));
}

Feign client and Spring retry

I have a restful service calling an external service using Spring Cloud Feign client
#FeignClient(name = "external-service", configuration = FeignClientConfig.class)
public interface ServiceClient {
#RequestMapping(value = "/test/payments", method = RequestMethod.POST)
public void addPayment(#Valid #RequestBody AddPaymentRequest addPaymentRequest);
#RequestMapping(value = "/test/payments/{paymentId}", method = RequestMethod.PUT)
public ChangePaymentStatusResponse updatePaymentStatus(#PathVariable("paymentId") String paymentId,
#Valid #RequestBody PaymentStatusUpdateRequest paymentStatusUpdateRequest);
}
I noticed the following failure 3-4 times in the last 3 months in my log file:
json.ERROR_RESPONSE_BODY:Connection refused executing POST
http://external-service/external/payments json.message:Send Payment
Add Payment Failure For other reason: {ERROR_RESPONSE_BODY=Connection
refused executing POST http://external-service/external/payments,
EVENT=ADD_PAYMENT_FAILURE, TRANSACTION_ID=XXXXXXX} {}
json.EVENT:ADD_PAYMENT_FAILURE
json.stack_trace:feign.RetryableException: Connection refused
executing POST http://external-service/external/payments at
feign.FeignException.errorExecuting(FeignException.java:67) at
feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:104)
at
feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
at
feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
Is it possible to add Spring Retry on a Feign client.
What I wanted to annotate the addPayment operation with
#Retryable(value = {feign.RetryableException.class }, maxAttempts = 3, backoff = #Backoff(delay = 2000, multiplier=2))
But this is not possible, what other options do I have?
You can add a Retryer in the FeignClientConfig
#Configuration
public class FeignClientConfig {
#Bean
public Retryer retryer() {
return new Custom();
}
}
class Custom implements Retryer {
private final int maxAttempts;
private final long backoff;
int attempt;
public Custom() {
this(2000, 3);
}
public Custom(long backoff, int maxAttempts) {
this.backoff = backoff;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}
public void continueOrPropagate(RetryableException e) {
if (attempt++ >= maxAttempts) {
throw e;
}
try {
Thread.sleep(backoff);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
#Override
public Retryer clone() {
return new Custom(backoff, maxAttempts);
}
}
Updated with sample Retryer example config based on the Retryer.Default.
If you are using ribbon you can set properties, you can use below properties for retry:
myapp.ribbon.MaxAutoRetries=5
myapp.ribbon.MaxAutoRetriesNextServer=5
myapp.ribbon.OkToRetryOnAllOperations=true
Note: "myapp" is your service id.
Checkout this Github implementation for working example
Just new a contructor Default
#Configuration
public class FeignClientConfig {
#Bean
public Retryer retryer() {
return new Retryer.Default(100, 2000, 3);
}
}
Adding this if it can help someone. I was getting connection reset using feign, as some unknown process was running on that port.
Try changing the port. Refer this to find the process running on a port
I prepared a blog post about using Spring Retry with Feign Client methods. You may consider checking the Post. All steps have been explained in the post.
This is my config. Test OK in spring boot 2.2.0.RELEASE
spring cloud Hoxton.M3.
feign.hystrix.enabled=true
MY-SPRING-API.ribbon.MaxAutoRetries=2
MY-SPRING-API.ribbon.MaxAutoRetriesNextServer=2
MY-SPRING-API.ribbon.OkToRetryOnAllOperations=true
MY-SPRING-API.ribbon.retryableStatusCodes=404,500
feign.client.config.PythonPatentClient.connectTimeout=500
feign.client.config.PythonPatentClient.readTimeout=500
hystrix.command.PythonPatentClient#timeTest(String).execution.isolation.thread.timeoutInMilliseconds=5000
java code is :
#FeignClient(name = "MY-SPRING-API",configuration = {PythonPatentConfig.class},fallbackFactory = FallBack.class)
public interface PythonPatentClient
#RequestLine("GET /test?q={q}")
void timeTest(#Param("appNo") String q);
Controller is :
#RequestMapping(value = "/test",method = {RequestMethod.POST,RequestMethod.GET})
public Object test() throws InterruptedException {
log.info("========important print enter test========");
TimeUnit.SECONDS.sleep(10L);
pom.xml additon add:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
optional:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
#EnableRetry
#SpringBootApplication
public class ApiApplication
this is document :
https://docs.spring.io/spring-cloud-netflix/docs/2.2.10.RELEASE/reference/html/#retrying-failed-requests
https://github.com/spring-projects/spring-retry
https://github.com/spring-cloud/spring-cloud-netflix/
I resolved that by creating a wrapper on top of ServiceClient
#Configuration
public class ServiceClient {
#Autowired
ServiceFeignClient serviceFeignClient;
#Retryable(value = { ClientReprocessException.class }, maxAttemptsExpression = "#{${retryMaxAttempts}}", backoff = #Backoff(delayExpression = "#{${retryDelayTime}}"))
public void addPayment( AddPaymentRequest addPaymentRequest){
return serviceFeignClient.addPayment(addPaymentRequest);
}
}

Test case using SpringJunitRunner

I want to write junit test case for the below code with springJunitRunner.
the below piece of code is one service in a class.
#Component
#Path(/techStack)
public class TechStackResource {
#Autowired
private transient TechStackService techStackService;
#GET
#Path("/{id}")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response getTechStackById(final #PathParam("id") Integer technicalstackid) {
final TechStackResponse response = new TechStackResponse();
int statusCode = Constants.HTTP_STATUS_OK_200;
try {
TechStackModel techStackModel = techStackService.findObjectById(technicalstackid);
response.setGetTechStackDetails(GetTechStackDetails.newBuilder().technicalStack(techStackModel).build());
if (techStackModel == null) {
statusCode = Constants.HTTP_STATUS_ERROR_404;
}
} catch (EmptyResultDataAccessException erde) {
} catch (Exception e) {
LOGGER.error("Exception occured in TechStackResource.getTechStackById(technicalstackid) ", e);
throw new APMRestException(
"Exception while executing TechStackResource.getTechStackById(technicalstackid) ",
Constants.UNKNOW_ERROR, e);
}
return Response.status(statusCode).entity(response).build();
}
}
the configuration in web.xml for servlet is
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
Since you are using Jersey as well as Spring, you can use the SpringJunitRunner only to wire-up TechStackResource with its dependency TechStackService.
In order to test your REST handler method getTestStackById, you could go the POJO approach and invoke it directly. Alternatively, you can use Jersey's own MockWeb environment. To find out more about this, I recommend looking at the Jersey example sources, e.g. HelloWorld.

Spring 3 MVC, stream response asynchronously

I'd like to stream out my content using OutputStream or Writer from Spring MVC controller method using async solution, i.e. not to block base threadpool used for http requests. As far as I could find is to use DefferedResult<?> for async in general. It's fine when you return a view string name but can't think of a way it would work with stream. Could not find anything helpful.
Thanks
As described here you can perform computation in another thread thereby unloded http thread pool.
You can try to combine DefferedResult and byte[] (DefferedResult) return type(previously registering ByteArrayHttpMessageConverter). So the final method will look like this:
#ResponseBody
public DefferedResult<byte[]> foo(HttpServlet response) {
//set headers using response
response.setContentType("someContentType");
...
DefferedResult<byte[]> r = new DefferedResult<>();
executionService.submit(() -> {
r.setResult(getBytes());
});
return r;
}
Another option is to combine Defferedresult and ResponseEntity. Do not forget to use it in servlet 3.0+ container
It may be that what you are looking for is the following. Not sure if it blocks the http thread pool though.
#Controller
public class TestController {
#RequestMapping("/")
public StreamingResponseBody handleRequest () {
return new StreamingResponseBody() {
#Override
public void writeTo (OutputStream out) throws IOException {
for (int i = 0; i < 1000; i++) {
out.write((Integer.toString(i) + " - ")
.getBytes());
out.flush();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
}
}

Resources