注册中心和负载均衡(黑马SpringCloud笔记)

一、服务远程调用

1. RestTemplate

假设有两个服务、两个数据库

查询订单服务:

@GetMapping("/order/{orderId}")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
    // 根据id查询订单并返回
    return orderService.queryOrderById(orderId);
}

订单数据库中的订单表

在这里插入图片描述

查询用户服务

@GetMapping("/user/{id}")
public User queryById(@PathVariable("id") Long id) {
    return userService.queryById(id);
}

用户数据库中的用户表

在这里插入图片描述

启动服务查询订单101:

http://localhost:8080/order/101返回:

{
    "id":101,
    "price":699900,
    "name":"Apple 苹果 iPhone 12 ",
    "num":1,
    "userId":1,
    "user":null
}

查询用户 1:

http://localhost:8081/user/1返回:

{
    "id":1,
    "username":"柳岩",
    "address":"湖南省衡阳市"
}

显然我们需要在查询订单的时候根据用户id再去调用查询用户的服务。

RestTemplate可以帮助我们发起Http请求调用其他服务的REST接口:

  1. 配置

    @SpringBootConfiguration
    public class CustomConfiguration {
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
  2. 使用

    @Autowired注入后

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.调用REST接口查询数据
        User user = restTemplate.getForObject("http://localhost:8081/user/" + order.getUserId(), User.class);
        // 3.封装数据
        order.setUser(user);
        // 4.返回
        return order;
    }
    

再次查询订单101:

{
    "id":101,
    "price":699900,
    "name":"Apple 苹果 iPhone 12 ",
    "num":1,
    "userId":1,
    "user":{
        "id":1,
        "username":"柳岩",
        "address":"湖南省衡阳市"
    }
}

2. 服务调用关系

  • 服务提供者:暴露接口给其他微服务调用
  • 服务消费者:调用其他微服务提供的接口

在刚刚的例子中查询用户的服务可以称为服务提供者查询订单的服务需要去调用它那么查询订单的服务可以称为服务消费者。

假如一个服务B调用了服务A服务C又调用了服务B那么服务B是什么角色呢?

由此可见服务提供者和消费者是相对的

3. 远程调用的问题

刚刚的例子中有这样一行代码:

User user = restTemplate.getForObject("http://localhost:8081/user/" + order.getUserId(), User.class);

