一、单体架构
测试高并发软件
二、微服务
三、SpringCloud

四、微服务拆分
黑马商城模块:
服务拆分原则:
拆分服务:
独立project:
maven聚合:
拆分案例:
远程调用:
package com.hmall.cart.config;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@org.springframework.context.annotation.Configuration
public class Configuration {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@Service
@RequiredArgsConstructor //自动生成必备参数的构造函数
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {
/*private final IItemService itemService;*/
// @Autowired Spring不建议使用@Autowired
private final RestTemplate restTemplate;
/*Spring建议使用构造函数注入*/
/* public CartServiceImpl(RestTemplate restTemplate){
this.restTemplate=restTemplate;
}*/
}
private void handleCartItems(List<CartVO> vos) {
// 1.获取商品id
Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
// 2.查询商品
//List<ItemDTO> items = itemService.queryItemByIds(itemIds);
//2.1 利用RestTemplate发送http请求,得到http响应
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
"http://localhost:8081/items?ids={ids}",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<ItemDTO>>() {
}, //利用反射拿到这个对象上的泛型
Map.of("ids", CollUtil.join(itemIds, ","))
);
//2.2解析响应
if(!response.getStatusCode().is2xxSuccessful()){
//查询失败 直接结束
return;
}
List<ItemDTO> items = response.getBody();
if (CollUtils.isEmpty(items)) {
return;
}
// 3.转为 id 到 item的map
Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
// 4.写入vo
for (CartVO v : vos) {
ItemDTO item = itemMap.get(v.getItemId());
if (item == null) {
continue;
}
v.setNewPrice(item.getPrice());
v.setStatus(item.getStatus());
v.setStock(item.getStock());
}
}
逻辑:
五、服务治理
上文提到的远程调用带来的问题:
注册中心:
Nacos注册中心:
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim
服务注册:
多实例部署:
服务发现:
六、OpenFeign
快速入门:
———————————————————————————————————————————
———————————————————————————————————————————
请求路径参数通过SpringMVC注解@GetMapping以及@RequestParam完成替代
请求参数通过调用该接口方法时传递的参数完成替代
返回值类型通过该接口定义的抽象方法的返回值类型完成替代
从而实现整体替代,简化代码
package com.hmall.cart.client;
import com.hmall.cart.domain.dto.ItemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;
@FeignClient("item-service")
public interface ItemClient {
@GetMapping("/items")
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
private final ItemClient itemClient; //自动注入
private void handleCartItems(List<CartVO> vos) {
// 1.获取商品id
Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
// 2.查询商品
//2.1 根据服务名称获取服务的实例列表
/* List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
if(CollUtil.isEmpty(instances)){
//判断为空 直接结束
return;
}
//2.2 手写负载均衡,从实例列表中挑选一个实例
ServiceInstance instance = instances.get(RandomUtil.randomInt(0, instances.size()));
URI uri = instance.getUri(); //主机地址 http://localhost:8081
//List<ItemDTO> items = itemService.queryItemByIds(itemIds);
//2.3 利用RestTemplate发送http请求,得到http响应
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
uri+"/items?ids={ids}",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<ItemDTO>>() {
}, //利用反射拿到这个对象上的泛型
Map.of("ids", CollUtil.join(itemIds, ","))
);
//2.4解析响应
if(!response.getStatusCode().is2xxSuccessful()){
//查询失败 直接结束
return;
}
List<ItemDTO> items = response.getBody();*/
List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
if (CollUtils.isEmpty(items)) {
return;
}
// 3.转为 id 到 item的map
Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
// 4.写入vo
for (CartVO v : vos) {
ItemDTO item = itemMap.get(v.getItemId());
if (item == null) {
continue;
}
v.setNewPrice(item.getPrice());
v.setStatus(item.getStatus());
v.setStock(item.getStock());
}
}
底层原理:
代码中通过 itemClient 对象来调用接口方法时,实际上它是一个动态代理对象。
动态代理对象底层逻辑都是由 InvocationHandler 实现
这里的 FeignInvocationHandler 是 ReflectiveFeign 的一个内部类,实现了 InvocationHandler 接口, InvocationHandler 接口中的 invoke 方法就是起到了代理的作用 : 所有的被代理对象中的所有业务(方法) 都会通过 invoke 方法实现代理,除了一些基本方法(比如 equals, hashCode ,toString)
InvocationHandler 接口的实现类 FeignInvocationHandler 中重写对应 invoke 从而实现代理的方法如下:
最后调用对应的方法发送请求(Client)
连接池:
OpenFeign底层发送请求使用的是 Client
OpenFeign整合并使用连接池步骤:
最佳实践:
拆分的最佳方式: