MP+Docker+SpringCloud+MQ+ES——6 服务注册和发现

6 服务注册和发现

6.1 问题引出

  • 在上一章实现了微服务拆分,并且通过Http请求实现了跨微服务的远程调用。不过这种手动发送Http请求的方式存在一些问题。试想一下,假如商品微服务被调用较多,为了应对更高的并发,我们进行了多实例部署,如图:

    在这里插入图片描述

  • 此时,每个item-service的实例其IP或端口不同,问题来了:

    • item-service这么多实例,cart-service如何知道每一个实例的地址?
    • http请求要写url地址,cart-service服务到底该调用哪个实例呢?
    • 如果在运行过程中,某一个item-service实例宕机,cart-service依然在调用该怎么办?
    • 如果并发太高,item-service临时多部署了N台实例,cart-service如何知道新实例的地址?
  • 为了解决上述问题,就必须引入注册中心的概念了。

6.2 注册中心原理

  • 在微服务远程调用的过程中,包括两个角色:

    • 服务提供者:提供接口供其它微服务访问,比如item-service
    • 服务消费者:调用其它微服务提供的接口,比如cart-service
  • 在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心的概念。注册中心、服务提供者、服务消费者三者间关系如下:

    在这里插入图片描述

  • 流程如下:

    • 服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心;
    • 调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1个服务可能多实例部署);
    • 调用者自己对实例列表负载均衡,挑选一个实例;
    • 调用者向该实例发起远程调用;
  • 当服务提供者的实例宕机或者启动新实例时,调用者如何得知呢?

    • 服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求);
    • 当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除;
    • 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表;
    • 当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表。

