hystrix dashboard doesnt load - spring-boot

I can see hystrix dashboard with : http://localhost:8081/hystrix
Also I see my circuits break and fallback methods working as well
Yet I can't see my application's data at : http://localhost:8081/actuator/hystrix.stream
When I look at /actuator/conditions I see following error for Hystrix
"HystrixAutoConfiguration.HystrixServletAutoConfiguration": {
"notMatched": [
{
"condition": "OnClassCondition",
"message": "#ConditionalOnClass did not find required class 'com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet'"
}
],
"matched": []
}
Code
pom.xml
Spring Boot parent version : 2.2.6.RELEASE
Spring Cloud version : Hoxton.SR3
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
application.properties
management.endpoints.web.exposure.include = hystrix.stream, conditions
MainApp
#SpringBootApplication
#EnableEurekaClient
#EnableCircuitBreaker
#EnableHystrixDashboard
#EnableHystrix
public class MovieCatalogServiceApplication {}
CatalogRestController
#RestController
#RequestMapping("/catalog")
public class CatalogRestController {
#Autowired
MovieInfoService movieInfo;
#Autowired
UserRatingsService userRatingInfo;
#RequestMapping("/{userId}")
public List<Catalog> getCatalogsByUserId(#PathVariable("userId") int userId){
UserRatings ratings = userRatingInfo.getUserRating(userId);
return ratings.getUserRatings().stream().map(oneOfRatings -> {
return movieInfo.getCatalog(oneOfRatings);
}).collect(Collectors.toList());
}
}
UserRatingsService
#Service
public class UserRatingsService {
#Autowired
private WebClient webClient;
#HystrixCommand(fallbackMethod="getFallbackUserRating")
public UserRatings getUserRating(int userId) {
return webClient
.get()
.uri("http://user-rated-movies-service/ratingsdata/users/"+ userId)
.retrieve()
.bodyToMono(UserRatings.class)
.block();
}
public UserRatings getFallbackUserRating(int userId) {
UserRatings userRatings = new UserRatings();
userRatings.setUserRatings(Arrays.asList(
new Rating("0", "fallback method of user rating", 0)
));
return userRatings;
}
}

Your Hixtrix port is 8081, so you need to use your endpoint on that 8081 port for metrics work.
E.G.
API-GATEWAY where Hystrix is used on port: 8081
USER-SERVICE port: 9001
Problem:
API-GATEWAY - http://localhost:8081/acturator/hystrix.stream
USER-SERVICE save user - http://localhost:9001/user/
Solution:
API-GATEWAY - http://localhost:8081/acturator/hystrix.stream
USER-SERVICE save user - http://localhost:8081/user/
Metrics now work well and the Hystrix Dashboard can work correctly.

Related

Spring Integraton RSocket and Spring RSocket interaction issues

I created a new sample and slipted the codes into client and server side.
The complete codes can be found here.
There are 3 version of server side.
server None Spring Boot app, using Spring Integration RSocket InboundGateway.
server-boot Reuse Spring RSocket autconfiguration, and created ServerRSocketConnecter through ServerRSocketMessageHanlder.
server-boot-messsagemapping Not use Spring Integration, just use Spring Boot RSocket autconfiguration, and #Controller and #MessageMapping.
There are 2 versions of client.
client, Sending messages using Spring Integration Rocket OutboundGateway.
client-requester Send messages using RSocketRequester, not use Spring Integration at all.
The client and server interaction mode is REQUEST_CHANNEL, and connect server via TCP/localhost:7000.
server
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-rsocket</artifactId>
</dependency>
The application class:
#Configuration
#ComponentScan
#IntegrationComponentScan
#EnableIntegration
public class DemoApplication {
public static void main(String[] args) throws IOException {
try (ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(DemoApplication.class)) {
System.out.println("Press any key to exit.");
System.in.read();
} finally {
System.out.println("Exited.");
}
}
#Bean
public ServerRSocketConnector serverRSocketConnector() {
return new ServerRSocketConnector("localhost", 7000);
}
#Bean
public IntegrationFlow rsocketUpperCaseFlow(ServerRSocketConnector serverRSocketConnector) {
return IntegrationFlows
.from(RSockets.inboundGateway("/uppercase")
.interactionModels(RSocketInteractionModel.requestChannel)
.rsocketConnector(serverRSocketConnector)
)
.<Flux<String>, Flux<String>>transform((flux) -> flux.map(String::toUpperCase))
.get();
}
}
server-boot
Dependencies in pom.xml.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-rsocket</artifactId>
</dependency>
application.properties
spring.rsocket.server.port=7000
spring.rsocket.server.transport=tcp
Application class.
#SpringBootApplication
#EnableIntegration
public class DemoApplication {
public static void main(String[] args) throws IOException {
SpringApplication.run(DemoApplication.class, args);
}
// see PR: https://github.com/spring-projects/spring-boot/pull/18834
#Bean
ServerRSocketMessageHandler serverRSocketMessageHandler(RSocketStrategies rSocketStrategies) {
var handler = new ServerRSocketMessageHandler(true);
handler.setRSocketStrategies(rSocketStrategies);
return handler;
}
#Bean
public ServerRSocketConnector serverRSocketConnector(ServerRSocketMessageHandler serverRSocketMessageHandler) {
return new ServerRSocketConnector(serverRSocketMessageHandler);
}
#Bean
public IntegrationFlow rsocketUpperCaseFlow(ServerRSocketConnector serverRSocketConnector) {
return IntegrationFlows
.from(RSockets.inboundGateway("/uppercase")
.interactionModels(RSocketInteractionModel.requestChannel)
.rsocketConnector(serverRSocketConnector)
)
.<Flux<String>, Flux<String>>transform((flux) -> flux.map(String::toUpperCase))
.get();
}
}
server-boot-messagemapping
Dependencies in pom.xml.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>
The application.properties.
spring.rsocket.server.port=7000
spring.rsocket.server.transport=tcp
The applcition class.
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
#Controller
class UpperCaseHandler {
#MessageMapping("/uppercase")
public Flux<String> uppercase(Flux<String> input) {
return input.map(String::toUpperCase);
}
}
client
In the client, the dependencies in the pom.xml is like.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-rsocket</artifactId>
</dependency>
The application class:
#SpringBootApplication
#EnableIntegration
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public ClientRSocketConnector clientRSocketConnector() {
ClientRSocketConnector clientRSocketConnector = new ClientRSocketConnector("localhost", 7000);
clientRSocketConnector.setAutoStartup(false);
return clientRSocketConnector;
}
#Bean
public IntegrationFlow rsocketUpperCaseRequestFlow(ClientRSocketConnector clientRSocketConnector) {
return IntegrationFlows
.from(Function.class)
.handle(RSockets.outboundGateway("/uppercase")
.interactionModel((message) -> RSocketInteractionModel.requestChannel)
.expectedResponseType("T(java.lang.String)")
.clientRSocketConnector(clientRSocketConnector))
.get();
}
}
#RestController
class HelloController {
#Autowired()
#Lazy
#Qualifier("rsocketUpperCaseRequestFlow.gateway")
private Function<Flux<String>, Flux<String>> rsocketUpperCaseFlowFunction;
#GetMapping(value = "hello", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> uppercase() {
return rsocketUpperCaseFlowFunction.apply(Flux.just("a", "b", "c", "d"));
}
}
When running the client and server application, and try to access the http://localhost:8080/hello by curl.
When using server and server-boot which uses InboundGateway to handle messages, the output looks like this.
curl http://localhost:8080/hello
data:ABCD
When using server-boot-messagemapping, the output is woking as I expected:
data:A
data:B
data:C
data:D
client-requester
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>
The application class:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
#RestController
class HelloController {
Mono<RSocketRequester> requesterMono;
public HelloController(RSocketRequester.Builder builder) {
this.requesterMono = builder.connectTcp("localhost", 7000);
}
#GetMapping(value = "hello", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> uppercase() {
return requesterMono.flatMapMany(
rSocketRequester -> rSocketRequester.route("/uppercase")
.data(Flux.just("a", "b", "c", "d"))
.retrieveFlux(String.class)
);
}
}
When running this client and the 3 servers, and try to access the http://localhost:8080/hello by curl.
When using server and server-boot which uses InboundGateway to handle messages, it throws a class cast exception.
When using server-boot-messagemapping, the output is woking as I expected:
data:A
data:B
data:C
data:D
I do not know where is the problem of the configuration of InboundGateway and OutboundGateway?
Thank you for such a detailed sample!
So, what I see. Both clients (plain RSocketRequester and Spring Integration) work well with plain RSocket server.
To make them working with Spring Integration server you have to do this changes:
The server side:
Add .requestElementType(ResolvableType.forClass(String.class)) into an RSockets.inboundGateway() definition, so it will know to what to convert an incoming payloads.
The client side:
.data(Flux.just("a\n", "b\n", "c\n", "d\n")).
Currently the server side of Spring Integration doesn't treat an incoming Flux as a stream of independent payloads. So, we try to connect all of them into a single value.
The new line delimiter is an indicator that we expect independent values. Spring Messaging on its side does exactly opposite: it checks for multi-value expected type and decode every element in the incoming Flux in its map() instead of an attempt for the whole Publisher decoding.
It's going to be kinda breaking change, but possibly need to consider to fix RSocketInboundGateway logic to be consistent with regular #MessageMapping for RSocket support. Feel free to raise a GH issue!

Generate YAML format response in springboot

I want to generate YAML format type response using Spring boot. can you please help me here to get it out?
Ensure you have the following dependency on the classpath:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson.version}</version>
</dependency>
Then define your own HttpMessageConverter:
class MappingJackson2YamlHttpMessageConverter extends AbstractJackson2HttpMessageConverter {
MappingJackson2YamlHttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.parseMediaType("application/x-yaml"));
}
}
Expose it as a Spring #Bean:
#Configuration
public class JacksonYamlConfig {
#Bean
public MappingJackson2YamlHttpMessageConverter yamlHttpMessageConverter() {
YAMLMapper mapper = new YAMLMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
return new MappingJackson2YamlHttpMessageConverter(mapper);
}
}
And finally configure your controller method to produce YAML:
#GetMapping(produces = "application/x-yaml")
public ResponseEntity<Foo> getFoo() {
...
}

