nacos学习笔记

OpenFeign

1.使用规则

  • 引入依赖xml
        <!-- openfeign 远程调用 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
         <!--feign-jackson -->
         <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-jackson</artifactId>
        </dependency>
        <!-- nacos服务注册与发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- 负载均衡 -->
         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>
  • 开启注解@EnableFeignClients
@SpringBootApplication
@EnableFeignClients //扫描和注册feign客户端bean定义
public class MallUserFeignDemoApplication {

    public static void main(String[] args) {

        SpringApplication.run(MallUserFeignDemoApplication.class, args);
    }
}
  • 使用@FeignClent,这里注意的是,参数前一定要加注解,否则默认是@RequestBody,多个参数的话一定要加
  • openFeign请求接受是String类型,所以用String接受也是可以的。
//FeignConfig局部配置,让指定的微服务生效,在@FeignClient 注解中指定configuration
//@FeignClient(value = "mall-order",path = "/order",configuration = FeignConfig.class)
@FeignClient(value = "mall-order",path = "/order")
public interface OrderFeignService {

    /**
     *  1. openfeign传递restful参数
     *  方法要求:
     *    返回值: 要对应
     *    方法名:随意
     *    参数: 要对应的注解
     *  方法上添加springmvc
     * @param userId
     * @return
     */
    @RequestMapping("/findOrderByUserId/{userId}")
    public R findOrderByUserId(@PathVariable("userId") Integer userId);


   @RequestMapping("/findOrderByUserId/{userId}")
    public String findOrderByUserId2(@PathVariable("userId") Integer userId);
    }
  • 使用
@RestController
@RequestMapping("/user")
public class UserController {
    //注入openFeign
    @Autowired
    OrderFeignService orderFeignService;

    @RequestMapping(value = "/findOrderByUserId/{id}")
    public R  findOrderByUserId(@PathVariable("id") Integer id) {
        //openFeign调用   远程调用
        R result = orderFeignService.findOrderByUserId(id);
        return result;
    }

问题

  1. @EnableFeignClients注解的作用?
  2. @FeignClient注解的作用?
  3. 为什么OrderFeignService 可以当做Bean来使用,并且能够调用发送请求,调用其他的服务节点?实现逻辑是什么?
  4. 为什么实现一个远程调用,需要引入四个pom依赖,这四个依赖是干什么的?

思考:

  1. 如果你理解了OpenFign源码,是否可以写一个简易的框架?使用mybatis引入使用一下?

2.测试openFign,启动springBoot装配了那些类

springBoot加载了那些路径的Bean

file:/E:/MyPro/spring-boot-2.6.6/spring-boot-project/spring-boot-autoconfigure/out/production/resources/META-INF/spring.factories
file:/E:/MyPro/spring-boot-2.6.6/spring-boot-project/spring-boot/out/production/resources/META-INF/spring.factories
jar:file:/F:/develop/gradle/.gradle/caches/modules-2/files-2.1/org.springframework/spring-beans/5.3.18/3f0ea6598a5a1eae0a672f025a33a0b7e0d6dfd3/spring-beans-5.3.18.jar!/META-INF/spring.factories

nacos应用

1.下载和启动

下载nacos源码 1.4.1
https://github.com/alibaba/nacos/tree/1.4.1

  • 怎么找到服务的启动类?
  1. 通过启动路径,找到启动文件

在这里插入图片描述
2. 打开文件,找到启动命令,

在这里插入图片描述
4.所以启动路径就是 java -jar xxx/target/naocs-server.jar那就找到这个路径
在这里插入图片描述
5.解压jar包,查看主启动类是什么?主启动类,存在MANIFEST.MF中
在这里插入图片描述
6.可以查看主启动类的路径是com.alibaba.nacos.Nacos,那就可以找到这个类对他进行启动了。
在这里插入图片描述
7.也可以在idea中全局搜索nacos-server,

在这里插入图片描述
发现主程序类
在这里插入图片描述

  • 启动Nacos
    设置JVM参数单机启动:-Dnacos.standalone=true
    在这里插入图片描述

2.客户端配置分析

启动一个springBoot项目,添加pom文件

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

yml 配置nacos

**server:
  port: 8040

spring:
  application:
    name: springcloud-nacos #微服务名称

  cloud:
    nacos:
      discovery:   #配置nacos注册中心地址
#        server-addr: nacos.mall.com:8848
        server-addr: localhost:8848
        #server-addr: 192.168.65.174:8848,192.168.65.192:8848,192.168.65.204:8848
#        namespace: eb7c9ad7-6429-4f79-aa90-e1387586316b
#        group: tulingshop
        cluster-name: SH
#        ephemeral: false
        username: nacos
        password: nacos

**

启动项目,就能将客户端注册到nacos中。

3.使用restTemplate请求nacos注册节点的服务

  • 使用@LoadBalanced ,才能使用RestTemplate获取到nacos各个服务节点,并且负载均衡请求,但这是为什么尼?
@Configuration
public class RestConfig {
    @Bean
    @LoadBalanced //必须加@LoadBalanced 
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

controller层

@RequestMapping(value = "/findOrderByUserId/{id}")
    public R  findOrderByUserId(@PathVariable("id") Integer id) {
        log.info("根据userId:"+id+"查询订单信息");
        // 方式1:restTemplate调用,url写死
        //利用@LoadBalanced,restTemplate需要添加@LoadBalanced注解
        String url = "http://mall-order/order/findOrderByUserId/"+id;
        System.out.println("url====: " + url);
        R result = restTemplate.getForObject(url,R.class);
        return result;
    }

4. 优先集群请求

  • 配置类添加@LoadBalancerClient注解,指定需要使用优先请求当前集群的节点服务,指定配置规则
@SpringBootApplication
@LoadBalancerClient(value = "mall-order", configuration = CustomLoadBalancerConfiguration.class)
public class MallUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(MallUserApplication.class, args);
    }
}

配置项目yml

  cloud:
    nacos:
      discovery:   #配置nacos注册中心地址
        server-addr: localhost:8848
        namespace: eb7c9ad7-6429-4f79-aa90-e1387586316b1
        cluster-name: SH   # 集群编号  SH
        username: nacos
        password: nacos
  • 配置类
public class CustomLoadBalancerConfiguration {
    
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
            LoadBalancerClientFactory loadBalancerClientFactory, NacosDiscoveryProperties nacosDiscoveryProperties){
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new NacosLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class),name,nacosDiscoveryProperties);
    }
}

集群核心代码

package com.alibaba.cloud.nacos.loadbalancer;class NacosLoadBalancer{}

//获取请求实例
 private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> serviceInstances) {
        if (serviceInstances.isEmpty()) {
            log.warn("No servers available for service: " + this.serviceId);
            return new EmptyResponse();
        } else {
            try {
                //获取当前服务节点的clusterName,为SH
                String clusterName = this.nacosDiscoveryProperties.getClusterName();
                //获取所有的服务节点
                List<ServiceInstance> instancesToChoose = serviceInstances;
                if (StringUtils.isNotBlank(clusterName)) {
                    //过滤节点中是SH的服务节点
                    List<ServiceInstance> sameClusterInstances = (List)serviceInstances.stream().filter((serviceInstance) -> {
                        String cluster = (String)serviceInstance.getMetadata().get("nacos.cluster");
                        return StringUtils.equals(cluster, clusterName);
                    }).collect(Collectors.toList());
                    //如果存在clusterName为SH的节点,则使用,如果没有则使用所有节点
                    if (!CollectionUtils.isEmpty(sameClusterInstances)) {
                        instancesToChoose = sameClusterInstances;
                    }
                } else {
                    log.warn("A cross-cluster call occurs,name = {}, clusterName = {}, instance = {}", new Object[]{this.serviceId, clusterName, serviceInstances});
                }

                instancesToChoose = this.filterInstanceByIpType(instancesToChoose);
                ServiceInstance instance = NacosBalancer.getHostByRandomWeight3(instancesToChoose);
                return new DefaultResponse(instance);
            } catch (Exception var5) {
                log.warn("NacosLoadBalancer error", var5);
                return null;
            }
        }
    }

规则:

  1. 拿到所有服务节点,和当前节点的cluster-name进行匹配
  2. 匹配到优先使用集群为SH的节点,否则使用全部节点进行轮询

问题:

  1. @LoadBalancerClient在SpringIoc的生命周期是什么时候?
  2. 在请求和什么时候触发进入配置类逻辑?
  3. LoadBalancerClientFactory 、NacosDiscoveryProperties 是如何成为Bean的?

nacos1.4.1源码分析

  • springBoot中,ApplicationListener接口的使用!!

引入nacos依赖,入口spring.factories中,配置了org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.alibaba.cloud.nacos.NacosDiscoveryAutoConfiguration,其中注册一个NacosAutoServiceRegistration类型的Bean
,这个Bean实现了ApplicationListener接口,会触发的onApplicationEvent()方法,那那它在springBoot生命周期怎么执行的呢?以ApplicationListener看一下源码。

ApplicationListener使用

问题:

  1. 实现ApplicationListener接口,在什么时期被调用?
  1. spring.factories中定义ApplicationListener时期
//1.从spring.factories中拿到SpringApplicationRunListener对象,其中springBoot默认的是EventPublishingRunListener 事件监听发布类
SpringApplicationRunListeners listeners = getRunListeners(args);
//2.EventPublishingRunListener  初始化的时候,会从SrpingApplication获取ApplicationListener类型的对象,添加到initialMulticaster属性中,其中这个
	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
// listeners  这个listeners会执行所有spring.factories中ApplicationListener类型的对象,并执行onApplicationEvent()
public void starting(ConfigurableBootstrapContext bootstrapContext) {
		this.initialMulticaster
				.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
	}	

在这里插入图片描述
https://www.processon.com/diagraming/66969ebd95a7657d4670f33a
2. 配置类中定义实现ApplicationListener接口的类

public class ZhqAutoSpringFactories {

@Configuration
public class AutoConfig {

	@Bean
	public ZhqApplicationListenerBean nacosAutoServiceRegistrationZhq() {
		return new ZhqApplicationListenerBean();
	}

}

在spring生命周期最后执行
在这里插入图片描述

  1. spring.factories中EnableAutoConfiguration配置实现ApplicationListener接口的Bean
    在这里插入图片描述
public class ZhqAutoSpringFactories {
	@Bean
	public ZhqApplicationListenerBean nacosAutoServiceRegistrationZhq() {
		return new ZhqApplicationListenerBean();
	}
}

和第二步执行流程一样,都是在生命周期完成之后执行。

Spring.factories中AutoConfiguration的使用

在这里插入图片描述

客户端源码入口

nacos注册
在这里插入图片描述
这里会发送一个url为/nacos/v1/ns/instancehttp请求,到nacos服务端
在这里插入图片描述

服务端源码入口(健康检查)

在这里插入图片描述
1.服务注册的时候会开启一个延时定时任务,进行服务健康检查
初始等待5s,每5s执行一次

在这里插入图片描述
执行定时任务的类为ClientBeatCheckTask中的run方法。

  • 系统时间大于实例注册最后一次更新时间15秒,设置成健康下线
  • 如果大于30s,则剔除该服务

问题:

  1. 实例注册的更新时间是怎么同步的呢?

后面的逻辑就将客户端实例instace放到一个阻塞队列中,然后开启一个定时任务,将所有的实例进行注册

问题:

  1. nacos是怎么解决读写并发问题的?

客户端拉去nacos服务端的信息入口

在这里插入图片描述
获取服务端的地址信息之后,缓存到本地,然后使用Rabbion进行轮询调用其他服务的

  • 第一次从本地Map中获取服务信息,如果没有则请求nacos获取服务信息
  • 下一次请求服务的时候,就从本地Map中获取服务信息,使用Rabbion对相应的服务进行调用、

问题

  1. 本地Map维护nacos中的服务信息,他是怎么保持更新的呢?
    解:本地维护一个定时任务,每1s执行一次,去服务端拉取服务列表,缓存并更新到本地。循环执行这个定时任务,定时任务入口在客户端拉去nacos服务端的信息入口,上述代码中
  2. Ribbion是怎么整合nacos,获取本地拉取的服务,去调取他的服务的?

客户端发送心跳检测

在这里插入图片描述
客户端发送/v1/ns/instance/beat到服务点,进行心跳检测

服务端主动推送

服务端感知到客户端注册成功之后,会发布一个事件,主动的推送注册列表到客户端去。更新客户端的本地注册表信息
在这里插入图片描述
在这里插入图片描述
udp推送客户端,让客户端更新本地的服务列表
在这里插入图片描述

naocs1.4.1集群架构源码分析

1.源码集群启动

  1. nacos集群需要配置mysql存储,需要先创建一个数据,名字随便取,然后执行 distribution/conf 目录下的 nacos-mysql.sql 脚本,然后修改 console\src\main\resources 目录下的 application.properties 文件里的mysql配置
    在这里插入图片描述
  2. 配置vm参数,分别
  • -Dserver.port=8847 -Dnacos.home=E:\project\Study\nacos\nacos-cluster\nacos-8847
  • -Dserver.port=8848 -Dnacos.home=E:\project\Study\nacos\nacos-cluster\nacos-8848
  • -Dserver.port=8849 -Dnacos.home=E:\project\Study\nacos\nacos-cluster\nacos-8849
  • 在这里插入图片描述
  1. 分别创建执行的目录
    在这里插入图片描述
    每个指定的文件创建四个文件夹,只有conf文件夹有数据,其他为空
    在这里插入图片描述

  2. conf文件夹添加,并指定其他节点的ip和端口地址
    在这里插入图片描述
    在这里插入图片描述

2.心跳在集群架构下的设计原理剖析

nacos心跳是,各个客户端实例注册到nacos服务端开启的一个心跳定时任务。每间一段时间检测服务是否下线,但是呢如果是集群的话,他的心跳机制是怎么做的呢?
在这里插入图片描述
这里就是用hash算法,对所有的服务取模,只用一个服务进行心跳检测,意思就是说,只允许一台机器上做健康检查任务。那如果说一台机器挂了呢,这就要看这个集群节点状态是怎么同步的问题了~!!!

3.nacos集群节点状态同步

ServerListManager这个类主要做集群节点状态同步问题的.就是说每隔一段时间,就会向其他服务发送一个/operator/server/statushttp请求,让其他的服务节点感知到自己还活着。
在这里插入图片描述
对集群所有服务节点发送一个/operator/server/statushttp请求
在这里插入图片描述

4.naocs集群服务新增数据同步

如果一个实例注册到nacos中,它是怎么做到其他的nacos集群节点是怎么同步的呢?
DistroConsistencyServiceImpl

distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE,
                globalConfig.getTaskDispatchPeriod() / 2);
public void sync(DistroKey distroKey, DataOperation action, long delay) {
       //获取除开自己的其他服务节点,进行同步
        for (Member each : memberManager.allMembersWithoutSelf()) {
            DistroKey distroKeyWithTarget = new DistroKey(distroKey.getResourceKey(), distroKey.getResourceType(),
                    each.getAddress());
            DistroDelayTask distroDelayTask = new DistroDelayTask(distroKeyWithTarget, action, delay);
            distroTaskEngineHolder.getDelayTaskExecuteEngine().addTask(distroKeyWithTarget, distroDelayTask);
            if (Loggers.DISTRO.isDebugEnabled()) {
                Loggers.DISTRO.debug("[DISTRO-SCHEDULE] {} to {}", distroKey, each.getAddress());
            }
        }
    }

5.naocs集群服务状态变动同步

如果一个客户端服务节点挂掉了,他们的状态的是怎么同步的呢,一个服务节点挂掉了,naocs中被hash取模的那个服务节点就会感知到,然后会发送一个请求,去同步其他的nacos服务节点,进行同步这个客户端实例挂掉的信息
ServiceManager中init()方法中,

//开启一个定时任务,去发送其他的服务节点,同
GlobalExecutor.scheduleServiceReporter(new ServiceReporter(), 60000, TimeUnit.MILLISECONDS);

向其他服务节点发送一个/service/statushttp请求,去同步其他的服务节点

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值