6.3 Nacos注册中心

  • 目前开源的注册中心框架有很多,国内比较常见的有:

    • Eureka:Netflix公司出品,目前被集成在SpringCloud当中,一般用于Java应用;
    • Nacos:Alibaba公司出品,目前被集成在SpringCloud Alibaba中,一般用于Java应用;
    • Consul:HashiCorp公司出品,目前集成在SpringCloud中,不限制微服务语言;
  • 以上几种注册中心都遵循SpringCloud中的API规范,因此在业务开发使用上没有太大差异。由于Nacos是国内产品,中文文档比较丰富,而且同时具备配置管理功能,因此在国内使用较多,此处以Nacos为例来学习,官网:Nacos官网| Nacos 配置中心 | Nacos 下载| Nacos 官方社区 | Nacos 官网

  • 由于是基于Docker来部署Nacos的注册中心,首先要准备MySQL数据库表,用来存储Nacos的数据。由于是Docker部署,所以需要将下面的SQL文件导入到Docker中的MySQL容器中:

    在这里插入图片描述

    create table if not exists config_info
    (
        id                 bigint auto_increment comment 'id'
            primary key,
        data_id            varchar(255)                           not null comment 'data_id',
        group_id           varchar(128)                           null,
        content            longtext                               not null comment 'content',
        md5                varchar(32)                            null comment 'md5',
        gmt_create         datetime     default CURRENT_TIMESTAMP not null comment '创建时间',
        gmt_modified       datetime     default CURRENT_TIMESTAMP not null comment '修改时间',
        src_user           text                                   null comment 'source user',
        src_ip             varchar(50)                            null comment 'source ip',
        app_name           varchar(128)                           null,
        tenant_id          varchar(128) default ''                null comment '租户字段',
        c_desc             varchar(256)                           null,
        c_use              varchar(64)                            null,
        effect             varchar(64)                            null,
        type               varchar(64)                            null,
        c_schema           text                                   null,
        encrypted_data_key text                                   not null comment '秘钥',
        constraint uk_configinfo_datagrouptenant
            unique (data_id, group_id, tenant_id)
    )
        comment 'config_info' collate = utf8mb3_bin;
    
    create table if not exists config_info_aggr
    (
        id           bigint auto_increment comment 'id'
            primary key,
        data_id      varchar(255)            not null comment 'data_id',
        group_id     varchar(128)            not null comment 'group_id',
        datum_id     varchar(255)            not null comment 'datum_id',
        content      longtext                not null comment '内容',
        gmt_modified datetime                not null comment '修改时间',
        app_name     varchar(128)            null,
        tenant_id    varchar(128) default '' null comment '租户字段',
        constraint uk_configinfoaggr_datagrouptenantdatum
            unique (data_id, group_id, tenant_id, datum_id)
    )
        comment '增加租户字段' collate = utf8mb3_bin;
    
    create table if not exists config_info_beta
    (
        id                 bigint auto_increment comment 'id'
            primary key,
        data_id            varchar(255)                           not null comment 'data_id',
        group_id           varchar(128)                           not null comment 'group_id',
        app_name           varchar(128)                           null comment 'app_name',
        content            longtext                               not null comment 'content',
        beta_ips           varchar(1024)                          null comment 'betaIps',
        md5                varchar(32)                            null comment 'md5',
        gmt_create         datetime     default CURRENT_TIMESTAMP not null comment '创建时间',
        gmt_modified       datetime     default CURRENT_TIMESTAMP not null comment '修改时间',
        src_user           text                                   null comment 'source user',
        src_ip             varchar(50)                            null comment 'source ip',
        tenant_id          varchar(128) default ''                null comment '租户字段',
        encrypted_data_key text                                   not null comment '秘钥',
        constraint uk_configinfobeta_datagrouptenant
            unique (data_id, group_id, tenant_id)
    )
        comment 'config_info_beta' collate = utf8mb3_bin;
    
    create table if not exists config_info_tag
    (
        id           bigint auto_increment comment 'id'
            primary key,
        data_id      varchar(255)                           not null comment 'data_id',
        group_id     varchar(128)                           not null comment 'group_id',
        tenant_id    varchar(128) default ''                null comment 'tenant_id',
        tag_id       varchar(128)                           not null comment 'tag_id',
        app_name     varchar(128)                           null comment 'app_name',
        content      longtext                               not null comment 'content',
        md5          varchar(32)                            null comment 'md5',
        gmt_create   datetime     default CURRENT_TIMESTAMP not null comment '创建时间',
        gmt_modified datetime     default CURRENT_TIMESTAMP not null comment '修改时间',
        src_user     text                                   null comment 'source user',
        src_ip       varchar(50)                            null comment 'source ip',
        constraint uk_configinfotag_datagrouptenanttag
            unique (data_id, group_id, tenant_id, tag_id)
    )
        comment 'config_info_tag' collate = utf8mb3_bin;
    
    create table if not exists config_tags_relation
    (
        id        bigint                  not null comment 'id',
        tag_name  varchar(128)            not null comment 'tag_name',
        tag_type  varchar(64)             null comment 'tag_type',
        data_id   varchar(255)            not null comment 'data_id',
        group_id  varchar(128)            not null comment 'group_id',
        tenant_id varchar(128) default '' null comment 'tenant_id',
        nid       bigint auto_increment
            primary key,
        constraint uk_configtagrelation_configidtag
            unique (id, tag_name, tag_type)
    )
        comment 'config_tag_relation' collate = utf8mb3_bin;
    
    create index idx_tenant_id
        on config_tags_relation (tenant_id);
    
    create table if not exists group_capacity
    (
        id                bigint unsigned auto_increment comment '主键ID'
            primary key,
        group_id          varchar(128) default ''                not null comment 'Group ID,空字符表示整个集群',
        quota             int unsigned default '0'               not null comment '配额,0表示使用默认值',
        `usage`           int unsigned default '0'               not null comment '使用量',
        max_size          int unsigned default '0'               not null comment '单个配置大小上限,单位为字节,0表示使用默认值',
        max_aggr_count    int unsigned default '0'               not null comment '聚合子配置最大个数,,0表示使用默认值',
        max_aggr_size     int unsigned default '0'               not null comment '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
        max_history_count int unsigned default '0'               not null comment '最大变更历史数量',
        gmt_create        datetime     default CURRENT_TIMESTAMP not null comment '创建时间',
        gmt_modified      datetime     default CURRENT_TIMESTAMP not null comment '修改时间',
        constraint uk_group_id
            unique (group_id)
    )
        comment '集群、各Group容量信息表' collate = utf8mb3_bin;
    
    create table if not exists his_config_info
    (
        id                 bigint unsigned                        not null,
        nid                bigint unsigned auto_increment
            primary key,
        data_id            varchar(255)                           not null,
        group_id           varchar(128)                           not null,
        app_name           varchar(128)                           null comment 'app_name',
        content            longtext                               not null,
        md5                varchar(32)                            null,
        gmt_create         datetime     default CURRENT_TIMESTAMP not null,
        gmt_modified       datetime     default CURRENT_TIMESTAMP not null,
        src_user           text                                   null,
        src_ip             varchar(50)                            null,
        op_type            char(10)                               null,
        tenant_id          varchar(128) default ''                null comment '租户字段',
        encrypted_data_key text                                   not null comment '秘钥'
    )
        comment '多租户改造' collate = utf8mb3_bin;
    
    create index idx_did
        on his_config_info (data_id);
    
    create index idx_gmt_create
        on his_config_info (gmt_create);
    
    create index idx_gmt_modified
        on his_config_info (gmt_modified);
    
    create table if not exists permissions
    (
        role     varchar(50)  not null,
        resource varchar(255) not null,
        action   varchar(8)   not null,
        constraint uk_role_permission
            unique (role, resource, action)
    );
    
    create table if not exists roles
    (
        username varchar(50) not null,
        role     varchar(50) not null,
        constraint idx_user_role
            unique (username, role)
    );
    
    create table if not exists tenant_capacity
    (
        id                bigint unsigned auto_increment comment '主键ID'
            primary key,
        tenant_id         varchar(128) default ''                not null comment 'Tenant ID',
        quota             int unsigned default '0'               not null comment '配额,0表示使用默认值',
        `usage`           int unsigned default '0'               not null comment '使用量',
        max_size          int unsigned default '0'               not null comment '单个配置大小上限,单位为字节,0表示使用默认值',
        max_aggr_count    int unsigned default '0'               not null comment '聚合子配置最大个数',
        max_aggr_size     int unsigned default '0'               not null comment '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
        max_history_count int unsigned default '0'               not null comment '最大变更历史数量',
        gmt_create        datetime     default CURRENT_TIMESTAMP not null comment '创建时间',
        gmt_modified      datetime     default CURRENT_TIMESTAMP not null comment '修改时间',
        constraint uk_tenant_id
            unique (tenant_id)
    )
        comment '租户容量信息表' collate = utf8mb3_bin;
    
    create table if not exists tenant_info
    (
        id            bigint auto_increment comment 'id'
            primary key,
        kp            varchar(128)            not null comment 'kp',
        tenant_id     varchar(128) default '' null comment 'tenant_id',
        tenant_name   varchar(128) default '' null comment 'tenant_name',
        tenant_desc   varchar(256)            null comment 'tenant_desc',
        create_source varchar(32)             null comment 'create_source',
        gmt_create    bigint                  not null comment '创建时间',
        gmt_modified  bigint                  not null comment '修改时间',
        constraint uk_tenant_info_kptenantid
            unique (kp, tenant_id)
    )
        comment 'tenant_info' collate = utf8mb3_bin;
    
    create index idx_tenant_id
        on tenant_info (tenant_id);
    
    create table if not exists users
    (
        username varchar(50)  not null
            primary key,
        password varchar(500) not null,
        enabled  tinyint(1)   not null
    );
    
  • 最终表结构如下:

    在这里插入图片描述

  • 新建一个nacos文件,创建custom.env文件,改文件主要用于 Nacos 与数据库交互:

    • SPRING_DATASOURCE_PLATFORM指定数据库为:mysql;

    • MYSQL_SERVICE_HOST是虚拟机地址;

    • MYSQL_SERVICE_DB_NAME指定要连接的数据库名为:nacos;

    • MYSQL_SERVICE_PORT指定访问数据库的端口为:3306;

    • MYSQL_SERVICE_USERMYSQL_SERVICE_PASSWORD为连接数据库的账号密码;

    在这里插入图片描述

    PREFER_HOST_MODE=hostname
    MODE=standalone
    SPRING_DATASOURCE_PLATFORM=mysql
    MYSQL_SERVICE_HOST=192.168.184.138
    MYSQL_SERVICE_DB_NAME=nacos
    MYSQL_SERVICE_PORT=3306
    MYSQL_SERVICE_USER=root
    MYSQL_SERVICE_PASSWORD=1234
    MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
    
  • nacos整个文件夹上传至虚拟机的/root目录:

    • 可以自己去官网下载压缩包;

    在这里插入图片描述

  • 进入root目录,然后执行下面的docker命令:

    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
    
  • 然后访问:http://192.168.184.138:8848/nacos/,会跳转到下面的页面。首次访问会跳转到登录页,账号密码都是nacos

    在这里插入图片描述

