Ribbon Spring provides a simple and convenient template class for API calls. It is RestTemplate.

  1. First let’s look at how the GET request is used: add two interfaces to the HouseController of the fsh-house service. One passes parameters via @RequestParam and returns an object information. The other passes the argument via @pathvarable, which returns a string. Try to assemble different forms through the two interfaces, as shown in the code below.

@GetMapping(“/data”)public HouseInfo getData( @RequestParam(“name”) String name) {

Return new HouseInfo(1L, "Shanghai", "Hongkou", "Dongti Residential Area ");

}

@GetMapping(“/data/{name}”)

public String getData2(@PathVariable( “name”) String name) {

return name;

} The restTemplate is used in the fsh-Substitution service to invoke the two interfaces we just defined, as shown in the following code.

@GetMapping(“/data”)

public HouseInfo getData(@RequestParam(“name”) String name) {

return restTemplate.getForobject(
    ");

}

@GetMapping(“/data/{name}”)

public String getData2(@PathVariable(“name”) String name) {

return restTemplate.getForobject(

  "{name}",String.class,name);

} fetching data results is done with the getForObject method of RestTemplate (as shown in the code below), which has three overloaded implementations:

URL: The API address of the request, which comes in two forms, one as a string, and the other as a URL. ResponseType: The type of the return value. UriVariables: PathVariable parameter, there are two ways, one is a variable parameter, the other is a Map form. public

T getForobject(String url, Class

responseType,

object... uriVariables);

public <T> T getForobject (String url, Class<T> responseType ,

Map<String, ? > uriVariables) ;

public

T getForobject(URI url, Class

responseType) ; In addition to GetForObject, we can also use GetFoEntity to retrieve data, in the code shown below.

@GetMapping(” /data”)public HouseInfo getData(@RequestParam(“name”) String name) {

ResponseEntity<HouseInfo> responseEntity = restTemplate.getForEntity( "http:/ /localhost: 8081 /house/ data? name= "+name, HouseInfo.class) ; if (responseEntity.getStatusCodeValue() == 200) { return responseEntity.getBody(); } return nu1l ;

} getForentity; the returned status code, request, etc., and the content of the response can be obtained through getBody. The rest, like getForObject, have three overloads.

Now let’s see how to use POST to call an interface. Add a save method to the HouseController to receive the HouseInfo data, as shown in the code below.

@PostMapping(“/save”)public Long addData(@RequestBody HouseInfo houseInfo) {

System.out.println(houseInfo. getName());
return 1001L;

} then write the calling code to be called with PostForObject, as shown below.

@GetMapping(“/save”)public Long add(){

HouseInfo houseInfo = new HouseInfo(); HouseInfo. SetCity (" Shanghai "); HouseInfo. SetRegion (" hongkou "); houseInfo.setName( "XXX"); Long id = restTemplate.postFor0bject( "http: //1ocalhost:8081/ house/save",houseInfo,Long.class); return id;

} PostForObject also has three overloaded implementations. In addition to PostForObject, we can also use the PostFoEntity method, the same way, as shown in the following code.

public <T> T postForobject(String url, object request,

Class<T> responseType, object... uriVariables);

public <T> T postForobject(String url, object request,

Class<T> responseType, Map<String, ? > urivariables);

public

T postForobject(URI url, object request, Class

responseType); In addition to the GET and POST methods, the RESTTemplate also provides operations such as PUT, DELETE, and, more useful, exchange. Exchange can perform GET, POST, PUT, and DELETE requests in four ways.

  1. To integrate the Ribbon in a Spring Cloud project, you simply need to add the following dependencies to the pom.xml. You don’t need to configure them because the Ribbon is already referenced in Eureka, as shown in the code below.

<dependency>

<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>

The configuration we add in fangjia-fsh-substitution-service.

We reworked the previous code a little and output some content to prove that our integrated Ribbon works.

Adapt the Hello interface to print the port of the current service to distinguish the called service, as shown in the following code.

@RestController@RequestMapping(“/house” )public class HouseController {

@Value("${server .port}")
private String serverPort; 

@GetMapping("/hel1o")
public String hel1o(){
    return "Hello"+serverPort;
}

} The above code starts the two services on ports 8081 and 8083 respectively, which will be used later.

Then modify the code for the CallHello interface to output the result of the call to the console, as shown in the following code.

@RestController@RequestMapping(“/substitution”)public class Substitut ionController{

@Autowired
private RestTemplate restTemplate;
@GetMapping ("/cal1Hel1o")
public String cal1Hello(){
    String result = restTemplate. getFor0bject(
        ");
    System.out.print1n("调用结果: " + result);
    return result ;
}

} Test steps are as follows:

Restart the service. Access interface. Look at the console output to see if the load is working.

I’m sure you have a question: Why is it that after adding @LoadBalanced to the RestTemplate, RestTemplate can be combined with Eureka, can call interfaces using service names, and can load balance?

This is due to the fact that the Spring Cloud does a lot of the underlying work for us, because it encapsulates all of this and makes it so easy to use. Frameworks are designed to simplify code and improve efficiency.

The main logic is to add an interceptor to the RestTemplate, replace the address of the request prior to the request, or select the service address according to the specific load policy and then invoke it. This is how @LoadBalanced works.

Let’s implement a simple interceptor and see if we can enter it before calling the interface. We’re not going to do anything, we’re just going to print out a sentence that says we can get in. This is shown in the following code.

public class MyLoadBalancerInterceptor implements ClientHttpRequestInterceptor{

@Override public ClientHttpResponse intercept (final HttpRequest request , final byte[] body, final ClientHttpRequestExecution execution) throws IOException{ final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); System.out.println(" Enter the custom request interceptor "+ ServiceName); return execution.execute(request, body); }

} Once the interceptor is ready, we define another annotation, copy the @LoadBalanced code and change the name, as shown in the code below.

@Target({ ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Qualifier public @Interface MyLoadBalanced {} Then defines a configuration class to give the RestTemplate interceptor, as shown in the code below.

@Configurationpublic class MyLoadBalancerAutoConfiguration{

@MyLoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); @Bean public MyLoadBalancerInterceptor myLoadBalancerInterceptor() { return new MyLoadBalancerInterceptor(); } @Bean public SmartInitializingSingleton myLoadBalancedRestTemplateInitializer() { return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { for (RestTemplate restTemplate : MyLoadBalancerAutoConfiguration.this.restTemplates){ List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors()); list.add(myLoadBalancerInterceptor()); restTemplate.setInterceptors(list); }}}; }

} to maintain a @ MyLoadBalanced RestTemplate list, the RestTemplate in SmartlnitializingSingleton blocker Settings.

