二、Nacos服务注册中心应用实践

Nacos服务注册中心



一、Nacos服务注册中心

1. 注册中心简介

背景分析

在微服务中,首先需要面对的问题就是如何查找服务(软件即服务),其次,就是如何在不同的服务之间进行通信?如何更好更方便的管理应用中的每一个服务,如何建立各个服务之间联系的纽带,由此注册中心诞生(例如淘宝网卖家提供服务,买家调用服务)。

市面上常用注册中心有Zookeeper(雅虎Apache),Eureka(Netfix),Nacos(Alibaba),Consul(Google),那他们分别都有什么特点,我们如何进行选型呢?我们主要从社区活跃度,稳定性,功能,性能等方面进行考虑即可.本次微服务的学习,我们选择Nacos,它很好的支持了阿里的双11活动,不仅可以做注册中心,还可以作为配置中心,稳定性和性能都很好。

Nacos概述

Nacos(DynamicNaming and Configuration Service)是一个应用于服务注册与发现、配置管理的平台。它孵化于阿里巴巴,成长于十年双十一的洪峰考验,沉淀了简单易用、稳定可靠、性能卓越的核心竞争力。其官网地址如下:https://nacos.io/zh-cn/docs/quick-start.html

2. 构建Nacos服务

2.1 准备工作

第一: 确保你电脑已配置JAVA_HOME环境变量(Nacos启动时需要)

在这里插入图片描述
第二: 确保你的MySQL版本为5.7以上(MariaDB10.5以上)

在这里插入图片描述

2.2 下载与安装

第一步: Nacos下载,可在浏览器直接输入如下地址

https://github.com/alibaba/nacos/releases

第二步: 选择对应版本,直接下载

在这里插入图片描述
第三步: 解压Nacos(最好不要解压到中文目录下),其目录结构如下

在这里插入图片描述

2.3 初始化配置

第一步: 打开操作系统命令行,登陆mysql,执行课前资料中的sql脚本,不是nacos/conf目录下自带的。

例如,我们可以使用mysql自带客户端,在命令行首先登录mysql,然后执行如下指令:

source d:/nacos-mysql.sql 

执行成功以后,会创建一个nacos_config数据库,打开数据库会看到一些表,例如;

在这里插入图片描述
说明:在执行此文件时,要求mysql的版本大于5.7版本(MariaDB最好10.5.11),否则会出现如下错误:

在这里插入图片描述
第二步: 打开/nacos/conf/application.properties里打开默认配置,并基于你当前环境配置要连接的数据库,连接数据库时使用的用户名和密码(假如前面有"#"要将其去掉):

### If use MySQL as datasource:
spring.datasource.platform=mysql

### Count of DB:
db.num=1

### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root

3. 服务启动与访问

第一步: 启动Nacos服务(nacos的bin目录去通过指令启动)。

Linux/Unix/Mac启动命令(standalone代表着单机模式运行,非集群模式):

./startup.sh -m standalone

Windows启动命令(standalone代表着单机模式运行,非集群模式):

startup.cmd -m standalone

说明:

  1. 执行执行令时要么配置环境变量,要么直接在nacos/bin目录下去执行.
  2. nacos启动时需要本地环境变量中配置了JAVA_HOME(对应jdk的安装目录),
  3. 一定要确保你连接的数据库(nacos_config)是存在的.
  4. 假如所有的配置都正确,还连不上,检查一下你有几个数据库(mysql,…)

第二步: 访问Nacos服务。

打开浏览器,输入http://localhost:8848/nacos地址,出现如下登陆页面:

在这里插入图片描述

其中,默认账号密码为nacos/nacos.

3.1 业务描述

创建两个项目Module分别为服务提供者和服务消费者(假如已有则无需创建),两者都要注册到NacosServer中(这个server本质上就是一个web服务,端口默认为8848),然后服务提供者可以为服务消费者提供远端调用服务(例如支付服务为服务提供方,订单服务为服务消费方),如图所示:

在这里插入图片描述

3.2 生产者服务创建及注册

第一步: 创建服务提供者工程(module名为sca-provider,假如已有则无需创建),继承parent工程(01-sca),其pom.xml文件内容如下:

<?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>01-sca</artifactId>
        <groupId>com.jt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>sca-provider</artifactId>
    <dependencies>
        <!--Web服务-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--服务的注册和发现(我们要讲服务注册到nacos)-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>

第二步: 创建并修改配置文件application.yml(或者application.properties),实现服务注册,关键代码如下:

server:
   port: 8081
spring:
  application:
    name: sca-provider #进行服务注册必须配置服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

注意: 服务名不要使用下划线(“_”),应使用横杠(“-”),这是规则

第三步: 创建启动类(假如已有则无需定义),关键代码如下:

package com.jt;

@SpringBootApplication
public class ProviderApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(ProviderApplication.class, args);
    }
  }

第四步: 启动启动类,然后刷先nacos服务,检测是否服务注册成功,如图所示:

第五步: 停掉sca-provider服务,然后不断刷新nacos服务列表,检查服务的健康状态。

3.3 消费者服务发现及调用

第一步: 在sca-provider项目中创建服务提供方对象,基于此对象对外提供服务,例如:

    package com.jt.provider.controller;
    /**定义Controller对象(这个对象在spring mvc中给他的定义是handler),
     * 基于此对象处理客户端的请求*/
    @RestController
    public class ProviderController{
    
    
        //@Value默认读取项目配置文件中配置的内容
        //8080为没有读到server.port的值时,给定的默认值
        @Value("${server.port:8080}")
        private String server;
        //http://localhost:8081/provider/echo/tedu
        @GetMapping("/provider/echo/{msg}")
        public String doRestEcho1(@PathVariable String msg){
    
    
            return server+" say hello "+msg;
        }
    }

第二步: 创建服务消费者工程(module名为sca-consumer,假如已有则无需创建),继承parent工程(01-sca),其pom.xml文件内容如下:

<?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>01-sca</artifactId>
        <groupId>com.jt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>sca-consumer</artifactId>
    
   <dependencies>
	    <!--Web服务-->
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-web</artifactId>
	    </dependency>
	    <!--服务的注册和发现(我们要讲服务注册到nacos)-->
	    <dependency>
	        <groupId>com.alibaba.cloud</groupId>
	        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
	    </dependency>
    </dependencies>
</project>

第三步: 创建sca-consumer服务中的配置文件application.yml,关键代码如下:

server:
  port: 8090
spring:
  application:
    name: sca-consumer #服务注册时,服务名必须配置
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #从哪里去查找服务

第四步: 创建消费端启动类并实现服务消费,关键代码如下:

package com.jt;
@SpringBootApplication
public class ConsumerApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

第五步: 在sca-consumer启动类中添加如下方法,用于创建RestTemplate对象

@Bean
public RestTemplate restTemplate(){
    
    //基于此对象实现远端服务调用
    return new RestTemplate();
}

第六步: 定义sca-consumer服务的消费端Controller,在此对象方法内部实现远端服务调用

package com.jt.consumer.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;


/**
 * 定义服务消费端Controller,在这个Controller对象
 * 的方法中实现对远端服务sca-provider的调用
 */
@RestController
public class ConsumerController {
    
    
    /**
     * 从spring容器获取一个RestTemplate对象,
     * 基于此对象实现远端服务调用
     */
    @Autowired
    private RestTemplate restTemplate;
    /**
     * 在此方法中通过一个RestTemplate对象调用远端sca-provider中的服务
     * @return
     * 访问此方法的url: http://localhost:8090/consumer/doRestEcho1
     */
    @GetMapping("/consumer/doRestEcho1")
    public String doRestEcho01(){
    
    
        //1.定义要调用的远端服务的url
        String url="http://localhost:8081/provider/echo/8090";
        //2.基于restTemplate对象中的相关方法进行服务调用
        return restTemplate.getForObject(url, String.class);
    }
}

第七步: 启动消费者服务,并在浏览器输入http://localhost:8090/consumer/doRestEcho1地址进行访问测试。

3.4 小节面试分析

  • 为什么要将服务注册到nacos?(为了更好的查找这些服务)
  • 在Nacos中服务提供者是如何向Nacos注册中心(Registry)续约的?(5秒心跳)
  • 对于Nacos服务来讲它是如何判定服务实例的状态?(检测心跳包,15,30)
  • 服务消费方是如何调用服务提供方的服务的?(RestTemplate)

二、服务负载均衡设计及实现

业务描述

一个服务实例可以处理请求是有限的,假如服务实例的并发访问比较大,我们会启动多个服务实例,让这些服务实例采用一定策略均衡(轮询,权重,随机,hash等)的处理并发请求,在Nacos中服务的负载均衡(Nacos客户端负载均衡)是如何应用的?

1. LoadBalancerClient应用

LoadBalancerClient对象可以从nacos中基于服务名获取服务实例,然后在工程中基于特点算法实现负载均衡方式的调用,案例实现如下:

第一步: 修改ConsumerController类,注入LoadBalancerClient对象,并添加doRestEcho2方法,然后进行服务访问.

 @Autowired
 private LoadBalancerClient loadBalancerClient;
  
 @Value("${spring.application.name:8090}")
 private String appName;
   
 @GetMapping("/consumer/doRestEcho02")
 public String doRestEcho02(){
    
    
     ServiceInstance serviceInstance = loadBalancerClient.choose("sca-provider");
     String url = String.format("http://%s:%s/provider/echo/%s",serviceInstance.getHost(),serviceInstance.getPort(),appName);
     System.out.println("request url:"+url);
     return restTemplate.getForObject(url, String.class);
     }
 }

第二步: 打开Idea服务启动配置,如图所示:

在这里插入图片描述
第三步: 修改并发运行选项(假如没有找到这个选项我们需要通过搜索引擎基于组合查询的方法,去找到对应的解决方案,例如搜索 idea allow parallel run),如图所示:

在这里插入图片描述

第四步: 修改sca-provider的配置文件端口,分别以8081,8082端口方式进行启动。

server:
  port: 8082
spring:
  application:
    name: sca-provider
  cloud:
    nacos:
      server-addr: localhost:8848

第五步: 启动成功以后,访问nacos的服务列表,检测服务是否成功注册,如图所示:

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

在这里插入图片描述

第六步: 启动sca-consumer项目模块,打开浏览器对consumer服务进行访问,访问时不断刷新,检测页面数据变化。

说明,这里多个实例并发提供服务的方式为负载均衡,这里的负载均衡实现默认是因为Nacos集成了Ribbon来实现的,Ribbon配合RestTemplate,可以非常容易的实现服务之间的访问。Ribbon是Spring Cloud核心组件之一,它提供的最重要的功能就是客户端的负载均衡(客户端可以采用一定算法,例如轮询访问,访问服务端实例信息),这个功能可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡方式的服务调用。

2. @LoadBalanced

当使用RestTemplate进行远程服务调用时,假如需要负载均衡,还可以在RestTemplate对象构建时,使用@LoadBalanced对构建RestTemplate的方法进行修饰,例如在ConsumerApplication中构建名字为loadBalancedRestTemplate的RestTemplate对象:

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

在需要RestTemplate实现负载均衡调用的地方进行依赖注入.例如在ConsumerController类中添加loadBalancedRestTemplate属性

@Autowired
private RestTemplate loadBalancedRestTemplate;

接下来,可以在对应的服务端调用方的方法内,基于RestTemplate借助服务名进行服务调用, 例如:

@GetMapping("/consumer/doRestEcho3")
public String doRestEcho03(){
    
    
    String url=String.format("http://%s/provider/echo/%s","sca-provider",appName);
    //向服务提供方发起http请求,获取响应数据
    return loadBalancedRestTemplate.getForObject(
            url,//要请求的服务的地址
            String.class);//String.class为请求服务的响应结果类型
}

RestTemplate在发送请求的时候会被LoadBalancerInterceptor拦截,它的作用就是用于RestTemplate的负载均衡,LoadBalancerInterceptor将负载均衡的核心逻辑交给了loadBalancer,核心代码如下所示(了解):

public ClientHttpResponse intercept(final HttpRequest request, 
    final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
    
    
	final URI originalUri = request.getURI();
	String serviceName = originalUri.getHost();
	return this.loadBalancer.execute(serviceName, 
    requestFactory.createRequest(request, body, execution));
}

@LoadBalanced注解是属于Spring,而不是Ribbon的,Spring在初始化容器的时候,如果检测到Bean被@LoadBalanced注解,Spring会为其设置LoadBalancerInterceptor的拦截器。

3. Ribbon负载均衡策略

基于Ribbon方式的负载均衡,Netflix默认提供了七种负载均衡策略,对于SpringCloud Alibaba解决方案中又提供了NacosRule策略,默认的负载均衡策略是轮训策略。如图所示:

在这里插入图片描述
当系统提供的负载均衡策略不能满足我们需求时,我们还可以基于IRule接口自己定义策略.

4. 小节面试分析

1.@Bean注解的作用?

一般用于配置类内部,描述相关方法,用于告诉spring此方法的返回值要交给spring管理,bean的名字默认为方法名,假如需要指定名字可以@Bean(“bean的名字”),最多的应用场景是整合第三方的资源-对象

2.@Autowired注解的作用?

此注解用于描述属性,构造方法,set方法等,用于告诉spring框架,按找一定的规则为属性进行DI操作,默认按属性,方法参数类型查找对应的对象,假如只找到一个,则直接注入,类型多个时还会按照属性名或方法参数名进行值的注入,假如名字也不同,就出报错.

3.Nacos中的负责均衡底层是如何实现的?

通过Ribbon实现,Ribbon中定义了一些负载均衡算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务

4.Ribbon 是什么?

Netflix公司提供的负载均衡客户端,一般应用于服务的消费方法

5.Ribbon 可以解决什么问题?

基于负载均衡策略进行服务调用, 所有策略都会实现IRule接口

6.Ribbon 内置的负载策略都有哪些?

8种,可以通过查看IRule接口的实现类进行分析

7.@LoadBalanced的作用是什么?

描述RestTemplate对象,用于告诉Spring框架,在使用RestTempalte进行服务调用时,这个调用过程会被一个拦截器进行拦截,然后在拦截器内部,启动负载均衡策略。

8.我们可以自己定义负载均衡策略吗?

可以,基于IRule接口进行策略定义,也可以参考NacosRule进行实现

三、基于Feign的远程服务调用

背景分析

服务消费方基于rest方式请求服务提供方的服务时,一种直接的方式就是自己拼接url,拼接参数然后实现服务调用,但每次服务调用都需要这样拼接,代码量复杂且不易维护,此时Feign诞生。

1. Feign是什么

Feign 是一种声明式Web服务客户端,底层封装了对Rest技术的应用,通过Feign可以简化服务消费方对远程服务提供方法的调用实现。如图所示:

在这里插入图片描述
Feign 最早是由 Netflix 公司进行维护的,后来 Netflix 不再对其进行维护,最终 Feign 由一些社区进行维护,更名为 OpenFeign。

2. Feign应用实践

第一步: 在服务消费方,添加项目依赖(SpringCloud团队基于OpenFeign研发了starter),代码如下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

第二步: 在启动类上添加@EnableFeignClients注解,代码如下:

@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {
    
    }

第三步: 定义Http请求API,基于此API借助OpenFeign访问远端服务,代码如下:

package com.jt.consumer.service;
@FeignClient(name="sca-provider")//sca-provider为服务提供者名称
public interface RemoteProviderService{
    
    
    @GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
    public String echoMessage(@PathVariable("string") String string);
}

其中,@FeignClient描述的接口底层会为其创建实现类。

第四步: 创建FeignConsumerController中并添加feign访问,代码如下:

package com.jt.consumer.controller;
@RestController
@RequestMapping("/consumer/ ")
public class FeignConsumerController {
    
    
    @Autowired
    private RemoteProviderService remoteProviderService;
    /**基于feign方式的服务调用*/
    @GetMapping("/echo/{msg}")
    public String doFeignEcho(@PathVariable  String msg){
    
    
        //基于feign方式进行远端服务调用(前提是服务必须存在)
        return remoteProviderService.echoMessage(msg);
    }
}

第五步: 启动消费者服务,在浏览器中直接通过feign客户端进行访问,如图所示(反复刷新检测其响应结果):

在这里插入图片描述
说明,feign方式的远程服务调用,底层会自动基于ribbon组件实现负载均衡。

3. Feign配置进阶实践

一个服务提供方通常会提供很多资源服务,服务消费方基于同一个服务提供方写了很多服务调用接口,此时假如没有指定contextId,服务启动就会失败,例如,假如在服务消费方再添加一个如下接口,消费方启动时就会启动失败,例如:

 @FeignClient(name="sca-provider")
 public interface RemoteOtherService {
    
    
     @GetMapping("/doSomeThing")
     public String doSomeThing();
}

其启动异常如下:

The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.

此时我们需要为远程调用服务接口指定一个contextId,作为远程调用服务的唯一标识(这个标识是Bean对象的名字)即可,例如:

@FeignClient(name="sca-provider",contextId="remoteProviderService")//sca-provider为服务提供者名称
interface RemoteProviderService{
    
    
    @GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
    public String echoMessage(@PathVariable("string") String string);
}