6.4 服务注册

  • 接下来,把item-service注册到Nacos,步骤如下:
    • 引入依赖
    • 配置Nacos地址
    • 重启

6.4.1 添加依赖

  • 在的pom.xml(item-service)中添加依赖:

    <!--nacos 服务注册发现-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    

6.4.2 配置Nacos

  • item-serviceapplication.yml中添加nacos地址配置:

    spring:
      application:
        name: item-service
      cloud:
        nacos:
          server-addr: 192.168.184.129:8848 # nacos地址
    

6.4.3 启动服务实例

  • 为了测试一个服务多个实例的情况,再配置一个item-service的部署实例:

    在这里插入图片描述

  • 然后配置启动项,注意重命名并且配置新的端口,避免冲突:

    -Dserver.port=8083
    

    在这里插入图片描述

  • 重启item-service的两个实例:

    在这里插入图片描述

  • 访问nacos控制台,可以发现服务注册成功:

    在这里插入图片描述

  • 点击详情,可以查看到item-service服务的两个实例信息:

    在这里插入图片描述

6.5 服务发现

  • 服务的消费者要去nacos订阅服务,这个过程就是服务发现,步骤如下:
    • 引入依赖
    • 配置Nacos地址
    • 发现并调用服务

6.5.1 引入依赖

  • 服务发现除了要引入nacos依赖以外,由于还需要负载均衡,因此要引入SpringCloud提供的LoadBalancer依赖;

  • cart-service中的pom.xml中添加下面的依赖:

    <!--nacos 服务注册发现-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
  • 可以发现,这里Nacos的依赖与服务注册时一致,这个依赖中同时包含了服务注册和发现的功能。因为任何一个微服务都可以调用别人,也可以被别人调用,即可以是调用者,也可以是提供者;

  • 因此,等一会儿cart-service启动,同样会注册到Nacos。

