一、Ribbon
Ribbon是Spring Cloud下的一个组件,是一个基于HTTP和TCP的客户端负载均衡工具,是一个负载均衡框架,是基于Netflix实现。我们可以通过Ribbon来实现服务间的调用,同时还可以实现各客户端服务间的负载均衡。
1.现有2个服务,一个为expense,另一个为payment服务,准备使用ribbon实现服务间的调用,payment服务调expense
expense服务为服务的提供方,代码如下:
@RestController
@RequestMapping("/api/report")
public class ReportController {
@GetMapping("/get/info/by/id")
public String getInfo(){
return "success";
}
}
payment服务为服务的消费方:
@RestController
@RequestMapping("/api/wallet")
public class DataContoller {
@Autowired WalletService walletService;
@GetMapping("/hello") public String sayHello(){
return walletService.sayHello();
}
}
service类代码:
@Service
public class WalletService {
@Autowired RestTemplate restTemplate;
public String sayHello() {
String str=restTemplate.getForObject("http://localhost:9096/api/report/get/info/by/id",String.class);
System.out.println(str);
return "expense服务下的:"+str;
}
}
可以发现:我们使用了RestTemplate 类,此处需要写一个Bean,该Bean为:
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
springboot启动的时候,将该Bean装载到spring容器里。
记一下在学习ribbon中遇到的问题:
第一次调用出现如上错误。
服务的代码为:
String str= restTemplate.getForObject("/api/report/get/info/by/id",String.class);
把它修改为:
还是出错
加上http,将url修改为:
String str= restTemplate.getForObject("http://expense/api/report/get/info/by/id",String.class);
再次访问:
调用成功!
接着我们去掉@LoadBalanced注解,不用@LoadBalanced注解,就可以使用使用ip地址来获取expense服务。
@Bean RestTemplate restTemplate(){ return new RestTemplate(); }
String str= restTemplate.getForObject("http://localhost:9096/api/report/get/info/by/id",String.class);
访问如下url, 同样调用成功!
这是为什么呢?
问题是: 当有@LoadBalanced注解时,RestTemplate会根据eureka服务上的应用名去寻找主机。因此调用的时候需要加上eureka上面的应用名。
请求的url为:
String str= restTemplate.getForObject("http://expense/api/report/get/info/by/id",String.class);
这样就能调用成功了!结果如上
2. 怎么请求带有参数的url?
比如我们要传一个id的get请求。
报错:
被调用方:
Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required Long parameter 'id' is not present]
调用方:
org.springframework.web.client.HttpClientErrorException$BadRequest: 400 null
查看源码后,发现getForObject方法的第三个参数需要传一个map,修改代码如下:
此处的RestTemplate没有加上@LoadBalanced注解,如果加的话,url的host需要修改为应用名。
package com.example.hand.wallet.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
@Service
public class WalletService {
@Autowired RestTemplate restTemplate;
public String sayHello() {
Long id=1L;
Map<String,Integer> map=new HashMap<>();
map.put("id",1); String str=
restTemplate.getForObject("http://localhost:9096/api/report/get/info/by/id?id= {id}",String.class,map);
System.out.println(str);
return "expense服务下的:"+str; } }
再次访问:
看一下RestTemplate类的源码:
可以找到常用的GET请求和POST请求对应的方法:
GET请求:
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables)
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables);
public <T> T getForObject(URI url, Class<T> responseType) ;
POST 请求:
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) ;
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType);