Spring Cloud Load Balance and Feign Client - java

I have been using spring-clound-openfeign with Consul as the service registry and Ribbon as Load Balancer. I am currently working with spring-boot 2.3.10.RELEASE.
I really like spring-cloud-feign-inheritance support which, in my understanding, allows me to write a single interface used by the server side and the client side.
https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html#spring-cloud-feign-inheritance
From spring-boot 2.4.x onward, the spring team recommended replacing Ribbon by spring-clould-loadbanced as a replacement since Ribbon is no longer being maintained.
If I have an interface let say:
interface Greeting {
#GetMapping
String hello(String name);
}
By using Spring Openfeign + Ribbon + Consul I could just extend it with:
#FeignClient(name="my-service-id")
interface GreetingClient extends Greeting { }
And with that I would have a client implementation with load balanced capabilities.
Can I still accomplish the same result as spring-cloud-openfeign with spring-clould-loadbalancer or I really need to work with RestTemplate or DiscoveryClient to have the client side of my API?
An over all picture of this would be highly appreciated since nonwhere else I have found a sensible answer.

If you want get work like with ribbon, need connect spring cloud loadbalancer to consul. You can read how connnect spring cloud loadbalanncer to consul into link
https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#serviceregistry

Related

how to get feign client name and url both dynamically in spring boot Java

I am new to feign client implementation , i have following code for current implementation .
FeignPaymentAbcService:
#FeignClient(name= "abc-service", url="abc url")
public interface FeignPaymentAbcService{
//methods
}
invoking call :
(feignPaymentAbcService.someFunctionality("some input")).getBody();
In future there is possibility of multiple feign services like feignPaymentAbcService ,feignPaymentxyz Service etc so according to service it should get feign client name and url in which service is running.
Basically want to make in dynamic way.
Can anybody suggest any approach ?
You can use the Feign along with Ribbon and Eureka server for dynamically getting the url along with the server( or list of urls depending on instances)
#FeignClient(name= "abc-service")
#RibbonClient(name = "abc-service")
but the name has to be there so that the particular service is identified from the app.properties file.
You need to add corresponding dependencies for eureka server & Ribbon Load balancer and you need to configure them in application.properties
You can lookup my sample code here

Differences between netflix.feign & openfeign