Then change our previous RESTTemplate configuration and change @LoadBalanced to our own custom @MyLoadBalanced, as shown in the code below.

@Bean//@LoadBalanced@MyLoadBalancedpublic RestTemplate getRestTemplate(){

return new RestTemplate() ;

} Restart the service and you can see the output of the console, which proves that the interceptor was entered when the interface was called. The output is as follows:

This small example will give you a good idea of how @LoadBalanced works. Next let’s look at the source code is how a logic.

Let’s start with the configuration class, how to set up an interceptor for RestTemplate, The code in the spring — cloud commonsjar orgspringframework. Cloud. Client. Loadbalancer. LoadBalancerAutoConfiguration class by looking at the inside LoadBalancerAutoConfiguration page + source code, you can see here is also maintains a @ LoadBalanced RestTemplate list, as shown in the following code.

@LoadBalanced@Autowired(required = false)private List<RestTemplate> restTemplates = Collections.emptyList(); @Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializer(

final List<RestTemplateCustomizer> customizers) { return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { for(RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer:customizers) { customizer.customize(restTemplate); }}}};

} Let’s look at the interceptor configuration. You can see that the interceptor is in the LoadBalancerInterceptor, and the RestTemplate Customizer is used to add the interceptor, as shown in the code below.

@Configuration @conditionalOnMissingClass(“org.springframework.retry.support.RetryTemplate”)static class LoadBalancerInterceptorConfig {

@Bean public LoadBalancerInterceptor ribbonInterceptor ( LoadBalancerClient loadBalancerClient , LoadBalancerRequestFactory requestFactory) return new LoadBalancer Interceptor(loadBalancerClient,requestFactory); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancer Interceptor); restTemplate. setInterceptors(list); }}; }

} the interceptor code at org.www.sangpi.com springframework. Cloud. Client. LoadbalancerLoadBalancerInterceptor, as shown in the following code.