6.5.2 配置Nacos地址

  • cart-serviceapplication.yml中添加nacos地址配置:

    spring:
      application:
        name: cart-service
      cloud:
        nacos:
          server-addr: 192.168.184.138:8848
    

6.5.3 发现并调用服务

  • 接下来,服务调用者cart-service就可以去订阅item-service服务了。不过item-service有多个实例,而真正发起调用时只需要知道一个实例的地址。因此,服务调用者必须利用负载均衡的算法,从多个实例中挑选一个去访问。常见的负载均衡算法有:

    • 随机
    • 轮询
    • IP的hash
    • 最近最少访问
  • 此处选择最简单的随机负载均衡;

  • 另外,服务发现需要用到一个DiscoveryClient的工具,SpringCloud已经帮我们自动装配,可以直接注入使用。在item-servercom/hmall/cart/service/impl/CartServiceImpl.java中:

    private final DiscoveryClient discoveryClient;
    
  • 接下来,就可以对原来的远程调用做修改了,之前调用时我们是写死服务提供者的IP和端口。但现在不需要了,可以通过DiscoveryClient发现服务实例列表,然后通过负载均衡算法,选择一个实例去调用:

    private void handleCartItems(List<CartVO> vos) {
        // 1.获取商品id
        Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
        // 2.查询商品
    
        // 2.1.发现item-service服务的实例列表
        List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
        // 2.2.负载均衡,挑选一个实例
        ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size()));
        // 2.3.利用RestTemplate发起http请求,得到http的响应
        ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
                instance.getUri() + "/items?ids={ids}", //请求路径
                HttpMethod.GET, //请求方式。HttpMethod的包是 org.springframework.http.HttpMethod;
                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());
        }
    }
    
  • 测试:启动CartApplicattion,访问http://localhost:8082/doc.html

    在这里插入图片描述

  • 在注册中心也可以看见cart-service

    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木木慕慕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值