认识微服务,微服务的拆分,服务治理(nacos注册中心,远程调用)
目录
- 认识微服务
- 一:单体架构
- 二:微服务架构
- 三:springcloud
- 微服务拆分
- 一:熟悉项目
- 二:拆分原则
- 三:微服务项目结构说明
- 四:拆分服务
- 五:远程调用
- 服务治理
- 一:注册中心原理
- 二:nacos注册中心
- 三:服务注册
- 四:服务发现与负载均衡
认识微服务
一:单体架构
优点:
架构简单,部署成本低
缺点:
1:团队协作成本高:大型的项目使用单体架构,业务的耦合度较高,冲突多,难合并
2:系统发布效率低;
3:系统可用性差:因为一个单体架构用的是一个tomcat,在高并发场景下,如果是大型项目,有一个功能被并发请求,其他功能都会受到影响性能会大大降低;
二:微服务架构
将大型项目按照业务功能拆分成独立的模块;
三:springcloud
微服务拆分
一:熟悉项目
二:拆分原则
什么时候拆分:
1:创业型项目:可以先做出单体架构的项目,因为成本低,而且前期的功能,需求不是太大,可以快速试错。等到业务变得复杂了再去做微服务拆分,但是从单体中抽取出来再做微服务的难度会更大;
2:确定的大型项目:如果一个项目已经确定了,而且资金充足,可以直接选择微服务架构,省去了后续拆分时的麻烦
怎么拆分:
1:拆分的目标:高内聚,低耦合,每个微服务的职责要单一,业务关联,完整度高;每个业务的功能要独立,减少对其他微服务的依赖;
2:拆分的方式:
- 纵向拆分:按照业务模块拆分;
- 横向拆分:抽取公共服务,提高复用性;
三:微服务项目结构说明
第一种方式:每个模块都是一个独立的project,所有的模块存储在一个文件夹里。这种适合特别大的项目,每个模块都有很多的代码和功能;
第二中方法:通过maven聚合实现:每个模块都是maven中的module,都在一个父工程中,适合不是很大的项目;这种更方便管理;
四:拆分服务
拆分的步骤:
第一步:创建一个maven模块
第二部将启动类创建好,还有mapper,controller,service,然后导入配置文件yaml
修改yam文件,首先就是端口号要改,每个模块的端口号都不一致;
然后就是name;
还有数据库,实际微服务拆分需要不同的容器来存储不同的模块的数据库,我们这里方便点,用不同的数据库来区分不同的模块,做到数据库的隔离
然后就是把相关的mapper,service,controller的代码导入就行;
五:远程调用
1:在启动类中将restTemplate加入ioc容器
@MapperScan("com.hmall.cart.mapper")
@SpringBootApplication
public class cartApplication {
public static void main(String[] args) {
SpringApplication.run(cartApplication.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2:注入restTemplate:
@Service
@RequiredArgsConstructor
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {
private final RestTemplate restTemplate;
之前我们注入都是使用autowired注入,但是这种方法spring不推荐,spring推荐我们使用构造器去注入对象,但是需要我们再去写构造函数比较麻烦,所以我们使用lombok的注解自动生成构造器,但是有的时候的属性并不是我们注入的对象,我们不想让他加入构造器,我们就可以使用RequiredArgsConstructor,这个是只会对final类型的加入构造函数;
3:使用restTemplate向item发出请求
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);
//发送http请求
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
"http://localhost:8081/items?ids={ids}",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<ItemDTO>>() {
},
Map.of("ids", CollUtils.join(itemIds, ","))
);
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());
}
}
服务治理
一:注册中心原理
我们为了应对大量的请求,会对一个模块部署多个实例,那么我们在代码远程调用的时候调用哪个地址呢,如果确定了,那么如果确定的地址的服务挂了该怎么办,怎么切换,如果不确定,如果我要加入新的实例,那么远程调用者怎么知道呢
这里就要使用到注册中心了,(1)我们的所有服务提供者实例都在注册中心注册服务信息,然后其中有的实例要去远程调用另外一个实例,(2)服务调用者就要从注册中心中去订阅这个服务的信息,获取到这个服务的所有实例,(3)然后再根据算法进行负载均衡,获取到实例后,(4)再远程调用服务
但是我们的实例如果挂了怎么办,这里我们推出了心跳续约和推送变更
心跳续约:我们的实例每隔一段时间就会向注册中心发送一个请求,代表服务正常,注册中心会做确认;
推送变更:如果注册中心发现心跳不正常了就会给订阅服务的服务推送变更;
二:nacos注册中心
需要创建一个数据库表用来存储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
然后输入命令安装nacos
就能访问了
三:服务注册
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
四:服务发现与负载均衡
提供spring提供的api-discoveryclient拉取服务的实例列表,然后我们通过负载均衡的随机算法获取其中的一个实例;然后获取IP和端口,就能向服务的实例发起请求了;解决了之前将ip地址写死的问题;
如何发现:
引入依赖,配置yaml文件;
然后就能注入discovery:
private final DiscoveryClient discoveryClient;
这里是通过构造器注入
然后就是,获取服务的所有实例。然后负载均衡获取实例,最后获取实例的地址:
List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
if (instances.isEmpty()){
return;
}
ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size()));
URI uri = instance.getUri();
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
uri+"/items?ids={ids}",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<ItemDTO>>() {
},
Map.of("ids", CollUtils.join(itemIds, ","))
);
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
uri+"/items?ids={ids}",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<ItemDTO>>() {
},
Map.of("ids", CollUtils.join(itemIds, ","))
);