Nacos深入原理从源码层面讲解

news2024/12/28 18:42:20

文章目录

  • 1 Nacos原理
    • 1.1 Nacos架构
    • 1.2 注册中心原理
    • 1.3 SpringCloud服务注册
    • 1.4 NacosServiceRegistry实现
      • 1.4.1 心跳机制
      • 1.4.2 注册原理
      • 1.4.3 总结
    • 1.5 服务提供者地址查询
    • 1.6 Nacos服务地址动态感知原理

1 Nacos原理

1.1 Nacos架构

图片

  • Provider APP:服务提供者
  • Consumer APP:服务消费者
  • Name Server:通过VIPVirtual IP)或DNS的方式实现Nacos高可用集群的服务路由
  • Nacos ServerNacos服务提供者,里面包含的Open API是功能访问入口,Conig ServiceNaming ServiceNacos提供的配置服务、命名服务模块。Consitency Protocol是一致性协议,用来实现Nacos集群节点的数据同步,这里使用的是Raft算法(Etcd、Redis哨兵选举)
  • Nacos Console:控制台

1.2 注册中心原理

注册中心原理:

  • 服务实例在启动时注册到服务注册表,并在关闭时注销
  • 服务消费者查询服务注册表,获得可用实例
  • 服务注册中心需要调用服务实例的健康检查API来验证它是否能够处理请求
    在这里插入图片描述

1.3 SpringCloud服务注册

Spring-Cloud-Common包中有一个类org.springframework.cloud. client.serviceregistry .ServiceRegistry ,它是Spring Cloud提供的服务注册的标准。集成到Spring Cloud中实现服务注册的组件,都会实现该接口。
在这里插入图片描述
该接口有一个实现类是 NacoServiceRegistry

SpringCloud集成Nacos的实现过程:
spring-clou-commons包的META-INF/spring.factories中包含自动装配的配置信息如下:

图片
其中AutoServiceRegistrationAutoConfiguration就是服务注册相关的配置类:

@Configuration(proxyBeanMethods = false)
@Import(AutoServiceRegistrationConfiguration.class)
@ConditionalOnProperty(value ="spring.cloud.service-registry.auto-registration.enabled",matchIfMissing = true)
public class AutoServiceRegistrationAutoConfiguration{
	@Autowired(required = false)
	private AutoServiceRegistration autoServiceRegistration;
	@Autowired
	private AutoServiceRegistrationProperties properties;
	@PostConstruct
	protected void init() {
		if (this.autoServiceRegistration == null && this.properties.isFailFast()) {
			throw new IllegalStateException("Auto Service Registration has been requested,but there is no AutoServiceRegistration bean");
}}}

AutoServiceRegistrationAutoConfiguration配置类中,可以看到注入了一个AutoServiceRegistration实例,该类的关系图如下所示。
图片

可以看出, AbstractAutoServiceRegistration 抽象类实现了该接口,并且最重要的是NacosAutoServiceRegistration继承了AbstractAutoServiceRegistration

看到EventListener我们就应该知道,Nacos是通过Spring的事件机制集成到SpringCloud中去的。

AbstractAutoServiceRegistration实现了onApplicationEvent抽象方法,并且监听WebServerInitializedEvent事件(当Webserver初始化完成之后) , 调用this.bind ( event )方法。

@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
	bind(event);
}
@Deprecated
public void bind(WebServerInitializedEvent event) {
	ApplicationContext context = event.getApplicationContext();
	if (context instanceof ConfigurableWebServerApplicationContext){
		if ("management".equals(((ConfigurableWebServerApplicationContext)context).getServerNamespace )))
			return;
		}
	}
	this.port.compareAndSet( 0,event.getWebServer() .getPort());
	this.start();
}

最终会调用NacosServiceREgistry.register()方法进行服务注册。

public void start() {
	if (!isEnabled()) {
		if (logger.isDebugEnabled()) [
			logger.debug("Discovery Lifecycle disabled. Not starting");
		}
		return;
	}
// only initialize if nonSecurePort is greater than 0 and it isn't already running
// because of containerPortInitializer below
	if (!this.running.get()){
		this.context.publishEvent(new InstancePreRegisteredEvent(this,getRegistration()));
		register();
		if (shouldRegisterManagement()){
			registerManagement();
		}
		this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));
		this.running.compareAndSet(false,true);
	}
}

protected void register(){
	this.serviceRegistry.register(getRegistration());
}

1.4 NacosServiceRegistry实现

NacosServiceRegistry.registry方法中,调用了Nacos Client SDK中的namingService.registerInstance完成服务的注册。

@Override
public void register(Registration registration){
	if (StringUtils.isEmpty(registration.getServiceId())) {
		log.warn("No service to register for nacos client...");
		return;
	}
	String serviceId = registration.getServiceId();
	Instance instance = getNacosInstanceFromRegistration(registration):
	try{
		namingService.registerInstance(serviceId,instance);
		log.info("nacos registry,{} {} : {}register finished", serviceId,instance.getIp(),instance.getPort());
	}catch (Exception e) {
		log.error("nacos registry, {} register failed... {},",serviceId,registration.toString(),e);
	}
}

跟踪NacosNamingServiceregisterInstance()方法:

@Override
public void registerInstance(String serviceName, Instance instance) throws NacosException {
	registerInstance(serviceName,Constants.DEFAULT_GROUP,instance);
}
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
	if (instance.isEphemeralO){
		BeatInfo beatInfo = new BeatInfo();
		beatInfo. setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
		beatInfo.setIp(instance.getIp());
		beatInfo.setPort(instance.getPort());
		beatInfo.setCluster(instance.getClusterName());
		beatInfo.setWeight(instance.getWeight());
		beatInfo.setMetadata(instance .getMetadata());
		beatInfo.setScheduled(false);
		long instanceInterval = instance.getInstanceHeartBeatInterval();
		beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval);
		
		beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
	}
serverProxy.registerService(Namingutils.getGroupedName(serviceName, groupName), groupName, instance);
}

通过beatReactor.addBeatInfo()创建心跳信息实现健康检测, Nacos Server必须要确保注册的服务实例是健康的,而心跳检测就是服务健康检测的手段。
serverProxy.registerService()实现服务注册

1.4.1 心跳机制

public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
	NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
	dom2Beat.put(buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort()), beatInfo);
	executorService.schedule(new BeatTask(beatInfo), 0, TimeUnit.MILLISECONDS);
	MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
}

从上述代码看,所谓心跳机制就是客户端通过schedule定时向服务端发送一个数据包 ,然后启动一个线程不断检测服务端的回应,如果在设定时间内没有收到服务端的回应,则认为服务器出现了故障。Nacos服务端会根据客户端的心跳包不断更新服务的状态。

1.4.2 注册原理

Nacos 提供了SDKOpen API两种形式来实现服务注册。

Open API:

curl -X POST ‘http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=192.16813.1&port=8080’

SDK:

void registerInstance(String serviceName, String ip, int port) throws NacosException;

这两种形式本质都一样,底层都是基于HTTP协议完成请求的。所以注册服务就是发送一个HTTP请求:

public void registerService(String serviceName,
String groupName,Instance instance) throws NacosException {

	NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}",namespaceId,serviceName,instance);
	final Map<String,String> params = new HashMap<>(9);
	params.put(CommonParams.NAMESPACE_ID,namespaceId);
	params.put(CommonParams.SERVICE_NAME,serviceName);
	params.put(CommonParams.GROUP_NAME,groupName);
	params.put(CommonParams.CLUSTERNAME,instance.getClusterName());
	params.put("ip",instance.getIp());
	params .put("port",String. valueOf(instance.getPort()));
	params.put("weight",String.valueOf(instance.getWeight()));
	params.put("enable",String.valueOf(instance.isEnabled()));
	params.put("healthy",String.valueOf(instance.isHealthy()));
	params.put("ephemeral",String.valueOf(instance.isEphemeral()));
	params.put("metadata",JSON.toJSONString(instance.getMetadata()));
	
	regAPI(UtilAndComs .NACOS_URL_INSTANCE,params,HttpMethod.POSD);
}

对于nacos服务端,对外提供的服务接口请求地址为nacos/v1/ns/instance,实现代码在nacos-naming模块下的InstanceController类中:

