之前我们已经通过Eureka实现了一个服务注册中心,并且在服务注册中心注册了一个服务。现在我们就来学习一下如何去调用注册中心中的服务。
其实,在springcloud中,各个服务之间的互相调用就是远程调用,而远程调用有很多实现方法,比如:webservice调用、直接通过java的http类库(如HttpClient)进行http调用或者是远程方法调用。这里就介绍两种比较常见的调用方式:
在代码中通过使用RestTemplate发送http请求直接调用
webservice调用
这里我们学习一下使用RestTemplate进行服务调用的方法,先创建一个springboot web项目,然后我们将这个项目作为调用者去调用之前在注册中心注册过的那个服务。
在新建的项目中引入以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
启动类:
@EnableDiscoveryClient
@SpringBootApplication
public class ServiceConsumerApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerApplication.class, args);
}
}
其中 RestTemplate就是spring封装的一个java的http类库,它简化了使用java发送http请求的过程,它也是对其他http类库的封装,默认底层使用JDK提供的http连接类,你也可以使用setRequestFactory
方法使用其他的底层类库,如,HttpClient或者OkHttp等。
这里将一个RestTemplate对象作为一个Bean放到spring容器中。后面就通过这个对象发送http请求,完成服务的调用。
@LoadBlanced
注解表面意思看是使它具有负载均衡的能力,但是在这里我们并不想实现负载均衡,一开始我是没有加这个注解的,但是在测试时发现,如果不加这个注解,在后面的服务调用时,必须在代码中指明服务提供者的ip和端口。只有加了这个注解,才可以使用注册中心的服务名称去调用。
配置文件:
spring.application.name=spring-cloud-consumer
server.port=9002
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
可以看出,这里的配置跟之前介绍的服务提供者的配置是一样的。因为服务提供者与服务消费者并没有明显的界限,一个服务既可以是服务的提供者也可以是服务的消费者,他们都是作为一个服务注册在服务注册中心的。
编写Controller类,调用服务:
@RestController
public class ServiceConsumerController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("hello")
public String hello() {
return restTemplate.getForEntity("http://service-provider/provider-hello"
, String.class).getBody();
}
}
可以看到,这与平时我们写的Controller并没有什么不一样。将之前放到spring容器中的TestTemplate对象注入,然后使用该对象发送http请求,获取到响应信息。注意:这里发送请求的URL是http://service-provider/provider-hello
,其中service-provider就是服务提供者在注册中心的服务名称,provider-hello就是要调用接口的RequestMapping。
以此启动Eureka、service-provider、service-consumer,在浏览器发送http://localhost:9002/hello,来访问service-consumer的接口,发现页面上已经打印出service-provider中controller中的输出语句了,说明调用成功。
基于上面的配置,我们的服务调用方已经实现了负载均衡的功能,在service-consumer中调用http://service-provider/provider-hello时,会从注册中心中的多个service-provider服务中选择一个实例来响应请求。下面就来测试一下。
首先对service-provider稍作修改,这里其实并不需要修改service-provider任何配置,只是便于观察实验结果,我们在controller中打印出每个service-provider实例占用的端口号。修改后的service-provider的controller如下:
@RestController
@RequestMapping(value = "/service-provider")
public class ServiceProviderController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/provider-hello")
public String hello() {
return "hello service provider : " + serverPort;
}
}
service-consumer基于之前的配置,无需做任何修改。下面依次启动Eureka注册中心、service-provider、service-consumer。注意,这里我们要启动两个service-provider实例,并且指定他们使用不同的端口。在eclipse中启动时,可以传入指定的参数:
这里我们启动的两个service-provider实例的端口号分别是9001和9003,访问Eureka页面也可以看到我们启动的所有服务:
然后我们到浏览器中多次访问service-consumer接口:http://localhost:9002/hello,观察页面输出结果:
可以看出,service-consumer中是随机调用两个consumer-provider服务的,以此达到负载均衡的目的。
**扩展:**spring cloud实现的是客户端负载均衡,负载均衡分为客户端负载均衡和服务端负载均衡。