还有,当我们在进行远程服务调用时,假如调用的服务突然不可用了或者调用过程超时了,怎么办呢?一般服务消费端会给出具体的容错方案,例如,在Feign应用中通过FallbackFactory接口的实现类进行默认的相关处理,例如:

第一步: 定义FallbackFactory接口的实现,代码如下:

package com.jt.service.factory;
/**
 * 基于此对象处理RemoteProviderService接口调用时出现的服务中断,超时等问题
 */
@Component
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
    
    
    /**
     * 此方法会在RemoteProviderService接口服务调用时,出现了异常后执行.
     * @param throwable 用于接收异常
     */
    @Override
    public RemoteProviderService create(Throwable throwable) {
    
    
        return (msg)->{
    
    
                return "服务维护中,稍等片刻再访问";
        };
    }
}

第二步: 在Feign访问接口中应用FallbackFactory对象,例如:

@FeignClient(name = "sca-provider", contextId = "remoteProviderService",
             fallbackFactory = ProviderFallbackFactory.class)//sca-provider为nacos中的服务名
public interface RemoteProviderService {
    
    
    @GetMapping("/provider/echo/{msg}")
    public String echoMsg(@PathVariable String msg);
}

第三步: 在配置文件application.yml中添加如下配置,启动feign方式调用时的服务中断处理机制.

feign:  
  hystrix:
    enabled: true #默认值为false

第四步: 在服务提供方对应的调用方法中添加Thread.sleep(5000)模拟耗时操作,然后启动服务进行访问测试.

4. Feign 调用过程分析

Feign应用过程分析(底层逻辑先了解):

  1. 通过 @EnableFeignCleints 注解告诉springcloud,启动 Feign Starter 组件。
  2. Feign Starter 会在项目启动过程中注册全局配置,扫描包下所由@FeignClient注解描述的接口,然后由系统底层创建接口实现类(JDK代理类),并构建类的对象,然后交给spring管理(注册 IOC 容器)。
  3. Feign接口被调用时,底层代理对象会将接口中的请求信息通过编码器创建 Request对象,基于此对象进行远程过程调用。
  4. Feign客户端请求对象会经Ribbon进行负载均衡,挑选出一个健康的 Server 实例(instance)。
  5. Feign客户端会携带 Request 调用远端服务并返回一个响应。
  6. Feign客户端对象对Response信息进行解析然后返回客户端。

四、Nacos配置快速入门

业务描述

在sca-provider项目中添加一个Controller对象,例如ProviderLogController,基于此Controller中的方法演示日志级别的配置。

1. 配置准备工作

第一步: 创建ProviderLogController对象,例如:

package com.jt.provider.controller;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * 基于此controller演示配置中心的作用.
 * 在这个controller中我们会基于日志对象
 * 进行日志输出测试.
 */
//@Slf4j
@RestController
public class ProviderLogController {
    
    
    //创建一个日志对象
    //org.slf4j.Logger (Java中的日志API规范,基于这个规范有Log4J,Logback等日志库)
    //org.slf4j.LoggerFactory
    //log对象在哪个类中创建,getLogger方法中的就传入哪个类的字节码对象
    //记住:以后只要Java中使用日志对象,你就采用下面之中方式创建即可.
    //假如在log对象所在的类上使用了@Slf4j注解,log不再需要我们手动创建,lombok会帮我们创建
   private static Logger log=
           LoggerFactory.getLogger(ProviderLogController.class);
    @GetMapping("/provider/log/doLog01")
    public String doLog01(){
    
    //trace<debug<info<warn<error
        System.out.println("==doLog01==");
        log.trace("===trace===");
        log.debug("===debug===");
        log.info("===info====");
        log.warn("===warn===");
        log.error("===error===");
        return "log config test";
    }
}

第二步: 在已有的sca-provider项目中添加如配置依赖,例如:

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

第三步: 将项目sca-provider的application.yml的名字修改为bootstrap.yml(启动优先级最高),并添加配置中心配置,代码如下:

spring:
  application:
    name: sca-provider
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yml # Configure the data format of the content, default to properties

2. 新建Nacos配置

打开nacos配置中心,新建配置,如图所示:

在这里插入图片描述
其中,Data ID的值要与bootstrap.yml中定义的spring.application.name的值相同(服务名-假如有多个服务一般会创建多个配置实例,不同服务对应不同的配置实例)。配置发布以后,会在配置列表中,显示我们的配置,例如:

在这里插入图片描述

3. 测试Nacos数据读取