Introduction
I recently used netflix feign along with ribbon which was quite useful.
An Example of this is:
#FeignClient(name = "ldap-proxy")
public interface LdapProxyClient {
#RequestMapping(path = "/ldap-proxy/v1/users/{userNameOrEMail}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
LdapUser search(#PathVariable("userNameOrEMail") String userNameOrEMail);
}
However, at some point I thought that instead of having to code all these definitions by hand (for an existing webservice), that I should see if a tool existed.
I stumbled across https://github.com/swagger-api/swagger-codegenand saw that there are examples in which clients are generated, e.g. https://github.com/swagger-api/swagger-codegen/tree/master/samples/client/petstore/java/feign .
However, once I looked closely at the imports I noticed the following:
import feign.Feign;
Netflix's opensource solution on the other hand has package names:
org.springframework.cloud.netflix.feign.
Additionally, I noticed that both use ribbon if available, but Netflix's notation is much cleaner with a lot happenning in the background. E.g. the #FeignClient annotation class javadoc states:
Annotation for interfaces declaring that a REST client with that interface should be * created (e.g. for autowiring into another
component). If ribbon is available it will be * used to load balance
the backend requests, and the load balancer can be configured * using
a #RibbonClient with the same name (i.e. value) as the
feign client.
However in the Feign.feign documentation (at https://github.com/OpenFeign/feign ) I see:
RibbonClient overrides URL resolution of Feign's client, adding smart routing and resiliency capabilities provided by Ribbon.
Integration requires you to pass your ribbon client name as the host
part of the url, for example myAppProd.
> MyService api =
> Feign.builder().client(RibbonClient.create()).target(MyService.class,
> "https://myAppProd");
So my questions are:
what is the history/relationship and differences between the two?
what are the pros and cons of each?
Are they completely different projects with no relation, or did netflix just fork/utilize OpenFeign and modify it to be within their integrated cloud solution? Essentially, did netflix just acquire and integrate different technologies like Discovery, ribbon, and feign from open-source projects?
"Netflix feign" is the old project designation. The last version (dependency below) is dated July 2016.
compile group: 'com.netflix.feign', name: 'feign-core', version:'8.18.0' // OLD
"Open feign" is the new project designation. It's the same project, but was moved to a different git repo and got a new group-id. Its versions start at 9.0.0.
compile group: 'io.github.openfeign', name: 'feign-core', version: '10.0.1' // NEW
See this github issue for a brief history of what happened. Most remarkably, you'll find out that Feign isn't used internally at Netflix anymore. :^o
org.springframework.cloud.netflix.feign is a part of Spring Cloud Netflix project which is a part of Spring Cloud.
Spring Cloud uses OpenFeign under the hood. It extends it to support Spring MVC annotations and makes it a first-class citizen in the Spring Environment by providing integrations for Spring Boot apps through autoconfiguration.
From the documentation:
Feign is a declarative web service client. Spring Cloud adds support
for Spring MVC annotations and for using the same
HttpMessageConverters used by default in Spring Web. Spring Cloud
integrates Ribbon and Eureka to provide a load balanced http client
when using Feign.
Note that in the documentation there is a link to OpenFeign project.
So if you use Spring Boot - it is better and easier to use Spring Cloud OpenFeign integrations.
See also the source code.

Spring-Boot without STOMP (raw websocket)

Edit: Please do not downvote without leaving at least a comment.
I'd like to implement websocket in a SpringBoot application without Message Broker and STOMP. However, I need to access Spring Beans (repositories and services).
The Java API is so simple that I can't understand why Spring made it so complex.
Here's what I need:
I need to implement a websocket endpoint with dynamic URI so I can use #PathParam like this:
#ServerEndpoint(value = "/chat/{username}")
public class wsEndpoint
Using #EnableWebSocket with WebSocketConfigurer and TextWebSocketHandler only allows me to implement static endpoints.
The alternative recommended by Spring documentation is to implement Message Brokers and STOMP, which makes usage (IMHO) unnecessarily complex for most cases.
I really would like to use #EnableWebSocket with WebSocketConfigurer, TextWebSocketHandler and dynamic URIs. Is there a way to do so?

How can I define the forward URLs (service Id + some path) Spring Zuul chooses when it retrieves the list of registered services from Spring Eureka?

Context
I'm developing a proof of concept with Spring Zuul and Eureka. I want Zuul to work as an API Gateway and I want it to discover my microservices registered in Eureka. I want my microservices to be versioned. Zuul, Eureka and each microservice run on their own (I have a separate project for all of them).
I'm using Spring Boot 1.5.3 and Spring Cloud Dalston. But I can upgrade, if necessary.
Now, let's say that I have one microservice configured in the following way in the application.yml:
spring:
application:
name: microservice-v1
server:
port: 11001
eureka:
client:
serviceUrl:
defaultZone: http://localhost:11000/eureka/
This microservice has a GET /microservice REST endpoint configured which I can access from the address http://localhost:11001/microservice. It runs well and it registers itself with Eureka.
This ZuulReverseProxy.java is the main class of my Zuul project:
#SpringBootApplication
#EnableDiscoveryClient
#EnableZuulProxy
public class ZuulReverseProxy {
public static void main(String[] args) {
SpringApplication.run(ZuulReverseProxy.class, args);
}
}
Based on the Spring Cloud Docs, I have this configuration class:
#Configuration
public class ZuulConfiguration {
#Bean
public PatternServiceRouteMapper serviceRouteMapper() {
return new PatternServiceRouteMapper(
"(?<name>^.+)-(?<version>v.+$)",
"${version}/${name}");
}
}
And this is the application.yml of my Zuul project:
spring:
application:
name: zuul
server:
port: 8888
zuul:
prefix: /api
strip-prefix: true
eureka:
client:
serviceUrl:
defaultZone: http://localhost:11000/eureka/
Zuul looks into Eureka, gets the list of registered microservices and creates a mapping for each one according to the PatternServiceRouteMapper above. I'm not ignoring any services for now.
All of the above, allows Zuul to redirect http://localhost:8888/api/v1/microservice/microservice to http://localhost:11001/microservice, for example. In a more general way, it redirects anything with the path /api/v1/microservice/<some-path> to http://localhost:11001/<some-path>
Question
How can I avoid writing microservice twice in the URL, but still be able to request the mapping /microservice in the controller of my microservice?
A solution to this problem could be requesting the mapping / instead of /microservice. But I don't want to do that. The reason is that I'm using Swagger and its Codegen to generate interfaces and if I request the mappings without writing a collection name, I feel that my specification is not as good.
I thought of writing a route Zuul filter, but I couldn't find a good example. Also, I think that if I could alter the code that handles the moment when Zuul tries to map the Eureka services to a path, that could be a solution.
When not in conjunction with Eureka, Zuul allows to specify an input path and a forward URL with address, port and something else. The same should be possible when specifying a Service Id. Maybe I can modify some code in a way that the routing is still dynamic but that I can add that something else.
Would modifying the classes SimpleRouteLocator or ZuulHandlerMapping help? or is another class in charge of that dynamic routing? Also what would be the best way of implementing something like this?
Edit
I realized that when you configure Zuul like this for example:
zuul:
add-proxy-headers: true
sensitive-headers: Cookie,Set-Cookie
routes:
uaa:
path: /uaa/**
service-id: oauth-v1
strip-prefix: false
sensitive-headers:
It does what I want it to do. That strip-prefix is different from the global one.
When I configured my Zuul that way, in the log I see:
... o.s.c.n.zuul.filters.SimpleRouteLocator : Matching pattern:/uaa/**
... : route matched=ZuulProperties.ZuulRoute(id=uaa, path=/uaa/**, serviceId=oauth-v1, url=null, stripPrefix=false, retryable=null, sensitiveHeaders=[], customSensitiveHeaders=true)
When Zuul loads the services registered in Eureka, in the log I see
.... o.s.c.n.zuul.filters.SimpleRouteLocator : route matched=ZuulProperties.ZuulRoute(id=v1/microservice, path=/v1/microservice/**, serviceId=microservice-v1, url=null, stripPrefix=true, retryable=null, sensitiveHeaders=[], customSensitiveHeaders=false)
The only significant difference is that the latter says stripPrefix=true and to solve my problem, it should be false.
I think that it should be possible to change that default behaviour somehow...

Hit external IPs from microservices using Spring Cloud

I'm trying to hit external services from one of my microservices. I'm using Spring Cloud, Eureka for the registry and Spring boot as main framework.
Map<String, String> urlVariables = new HashMap<>();
urlVariables.put("ip_address", IP);
urlVariables.put("port", PORT);
ResponseObject state =
restTemplate.getForObject("http://{ip_address}:{port}/state/", ResponseObject.class, urlVariables);
From what I see, Spring Cloud injects Ribbon as the HTTP client for the Rest Template, and when I try to hit this IP (e.g: 193.172.x.x) it produces the following error:
java.lang.IllegalStateException: No instances available for 193.172.x.x
at org.springframework.cloud.netflix.ribbon.RibbonClientHttpRequestFactory.createRequest(RibbonClientHttpRequestFactory.java:64)
at org.springframework.http.client.support.HttpAccessor.createRequest(HttpAccessor.java:76)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:567)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:540)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:247)
It looks like Ribbon is trying to find a microservice instance with that name instead of looking outside. Is there any way to configure Ribbon to look for external IPs, or is it only for internal use?
You are injecting a #LoadBalanced version of RestTemplate. You have to ensure that your RestTemplate is a plain vanilla one. You can just create it with new RestTemplate(). If it's a bean just add a qualifier to ensure you're injecting a proper version of RestTemplate.
On thing you could try is to use the service id in your code and configure the real instances:
ResponseObject state =
restTemplate.getForObject("http://myExternalService/state/", ResponseObject.class, urlVariables);
Than you configure a static list of endpoints for your service
myExternalService.ribbon.listOfServers=http://ip:port
This way you do not use service discovery for this service.
http://projects.spring.io/spring-cloud/docs/1.0.3/spring-cloud.html#spring-cloud-ribbon-without-eureka

Categories

Resources