Spring Cloud(二)Eureka注册中心、Ribbon负载均衡、Nacos注册中心

Eureka注册中心

在上一篇中,我们提到了服务调用。

但在服务调用中,存在一系列问题:

  • 服务消费者该如何获取服务提供者的地址信息?
  • 如果有多个服务提供者,消费者该如何选择?
  • 消费者如何得知服务提供者的健康状态(是否宕机)?
    在这里插入图片描述

Eureka的作用

在这里插入图片描述
通过使用Eureka,就可以解决服务调用所产生的问题了:

消费者如何获取服务提供者具体信息?

  • 服务提供者启动时向eureka注册自己的信息。
  • eureka保存这些信息。
  • 消费者根据服务名称向eureka拉取提供者信息。

如果有多个消费者,消费者该如何选择?

  • 服务消费者利用负载均衡算法,从服务列表中挑选一个。

消费者如何感知服务提供者的健康状态?

  • 服务提供者每隔30秒向EurekaServer发送心跳请求,报告健康状态。
  • eureka会更新记录服务列表信息,心跳不正常会被剔除出列表。
  • 消费者可以拉取到最新的信息。

在Eureka架构中,微服务角色有两类:

  1. EurekaServer:服务端,注册中心
    • 记录服务信息
    • 心跳监控
  2. EurekaClient:客户端
    • Provider:服务提供者,例如上例的user-service
      • 注册自己的信息到EurekaServer
      • 每隔30秒向EurekaServer发送心跳
    • Consumer:服务消费者,例如案例中的order-service
      • 根据服务名称从EurekaServer拉取服务列表
      • 基于服务列表做负载均衡,选中一个微服务后发起远程调用。

搭建Eureka Server

第一步,引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-demo</artifactId>
        <groupId>cn.itcast.demo</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

第二步,编写启动类,添加@EnableEurekaServer注解

package cn.itcast.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * @version 1.0
 * @Description
 * @Author 月上叁竿
 * @Date 2022-05-04 19:15
 **/
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class,args);
    }
}

第三步,编写application.yml配置文件

server:
  port: 10086
spring:
  application:
    name: eurekaserver
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka/

Eureka自身也是一个微服务,因此在启动时会自己注册自己。
在这里插入图片描述

搭建Eureka Client

第一步,引入依赖

<!-- 引入eureka客户端依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

第二步,编写application.yml配置文件

spring:
  application:
    name: userservice # user服务的服务名称
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka/

如何模拟多实例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

服务发现

步骤一:修改url路径,用服务名代替ip、端口

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private RestTemplate restTemplate;

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.得到user信息  ip 端口 修改为服务名称
        String url = "http://userservice/user/" + order.getUserId();
        User user = restTemplate.getForObject(url, User.class);
        // 3.将user信息进行封装
        order.setUser(user);
        // 4.返回
        return order;
    }
}

步骤二:在order-service项目的启动类中的RestTemplate添加负载均衡注解

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

Ribbon负载均衡

负载均衡流程

在这里插入图片描述
当order-serivice发送请求后,Ribbon会对请求进行拦截,向eureka-server拉取user-service的信息,若查到则返回服务列表,通过Ribbon进行负载均衡,选择列表中的一个服务进行调用。

在这里插入图片描述

负载均衡策略

Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每一个子接口都是一种规则:
在这里插入图片描述
IRule的默认实现是ZoneAvoidanceRule,根据zone选择服务列表,然后轮询
在这里插入图片描述
通过定义IRule实现可以修改负载均衡规则,有两种方式:

  1. 代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
    @Bean
    public IRule randomRule(){
        return new RandomRule();
    }
    
  2. 配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
    userservice:
      ribbon:
    	NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #负责均衡规则
    

注:约定大于配置,因此代码方式的优先级要高于配置文件方式

饥饿加载(饿汉式)

Ribbon默认采用懒加载,即第一次访问时才会创建LoadBalanceClient,请求时间会很长。

而饥饿加载则会在项目启动时就创建,从而降低第一次访问的耗时,通过以下配置开启饥饿加载:

ribbon:
  eager-load:
    # 指定对userservice这个服务进行饥饿加载,这里的clients可以是列表
    clients: userservice 
    enabled: true # 开启饥饿加载