配置创建好以后,启动sca-provider服务,然后打开浏览器,输入http://localhost:8081/provider/log/doLog01,检测idea控制台日志输出。然后再打开nacos控制台动态更新日志级别,再访问资源并检测后台日志输出.
在这里插入图片描述
然后,修改nacos配置中心的日志级别,再刷新浏览器,检测日志的输出,是否会发生变化.

4. @RefreshScope注解的应用

对于nacos配置中心而言,有系统内部对配置变化的感知,还有外部系统对配置的感知,假如我们系统在浏览器中能看到日志级别的变化,该如何实现呢?我们现在来实现一个案例.

第一步: 在ProviderLogController类的上面添加一个@RefreshScope注解,例如:

@RefreshScope
@RestController
public class ProviderLogController{
    
    
  //.....
}

其中,@RefreshScope的作用是在配置中心的相关配置发生变化以后,能够及时看到类中属性值的更新(底层是通过重新创建Controller对象的方式,对属性进行了重新初始化)。

第二步: 添加ProviderLogController中添加一个获取日志级别(debug<info<warn<error)的的属性和方法,代码如下:

@Value("${logging.level.com.jt:error}")
private String logLevel;
@GetMapping("/provider/log/doLog02")
public String doLog02(){
    
    
   log.info("log level is  {}",logLevel);
   return  "log level  is "+logLevel;
}

5. 小节面试分析

1.配置中心一般都会配置什么内容?

可能会经常变化的配置信息,例如连接池,日志、线程池、限流熔断规则

2.什么信息一般不会写到配置中心?

服务端口,服务名,服务的注册地址,配置中心

3.项目中为什么要定义bootstrap.yml文件?

此文件被读取的优先级比较高,可以在服务启动时读取配置中心的数据

4.Nacos配置中心宕机了,我们的服务还可以读取到配置信息吗?

可以从内存,客户端获取了配置中心的配置信息以后,会将配置信息在本地内存中存储一份.

5.微服务应用中我们的客户端如何获取配置中心的信息?

我们的服务一般首先会从内存读取配置信息,同时我们的微服务还可以定时向nacos配置中心发请求拉取(pull)更新的配置信息

6.微服务应用中客户端如何感知配置中心数据变化?

1.4.x版本的nacos客户端会基于长轮询机制从nacos获取配置信息,所谓的长轮询就是没有配置更新时,会在nacos服务端的队列进行等待.

7.服务启动后没有从配置中心获取我们的配置数据是什么原因?

依赖,配置文件名字bootstrap.yml,配置中心的dataId名字是否正确,分组是否正确,配置的名字是否正确,缩进关系是否正确,假如是动态发布,类上是否有@RefreshScope注解

8.你项目中使用的日志规范是什么?

SLF4J

9.你了解项目中的日志级别吗?

debug,info,error,…,可以基于日志级别控制日志的输出

五、Nacos配置管理模型

1. 概述

Nacos 配置管理模型由三部分构成,如图所示:
在这里插入图片描述
其中:

  • Namespace:命名空间,对不同的环境进⾏隔离,⽐如隔离开发环境和⽣产环境。
  • Group:分组,将若⼲个服务或者若⼲个配置集归为⼀组。
  • Service/DataId:某⼀个服务或配置集,一般对应一个配置文件。

2. 命名空间设计

Nacos中的命名空间一般用于配置隔离,这种命名空间的定义一般会按照环境(开发,生产等环境)进行设计和实现.我们默认创建的配置都存储到了public命名空间,如图所示:
在这里插入图片描述
创建新的开发环境并定义其配置,然后从开发环境的配置中读取配置信息,该如何实现呢?

第一步: 创建新命名空间,如图所示:
在这里插入图片描述
命名空间成功创建以后,会在如下列表进行呈现。
在这里插入图片描述
在指定命名空间下添加配置,也可以直接取配置列表中克隆,例如:
在这里插入图片描述
在这里插入图片描述
克隆成功以后,我们会发现在指定的命名空间中有了我们克隆的配置,如图所示:
在这里插入图片描述
此时我们修改dev命名空间中Data Id的sca-provider配置,如图所示:
在这里插入图片描述
修改项目module中的配置文件bootstrap.yml,添加如下配置,关键代码如下:

spring:
  cloud:
    nacos:
      config:
        namespace: 6058fd3f-0d4d-44f2-85d6-5fc7d2348046
        ……

