初识Spring Cloud Eureka(三)(Eureka客户端之间 服务的相互调用)

时间:2024-04-13 21:05:44

接着上一篇博客,客户端的创建 我们知道了服务的注册与发现,那么,服务之间是怎么进行互相调用的呢?

我们先看一下服务列表,然后通过实例,来看一下怎么进行服务之间的调用,再稍微看一下源码,看看调用是怎么实现的。

首先,我们按照上一篇博客的方法,创建了三个服务,一个server端,两个client,通过访问server,如下所示:

初识Spring Cloud Eureka(三)(Eureka客户端之间 服务的相互调用)

 使用eureka_client-2调用eureka_client-1的服务,首先在eureka_client-1中定义controller,返回发送过来的值,如下所示:

@RestController
@RequestMapping("test")
public class TestController {

    @GetMapping("getSendStr")
    public String getSendStr(String string) {
        return "收到的消息是:" + string;
    }
}

然后,在 eureka_client-2中定义一个controller,用来获取客户端的请求,然后调用eureka_client-1服务中刚刚定义的方法。

我们先使用RestTemplate的api去进行调用。

使用方法很简单,在启动类配置RestTemplate的Bean,把他交由spring进行管理,然后引入Ribbon的jar包,用来进行负载均衡。首先引入jar,如下所示:

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

 然后,添加bean:

@Bean
//实现负载均衡的注解
@LoadBalanced
public RestTemplate getRestTemplate(){
    return new RestTemplate();
}

接着,就可以实现controller了,如下所示:

@RestController
@RequestMapping("test")
public class GetController {

    //注入刚才交由spring托管的bean
    @Autowired
    RestTemplate restTemplate;

    @GetMapping("sendStr")
    public String sendStr(String string) {
        Map map = new HashMap();
        map.put("string", string);

        //这里的url不用直接写成ip,使用服务名称代替即可,切记,RestTemplate的bean实现是必须添加@LoadBalanced注解,后续会解释为什么
        return restTemplate.getForEntity("http://eureka-client-1/test/getSendStr?string="+string, String.class).getBody();
    }
}

 好了,一切就绪了,为了调试方便,这里直接使用的get方法,在浏览器*问eureka_client-2,然后调用eureka_client-1,接着返回信息到浏览器,如下:

初识Spring Cloud Eureka(三)(Eureka客户端之间 服务的相互调用)

这样就完了一次服务之间的调用了,是不是很简单呢。

那么,这一切究竟是怎么实现的呢?

我们追踪restTemplate.getForEntity,如下图,最终调用了execute方法,此时的url值为:

http://eureka-client-1/test/getSendStr?string=我是消息 ,显然,这样是无法访问到相关服务的,接着向下看。

	@Override
	public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
			throws RestClientException {

		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
		return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
	}

后续调用了DefaultUriBuilderFactory的expand()方法,得到一个UriBuilder,此时我们看,UriBuilder实际上就是把请求连接给拆分开了,其中host部分,就是我们想要调用的服务的名称。如下图:

初识Spring Cloud Eureka(三)(Eureka客户端之间 服务的相互调用)

然后,我们回到RestTemplate,来跟踪执行doExecute方法,先看一下结果,IDEA,alt+鼠标左键即可,可以发现,返回了服务调用的结果:

初识Spring Cloud Eureka(三)(Eureka客户端之间 服务的相互调用)

那里边究竟发生了什么呢?接着,我们追踪到了InterceptingClientHttpRequest的executeInternal()方法,直到此时,我们的url还没有发生变化,那么,秘密肯定就在其中,我们接着追踪:看我们发现了什么?

初识Spring Cloud Eureka(三)(Eureka客户端之间 服务的相互调用)

 LoadBalanceInterceptor拦截器!秘密肯定在他身上,我们先去看他一眼,他在spring-cloud-commons包下,实现了ClientHttpRequestInterceptor接口。知道他之后,我们接着往后走,执行他的intercept方法,拿到了前边准备的host,即此处的serviceName,如下图:

初识Spring Cloud Eureka(三)(Eureka客户端之间 服务的相互调用)

看来到了这里,也是时候施展变换魔法了,首先是执行loadBalancer的execute方法,这个loadBalancer也是大有来头,我们可以看到,它来自ribbon,继承了LoadBalancerClient,作为bean注册到了这里:

初识Spring Cloud Eureka(三)(Eureka客户端之间 服务的相互调用)

 在RibbonLoadBalancerClient中, 执行的execute方法,

 

	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		Server server = getServer(loadBalancer, hint);
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		RibbonServer ribbonServer = new RibbonServer(serviceId, server,
				isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));

		return execute(serviceId, ribbonServer, request);
	}

 获得一个IloadBalancer ,这是通过ribbon的负载均衡策略,再根据传入的serviceid,从spring的context里拿到的,具体的拿法现在先不细说,当然里边的请求的url,已经被变换为真是的请求路径。

后续因为工作上需要使用Nacos,也就是springcloud-alibaba来作为微服务基础,所有后续的内容,会重点在说一下Nacos的使用。