Spring Cloud 细节(二)
五、Eureka的细节
-
服务的注册:
服务把自己的元数据注册到注册中心上,服务会每30秒向注册中心发一次心跳,进行服务续约。
-
服务的剔除:
注册中心每隔60秒,检查本地的地址列表中有没有超过90秒没有续约的服务,进行剔除。
-
自我保护:
如果注册中心发现服务比例(15分钟低于85%在线),那么注册中心就认为当前很有可能会出现误删的情况,于是自我保护就开启了。
六、Ribbon的细节
1.Ribbon中的负载均衡
ribbon的负载均衡策略有多种
默认的负载均衡策略是:roundrobin 轮询策略
- RoundRobin: 轮询
- Random:随机
- WeightedResponseTime: 以响应时间为权重
- Retry:如果在访问第一台不成功后会进行重试
如何修改负载均衡策略,在ribbon的配置类中提供IRule的bean即可
package com.qf.my.product.service.config;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import com.netflix.loadbalancer.WeightedResponseTimeRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RibbonConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
public IRule getRule(){
return new RandomRule();//随机的负载均衡策略
// new WeightedResponseTimeRule(); 根据响应时间为权重的负载均衡策略
}
}
2.Ribbon是如何进行负载均衡的?
七、RestTemplate详解
RestTemplate是Spring封装的一个用来进行Http通信的工具。之前的工具:
-
URLConnection:偏底层,需要加上自己的封装
-
HttpClient:Apache孵化项目,需要引入第三方来实现
-
OkHttp:第三方
1.Http协议包含的部分
- 请求
- 请求头
- 请求行
- 请求体
- 响应- HttpEntity- ResponseEntity
- 响应头
- 响应行
- 响应体
2. RestTemplate发送Get请求
1)getForEntity
getForEntity返回的是ResponseEntity对象,该对象中封装了响应消息头、响应消息行、响应消息体。
- 重载方法一(String url, Class Response)
直接把携带的请求参数写在url后面
ResponseEntity<User> entity = restTemplate.getForEntity("http://user-service/getUser/1/xiaoming", User.class);
//entity是封装了响应消息的,获取的消息体
User user = entity.getBody();
服务提供者如何接收参数:
@GetMapping("/getUser/{id}/{name}")
public User getUser(@PathVariable Long id,@PathVariable String name){
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
- 重载方法二 ("http://user-service/getUserByVP?id={1}&name={2}", User.class,new Object[]{id, name})
用数组来封装请求参数
ResponseEntity<User> entity = restTemplate.getForEntity("http://user-service/getUserByVP?id={1}&name={2}", User.class,
new Object[]{id, name});
User body = entity.getBody();
return body;
服务提供者如何接收参数
@GetMapping("/getUserByVP")
public User getUserByVP(@RequestParam Long id,@RequestParam String name){
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
- 重载方法三("http://user-service/getUserByMap?id={id}&name={name}", User.class,map);
用map来封装请求参数
@Override
public User getUserByMap(Long id,String name){
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("name",name);
ResponseEntity<User> entity = restTemplate.getForEntity("http://user-service/getUserByMap?id={id}&name={name}", User.class,
map);
return entity.getBody();
}
服务提供者接收参数
@GetMapping("/getUserByMap")
public User getUserByMap(@RequestParam Long id,@RequestParam String name){
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
2) getForObject
getForObject和getForEntity的底层逻辑是一样的,只不过getForObject是直接获取响应消息体中的内容。而getForEntity是获取了整个响应消息(头、行、体)。
User user = restTemplate.getForObject("http://user-service/getUserByMap?id={id}&name={name}", User.class,map);
3.RestTemplate的Post请求
restTemplate的post请求比get请求多了一个request参数,这个request参数可以用来封装请求消息头和请求消息体的内容。
- 重载方法一:("http://user-service/addUser", user, String.class)
@Override
public String addUser(User user) {
ResponseEntity<String> entity = restTemplate.postForEntity("http://user-service/addUser", user, String.class);
return entity.getBody();
}
服务提供者的内容:
@PostMapping("/addUser")
public String addUser(@RequestBody User user){
if(Objects.nonNull(user)){
return "success";
}
return "error";
}
- 重载方法二:第二个参数使用HttpEntity
HttpEntity可以封装请求头和请求体两部分内容,我们经常需要把Cookie放到请求头中传递到下游。
情况一:
传递的请求体中的内容是键值对,而不是具体的Java对象
服务消费者端:
@Override
public String addUserWithHeader(Long id, String name) {
//封装Http的请求消息头
HttpHeaders headers = new HttpHeaders();
//因为传递的是键值对,不需要使用json,下游使用@RequestParam来接参,所以需要设置成如下的mime类型
headers.add("Content-Type","application/x-www-form-urlencoded");
headers.add("myname","xiaoming");
List<String> cook_list = new ArrayList<>();
cook_list.add("login_token="+ UUID.randomUUID().toString().replaceAll("-",""));
cook_list.add("cart_token="+ UUID.randomUUID().toString().replaceAll("-",""));
headers.put(HttpHeaders.COOKIE,cook_list);//Cookie login_token=sdfsdfsdf;cart_token=1wlkwekrjwer
//封装请求消息体
MultiValueMap<String,Object> map = new LinkedMultiValueMap<>();
map.add("id",id);
map.add("name",name);
HttpEntity<Map> entity = new HttpEntity<>(map,headers);
restTemplate.postForEntity("http://user-service/addUserWithHeader",entity,String.class);
return null;
}
服务提供者端:
@PostMapping("/addUserWithHeader")
public String addUserWithHeader(@RequestParam Long id, @RequestParam String name,@CookieValue(name = "login_token") String login_token,
@CookieValue(name="cart_token") String cart_token){
System.out.println("name:"+name);
System.out.println("login_token:"+login_token);
System.out.println("cart_token:"+cart_token);
return "success";
}
情况二:
传递参数是具体的Java对象,需要把java对象以json字符串的形式进行传递
服务消费者端:
@Override
public String addUserWithHeader(Long id, String name) {
//封装Http的请求消息头
HttpHeaders headers = new HttpHeaders();
//java对象以json形式传递
headers.add("Content-Type","application/json");
headers.add("myname","xiaoming");
List<String> cook_list = new ArrayList<>();
cook_list.add("login_token="+ UUID.randomUUID().toString().replaceAll("-",""));
cook_list.add("cart_token="+ UUID.randomUUID().toString().replaceAll("-",""));
headers.put(HttpHeaders.COOKIE,cook_list);//Cookie login_token=sdfsdfsdf;cart_token=1wlkwekrjwer
//封装请求消息体
User user = new User();
user.setId(1001L);
user.setName("xiaohong");
HttpEntity<User> entity = new HttpEntity<>(user,headers);
restTemplate.postForEntity("http://user-service/addUserWithHeader",entity,String.class);
return null;
}
服务提供者端:
@PostMapping("/addUserWithHeader")
public String addUserWithHeader(@RequestBody User user,@CookieValue(name = "login_token") String login_token,
@CookieValue(name="cart_token") String cart_token){
System.out.println(user.getName());
System.out.println("login_token:"+login_token);
System.out.println("cart_token:"+cart_token);
return "success";
}
注意:postForObject和getForObject是一个意思,直接获得响应体中的内容。