其中,namespace后面的字符串为命名空间的id,可直接从命名空间列表中进行拷贝.然后重启服务,继续刷新http://localhost:8081/provider/log/doLog02地址。检测输出,看看输出的内容是什么,是否为dev命名空间下配置的内容。

3. 分组设计及实现

当我们在指定命名空间下,按环境或服务做好了配置以后,有时还需要基于服务做分组配置,例如,一个服务在不同时间节点(节假日,活动等)切换不同的配置,可以在新建配置时指定分组名称,如图所示:
在这里插入图片描述
其中,这里的useLocalCache为自己定义的配置值,表示是否使用本地缓存.

配置发布以后,修改boostrap.yml配置类,在其内部指定我们刚刚创建的分组,代码如下:

server:
  port: 8081
spring:
  application:
    name: sca-provider
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        group: DEFAULT_GROUP_51 # Group, default is DEFAULT_GROUP
        file-extension: yml # Configure the data format of the content, default to properties
        namespace: 7da4aa75-f64c-43c6-b101-9d77ad96f1c0

在指定的Controller类中添加属性和方法用于获取和输出DEFAULT_GROUP_51中的useLocalCache的值,代码如下:

package com.jt.provider.controller;

@RefreshScope
@RestController
public class ProviderCacheController {
    
    
    @Value("${useLocalCache:false}")
    private boolean useLocalCache;

    @RequestMapping("/provider/cache01")
    public String doUseLocalCache01(){
    
    
        return "useLocalCache'value is   "+useLocalCache;
    }
}

然后重启服务,进行访问测试,检测内容输出。

在这里插入图片描述

4. 共享配置设计及读取

当同一个namespace的多个配置文件中都有相同配置时,可以对这些配置进行提取,然后存储到nacos配置中心的一个或多个指定配置文件,哪个微服务需要,就在服务的配置中设置读取即可。例如:

第一步: 在nacos中创建一个共享配置文件,例如:

在这里插入图片描述

其中,这里的secret可以理解为一个密钥。

第二步: 在指定的微服务配置文件(bootstrap.yml)中设置对共享配置文件的读取,例如:

spring:
  application:
    name: sca-provider
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        # 命名空间
        namespace: 83ed55a5-1dd9-4b84-a5fe-a734e4a6ec6d
        # 分组名
        # group: DEFAULT_GROUP
        # 配置中心文件扩展名
        file-extension: yml
        # 共享配置
        shared-configs[0]:
                data-id: app-public.yml
                refresh: true #默认false,共享配置更新,引用此配置的地方是否要更新

第三步: 在指定的Controller类中读取和应用共享配置即可,例如:

package com.jt.provider.controller;

@RefreshScope
@RestController
public class ProviderSecretController {
    
    
    @Value("${app.secret:123456}")
    private String secret;
    @GetMapping("/provider/secret")
    public String doGetSecret(){
    
    
        //return String.format()
        return "The Secret is "+secret;
    }
}

**第四步:**启动服务,然后打开浏览器进行访问测试。
在这里插入图片描述

5. 小节面试分析

1.Nacos配置管理模型的背景?

环境不同配置不同

2.Nacos配置中的管理模型是怎样的?

namespace,group,service/data-id

3.Nacos客户端(微服务)是否可以读取共享配置?

可以

6. 总结(Summary)

重难点分析

  • 配置中心的选型。(市场活跃度、稳定性、性能、易用)
  • Nacos配置中心基本应用。(新建,修改、删除配置以后,在Nacos客户端应用配置)
  • 配置管理模型应用。(namespace,group,service/dataId)
  • Nacos配置变更的动态感知。(底层原理分析)

FAQ分析

  • 为什么需要配置中心?(动态管理发布配置,无需重启服务,更好保证服务的可用)
  • 配置中一般要配置什么内容?(经常变化的配置数据-日志级别,线程池、连接池、…)
  • 市面上有哪些主流配置中心?(Nacos,….)
  • 配置中心选型时要重点考虑哪些因素?(市场活跃度、稳定性、性能、易用)
  • Nacos客户端(微服务业务)如何动态感知配置中心数据变化的?(nacos2.0之前nacos客户端采用长轮询机制每隔30秒拉取nacos配置信息.)
  • Nacos配置管理模型是怎样的?(命名空间-namespace,分组-group,服务实例-dataId)

猜你喜欢

转载自blog.csdn.net/weixin_47410172/article/details/127996729