Resilience4j and Spring Actuator - Open circuit killing service - spring-boot-actuator

I have added the following dependency to my Spring Boot project
implementation 'io.github.resilience4j:resilience4j-spring-boot2:0.14.1'
When a circuit breaker opens, I get the following response on my actuator/health endpoint, with status code 503 Service Unavailable:
{
"status": "DOWN",
"details": {
"diskSpace": {
"status": "UP",
"details": {
"total": 499963174912,
"free": 432263229440,
"threshold": 10485760
}
},
"refreshScope": {
"status": "UP"
},
"getFlightInfoCircuitBreaker": {
"status": "DOWN",
"details": {
"failureRate": "100.0%",
"failureRateThreshold": "2.0%",
"maxBufferedCalls": 1,
"bufferedCalls": 1,
"failedCalls": 1,
"notPermittedCalls": 1,
"state": "OPEN"
}
}
}
}
My AWS ECS container health check uses this endpoint to determine its health, and restarts the container on a non-200 response.
As I do not want my service to be restarted when a circuit breaker opens, is there a way to have a circuit breaker being open, without causing the status of the service to be down?
I am aware of the registerHealthIndicator: false property to get round this issue, but this removes the circuit breaker stats from actuator, which I would still like to see.

Since 1.2.0 you can set the allowHealthIndicatorToFail to false for this.

I can think of two possibilities.
1) Create a custom HealthIndicator based on resilience4j's code:
https://github.com/resilience4j/resilience4j/blob/master/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java
You would need to return Health.up() or Health.unknown() to avoid the 503 from the /health endpoint.
2) Disable the resilience4j's health indicator and get the same information from the metrics actuator endpoint.

Related

Configuring retry policy for grpc request

I was trying to configure a retry policy from the client side for for some grpc services but it's not behaving the way I expect it to behave so I might be misunderstanding how retry policy works in grpc or there's a mistake in the policy. Here's the policy:
var retryPolicy = `{
"methodConfig": [{
"name": [{"service": "serviceA"}, {"service":"serviceB"}],
"timeout":"30.0s",
"waitForReady": true,
"retryPolicy": {
"MaxAttempts": 10,
"InitialBackoff": ".5s",
"MaxBackoff": "10s",
"BackoffMultiplier": 1.5,
"RetryableStatusCodes": [ "UNAVAILABLE", "UNKNOWN" ]
}
}]
}`
What I expected was that if the client's grpc request to a method defined in one the services(serviceA or serviceB) failed then I expect a retry and since waitForReady is true the client will block the call until a connection is available (or the call is canceled or times out) and will retry the call if it fails due to a transient error. But when I purposefully down the server which this request is going to. The client gets an Unavailable grpc status code and error is: Error while dialing dial tcp xx.xx.xx.xx:xxxx: i/o timeout but the client didn't get this error message 30 seconds later, instead received this error right away. Could the reason be because of how I'm giving the service names? Does it need the path of the file where the service is defined? For a bit more context, the grpc service is defined in another package which the client imports. Any help would be greatly appreciated.
Looking through the documentation, came across this link: https://github.com/grpc/grpc-proto/blob/master/grpc/service_config/service_config.proto and on line 72 it mentions
message Name {
string service = 1; // Required. Includes proto package name.
string method = 2;
}
I wasn't adding the proto package name when listing the services. So the retry policy should be:
var retryPolicy = `{
"methodConfig": [{
"name": [{"service": "pkgA.serviceA"}, {"service":"pkgB.serviceB"}],
"timeout":"30.0s",
"waitForReady": true,
"retryPolicy": {
"MaxAttempts": 10,
"InitialBackoff": ".5s",
"MaxBackoff": "10s",
"BackoffMultiplier": 1.5,
"RetryableStatusCodes": [ "UNAVAILABLE", "UNKNOWN" ]
}
}]
}`
where pkgA and pkgB are the proto package names.

Sabre Revalidate Itinerary with Ancillaries