public class LoadBalancerInterceptor imp1ements

ClientHttpRequestInterceptor { private LoadBalancerClient loadBalancer; private LoadBalancerRequestFactory requestFactory; public LoadBalancerInterceptor(LoadBalancerClient loadBalancer , LoadBalancerRequestFactory requestFactory){ this. loadBalancer = loadBalancer; this. requestFactory = requestFactory; } public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) { this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer)); } @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution ) throws IOException { final URI originaluri = request. getURI(); String serviceName = originalUri.getHost(); Assert.state(serviceName ! = nu11, "Request URI does not contain a valid hostname:" + originalUri) ; return this.loadBalancer.execute (serviceName , requestFactory . createRequest(request, body, execution)); }

} The main logic is in the intercept, and the execution is handled by the LoadBalancerClient, which uses the LoadBalancer RequestFactory to build a LoadBalancerRequest object, as shown in the code below.

public LoadBalancerRequest<ClientHttpResponse> createRequest(final

    HttpRequest request, final byte[] body,
        final ClientHttpRequestExecution execution) {
return new LoadBalancerRequest<ClientHttpResponse>() {
    @Override
    public ClientHttpResponse apply(final ServiceInstance instance)
            throws Exception {
        HttpRequest serviceRequest = new 
            ServiceRequestwrapper(request, instance, loadBalancer);
        if (transformers != nu11) {
            for(LoadBalancerRequestTransformer transformer :
                    transformers) {
                serviceRequest =
                        transformer . transformRequest(serviceRequest,
                            instance);
            }
        }
        return execution. execute ( serviceRequest, body) ;
    }
};

} CreateRequest uses ServiceRequestWrapper to execute the replacement URI logic. ServiceRequest Wrapper will URI access to org. Springframework. Cloud. Client. Loadbalancer. Loadbalancer Client# reconstructURI Methods.

The above is the entire implementation process of RestTemplate combined with @LoadBalanced. As for the specific implementation, you can study it by yourself. Here is just an introduction to the principle and the whole process.

When you have some special needs and want to get the corresponding service information through the Ribbon, you can use the LoadBalancer Client to get it. For example, if you want to get the service address of an fsh-house service, you can use the LoadBalancer Client to get it. One can be selected by the choose method of LoadBalancerClient:

@Autowiredprivate LoadBalancerClient loadBalancer; @GetMapping(“/choose”)public object chooseUrl() {

ServiceInstance instance = loadBalancer.choose("fsh-house");
return instance;

} access interface, you can see the following information returned:

{

serviceId: "fsh-house",
server: {
    host: "localhost",
    port: 8081,
    id: "localhost:8081",
    zone: "UNKNOWN",
    readyToServe: true,
    alive: true,
    hostPort: "localhost:8081",
    metaInfo: {
        serverGroup: null,
        serviceIdForDiscovery: null,
        instanceId: "localhost:8081",
        appName: null
    }
},
secure: false,
metadata:
{ },
host: "localhost",
port: 8081,
uri: "

I read about a situation in many blogs: when making a service call, if the network condition is bad, the first call will timeout. There are a number of great people who have come up with solutions to this, such as making the timeout longer, disabling timeouts, and so on. Spring Cloud is currently in rapid development, the version is updated very quickly, we can find the problems are basically the new version of the time to fix, or provide the optimal solution.

The Ribbon’s clients are initialized when the first request is made. If the timeout is short, the time required to initialize the Client plus the time required to request the interface will result in a timeout for the first request.

This problem can be solved by configuring the Eager – Load to initialize the client ahead of time.

Clients = fsh-house ribbon. Eager load. Enabled: Enables the “hungry” mode of the ribbon. Ribbon. Eager – Load. CLIENTS: Specifies the name of the service you want to load, or the service you want to call, separated by commas.