100并发 -> 千万并发,阿里淘宝的 14 次架构演进之路!
准备微服务环境
创建两个数据库,分别写一套对应的增删改查操作
依赖
RestTemplate: 模拟浏览器的行为向服务器发送请求
/**
?* @Author:?huahua
?* @name:RestTemplateTest
?* @Date:2023/7/18 10:41
?*/
@SpringBootTest
public class RestTemplateTest {
? ? /**
? ? ?* RPC(Remote Procedure Call Protocol): 远程过程调用
? ? ?* ? ? ?只要实现了两台机器之间的数据交互就可以称之为远程过程调用
? ? ?* RestTemplate: 封装了远程调用的客户端
? ? ?*
? ? ?*/
? ? @Autowired
? ? private RestTemplate restTemplate;
? ? // get方式的请求
? ? @Test
? ? public void test01(){
? ? ? ? // 向目标服务器发送请求,并接收对应的结果
? ? ? ? //String resp = restTemplate.getForObject("http://user-service/user/1", String.class);
? ? ? ? //TbUser resp = restTemplate.getForObject("http://user-service/user/1", TbUser.class);
? ? ? ? // 携带参数
? ? ? ? //TbUser resp = restTemplate.getForObject("http://user-service/user/findByName/柳岩", TbUser.class);
? ? ? ? //System.out.println(resp);
? ? ? ? ResponseEntity<TbUser> resp = restTemplate.getForEntity("http://127.0.0.1:8081/user/1", TbUser.class);
? ? ? ? // 获取响应状态
? ? ? ? HttpStatus statusCode = resp.getStatusCode();
? ? ? ? // 获取响应数据
? ? ? ? TbUser tbUser = resp.getBody();
? ? ? ? // 获取响应头
? ? ? ? HttpHeaders headers = resp.getHeaders();
? ? ? ? // 获取响应头中的cookie头
? ? ? ? List<String> list = headers.get("Set-Cookie");
? ? ? ? System.out.println(statusCode);
? ? ? ? System.out.println(tbUser);
? ? ? ? System.out.println(headers);
? ? ? ? System.out.println(list);
? ? }
? ? /**
? ? ?* 请求头设置参数,访问指定接口
? ? ?*/
? ? @Test
? ? public void test03(){
? ? ? ? String url="http://127.0.0.1:8081/user/2";
? ? ? ? //设置请求头参数
? ? ? ? HttpHeaders headers = new HttpHeaders();
? ? ? ? headers.add("token","damimi");
? ? ? ? //请求头填充到请求对象下
? ? ? ? HttpEntity<Map> entry = new HttpEntity<>(headers);
? ? ? ? //发送请求
? ? ? ? ResponseEntity<TbUser> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entry, TbUser.class);
? ? ? ? TbUser result = responseEntity.getBody();
? ? ? ? System.out.println(result);
? ? }
? ? /**
? ? ?* post模拟form表单提交数据
? ? ?*/
? ? @Test
? ? public void test04(){
? ? ? ? String url="http://localhost:8081/user/save";
? ? ? ? //设置请求头,指定请求数据方式
? ? ? ? HttpHeaders headers = new HttpHeaders();
? ? ? ? //告知被调用方,请求方式是form表单提交,这样对方解析数据时,就会按照form表单的方式解析处理
? ? ? ? headers.add("Content-type","application/x-www-form-urlencoded");
? ? ? ? //组装模拟form表单提交数据,内部元素相当于form表单的input框
? ? ? ? LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
? ? ? ? map.add("username","迪丽热巴");
? ? ? ? map.add("address","天津");
? ? ? ? HttpEntity<LinkedMultiValueMap<String, Object>> httpEntity = new HttpEntity<>(map, headers);
? ? ? ? /*
? ? ? ? ? ? 参数1:请求url地址
? ? ? ? ? ? 参数2:请求方式 POST
? ? ? ? ? ? 参数3:请求体对象,携带了请求头和请求体相关的参数
? ? ? ? ? ? 参数4:响应数据类型
? ? ? ? ?*/
? ? ? ? ResponseEntity<TbUser> exchange = restTemplate.exchange(url, HttpMethod.POST, httpEntity, TbUser.class);
? ? ? ? TbUser body = exchange.getBody();
? ? ? ? System.out.println(body);
? ? }
? ? /**
? ? ?* post发送json数据
? ? ?*/
? ? @Test
? ? public void test05() throws JsonProcessingException {
? ? ? ? String url="http://localhost:8081/user/save2";
? ? ? ? //设置请求头的请求参数类型
? ? ? ? HttpHeaders headers = new HttpHeaders();
? ? ? ? //告知被调用方,发送的数据格式的json格式,对方要以json的方式解析处理
? ? ? ? headers.add("Content-type","application/json; charset=utf-8");
? ? ? ? //组装json格式数据
? ? ? ? HashMap<String, String> reqMap = new HashMap<>();
? ? ? ? reqMap.put("username","zhangsan");
? ? ? ? reqMap.put("address","上海");
? ? ? ? ObjectMapper objectMapper = new ObjectMapper();
? ? ? ? String reqMapJson = objectMapper.writeValueAsString(reqMap);
? ? ? ? //构建请求对象
? ? ? ? HttpEntity<String> httpEntity = new HttpEntity<>(reqMapJson, headers);
? ? ? ? ? /*
? ? ? ? ? ? 发送数据
? ? ? ? ? ? 参数1:请求url地址
? ? ? ? ? ? 参数2:请求方式
? ? ? ? ? ? 参数3:请求体对象,携带了请求头和请求体相关的参数
? ? ? ? ? ? 参数4:响应数据类型
? ? ? ? ?*/
? ? ? ? ResponseEntity<TbUser> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, TbUser.class);
? ? ? ? //或者
? ? ? ? // Account account=restTemplate.postForObject(url,httpEntity,Account.class);
? ? ? ? TbUser body = responseEntity.getBody();
? ? ? ? System.out.println(body);
? ? }
? ? @Test
? ? public void test07(){
? ? ? ? ResponseEntity<String> resp = restTemplate.getForEntity("http://www.takungpao.com/news/index.html", String.class);
? ? ? ? String body = resp.getBody();
? ? ? ? System.out.println(body);
? ? }
}
当服务从Eureka中拉取多个服务地址时,Ribbon可以实现负载均衡(从多个地址中选择一个)
在RestTemplate对象上添加注解 @LoadBalanced
作用: 解决雪崩问题
? ?? ?雪崩问题: 在一个业务链路中,由于下游服务的故障,导致整个链路关联的所以服务宕机.
解决方案:
?? ?服务降级: 换一种方式快速给上游服务一个响应.
?? ?服务熔断: 当出错率到达一定的阈值时,直接熔断,不再访问下游服务,直接降级.
在消费者/上游服务方导入
?<!-- Hystrix启动器 -->
<dependency>
? <groupId>org.springframework.cloud</groupId>
? <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
@EnableHystrix
a.在需要降级的方法上降级注解: @HystrixCommand(fallbackMethod="降级方法名")
b.编写降级的方法
? 降级处理的方法,与原方法返回值,参数列表保持一致
package com.bw.order.controller;
import com.bw.order.domain.TbOrder;
import com.bw.order.domain.TbUser;
import com.bw.order.service.TbOrderService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: huahaua
* @name:OrderController
* @Date:2023/8/14 18:48
*/
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private TbOrderService orderService;
// 此方法执行超时后,调用对应降级的方法快速处理业务
@RequestMapping("/findById/{id}")
@HystrixCommand(fallbackMethod="findUserByIdForFail")
public TbOrder findById(@PathVariable Integer id){
TbOrder order = orderService.findById(id);
return order;
}
// todo:降级处理的方法,与原方法返回值,参数列表保持一致
public TbOrder findUserByIdForFail(Integer id){
TbOrder order = new TbOrder();
order.setName("你的小可爱走丢了....服务降级");
return order;
}
}
Hystrix默认降级时间为1秒钟
配置降级时间:
hystrix:
command:
default:
execution.isolation.thread.timeoutInMilliseconds: 2000 # 单位毫秒