SpringCloud远程调用服务
使用DiscoveryClient
@Autowired
DiscoveryClient client;
注意导的包是: org.springframework.cloud.client.discovery.DiscoveryClient;
// 查看注册的实例
List<String> services = client.getServices();
// 通过服务serviceId获取服务实例,可能配置多个(分布式)
List<ServiceInstance> serviceInstances = client.getInstances("providerclinet");
// 获取主机地址和端口
String host = ins.getHost();
int port = ins.getPort();
// 在补充上服务地址即可调用相关服务
// hi
String url = "http://"+hostName + ":"+port + "/hi";
RestTemplate restTemplate = new RestTemplate();
// RestTemplate调用微服务
String repsStr = restTemplate.getForObject(url, String.class);
使用EurekaClient
@Autowired
EurekaClient client2;
导包是: com.netflix.discovery.EurekaClient;
// 通过Eureka中注册的服务Id,获取实例信息
List<InstanceInfo> instanceInfos = client2.getInstancesByVipAddress("providerclinet", false);
// 获取单个实例
InstanceInfo instanceInfo = instanceInfos.get(0);
// 获取主机地址和端口
String hostName = instanceInfo.getHostName();
int port = instanceInfo.getPort();
// 获取实例
InstanceInfo.InstanceStatus status = instanceInfo.getStatus();
if (status== InstanceInfo.InstanceStatus.UP) {
String url = "http://"+hostName + ":"+port + "/hi";
RestTemplate restTemplate = new RestTemplate();
// RestTemplate调用微服务
String repsStr = restTemplate.getForObject(url, String.class);
System.out.println("repsStr: "+repsStr);
}
Ribbon负载均衡
现在Ribbon的依赖直接增加到了Eureka Clinet的依赖中了。
ribbon的使用:
通过注入
@Autowired
LoadBalancerClient lb;
// ribbon 完成负载均衡,过滤掉down了的节点
ServiceInstance instance = lb.choose("providerclinet");
String url ="http://" + instance.getHost() +":"+ instance.getPort() + "/hi";
// 通过RestTemplate调用
String respStr = restTemplate.getForObject(url, String.class);
ribbon的特点
ribbon是客户端负载均衡,就是根据自己的情况做负载。在客户端负载均衡中,所有的客户端节点都有一份自己要访问的服务端代理的地址,服务代理则知道所有服务端的地址。
ribbon作为Spring Cloud的负载均衡机制的实现:
- Ribbon可以单独使用,作为一个独立的负载均衡组件。只是需要我们手动配置服务地址列表。
- Ribbon与Eureka配合使用时,Ribbon可自动从EurekaServer获取服务提供者地址列表(DiscoveryClient),并基于负载均衡算法,请求其中一个服务提供者实例。
- Ribbon与OpenFeign和RestTemplate进行无缝对接,让二者具有负载均衡的能力。OpenFeign默认集成了ribbon。
ribbon的负载均衡算法
- ZoneAvoidanceRule: 区域权衡策略,复合判断Server所在区域的性能和Server的可用性,轮询选择服务。
- BestAvailableRule:最低并发策略,过滤掉由于多次访问故障而处于断路器跳闸装的服务,再选择一个并发量最小的服务。
- RoundRobinRule:轮询策略,按顺序循环选择一个server。
- RandomRule:随机策略,随机选择一个服务器。
- AvailabilityFilteringRule:过滤策略,会先过滤掉多次访问故障而处于断路器跳闸状态的服务和过滤并发的连接数量超过阀值得服务,然后对剩余的服务列表安装轮询策略进行访问。
- WeightedResponseTimeRule:响应时间加权策略。根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大,容易被选中的概率就越高。
- RetryRule:重试策略。按照RoundRobinRule(轮询)的策略,获取某个服务失败则在指定的时间会进行重试,直到超时。
如何进行切换负载均衡的策略
注解方式:
@Bean
public IRule myRule(){
//return new RoundRobinRule();
//return new RandomRule();
return new RetryRule();
}
配置文件:
针对服务定ribbon策略:
provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
给所有服务定ribbon策略:
ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
Ribbon脱离Eureka
ribbon.eureka.enabled=false
# 指定服务端的地址
ribbon.listOfServers=localhost:80,localhost:81
RestTemplate的使用
getForObject(url,classType,params)
请求返回一个指定类型的数据;参数传递占位符则指定多个,也可以传递Map来封装参数。
例子:
@Autowired
RestTemplate restTemplate;
@GetMapping("/test1")
public void test1(){
String url = "http://providerclinet/hi";
String res = restTemplate.getForObject(url, String.class);
System.out.println(res);
}
这里边的RestTemplate是注入到Spring容器中的:
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
@LoadBalanced: Ribbon实现客户端负载均衡
参数传递:
@GetMapping("/test2")
public void test2(){
String url = "http://providerclinet/hi?name={1}";
String res = restTemplate.getForObject(url, String.class,"韩梅梅");
System.out.println(res);
}
@GetMapping("/test3")
public void test3(){
// 在url上拼接上参数
String url = "http://providerclinet/hi?name={name}";
Map<String,String> map = new HashMap<>();
// 再用map封装参数
map.put("name","韩梅梅");
String res = restTemplate.getForObject(url, String.class,map);
System.out.println(res);
}
getForEntity(url,classType,params)
参数用法和上个差不多,这个返回值是一个ResponseEntity;包装一些haaders,status,body信息。
例子:
@GetMapping("/client12")
public void client12(){
String url = "http://providerclinet/getMap";
ResponseEntity<Map> entity = restTemplate.getForEntity(url, Map.class);
// 在body中包装返回的内容
System.out.println(entity.getBody());
}
/*
返回的是对象
*/
@GetMapping("/client13")
public void client13(){
String url = "http://providerclinet/getPerson";
ResponseEntity<Person> entity = restTemplate.getForEntity(url, Person.class);
System.out.println("respStr"+ ToStringBuilder.reflectionToString(entity.getBody()));
}
传递参数方式与"getForObject"类似
psot方式调用
例子:
@GetMapping("/client16")
public void client16(){
String url = "http://providerclinet/postParam";
String name = "abc";
ResponseEntity<Person> entity = restTemplate.postForEntity(url,name,Person.class);
System.out.println("respStr"+ ToStringBuilder.reflectionToString(entity.getBody()));
}
postForLocation: 重定向
@GetMapping("/client17")
public Object client17(HttpServletResponse response) throws Exception {
String url = "http://providerclinet/postForLocation";
Map<String, String> map = Collections.singletonMap("name", " abc");
URI location = restTemplate.postForLocation(url, map);
System.out.println(location);
response.sendRedirect(location.toURL().toString());
return null;
}
提供服务:
@PostMapping("/postForLocation")
public URI postForLocation (@RequestBody Person person, HttpServletResponse response) throws URISyntaxException {
URI uri = new URI("https://www.baidu.com/s?wd="+person.getName().trim());
// 需要设置头信息,不然返回的是null
response.addHeader("Location",uri.toString());
return uri;
}
在RestTemplate设置拦截器
实现ClientHttpRequestInterceptor接口来实现拦截器
例子:
public class LoggingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
// 拦截的请求进行操作
System.out.println("拦截请求!!!");
System.out.println(request.getURI());
ClientHttpResponse execute = execution.execute(request, body);
System.out.println(execute.getHeaders());
return execute;
}
}
在刚才写的RestTemplate增加拦截器:
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
// 增加实现的拦截器
restTemplate.getInterceptors().add(new LoggingClientHttpRequestInterceptor());
return restTemplate;
}