Ribbon MaxAutoRetries properties is not working

I've set a couple of retry configurations in my application.properties file. However, none of them is working when I ran the ribbon application.
//this is my service
#RestController
#SpringBootApplication
public class HelloApplication {
#Value("${server.port}")
private int port;
public static void main(String[] args) {
SpringApplication.run(HelloApplication .class, args);
}
#GetMapping(value="/app")
public String notification() {
return "This Is HelloService running on port:"+ port;
}
}
Here is my RibbonAppApplication class:
#SpringBootApplication(scanBasePackages={"com.netflix.client.config.IClientConfig"})
#RestController
#RibbonClient(name= "hello", configuration=RibbonConfig.class )
public class RibbonAppApplication {
#Autowired
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(RibbonAppApplication.class, args);
}
#GetMapping
public String getService() {
return restTemplate.getForObject("http://hello/app",String.class);
}
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
This is the application.properties for the RibbonAppApplication:
ribbon.eureka.enabled=false
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
hello.ribbon.listOfServers=http://localhost:1111, http://localhost:2222
hello.ribbon.OkToRetryOnAllOperations=false
hello.ribbon.MaxAutoRetries=0
hello.ribbon.MaxAutoRetriesNextServer=1
Thank you guys so much for helping!
Missing dependency of Sprint Retry is almost always the reason for Ribbon not able to retry. Spring Retry a dependency for retry functionality for Zuul/Ribbon.
When a request fails, you may want to have the request be retried automatically. To do so when using Sping Cloud Netflix, you need to include Spring Retry on your application’s classpath. When Spring Retry is present, load-balanced RestTemplates, Feign, and Zuul automatically retry any failed requests (assuming your configuration allows doing so)
Adding Spring Retry to pom.xml should fix this.
Related docs: https://cloud.spring.io/spring-cloud-netflix/multi/multi_retrying-failed-requests.html
You have to add the spring-retry dependency to your pom.xml file:
<!-- https://mvnrepository.com/artifact/org.springframework.retry/spring-retry -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.4.RELEASE</version>
</dependency>

Spring Security + Spring-Boot Testing Controller

I'm trying to test the home controller
#RequestMapping("/")
#ResponseBody
String home() {
return "Hello World!";
}
I'm using spring security using as username "user" and test as password by default but #PreAuthorize is not working
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#PreAuthorize("hasRole('ADMIN')")
public class HomeControllerTest {
#Autowired
private TestRestTemplate restTemplate;
#Test
#WithMockUser(username = "user", password = "test", roles = "ADMIN")
public void home() throws Exception {
String body = this.restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World!");
}
}
The result
Expected result:
<"[Hello World!]">
Actual result:
<"{"timestamp":1501100448216,"status":401,"error":"Unauthorized","message":"Full
authentication is required to access this resource","path":"/"}]">
Am I missing something?
Try to add the following to your test class:
#TestExecutionListeners(mergeMode = MergeMode.MERGE_WITH_DEFAULTS, listeners = {
WithSecurityContextTestExecutionListener.class
})
And the following dependency if you don't have it:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
Spring security require an extra listener that is not present in tests by default so you need to tell spring to add it by specifing the #TestExecutionListeners annotation in merge mode so it will merge the current listed listeners with the listeners you want to add - in this case WithSecurityContextTestExecutionListener

Query parameters reading spring integration

I am new to Spring Integration, I just started looking into the specification. My requirement is to get the HTTP request
(example : http://localhost:8080/LoginCheck?name=xyz&dob=zyz).
Can anybody guide me, how to proceed as i googled and found some information that we can use inbound gateway to read the parameters, my requirement like get the Http client data and do some process and finally respond to client in XML format.
I got stucked in reading the input data only.
You have to get the payload of the received message. There should be a Map with the request parameters.
I have made a simple SI DSL application that does just that
#SpringBootApplication
public class JmsResponderApplication {
public static void main(String[] args) {
SpringApplication.run(JmsResponderApplication.class, args);
}
#Bean
public HttpRequestHandlingMessagingGateway httpGate() {
HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true);
RequestMapping requestMapping = new RequestMapping();
requestMapping.setMethods(HttpMethod.GET);
requestMapping.setPathPatterns("/foo");
gateway.setRequestMapping(requestMapping);
gateway.setRequestChannel(requestChannel());
return gateway;
}
#Bean
public DirectChannel requestChannel() {
return MessageChannels.direct().get();
}
#Bean
public IntegrationFlow flow() {
return IntegrationFlows.from(requestChannel())
.handle(new MessageHandler() {
#Override
public void handleMessage(Message<?> m) throws MessagingException {
Object payload = m.getPayload();
System.out.println(payload); // the payload is a Map that holds the params
System.out.println(m);
}
})
.get();
}
}
It's a simple Spring boot starter project with this dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-java-dsl</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Source - here

Resources