Spring boot webflux TEXT_EVENT_STREAM_VALUE is not working - spring-boot

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));
}

Related

How to send messages to one of the multiple topics based on condition in Spring Cloud Stream Kafka application

Currently i have a spring clound funtion which consumes a topic and publish in to another topic. Now I have multiple topics and need to publish message to one of the multiple topic based on certain checks from spring cloud function. How can I achieve this? Here is current implementation.
#Bean("producerBean")
public Function<Message<SourceMessage>, Message<SinkMessage>> producerBean(SinkService<SourceMessage> sinkService) {
return sinkService::processMessage;
}
#Service("SinkService")
public class SinkService<T> {
public Message<SinkMessage> processMessage(Message<SourceMessage> message) {
log.info("Message consumed at {} \n{}", message.getHeaders().getTimestamp(), message.getPayload());
try {
if (message.getPayload().isManaged()) {
/*
Need to add one more check here.
if (type==2)
send to topic1
else if(type==4)
send to topic2
else
Just log the type, do not send to any topic.
*/
Message<SinkMessage> output = new GenericMessage<>(new SinkMessage());
output.getPayload().setPayload(message.getPayload());
return output;
}
} catch (Exception exception) {
exception.printStackTrace();
}
return null;
}
}
application.properties
spring.cloud.stream.kafka.binder.brokers=${bootstrap.servers}
spring.cloud.stream.kafka.binder.configuration.enable.idempotence=false
spring.cloud.stream.binders.test_binder.type=kafka
spring.cloud.stream.bindings.producerBean.binder=test_binder
spring.cloud.stream.bindings.producerBean-in-0.destination=${input-destination}
spring.cloud.stream.bindings.producerBean-in-0.group=${input-group}
spring.cloud.stream.bindings.producerBean-out-0.destination=topic1
spring.cloud.stream.bindings.producerBean-out-1.destination=topic2
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
<version>3.2.5</version>
</dependency>
You can use StreamBridge with kafka-topicname and spring-cloud will bind it automatically in runtime. That approach also auto creates topic if that not exist, you can turn it off.
#Autowired
private final StreamBridge streamBridge;
public void sendDynamically(Message message, String topicName) {
streamBridge.send(route, topicName);
}
https://docs.spring.io/spring-cloud-stream/docs/current/reference/html/spring-cloud-stream.html#_streambridge_and_dynamic_destinations

Feign get stuck inside Asnyc Method In 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.

How to configure the ObjectMapper for Unirest in a spring boot project

I am using Unirest in a project which is working fine for me. However, I want to post some data and do not want to escape all the JSON as it looks ugly and is just a pain in the neck.
I found a few links on how to configure the ObjectMapper for Unirest and it gives this code.
Unirest.setObjectMapper(new ObjectMapper() {
com.fasterxml.jackson.databind.ObjectMapper mapper =
new com.fasterxml.jackson.databind.ObjectMapper();
public String writeValue(Object value) {
try {
return mapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public <T> T readValue(String value, Class<T> valueType) {
try {
return mapper.readValue(value, valueType);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
But, no examples show where it is best to do this in a Spring Boot API project.
I tried to set it up in the main class method, but I am getting an error that 'setObjectMapper' cannot be resolved. I also tried to do this in the controller but I get the same error.
My Gradle deps for these two libraries are:
// https://mvnrepository.com/artifact/com.mashape.unirest/unirest-java
compile group: 'com.mashape.unirest', name: 'unirest-java', version: '1.4.5'
compile 'com.fasterxml.jackson.core:jackson-databind:2.10.1'
Can anyone show me how to use the Jackson object mapper with Unirest in a Spring Boot API project as I have been googling and reading docs for two days now. Would appreciate some help.
Thank you in advance
You have several issues here:
The version of unirest you're using (1.4.5) does not contain the feature to configure object mapper. This feature was added later (github PR). So you should update to the latest version available at maven central - 1.4.9. This alone will fix your compilation problem.
You can keep your Unirest configuration code in the main method. However if you want to use not default jackson ObjectMapper(), but the one from the spring context, then it's better to create something like a fake spring bean to inject ObjectMapper:
#Configuration
public class UnirestConfig {
#Autowired
private com.fasterxml.jackson.databind.ObjectMapper mapper;
#PostConstruct
public void postConstruct() {
Unirest.setObjectMapper(new ObjectMapper() {
public String writeValue(Object value) {
try {
return mapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public <T> T readValue(String value, Class<T> valueType) {
try {
return mapper.readValue(value, valueType);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
}
Other than that it looks this library changed the package name. Now it's com.konghq. You might want to consider updating, but library API might have changed.
Upd: for the latest version
compile group: 'com.konghq', name: 'unirest-java', version: '3.1.04'
the new API is Unirest.config().setObjectMapper(...)

Spring-Boot Elasticseach EntityMapper can not be autowired

Based on this answer and the comments I implemented the code to receive the scores of an elastic search query.
public class CustomizedHotelRepositoryImpl implements CustomizedHotelRepository {
private final ElasticsearchTemplate elasticsearchTemplate;
#Autowired
public CustomizedHotelRepositoryImpl(ElasticsearchTemplate elasticsearchTemplate) {
super();
this.elasticsearchTemplate = elasticsearchTemplate;
}
#Override
public Page<Hotel> findHotelsAndScoreByName(String name) {
QueryBuilder queryBuilder = QueryBuilders.boolQuery()
.should(QueryBuilders.queryStringQuery(name).lenient(true).defaultOperator(Operator.OR).field("name"));
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder)
.withPageable(PageRequest.of(0, 100)).build();
DefaultEntityMapper mapper = new DefaultEntityMapper();
ResultsExtractor<Page<Hotel>> rs = new ResultsExtractor<Page<Hotel>>() {
#Override
public Page<Hotel> extract(SearchResponse response) {
ArrayList<Hotel> hotels = new ArrayList<>();
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
try {
Hotel hotel = mapper.mapToObject(hit.getSourceAsString(), Hotel.class);
hotel.setScore(hit.getScore());
hotels.add(hotel);
} catch (IOException e) {
e.printStackTrace();
}
}
return new PageImpl<>(hotels, PageRequest.of(0, 100), response.getHits().getTotalHits());
}
};
return elasticsearchTemplate.query(nativeSearchQuery, rs);
}
}
As you can see I needed to create a new instance of DefaultEntityMapper mapper = new DefaultEntityMapper(); which should not be the case because it should be possible to #Autowire EntityMapper. If I do so, I get the exception that there is no bean.
Description:
Field entityMapper in com.example.elasticsearch5.es.cluster.repository.impl.CustomizedCluserRepositoryImpl required a bean of type 'org.springframework.data.elasticsearch.core.EntityMapper' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.data.elasticsearch.core.EntityMapper' in your configuration.
So does anybody know if its possible to autowire EntityMapper directly or does it needs to create the bean manually using #Bean annotation.
I use spring-data-elasticsearch-3.0.2.RELEASE.jar where the core package is inside.
My pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
</dependency>
I checked out the source code of spring-data-elasticsearch. There is no bean/comoponent definition for EntityMapper. It seems this answer is wrong. I test it on my project and get the same error.
Consider defining a bean of type 'org.springframework.data.elasticsearch.core.EntityMapper' in your configuration.
I couldn't find any other option by except defining a #Bean

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);
}
}

Resources