@RestController
@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT+"/instance")
public class InstanceController{
//省略部分代码
@CanDistro
@PostMapping
public String register(HttpServletRequest request) throws Exception {
	String serviceName = WebUtils.required(request,CommonParams.SERVICENAME);
	String namespaceId = WebUtils.optional(request,CommonParams,NAMESPACE_ID,Constants.DEFAULT_NAMESPACE_ID);
	serviceManager.registerInstance(namespaceId,serviceName,parseInstance(request));
	return"ok";
	}
//省略部分代码
}
  • 从请求参数汇总获得serviceName(服务名)和namespaceId(命名空间Id)

  • 调用registerInstance注册实例

public void registerInstance(String namespaceld, String serviceName, Instance instance)throws NacosException{
	createEmptyService(namespaceId,serviceNameinstance.isEphemeral());
	Service service=getService(namespaceId,serviceName);
	if (service== null){
		throw new NacosException(NacosException.INVALID_PARAM,"service not found,namespace:"+namespaceId +",service:"+serviceName);
	}
	addInstance(namespaceId,serviceName,instance.isEphemeral(),instance);
}
  • 创建一个控服务(在Nacos控制台服务列表中展示的服务信息),实际上是初始化一个serviceMap,它是一个ConcurrentHashMap集合
  • getService,从serviceMap中根据namespaceIdserviceName得到一个服务对象
  • 调用addInstance添加服务实例
public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local,Cluster cluster) throws NacosException {
	Service service = getService(namespaceId,serviceName);
	if(service== null){
		service= new Service();
		service.setName(serviceName);
		service.setNamespaceId(namespaceId);
		service.setGroupName(NamingUtils.getGroupName(serviceName));
		service.setLastModifiedMillis(System.currentTimeMillis());
		service.recalculateChecksum();
		if(cluster != null){
			cluster.setService(service);
			service.getClusterMap().put(cluster.getName(),cluster);
		}
		service.validate();
		putServiceAndInit(service);
		if(!local){
			addOrReplaceService(service);
		}
	}
}
  • 根据namespaceIdserviceName从缓存中获取Service实例
  • 如果Service实例为空,则创建并保存到缓存中
private void putServiceAndInit(Service service) throws NacosException{
	putService(service);
	service.init();
	consistencyService.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(),service.getName(),true),service);
	consistencyService.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(),service.getName(),false),service);
	Loggers.SRV_LOG.info("[NEW-SERVICE]{}",service.toJSON());
}
  • 通过putService()方法将服务缓存到内存
  • service.init()建立心跳机制
  • consistencyService.listen实现数据一致性监听

service.init()方法的如下图所示,它主要通过定时任务不断检测当前服务下所有实例最后发送心跳包的时间。如果超时,则设置healthyfalse表示服务不健康,并且发送服务变更事件。

在这里请大家思考一一个问题,服务实例的最后心跳包更新时间是谁来触发的?实际上前面有讲到, Nacos客户端注册服务的同时也建立了心跳机制。
在这里插入图片描述

putService方法,它的功能是将Service保存到serviceMap中:

public void putService(Service service)(
	if(!serviceMap.containsKey(service.getNamespaceId())){
		synchronized (putServiceLock){
			if(!serviceMap.containsKey(service,getNamespaceId())){
				serviceMap.put(service.getNamespaceId(),new ConcurrentHashMap<>(16));
			}
		}
	}
	serviceMap.get(service.getNamespaceId()).put(service.getName(),service);
}

继续调用addInstance方法把当前注册的服务实例保存到Service中:

addInstance(namespaceId,serviceName,instance.isEphemeral(),instance)

1.4.3 总结

  • Nacos客户端通过Open API的形式发送服务注册请求
  • Nacos服务端收到请求后,做以下三件事:
    • 构建一个Service对象保存到ConcurrentHashMap集合中
    • 使用定时任务对当前服务下的所有实例建立心跳检测机制
    • 基于数据一致性协议服务数据进行同步

1.5 服务提供者地址查询

Open API:

curl -X GET127.00.1:8848/nacos/v1/ns/instance/list?serviceName=example

SDK:

List<Instance> selectInstances(String serviceName, boolean healthy) throws NacosException;

InstanceController中的list方法:

@GetMapping("/list")
public JSONObject list(HttpServletRequest request) throws Exception {
	String namespaceId = WebUtils.optional(request,CommonParams,NAMESPACE_ID,Constants.DEFAULT_NAMESPACE_ID);
	String serviceName = WebUtils.required(request,CommonParams.SERVICE_NAME);
	String agent =WebUtils.getUserAgent(request);
	String clusters = WebUtils.optional(request,"clusters",StringUtils.EMPTY);
	String clientIP = WebUtils.optional(request,"clientIp", StringUtils.EMPTY);
	Integer udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort","0"));
	String env= WebUtils.optional(request,"env",StringUtils.EMPTY);
	boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request,"isCheck","false"));
	String app= WebUtils.optional(request,"app",StringUtils.EMPTY);
	String tenant = WebUtils.optional(request,"tid",StringUtils.EMPTY);
	boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request,"healthyOnly""false"));
	return doSrvIPXT(namespaceld, serviceName, agent, clusters, clientIP, udpPort, env,isCheck,app,tenant,healthyOnly);
}
  • 解析请求参数
  • 通过doSrvIPXT返回服务列表数据
public JSONObject doSrvIPXT(String namespaceld, String serviceName, String agent, String clusters,String clientIP,int udpPort,String env,boolean isCheck,String app,String tid,boolean healthyonly)
throws Exception {
	//以下代码中移除了很多非核心代码
	ClientInfo clientInfo = new ClientInfo(agent);
	JSONObject result=new JSONObject();
	Service service= serviceManager.getService(namespaceId,serviceName);
	List<Instance> srvedIPs;
	//获取指定服务下的所有实例 IP
	srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters,",")));
	Map<Boolean,List<Instance>>ipMap =new HashMap<>(2);
	ipMap.put(Boolean.TRUE,new ArrayList<>());
	ipMap.put(Boolean.FALSE,new ArrayList<>());
	for (Instance ip : srvedIPs){
		ipMap.get(ip.isHealthy()).add(ip);
	}
	//遍历,完成JSON字管中的纠装
	JSONArray hosts = new JSONArray();
	for (Map.Entry<Boolean, List<Instance>> entry : ipMap.entrySet()) {
		List<Instance> ips = entry.getValue();
		if (healthyOnly && !entry.getKey()){
			continue;
		}
		for (Instance instance :ips) {
			if (!instanceisEnabled()) {
				continue;
			}
			JSONObject ipobj=new JSONObject();
			ipobj.put("ip",instance.getIp());
			ipObj.put("port",instance.getPort());
			ipObj.put("valid",entry.getKey());
			ipObj.put("healthy",entry.getKey());
			ipObj.put("marked",instance.isMarked());
			ipObj.put("instanceId",instance.getInstanceId());
			ipObj.put("metadata",instance.getMetadata());
			ipObj.put("enabled",instance.isEnabled());
			ipObj.put("weight",instance.getweight());
			ipObj.put("clusterName",instance.getClusterName());
			if(clientInfo.type== ClientInfo.ClientType.JAVA 
				&& clientInfo.version.compareTo(VersionUtil.parseVersion("1.0."))>=0){
				ipObj.put("serviceName",instance.getServiceName());
			}else{
				ipObj.put("serviceName",NamingUtils.getServiceName(instance.getServiceName()));
			}
			ipObj.put("ephemeral",instance.isEphemeral());
			hosts.add(ipobj);
		}
	}
	result.put("hosts",hosts);
	result.put("name",serviceName);
	result.put("cacheMillis",cacheMillis);
	result.put("lastRefTime",System.currentTimeMillis());
	result.put("checksum",service.getChecksum());
	result.put("useSpecifiedURL",false);
	result.put("clusters",clusters);
	result.put("env",env);
	result.put("metadata",service.getMetadata());
	return result;
}
  • 根据namespaceIdserviceName获得Service实例
  • Service实例中基于srvIPs得到所有服务提供者实例
  • 遍历组装JSON字符串并返回

1.6 Nacos服务地址动态感知原理

可以通过subscribe方法来实现监听,其中serviceName表示服务名、EventListener表示监听到的事件:

void subscribe(String serviceName, EventListener listener) throws NacosException;

具体调用方式如下:

NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr"));
naming.subscribe("example",event->(
	if (event instanceof NamingEvent) {
		System.out.println(((NamingEvent) event).getServceName());
		System.out.printIn(((NamingEvent) event).getInstances());
	}
});

或者调用selectInstance方法,如果将subscribe属性设置为true,会自动注册监听:

public List<Instance> selectInstances(String serviceName, List<String> clusters, boolean healthy,boolean subscribe)

在这里插入图片描述

Nacos客户端中有一个HostReactor类,它的功能是实现服务的动态更新,基本原理是:

  • 客户端发起时间订阅后,在HostReactor中有一个UpdateTask线程,每10s发送一次Pull请求,获得服务端最新的地址列表
  • 对于服务端,它和服务提供者的实例之间维持了心跳检测,一旦服务提供者出现异常,则会发送一个Push消息给Nacos客户端,也就是服务端消费者
  • 服务消费者收到请求之后,使用HostReactor中提供的processServiceJSON解析消息,并更新本地服务地址列表

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1017823.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32 学习笔记1:STM32简介

1 概述 STM32&#xff0c;从字面上来理解&#xff0c;ST 是意法半导体&#xff0c;M 是 Microelectronics 的缩写&#xff0c;32 表示 32 位&#xff0c;合起来理解&#xff0c;STM32 就是 ST 公司开发的 32 位微控制器。是一款基于 ARM 公司推出的基于 ARMv7 架构的 32 位 Co…

【详细教程hexo博客搭建】1、从零开始搭建一个能用的博客

1、开始 2.环境与工具准备 本教程主要面对的是Windows用户 操作系统&#xff1a;Windows10NodeGitHexo文本编辑器(强烈推荐VSCODE)GitHub 帐号一个域名&#xff08;强烈推荐买个域名&#xff09;云服务器&#xff08;可选&#xff09; 3.Node的安装 打开Node官网&#xff0…

vivo数据中心网络链路质量监测的探索实践

作者&#xff1a;vivo 互联网服务器团队- Wang Shimin 网络质量监测中心是一个用于数据中心网络延迟测量和分析的大型系统。通过部署在服务器上的Agent发起5次ICMP Ping以获取端到端之间的网络延迟和丢包率并推送到存储与分析模块进行聚合和分析与存储。控制器负责分发PingList…

【大数据】Neo4j 图数据库使用详解

目录 一、图数据库介绍 1.1 什么是图数据库 1.2 为什么需要图数据库 1.3 图数据库应用领域 二、图数据库Neo4j简介 2.1 Neo4j特性 2.2 Neo4j优点 三、Neo4j数据模型 3.1 图论基础 3.2 属性图模型 3.3 Neo4j的构建元素 3.3.1 节点 3.3.2 属性 3.3.3 关系 3.3.4 标…

JS生成器的介绍

1、 什么是生成器 生成器是ES6中新增的一种函数控制、使用的方案&#xff0c;它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。 平时我们会编写很多的函数&#xff0c;这些函数终止的条件通常是返回值或者发生了异常。 生成器函数也是一个函数&#xff0c;但是…

阿里云无影云电脑是干什么用的?五大使用场景

阿里云无影云电脑是一种易用、安全、高效的云上桌面服务&#xff0c;阿里云无影云电脑可用于高数据安全管控、高性能计算等要求的金融、设计、视频、教育等领域&#xff0c;适用于多种办公场景&#xff0c;如远程办公、多分支机构、安全OA、短期使用、专业制图等。阿里云百科来…

【LeetCode热题100】--49.字母异位词分组

49.字母异位词分组 两个字符串互为字母异位词&#xff0c;当且仅当两个字符串包含的字母相同。同一组字母异位词中的字符串具备相同点&#xff0c;可以使用相同点作为一组字母异位词的标志&#xff0c;使用哈希表存储每一组字母异位词&#xff0c;哈希表的键为一组字母异位词的…

DockerCompose

DockerCompose Docker Compose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器&#xff01; 初识DockerCompose Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。格式如下&#xff1a; version: &…

[golang 流媒体在线直播系统] 4.真实RTMP推流摄像头把摄像头拍摄的信息发送到腾讯云流媒体服务器实现直播

用RTMP推流摄像头把摄像头拍摄的信息发送到腾讯云流媒体服务器实现直播,该功能适用范围广,比如:幼儿园直播、农场视频直播, 一.准备工作 要实现上面的功能,需要准备如下设备: 推流摄像机&#xff08;监控&#xff09; 流媒体直播服务器(腾讯云流媒体服务器,自己搭建的流媒体服务…

React中组件通信01——props

React中组件通信01——props 1. 父传子——props1.1 简单例子——props1.2 props 可以传递任何数据1.2.1 传递数字、对象等1.2.2 传递函数1.2.3 传递模版jsx 2. 子传父 子传子——props2.1 父传子——传递函数2.2 子传父——通过父传子的函数实现2.3 优化 子传子&#xff08;…

uniapp开发小程序中实现骨架屏

第一步&#xff1a;小程序中实现骨架屏在微信开发者工具中点击生成骨架屏&#xff1a; 第二步&#xff1a;复制html代码&#xff0c;到骨架屏vue组件汇中再把之前写的样式代码引入进去&#xff1a; import ../../pages/user/user.css; 第三步&#xff1a;组件中引入骨架屏&am…

python pytesseract 中文文字批量识别

用pytesseract 来批量把图片转成文字 1、安装好 pytesseract 包 2、下载安装OCR https://download.csdn.net/download/m0_37622302/88348824https://download.csdn.net/download/m0_37622302/88348824 Index of /tesseracthttps://digi.bib.uni-mannheim.de/tesseract/ 我是…

百度SEO优化TDK介绍(分析下降原因并总结百度优化SEO策略)

TDK是SEO优化中很重要的部分&#xff0c;包括标题&#xff08;Title&#xff09;、描述&#xff08;Description&#xff09;和关键词&#xff08;Keyword&#xff09;&#xff0c;为百度提供网页内容信息。其中标题是最重要的&#xff0c;应尽量突出关键词&#xff0c;同时描述…

【C++学习】继承

目录 一、继承的概念及定义 1、继承的概念 2、继承的定义 2.1 定义格式 2.2 继承关系和访问限定符 2.3 继承基类成员访问方式的变化 二、基类和派生类对象赋值转换 三、继承中的作用域 四、派生类的默认成员函数 五、继承与友元 六、继承与静态成员 七、复杂的菱形…

天然气跟踪监管系统功能模块实现

天然气跟踪监管系统功能模块实现 1. 数据库查询3. 仓库管理&#xff08;1&#xff09;仓库查询与展示。代码说明 1. 数据库查询 救援物资跟踪监管系统的绝大部分功能都会涉及关系数据库中的业务数据&#xff0c;因此关系数据库的查询是本系统不可或缺的重要部分。 本系统中的数…

Matlab--微积分问题的计算机求解

目录 1.单变量函数的极限问题 1.1.公式例子 1.2.对应例题 1 2.多变量函数的极限问题 3.函数导数的解析解 4.多元函数的偏导数 5.Jacobian函数 6.Hessian矩阵 7.隐函数的偏导 8.不定积分问题的求解 9.定积分的求解问题 10. 多重积分的问题求解 1.单变量函数的极限问题 …

【Vue】快速入门案例与工作流程的讲解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Vue快速入门》。&#x1f…

运维Shell牛刀小试(十一):for循环读取多个命令行参数|read重定向读取文件内容

运维Shell脚本小试牛刀(一) 运维Shell脚本小试牛刀(二) 运维Shell脚本小试牛刀(三)::$(cd $(dirname $0)&#xff1b; pwd)命令详解 运维Shell脚本小试牛刀(四): 多层嵌套if...elif...elif....else fi_蜗牛杨哥的博客-CSDN博客 Cenos7安装小火车程序动画 运维Shell脚本小试…

【系统架构】系统架构设计基础知识

导读&#xff1a;本文整理关于系统架构设计基础知识来构建系统架构知识体系。完整和扎实的系统架构知识体系是作为架构设计的理论支撑&#xff0c;基于大量项目实践经验基础上&#xff0c;不断加深理论体系的理解&#xff0c;从而能够创造新解决系统相关问题。 目录 1、软件架…

高速信号处理板资料保存:383-基于kintex UltraScale XCKU060的双路QSFP+光纤PCIe 卡设计原理图

基于kintex UltraScale XCKU060的双路QSFP光纤PCIe 卡 一、板卡概述 本板卡系我司自主研发&#xff0c;基于Xilinx UltraScale Kintex系列FPGA XCKU060-FFVA1156-2-I架构&#xff0c;支持PCIE Gen3 x8模式的高速信号处理板卡&#xff0c;搭配两路40G QSFP接口&#xf…