Nacos注册中心

Nacos是阿里巴巴的产品,现在是Spring Cloud中的一个组件。相比Eureka功能更加丰富,在国内受欢迎程度较高。

Nacos的安装

GitHub的Release下载页:https://github.com/alibaba/nacos/releases
在这里插入图片描述
下载好压缩文件后,将其解压至非中文路径下,会有两个目录:

  • conf:配置文件
  • bin:启动脚本

在conf中的application.properties中可以修改端口,若8848端口已被占用,可以进行修改。

bin目录下有四个启动脚本,注意不要直接启动,而是通过命令行窗口用命令进行启动:

在这里插入图片描述在这里插入图片描述
启动服务后,在浏览器键入http://localhost:8848/nacos/index.html#login

在这里插入图片描述
会进入Nacos的Web管理界面,密码账号均是nacos。

服务注册

第一步,在cloud-demo父工程中添加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>

第二步,添加nacos的客户端依赖

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

第三步,修改user-service和order-service的application.yml,添加nacos地址

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

启动服务并测试

在这里插入图片描述

Nacos服务分级存储模型

分级存储模型:

第一级是服务,例如user-service
第二级是集群,例如杭州或上海
第三级是实例,例如杭州机房的某台部署了userservice的服务器
在这里插入图片描述

服务跨集群调用问题

服务调用尽可能地选择本地集群的服务,跨集群调用延迟较高。

本地集群不可访问时,再考虑取访问其他集群。

在这里插入图片描述

服务集群属性

第一步,修改application.yml,添加如下属性

spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: SH # 集群名称

在Nacos控制台可以看到集群的变化

在这里插入图片描述

集群负载均衡策略

根据集群负载均衡

第一步,修改order-service的application.yml,设置集群为HZ

spring:
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos服务地址
      discovery:
        cluster-name: HZ # 集群名称

第二步,在order-service中设置负载均衡的IRule为NacosRule,这个规则优先会寻找与自己同集群的服务

userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则

NacosRule负载均衡策略:

  1. 优先选择同集群服务实例列表。
  2. 本地集群找不到提供者,才会去其他集群寻找。
  3. 确定了可用实例列表后,再采用随机负载均衡挑选实例

根据权重负载均衡

实际部署中会出现这样的场景:

  • 服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的及其承担更多的用户请求。

Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。

在这里插入图片描述
在Nacos控制台可以设置实例的权重值,首先选中实例后的编辑按钮,在弹出的界面中将权重设置为0.1,测试发现8081端口所在的服务被访问到的频率大大降低。

实例的权重控制总结:

  1. Nacos控制台可以设置实例的权重值,0~1之间。
  2. 同集群内的多个实例,权重越高被访问的频率越高。
  3. 权重设置为0完全不会被访问。

环境隔离

Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用于做最外层隔离。

在Nacos控制台可以创建namespace,用于隔离不同环境

在这里插入图片描述

填写一个新的命名空间信息

在这里插入图片描述

保存后在后台可以看到命名空间信息

在这里插入图片描述

修改order-service的application.yml,添加namespace

spring:
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos服务地址
      discovery:
        cluster-name: HZ # 集群名称
        namespace: 3e16f3b3-6ea4-47fa-af3c-abb2df9c598e # dev环境

重启order-service后,查看控制台

在这里插入图片描述
在这里插入图片描述
此时访问order-service,由于namespace的不同,会导致找不到userservice,控制台会报错。

环境隔离总结:

  1. 每个namespace都有唯一的id
  2. 服务设置namespace时要写id而不是名称
  3. 不同namespace下的服务互相不可见

原理分析

在这里插入图片描述
①服务提供者在启动时会向Nacos注册服务信息,服务消费者启动后会向Nacos定时拉取服务列表,若有对应的服务,则返回列表信息给消费者,消费者通过负载均衡算法选择一个服务进行远程调用。

②服务提供者分为临时实例和非临时实例,临时实例采用心跳监测,每隔一段时间会向Nacos发送请求,以报告自身健康状态,若服务挂掉,一段时间不给Nacos发送请求,Nacos会将其进行剔除;非临时实例由Nacos主动询问其健康信息,若服务挂掉,Nacos不会对其进行剔除,而是等待其恢复正常。

③当服务信息变更时,Nacos会主动推送更新后的服务列表信息。

临时实例和非临时实例

服务注册到Nacos时,可以选择注册为临时或非临时实例,通过修改application.yml文件来进行配置:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos服务地址
      discovery:
        cluster-name: HZ # 集群名称
        namespace: 3e16f3b3-6ea4-47fa-af3c-abb2df9c598e # dev环境
        ephemeral: false # 是否是临时实例

Nacos和Eureka的异同点

  • Nacos与Eureka的共同点
    1. 都支持服务注册和服务拉取。
    2. 都支持服务提供者心跳方式做健康检测。
  • Nacos与Eureka的不同点
    1. Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式。
    2. 临时实例心跳不正常会被剔除,非临时实例则不会被剔除。
    3. Nacos支持服务列表变更的消息推送模式,服务列表更新更及时。
    4. Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式。

配置管理

Nacos不仅提供了注册服务,还提供了配置管理服务。
在这里插入图片描述
在这里插入图片描述
配置获取的步骤如下:

在这里插入图片描述
在项目启动后,会将nacos中配置文件内容和本地配置文件application.yml合并,然后再创建Spring容器,加载bean。

由于我们在编写nacos配置文件时,Data ID需要写服务名称,而我们对于服务名称的配置是在application.yml中的,这就造成了无法读取nacos配置文件,那要怎么做呢?

我们可以添加一个引导文件bootstrap.yml,它的优先级是要高于application.yml的

引入Nacos的配置管理客户端依赖

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

在userservice中的resource目录添加一个bootstrap.yml文件

spring:
  application:
    name: userservice # 服务名称
  profiles:
    active: dev # 环境
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
      config:
        file-extension: yaml # 文件后缀名

测试

package cn.itcast.user.web;

import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Value("${pattern.dateformat}")
    private String dateformat;

    /**
     * 路径: /user/110
     *
     * @param id 用户id
     * @return 用户
     */
    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id) {
        return userService.queryById(id);
    }

    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
    }
}

将配置交给Nacos管理的步骤:

  1. 在Nacos中添加配置文件
  2. 在微服务中引入Nacos的config依赖
  3. 在微服务中添加bootstrap.yml,配置Nacos地址、当前环境、服务名称、文件后缀名。这些决定了程序启动时去Nacos读取哪个配置文件。

配置自动刷新(热更新)

Nacos中的配置文件变更后,微服务无需重启就可以感知。不过需要下面两种配置来实现。

方式一:在@Value注入的变量所在类上添加注解@RefreshScope

package cn.itcast.user.web;

import cn.itcast.user.config.PatternProperties;
import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController {
    @Autowired
    private UserService userService;

    @Value("${pattern.dateformat}")
    private String dateformat;

    /**
     * 路径: /user/110
     *
     * @param id 用户id
     * @return 用户
     */
    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id) {
        return userService.queryById(id);
    }

    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat);
    }
}

方式二:使用@ConfigurationProperties注解

package cn.itcast.user.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @version 1.0
 * @Description
 * @Author 月上叁竿
 * @Date 2022-05-07 10:05
 **/
@Component
@Data
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
    private String dateformat;
}
@Slf4j
@RestController
@RequestMapping("/user")
//@RefreshScope
public class UserController {

    @Autowired
    private UserService userService;

//    @Value("${pattern.dateformat}")
//    private String dateformat;

    @Autowired
    private PatternProperties properties;

    /**
     * 路径: /user/110
     *
     * @param id 用户id
     * @return 用户
     */
    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id) {
        return userService.queryById(id);
    }

    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(properties.getDateformat()));
    }
}

注意事项:

  • 不是所有的配置都适合放到配置中心,维护起来比较麻烦。
  • 建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义配置。
  • 推荐使用第二种方式进行热更新

多环境配置共享

微服务启动时会从Nacos中读取多个配置文件:

  • [spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml,环境配置
  • [spring.application.name].yaml,例如:userservice.yaml,默认配置。

无论profile如何变化,[spring.application.name].yaml这个文件一定会被加载,因此多环境共享配置可以写入这个文件

在这里插入图片描述

多种配置的优先级

在这里插入图片描述
[服务名]-[环境].yaml > [服务名].yaml > 本地配置

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值