I'm currently working on the integration of the API to be able to search, confirm the price and book.
Currently we have a problem on the second step:
What I'm trying to get is to have a revalidate response having also all Ancillary and Baggage (hand and hold, also with a fee) to be able to create the page to show the information about the reservation.
I've tried to add the following (in a successfully request):
"TravelPreferences": {
"AncillaryFees": {
"Enable": true,
"Summary": true
},
"TPA_Extensions": {
"VerificationItinCallLogic": {
"Value": "B"
}
}
},
but I'm getting following error:
AIR EXTRAS SUMMARY REQUEST REQUIRES AT LEAST ONE GROUP CODE
Error during Processing
For the luggage, with this part
"Baggage": {
"CarryOnInfo": true,
"Description": true
},
I'll get info about baggage but no prices.
Any idea?
Thank you!

WireMock To Use As Proxy For SOAP Service

Here is the scenario I'm trying to work on:
I'm writing Contract Driven Tests using Spring Cloud Contract. The tests for inter-communication between the microservices works fine.
Some microservices are calling SOAP-based services. As part of integration tests, I'm trying to use
WireMock as a proxy for the SOAP-based services. Basically, the WireMock should intercept the call, then call the target live environment with the same request, return the same response to the test as a stub.
Unfortunately, I couldn't find any examples how to proceed with that. These services use the HTTP protocol. Any examples of how or any pointers to achieve this would be great. Thanks!
Firstly you need to point your SOAP client to the WireMock base URL, so e.g. if you're using a Spring properties file you might have something like this:
soap.api.host=wiremock-host.internal
soap.api.port=8888
Then you need to configure the WireMock server with a low-priority, broad matching proxy stub. Here's an example of how that would look in JSON form:
{
"priority": 8,
"response": {
"proxyBaseUrl" : "http://target.soap.endpoint"
}
}
Then finally, you would create additional stubs (at the default priority) for each request you want to intercept e.g.
{
"request": {
"method": "POST",
"urlPath": "/v1/some/thing",
"headers": {
"SOAPAction": {
"contains": "MyAction"
}
}
},
"response": {
"status": 200,
"body": "<soap:Envelope ..."
}
}

How can I retrieve a RingCentral call recording from a monitored incoming call?

