「这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战」
通过上一篇文章:初始Eureka,学习到如何基于SpringBoot项目构建Eureka的服务端和客户端,并以此形成微服务的架构模式。
服务注册与发现中心(Eureka)作为微服务的基础和桥梁,每个客户端都可以通过注册中心发现其他存在的服务,并使用合适的调用方法发起请求,这一次我们就来学习如何在微服务中进行请求调用。
1. 生产者提供服务
服务调用之前需要有服务的提供模块,首先要构建一个Eureka的客户端服务来提供一个Web功能服务。
定义的生产者模块内容有:
- 生产者服务的启动端口号为8090
- 具体的功能接口实现
//生产者控制器,接收请求中的id参数并返回数据
@RestController
public class ProducerController {
@RequestMapping("/getProducerInfo")
public String getProducerInfo(@RequestParam("id") String id){
System.out.println("接收到请求,参数id=" + id + ",时间:" + System.currentTimeMillis());
return "hello , this is producer!id=" + id;
}
}
接下来就需要创建另一个消费者模块来调用生产者提供的功能。
2. 消费者调用服务
对于Spring Cloud Netflix项目的Eureka注册中心,服务间的调用方式可以使用RestTemplate或者Feign。
2.1 RestTemplate调用方式
这是Spring Boot提供的用于REST API请求调用的模板类,在微服务下能够很好的实现跨服务的请求。RestTemplate的具体使用方式有:
2.1.1 ip:port/path的请求方式
该方式直接请求调用服务的ip地址和接口路径,此方式类似前端调用后端模式一致,并没有真实用到微服务的架构,对于所有的第三方服务都可以使用该种方式请求。
//ip:port/path 的请求方式
@RequestMapping("/getConsumerInfo/{id}")
public String getConsumerInfo(@PathVariable("id") String id){
RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String,Object> params = new LinkedMultiValueMap<>();
params.add("id", id);
String url = "http://127.0.0.1:8090";
String path = "/getProducerinfo";
String result = restTemplate.postForObject(producer_prefix + path, params, String.class);
return "hello cloud,we get message:" + result;
}
严格的讲这种方式并没有真正使用到微服务方式,且不能支持服务的负载均衡。
2.1.2 LocaBalancerClient和RestTemplate
此方式需要指定提供方注册的服务名进行请求,而负载均衡客户端LocaBalancerClient用来获取提供方在注册中心的信息和真实url地址,并使用RestTemplate发起请求。
//LocaBalancerClient + RestTemplate 的请求方式
@Autowired
private LoadBalancerClient loadBalancerClient;
@RequestMapping("/getConsumerInfo/{id}")
public String getConsumerInfo(@PathVariable("id") String id){
//根据服务名获取服务真实地址
ServiceInstance service = loadBalancerClient.choose("PRODUCT");
String url = String.format("http://%s:%s/getProducerinfo",service.getHost(),service.getPort());
RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String,Object> params = new LinkedMultiValueMap<>();
params.add("id", id);
String result = restTemplate.postForObject(url, params, String.class);
return "hello cloud,we get message:" + result;
}
2.1.3 @LoadBalanced注解配合RestTemplate注入
使用Spring Cloud提供的@LoadBalanced注解配置服务地址自动实现,直接使用服务的注册名称进行请求调用。
此种方式使用方式最简单方便,只需要对RestTemplate进行自定义配置并使用@LoadBalanced注解,并在需要的地方注入RestTemplate对象使用即可,推荐使用该调用方式。
RestTemplate配置信息:
@Configuration
public class RestConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
在调用服务时注入并使用RestTemplate对象
//注入RestTemplate对象进行调用
@Autowired
private RestTemplate restTemplate;
private String producer_prefix = "http://producer";
@RequestMapping("/getConsumerInfo/{id}")
public String getConsumerInfo(@PathVariable("id") String id){
MultiValueMap<String,Object> params = new LinkedMultiValueMap<>();
params.add("id", id);
String result = restTemplate.postForObject(producer_prefix + "/getProducerInfo", params, String.class);
return "hello cloud,we get message:" + result;
}
2.2 Feign微服务调用方式
Feign是一种比较规范的微服务调用方式,这也是Spring Cloud Netflix项目提供的服务调用框架。使用Feign进行服务调用时需要额外引入其依赖信息:
<!-- feign依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Feign使用时,需要在消费模块种定义调用服务的接口类信息,以此作为调用服务的映射服务。
//定义服务提供模块对应服务的映射接口类
@FeignClient(value = "PRODUCER")
public interface ProducerFeign {
@PostMapping("/getProducerInfo")
String getProductById(@RequestParam("id") String id);
}
然后在控制器种定义Feign调用的逻辑,和调用自身业务逻辑层一致,只需要注入映射接口并执行其中的方法就可以返回结果。
//注入并使用ProducerFeign
@Autowired
private ProducerFeign producerFeign;
@RequestMapping("/getConsumerInfoByFeign/{id}")
public String getConsumerInfoByFeign(@PathVariable("id") String id){
String result = producerFeign.getProductById(id);
return "hello cloud,we get message:" + result;
}
最后需要在消费者模块启动类上使用@EnableFeignClients注解来开启Feign功能。
3. 服务调用结果
服务提供模块和调用模块构建完成后,启动Eureka服务端和两个客户端模块,打开Eureka注册中心地址,可以看到producer和customer模块已经注册成功。
然后执行对customer模块的http://localhost:8092/getConsumerInfo接口请求,查看customer是否能够通过内部调用方式请求producer模块的服务。
3.1 RestTemplate调用
使用@LoadBalanced注解和RestTemplate注入的方式请求,得到成功结果返回。
3.2 Feign调用
使用Feign的调用方式调用服务,也可以得到正确的结果信息。
使用RestTemplate和Feign两种方式情况,对customer模块接口请求时,返回结果中包含了producer的返回内容,因此请求是成功的。