Service Invocation
Li Wei
Service Invocation
Overview
In the introductory section, we briefly described how the RestTemplate API enables remote calls between services by sending HTTP requests. However, this approach differs greatly from local method calls, leading to an inconsistent programming experience—sometimes it feels like a remote call, other times like a local call. Therefore, we need to change the development model for remote calls so that remote calls are as simple as local method calls. This is where a component comes into play: we need a remote‑call component.
Ribbon (Overview)
Basic Introduction
Spring Cloud Ribbon is a load‑balancing tool built on Netflix Ribbon. It provides client‑side load‑balancing algorithms and service invocation capabilities. The Ribbon client component offers a range of configurable options such as connection timeout, retries, etc.
Official site: (now in maintenance mode, will be replaced by Load Balancer in the future)
Load balancing (LB) distributes user requests across multiple services, achieving high availability (HA). Common load‑balancing algorithms:
- Round‑robin: selects the first healthy backend server in the pool, then proceeds sequentially.
- Least connections: prefers the server with the fewest active connections (i.e., the least load), useful for long‑lived sessions.
- Hashing: uses a hash of the request source IP to pick a target server, helping ensure a particular user consistently reaches the same server—useful when the application maintains state per user.
Ribbon vs. Nginx Load Balancing
- Nginx performs server‑side load balancing: all client requests go to Nginx, which then forwards them according to its policies.
- Ribbon performs client‑side load balancing: when invoking a microservice, it retrieves the service list from the registry, caches it locally in the JVM, and then makes RPC calls directly.
Centralized LB vs. In‑process LB
- Centralized LB: an independent load‑balancer (e.g., Nginx) sits between consumers and providers, forwarding requests based on a strategy.
- In‑process LB: the LB logic is embedded in the consumer. The consumer queries the service registry for available instances and selects one locally. Ribbon belongs to this category.
Workflow
Spring Cloud Ribbon uses an interceptor that captures RestTemplate requests and rewrites the address. The diagram below summarizes the process:
- Intercept our
RestTemplaterequesthttp://userservice/user/1. RibbonLoadBalancerClientextracts the service name from the URL, i.e.,user-service.DynamicServerListLoadBalancerquerieseurekafor the list of instances ofuser-service.eurekareturns the list, e.g.,localhost:8081,localhost:8082.IRuleapplies the built‑in load‑balancing rule to pick one, saylocalhost:8081.RibbonLoadBalancerClientrewrites the request URL tohttp://localhost:8081/user/1and sends the real request.
Source Code Walkthrough
LoadBalancerInterceptor
The intercept method captures the user's HttpRequest and performs several actions:
request.getURI()obtains the request URI, e.g.,http://user-service/user/8.originalUri.getHost()extracts the host part of the URI, which is the service ID,user-service.this.loadBalancer.execute()processes the service ID and the user request.
Here, this.loadBalancer is of type LoadBalancerClient; we continue tracing.
LoadBalancerClient
Continuing into the execute method:
getLoadBalancer(serviceId)retrieves anILoadBalancerfor the service ID; theILoadBalancerfetches the instance list from Eureka and caches it.getServer(loadBalancer)uses the built‑in load‑balancing algorithm to select an instance. In this example, it picks the service on port 8082.
After the request passes through, a subsequent trace shows the selection of port 8081—confirming that load balancing works.
Ribbon uses lazy initialization by default, creating LoadBalanceClient only on the first request, which can cause a long initial latency. Eager initialization creates it at application startup, reducing the first‑call delay. Enable eager loading with the following configuration:
## (configuration snippet)
Load‑Balancing Strategies
The core Ribbon component is the IRule interface. Main implementations include:
RoundRobinRule: Round‑robinRandomRule: RandomRetryRule: Retry on failure after attempting withRoundRobinRuleWeightedResponseTimeRule: Weighted response time (faster instances get higher weight)BestAvailableRule: Filters out instances in a circuit‑breaker state and selects the one with the fewest concurrent connectionsAvailabilityFilteringRule: Ignores two kinds of servers:- By default, a server that fails three consecutive connections is marked “tripped.” The tripped state lasts 30 seconds and grows exponentially with repeated failures.
- Servers with excessively high concurrent connections. The limit can be configured via the client’s
. .ActiveConnectionsLimitproperty.
ZoneAvoidanceRule: Default rule—considers both the server’s zone performance and its availability.
Note: The official docs warn that custom load‑balancing configuration classes must not reside in the package scanned by @ComponentScan (or its sub‑packages). Generally, stick with the default rule and avoid modifications.
OpenFeign
Basic Introduction
Feign is a declarative web‑service client that simplifies writing HTTP clients: you just define an interface and add the @Feign annotation. It can be combined with Eureka and Ribbon for load balancing, so it’s typically used on the consumer side.
OpenFeign extends Feign with Spring MVC annotations, and the @FeignClient annotation can parse interfaces annotated with @RequestMapping, generating implementations via dynamic proxies that handle load balancing and service calls.
Advantages: By wrapping RestTemplate’s HTTP handling, Feign provides a templated way to invoke services. Since a service may be called from many places, annotating a microservice interface once with an EE annotation (presumably @FeignClient) suffices for all callers.
Basic Usage
Steps to use Feign:
- Add the required dependencies.
- Annotate the main class with
@EnableFeignClients. - Define a FeignClient interface.
- Replace RestTemplate calls with methods defined in the FeignClient.
@FeignClient("provider name") Annotation Rules
- The declared method signatures must match those of the provider microservice’s controller methods.
- If parameters are needed,
@RequestParam,@RequestBody, and@PathVariablemust also be added.
Refactor Consumer Service
- Add the Maven dependency.
- In
application.yml, configure the service not to register itself with Eureka. - Enable Feign in the main application class.
- Create a Service interface: annotate with
PaymentFeignServiceand@FeignClientto wrap Feign calls. - In
ServiceImpl, replace code with direct calls to the Feign client.
Timeout Issues
Feign relies on Ribbon for load balancing and timeout control. Configure Feign client timeout values accordingly.
Connection Pool
Feign’s underlying HTTP calls depend on other frameworks. Supported HTTP client implementations include:
HttpURLConnection(default, no connection pool)- Apache
HttpClient(supports pooling) - OKHttp (supports pooling)
Therefore, it’s common to replace the default HttpURLConnection with a pooled client such as OKHttp.
<!-- Add dependency in cart-service pom.xml -->
Enable the pool in application.yml:
## Feign connection pool settings
Restart the service and the pool becomes active.
Best Practices
To avoid duplicating client interfaces, consider two extraction strategies:
- Strategy 1: Extract common interfaces into a separate module outside the microservices. Simpler and clearer project structure, but increases overall coupling.
- Strategy 2: Each microservice creates its own module. More work and a more complex structure, but reduces inter‑service coupling.
Logging Levels
Feign provides logging capabilities. Configure the log level to see request details:
- NONE – default, no logs.
- BASIC – logs request method, URL, response status, and execution time.
- HEADERS – adds request and response headers.
- FULL – adds request/response bodies and metadata.
By default, Feign uses NONE, so no request logs appear.
Defining a Log Level
- Create a configuration class that sets Feign’s log level.
- Register the class:
- Local: apply to a specific FeignClient.
- Global: configure in
@EnableFeignClientsto affect all FeignClients.
Propagating User Information
Frontend requests pass through the gateway to microservices. Because we have filters and interceptors, a microservice can easily obtain the logged‑in user’s info. However, some business flows are more complex and require calls to multiple downstream services. For example, an order service creates an order, then calls the product service to decrement inventory and the cart service to clear the user’s cart. The cart service needs to know which user is making the request, but the order service does not forward user information when calling the cart service, leaving the cart service unaware of the current user.
Since microservices obtain user info from request headers via interceptors, we must inject the user information into the request headers whenever a microservice initiates a call.
Calls between microservices are made with OpenFeign, not with manually crafted HTTP requests. To automatically attach the logged‑in user’s data to every OpenFeign request, we use Feign’s interceptor interface: feign.RequestInterceptor.
Implement this interface, override the apply method, and use the RequestTemplate class to add the user info to the request headers. Consequently, each OpenFeign request will carry the user data.
Create the interceptor bean in a configuration class, and the user information will be propagated automatically during inter‑service OpenFeign calls.
Originally written by Li Wei (李唯_) and published in Chinese on 后端技术栈全书 (Full-Stack Backend Engineering). Translated and adapted for DriftSeas with permission.