I'm monitoring incoming calls on RingCentral by listening for the Call Session Notifications (CSN) telephony/sessions event filter:
/restapi/v1.0/account/~/extension/~/telephony/sessions
From this, I will receive events like the following. The recordings property will appear to indicate a recording is available. How can I retrieve this recording?
{
"uuid":"12345678901234567890",
"event":"/restapi/v1.0/account/11111111/extension/22222222/telephony/sessions",
"timestamp":"2019-03-08T22:30:40.059Z",
"subscriptionId":"11112222-3333-4444-5555-666677778888",
"ownerId":"33333333",
"body":{
"sequence":7,
"sessionId":"1234567890",
"telephonySessionId":"1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
"serverId":"10.11.12.13.TAM",
"eventTime":"2019-03-08T22:30:39.938Z",
"parties":[
{
"accountId":"11111111",
"extensionId":"22222222",
"id":"cs12345678901234567890-2",
"direction":"Inbound",
"to":{
"phoneNumber":"+16505550100",
"name":"Jane Doe",
"extensionId":"22222222"
},
"from":{
"phoneNumber":"+14155550100",
"name":"John Smith"
},
"recordings":[
{
"id":"44444444",
"active":false
}
],
"status":{
"code":"Answered",
"rcc":false
},
"missedCall":false,
"standAlone":false,
"muted":false
}
],
"origin":{
"type":"Call"
}
}
}
There are two ways to retrieve the recording using information in the Call Session Notification (CSN) event, specifically the recordings[0].id property and the sessionID property.
retrieving a full media URL by calling the call-log endpoint with the sessionId property
manually creating recording media URL using the recordings[0].id property.
Note 1: While the call is ongoing, the recording will not be available for retrieval, even when the recording id is present in the Call Session Notification event. The recording will be available to be retrieved shortly after the call concludes.
Note 2: Call recordings can be in MP3 or WAV format determined by the company. To distinguish check the response Content-Type header for the MIME type when retrieving the recording media file.
1) Retrieving Full Medial URL via Call Log API
Making an intermediate API call to the call-log API has the dual benefits of being the official approach for receiving a media URL an providing more metadata for the call. In this approach, the recording.id in the call-log record will match the recordings[0].id property in the Call Session Notification event.
Both the company account and user extension call-log APIs can be called with the sessionId parameter from the event as shown:
GET /restapi/v1.0/account/~/call-log?sessionId={sessionId}
GET /restapi/v1.0/account/~/extension/~/call-log?sessionId={sessionId}
In this example, the sessionId is 1234567890 so you would have a Company Call Log API URL as follows
GET /restapi/v1.0/account/~/call-log?sessionId=1234567890
The response object will have a recording property that provides hypermedia links to get the media file. The file can be WAV or MP3 format which is communicated in the response Content-Type header.
{
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/11111111/extension/22222222/call-log?view=Simple&sessionId=1234567890&page=1&perPage=100",
"records": [
{
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/11111111/extension/22222222/call-log/1234567890ABCDEFGabcdefgh?view=Simple",
"id": "1234567890ABCDEFGabcdefgh",
"sessionId": "1234567890",
"startTime": "2019-03-08T22:30:29.505Z",
"duration": 35,
"type": "Voice",
"direction": "Inbound",
"action": "Phone Call",
"result": "Accepted",
"to": {
"phoneNumber": "+16505550100",
"name": "Jane Doe"
},
"from": {
"phoneNumber": "+14155550100",
"name": "John Smith",
"location": "San Francisco, CA"
},
"recording": {
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/11111111/recording/44444444",
"id": "44444444",
"type": "OnDemand",
"contentUri": "https://media.ringcentral.com/restapi/v1.0/account/111111111/recording/44444444/content"
},
"extension": {
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/111111111/extension/22222222",
"id": 22222222
},
"reason": "Accepted",
"reasonDescription": "The call connected to and was accepted by this number."
}
],
"paging": {
"page": 1,
"perPage": 100,
"pageStart": 0,
"pageEnd": 0
},
"navigation": {
"firstPage": {
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/11111111/extension/22222222/call-log?view=Simple&sessionId=1234567890&page=1&perPage=100"
},
"lastPage": {
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/11111111/extension/22222222/call-log?view=Simple&sessionId=1234567890&page=1&perPage=100"
}
}
}
2) Manually Creating Media URL
You can call the Recording API endpoint and retrieve the media directly by manually constructing the recording URL as follows:
https://media.ringcentral.com/restapi/v1.0/account/{accountId}/recording/{recordingId}/content
In this example, the accountId is 11111111 and the recordingId is 44444444 for the following:
https://media.ringcentral.com/restapi/v1.0/account/11111111/recording/44444444/content
The accountId in the URL path can be set to the currently authorized user's account using ~. Alternately, it can be set explicitly by extracting the accountId from the event property or using the accountId property in the relevant party object. Using ~ is the recommended way to set accountId.
Note: This this approach can be quick, it may be error prone as RingCentral has changed the media hostname once in the past. While there are no anticipated, future changes, calling the call-log API and retrieving the full media URL from the response is the safer and recommended approach. See below for this approach. This is only included as some people will try this and potentially run into issues later.
3) Hybrid Approach
The first approach of calling the call-log end point is the recommended approach, however, it involves an extra API call and most of the time the second approach should work fine.
A hybrid approach is to construct the URL as in approach 2 and then fall back to approach 1 if approach 2 returns a 404 or other error.

How can I consume Spring Hateoas embedded?

I am studying Spring Projects (Web, Security, DATA JPA, Hateoas)
And I am developing sample web service.
It is consist of two module.
Rest API server (provide data, Spring Data JPA, Spring Data Rest, Spring Hateoas)
Web server (provide Web service, Spring MVC, Spring Security)
Actually "Rest API server" is simple and easy.
I just defined some Entity class, and just use "#RepositoryRestResource"
#RepositoryRestResource generated RestController, right?
So when I call REST API like "localhost:8080/users", I can receive the responses.
But there is very critical issue.
I got the response like below:
{
"_links":{
"self":{
"href": "http://localhost:8888/users{?page,size,sort}",
"templated": true
},
"search":{
"href": "http://localhost:8888/users/search"
}
},
"_embedded":{
"users":[
{
"email": "test#gmail.com", "name": null, "isShowName": null, "nickName": null
}
]
},
"page":{
"size": 20,
"totalElements": 5,
"totalPages": 1,
"number": 0
}
}
When I print the response as toString, there are no "Embedded" values.
There are just "link" values.
I need the "Embedded" values.
I tried to googling, but there are no clear resolutions.
So I am contemplating using not Hateoas but RestController.
If I use RestController, I can resolve this easily.
Who can help me please?

Resources