认识微服务

一:单体架构

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

优点:

架构简单,部署成本低

缺点:

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, ","))
);

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部