环境变更后(ip怎么办?难道去改代码重新编译?还有如果只能调用8081集群意义何在?

问题1:消费者如何获取提供者的地址信息?

问题2:如果有多个提供者消费者如何选择?

问题3:消费者如何得知提供者的健康状态?

在这里插入图片描述

注册中心解决了这些问题。

二、注册中心

注册中心是用来记录管理服务的。

注册中心解决了之前服务调用的三个问题:

在这里插入图片描述

  1. 首先服务提供者需要在注册中心注册服务注册中心记录服务。
  2. 服务消费者在注册中心拉取服务信息负载均衡后挑选一个服务使用。
  3. 向注册中心发送心跳包若心跳异常则服务下线。

1. Eureka注册中心

1.1 搭建Eureka注册中心

搭建Eureka注册中心三步走即可:

  1. 引入Eureka依赖(服务端引入server依赖客户端引入client

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    
  2. 编写启动类加上@EnableEurekaServer注解

    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaServiceApplication {
        public static void main(String[] args) {
            SpringApplication.run(EurekaServiceApplication.class, args);
        }
    }
    
  3. application.yml配置

    server:
      port: 10086
    spring:
      application:
        name: eurekaServer
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://127.0.0.1:10086/eureka/
    

这样就搭建了一个Eureka注册中心访问ip+端口即可看到Eureka管理面板。

1.2 服务注册

注册一个服务

  1. 引依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  2. application.yml中配置

    eureka:
      client:
        serviceUrl: #???????????
          defaultZone: http://127.0.0.1:10086/eureka/
    

可以在启动类上加一个@EnableEurekaClient注解不加暂时看起来也可以注册。然后直接启动就可以在Eureka管理面板中看到一个新实例。

1.3 服务拉取

之前的代码是这样的:

User user = restTemplate.getForObject("http://localhost:8081/user/" + order.getUserId(), User.class);

现在我们不再把ip写死而是去Eureka注册中心拉取服务两步:

  1. 将ip改为服务名

    User user = restTemplate.getForObject("http://userservice/user/" + order.getUserId(), User.class);
    
  2. 添加负载均衡注解@LoadBalanced

    @SpringBootConfiguration
    public class CustomConfiguration {
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    

测试一下:

在这里插入图片描述

1.4 小结

在这里插入图片描述

2. nacos注册中心

nacos不仅可以作为注册中心还有很多功能。

2.1Nacos搭建

下载nacos

下载对应系统版本的解压即可。

在/conf/application.properties下可以配置nacos的运行端口等。

在/bin目录下打开命令行输入

startup.cmd -m standalone

单机启动nacos

访问控制台提供的url即可访问管理面板:

在这里插入图片描述

默认用户名密码都是nacos

2.2 服务注册

  1. 加入spring-cloud-alibaba的管理依赖

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2.2.5.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
    
  2. 添加nacos客户端依赖

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
  3. 配置nacos注册中心地址

    spring:
     cloud:
      nacos:
        server-addr: localhost:8848
    

启动后即可在nacos管理面板中发现该服务。

2.3 服务拉取

把刚刚的两个服务从Eureka换成nacos后测试。

在这里插入图片描述

有一个小小的坑那就是nacos的服务名是要区分大小写的Eureka是不区分的所以要特别注意服务名不然会请求失败。

2.4 服务分级存储模型

也就是可以给服务分组。这个模型是这样的:

在这里插入图片描述

可以通过discovery.cluster-name来配置

spring:
 cloud:
  nacos:
   server-addr: localhost:8848
   discovery:
     cluster-name: SC

我们启动三个userservice实例两个配置为SC一个配置为SH查看nacos变化:

在这里插入图片描述

设置这个有啥用呢?我们可以配置负载均衡规则来让ribbon优先调用和自己的discovery.cluster-name一样的服务集群。

配置:

userService:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule

重启服务后请求order-service多请求几次会发现调用的都是clustername为SC的服务(order-service的clusterName也要设置为SC。

2.5 权重配置

经过如上配置后我们请求orderservice时orderservice就会去找集群名一样的userservice然后以轮询的方式请求现在我们可以通过nacos管理面板来配置服务的权重来控制服务被访问的频率。

  • 权重一般配置为0~1之间
  • 权重为0则服务不会被访问

2.6 环境隔离

nacos还可以配置namespace来进行环境隔离

在这里插入图片描述

不同namespace之间不能互相访问。

  1. nacos管理面板创建命名空间不填id的话会自动生成uuid

  2. 在application.yml中配置命名空间例如修改orderservice的命名空间:

    spring:
     cloud:
      nacos:
        server-addr: localhost:8848
        discovery:
          cluster-name: SC
          namespace: c8ae37f8-748e-4ada-8a9e-a9fb42c38116
    

重启实例后请求orderservice此时orderservice无法找到userservice。

2.7 临时实例和非临时实例

两者健康检测机制不同。

  • 临时:定时发送心跳给nacos不健康的临时实例会被nacos剔除
  • 非临时:nacos主动发请求询问不健康的非临时实例不会被nacos剔除

配置ephemeral属性:

spring:
 cloud:
  nacos:
   server-addr: localhost:8848
   discovery:
    ephemeral: false

nacos会主动推送消息给消费者告诉某个服务挂了eureka不会。

2.8 nacos配置管理

服务很多时配置复杂繁琐统一配置管理解决了很多问题。

  1. 在nacos管理面板上创建配置文件:

在这里插入图片描述

  1. 为了在读取application.yml前读取nacos配置文件我们需要创建一个bootstrap.yml文件(里面存放nacos的地址等信息该文件会在application.yml前读取:

    需要引入依赖:

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    

    创建bootstrap.yml(resource目录下):

    spring:
      application:
        name: userService
      profiles:
        active: dev
      cloud:
        nacos:
          server-addr: localhost:8848
          config:
            file-extension: yaml
    
  2. 写个接口测试一下(配置热更新加上@RefreshScope注解:

    @Slf4j
    @RestController
    @RequestMapping("/user")
    @RefreshScope
    public class UserController {
        @Value("${pattern.dateformat}")
      
        @GetMapping("/now")
        public String now() {
            return new SimpleDateFormat(dateformat, Locale.CHINA).format(new Date());
        }
    }
    

使用@ConfigurationProperties(prefix = "pattern")也可以热更新

稍微解释一下刚刚bootstrap.yml里面配置的东西:

微服务会去nacos读取多个配置文件(如果有

  • [spring.application.name]-[profiles.active].yaml
  • [spring.application.name].yaml

配置内容相同的部分前者优先级大于后者(本地优先级最低可以在nacos中再建一个配置文件试试后者就可以存放公共配置实现多环境配置共享。

2.9 nacos集群搭建

在这里插入图片描述

部署多个nacos并用nginx做负载均衡。

五个步骤:

  1. 搭建MySQL集群并初始化数据库表:

    sql:https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql

  2. 下载解压nacos

  3. 配置nacos

    在conf目录下创建cluster.conf配置文件并按照cluster.conf.example的内容配置节点。

    # 配置nacos节点
    127.0.0.1:8847
    127.0.0.1:8849
    127.0.0.1:8851
    

    配置application.properties

    spring.datasource.platform=mysql
    db.num=1
    
    db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
    db.user.0=nacos
    db.password.0=nacos
    
  4. 启动nacos

    三台nacos分别启动在bin目录下运行startup.cmd

    启动即可。

    这里有个小坑:

    Nacos2.0版本相比1.X新增了gRPC的通信方式因此需要增加2个端口。新增端口是在配置的主端口(server.port)基础上进行一定偏移量自动生成。

    端口与主端口的偏移量描述
    98481000客户端gRPC请求服务端端口用于客户端向服务端发起连接和请求
    98491001服务端gRPC请求服务端端口用于服务间同步等

    也就是在同一台机器上如果搭建集群建议使用884888508852这种有间隔的端口否则可能导致端口冲突。

  5. nginx负载均衡

    upstream nacos-cluster {
        server 127.0.0.1:8847;
        server 127.0.0.1:8849;
        server 127.0.0.1:8851;
    }
    
    server {
        listen       80;
        server_name  localhost;
    
        location /nacos {
            proxy_pass http://nacos-cluster/nacos/;
        }
    }
    

    启动nginx后访问localhost/nacos即可访问。

    代码中将以前的8848改为80端口即可。

三、Ribbon负载均衡

1. 负载均衡策略

http://userservice/user/1 这个地址dns是解析不出来的所以在order-service和user-service之间肯定有个东西能让他们通信那就是RibbonRibbon拿到这个地址后根据配置的负载均衡策略去调用相应的服务实例。

在这里插入图片描述

Ribbon默认是ZoneAvoidanceRule策略Eureka可配置的策略有:

在这里插入图片描述

配置策略有两种方式代码方式和配置文件:

  1. 代码方式:

    @Bean
    public IRule randomRule() {
        return  new RandomRule();   
    }
    
  2. 配置文件:

    userService:
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    

2. 饥饿加载

ribbon默认是懒加载即客户端第一次请求服务时ribbon才会去拉取服务列表这样会导致第一次请求延迟大。我们可以设置饥饿加载策略来让ribbon在客户端创建时就拉取服务列表降低第一次访问耗时。

在application.yml中配置:

ribbon:
  eager-load:
    enabled: true
    clients:
      - userservice

clients是一个数组里面写需要饥饿加载的服务。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Spring