Nacos源码—8.Nacos升级gRPC分析五

大纲

7.服务端对服务实例进行健康检查

8.服务下线如何注销注册表和客户端等信息

9.事件驱动架构源码分析

7.服务端对服务实例进行健康检查

(1)服务端对服务实例进行健康检查的设计逻辑

(2)服务端对服务实例进行健康检查的源码

(3)服务端检查服务实例不健康后的注销处理

(1)服务端对服务实例进行健康检查的设计逻辑

一.首先会获取所有客户端的Connection连接对象

Connection连接对象里有个属性叫lastActiveTime,表示的是最后存活时间。

二.然后判断当前时间-最后存活时间是否大于20s

如果大于,则把该Connection连接对象的connectionId放入到一个集合里。这个集合是一个名为outDatedConnections的待移除集合Set,此时该Connection连接对象并不会马上删除。

三.当判断完全部的Connection连接对象后会遍历outDatedConnections集合

向遍历到的Connection连接对象发起一次请求,确认是否真的下线。如果响应成功,则往successConnections集合中添加connectionId,并且刷新Connection连接对象的lastActiveTime属性。这个机制有一个专业的名称叫做:探活机制。

四.遍历待移除集合进行注销并且在注销之前先判断一下是否探活成功

也就是connectionId存在于待移除集合outDatedConnections中,但是不存在于探活成功集合successConnections中,那么这个connectionId对应的客户端就会被注销掉。

(2)服务端对服务实例进行健康检查的源码

对服务实例进行健康检查的源码入口是ConnectionManager的start()方法。

@Service
public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent> {Map<String, Connection> connections = new ConcurrentHashMap<>();...//Start Task:Expel the connection which active Time expire.@PostConstructpublic void start() {//Start UnHealthy Connection Expel Task.RpcScheduledExecutor.COMMON_SERVER_EXECUTOR.scheduleWithFixedDelay(new Runnable() {@Overridepublic void run() {...//一.首先获取所有的连接Set<Map.Entry<String, Connection>> entries = connections.entrySet();...//二.然后判断客户端是否超过20s没有发来心跳信息了,如果是则会将clientId加入outDatedConnections集合中Set<String> outDatedConnections = new HashSet<>();long now = System.currentTimeMillis();for (Map.Entry<String, Connection> entry : entries) {Connection client = entry.getValue();String clientIp = client.getMetaInfo().getClientIp();AtomicInteger integer = expelForIp.get(clientIp);if (integer != null && integer.intValue() > 0) {integer.decrementAndGet();expelClient.add(client.getMetaInfo().getConnectionId());expelCount--;} else if (now - client.getMetaInfo().getLastActiveTime() >= KEEP_ALIVE_TIME) {//判断心跳时间//添加到待移除列表outDatedConnections.add(client.getMetaInfo().getConnectionId());}}...//client active detection.//三.初次检测完超过20s的Connection连接对象后,并不会立马进行删除,而是进行探活,服务端主动请求客户端,来确认是否真的下线Loggers.REMOTE_DIGEST.info("Out dated connection ,size={}", outDatedConnections.size());if (CollectionUtils.isNotEmpty(outDatedConnections)) {Set<String> successConnections = new HashSet<>();final CountDownLatch latch = new CountDownLatch(outDatedConnections.size());//遍历超过20s没有心跳的客户端clientIdfor (String outDateConnectionId : outDatedConnections) {try {Connection connection = getConnection(outDateConnectionId);if (connection != null) {ClientDetectionRequest clientDetectionRequest = new ClientDetectionRequest();//调用GrpcConnection.asyncRequest()方法异步发送请求connection.asyncRequest(clientDetectionRequest, new RequestCallBack() {@Overridepublic Executor getExecutor() {return null;}@Overridepublic long getTimeout() {return 1000L;}@Overridepublic void onResponse(Response response) {latch.countDown();if (response != null && response.isSuccess()) {//响应成功刷新心跳时间connection.freshActiveTime();//并且加入到探活成功的集合列表中successConnections.add(outDateConnectionId);}}@Overridepublic void onException(Throwable e) {latch.countDown();}});Loggers.REMOTE_DIGEST.info("[{}]send connection active request ", outDateConnectionId);} else {latch.countDown();}                            } catch (ConnectionAlreadyClosedException e) {latch.countDown();} catch (Exception e) {Loggers.REMOTE_DIGEST.error("[{}]Error occurs when check client active detection ,error={}", outDateConnectionId, e);latch.countDown();}}latch.await(3000L, TimeUnit.MILLISECONDS);Loggers.REMOTE_DIGEST.info("Out dated connection check successCount={}", successConnections.size());//经过探活还是不成功的Connection连接对象,就准备进行移除了//遍历20s没有心跳的客户端,准备移除客户端信息for (String outDateConnectionId : outDatedConnections) {//判断探活是否成功,如果成功了则不需要移除if (!successConnections.contains(outDateConnectionId)) {Loggers.REMOTE_DIGEST.info("[{}]Unregister Out dated connection....", outDateConnectionId);//执行客户端注销逻辑unregister(outDateConnectionId);}}}...}}, 1000L, 3000L, TimeUnit.MILLISECONDS);}...
}

(3)服务端检查服务实例不健康后的注销处理

进行注销处理的方法是ConnectionManager的unregister()方法。该方法主要会移除Connection连接对象 + 清除一些数据,以及发布一个ClientDisconnectEvent客户端注销事件。

@Service
public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent> {private Map<String, AtomicInteger> connectionForClientIp = new ConcurrentHashMap<String, AtomicInteger>(16);Map<String, Connection> connections = new ConcurrentHashMap<>();@Autowiredprivate ClientConnectionEventListenerRegistry clientConnectionEventListenerRegistry;...//unregister a connection .public synchronized void unregister(String connectionId) {//移除客户端信息Connection remove = this.connections.remove(connectionId);if (remove != null) {String clientIp = remove.getMetaInfo().clientIp;AtomicInteger atomicInteger = connectionForClientIp.get(clientIp);if (atomicInteger != null) {int count = atomicInteger.decrementAndGet();if (count <= 0) {connectionForClientIp.remove(clientIp);}}remove.close();Loggers.REMOTE_DIGEST.info("[{}]Connection unregistered successfully. ", connectionId);//通知客户端注销连接clientConnectionEventListenerRegistry.notifyClientDisConnected(remove);}}...
}@Service
public class ClientConnectionEventListenerRegistry {...//notify where a new client disconnected.public void notifyClientDisConnected(final Connection connection) {for (ClientConnectionEventListener clientConnectionEventListener : clientConnectionEventListeners) {try {//调用ConnectionBasedClientManager.clientDisConnected()方法clientConnectionEventListener.clientDisConnected(connection);} catch (Throwable throwable) {Loggers.REMOTE.info("[NotifyClientDisConnected] failed for listener {}", clientConnectionEventListener.getName(), throwable);}}}...
}@Component("connectionBasedClientManager")
public class ConnectionBasedClientManager extends ClientConnectionEventListener implements ClientManager {private final ConcurrentMap<String, ConnectionBasedClient> clients = new ConcurrentHashMap<>();...@Overridepublic boolean clientDisconnected(String clientId) {Loggers.SRV_LOG.info("Client connection {} disconnect, remove instances and subscribers", clientId);ConnectionBasedClient client = clients.remove(clientId);if (null == client) {return true;}client.release();//最后发布客户端注销事件NotifyCenter.publishEvent(new ClientEvent.ClientDisconnectEvent(client));return true;}...
}

ClientDisconnectEvent客户端注销事件会被两个监听响应:一是ClientServiceIndexesManager的onEvent()方法用来移除注册表 + 订阅表信息,二是DistroClientDataProcessor的onEvent()方法用来同步服务实例被注销后的数据。

@Component
public class ClientServiceIndexesManager extends SmartSubscriber {//注册表(服务提供者),一个Service服务对象,对应多个服务实例的clientIdprivate final ConcurrentMap<Service, Set<String>> publisherIndexes = new ConcurrentHashMap<>();//订阅者列表(服务消费者),一个Service服务对象,对应多个订阅者的clientIdprivate final ConcurrentMap<Service, Set<String>> subscriberIndexes = new ConcurrentHashMap<>();...@Overridepublic void onEvent(Event event) {if (event instanceof ClientEvent.ClientDisconnectEvent) {handleClientDisconnect((ClientEvent.ClientDisconnectEvent) event);} else if (event instanceof ClientOperationEvent) {handleClientOperation((ClientOperationEvent) event);}}private void handleClientDisconnect(ClientEvent.ClientDisconnectEvent event) {Client client = event.getClient();for (Service each : client.getAllSubscribeService()) {//移除订阅者列表的元素removeSubscriberIndexes(each, client.getClientId());}for (Service each : client.getAllPublishedService()) {//移除注册表的元素removePublisherIndexes(each, client.getClientId());}}private void removePublisherIndexes(Service service, String clientId) {if (!publisherIndexes.containsKey(service)) {return;}publisherIndexes.get(service).remove(clientId);NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));}private void removeSubscriberIndexes(Service service, String clientId) {if (!subscriberIndexes.containsKey(service)) {return;}subscriberIndexes.get(service).remove(clientId);if (subscriberIndexes.get(service).isEmpty()) {subscriberIndexes.remove(service);}}...
}public class DistroClientDataProcessor extends SmartSubscriber implements DistroDataStorage, DistroDataProcessor {...@Overridepublic void onEvent(Event event) {...if (event instanceof ClientEvent.ClientVerifyFailedEvent) {syncToVerifyFailedServer((ClientEvent.ClientVerifyFailedEvent) event);} else {syncToAllServer((ClientEvent) event);}}private void syncToAllServer(ClientEvent event) {Client client = event.getClient();//Only ephemeral data sync by Distro, persist client should sync by raft.//临时实例使用Distro协议,持久化实例使用Raft协议//ClientManager.isResponsibleClient()方法,判断只有该client的责任节点才能进行集群数据同步if (null == client || !client.isEphemeral() || !clientManager.isResponsibleClient(client)) {return;}if (event instanceof ClientEvent.ClientDisconnectEvent) {//如果event是客户端注销实例时需要进行集群节点同步的事件DistroKey distroKey = new DistroKey(client.getClientId(), TYPE);distroProtocol.sync(distroKey, DataOperation.DELETE);} else if (event instanceof ClientEvent.ClientChangedEvent) {//如果event是客户端注册实例时需要进行集群节点同步的事件DistroKey distroKey = new DistroKey(client.getClientId(), TYPE);distroProtocol.sync(distroKey, DataOperation.CHANGE);}}...
}

(4)总结

在Nacos 1.4.1版本中的服务健康检查:是15s没心跳则把健康状态修改为不健康,30s没心跳则把实例对象移除。

在Nacos 2.1.0版本中的服务健康检查:是20s没心跳则把客户端放入一个过期集合,此时并不移除客户端连接。由于引入了gRPC长连接,所以可以新增探活机制检查过期集合中的连接。服务端发送探活请求给客户端时的代价并不大,可确保客户端下线。

8.服务下线如何注销注册表和客户端等信息

(1)客户端发出服务下线请求的源码

(2)服务端处理服务下线请求的源码

(1)客户端发出服务下线请求的源码

Nacos客户端发起服务下线的入口在AbstractAutoServiceRegistration这个类之中,而AbstractAutoServiceRegistration是nacos-discovery中的类。

由于AbstractAutoServiceRegistration的destroy()方法被@PreDestroy修饰,所以当容器关闭时,会调用AbstractAutoServiceRegistration的destroy()方法。该方法最后会触发调用NacosNamingService的deregisterInstance()方法,然后调用NamingClientProxyDelegate的deregisterService()方法,接着调用NamingGrpcClientProxy的deregisterService()方法。和客户端发起服务注册一样,首先会创建请求参数对象,然后通过NamingGrpcClientProxy的requestToServer()方法发起请求,也就是调用RpcClient的request()方法发起gRPC请求进行服务下线。

public abstract class AbstractAutoServiceRegistration<R extends Registration> implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {...@PreDestroypublic void destroy() {stop();}public void stop() {if (this.getRunning().compareAndSet(true, false) && isEnabled()) {deregister();if (shouldRegisterManagement()) {deregisterManagement();}this.serviceRegistry.close();}}protected void deregister() {this.serviceRegistry.deregister(getRegistration());}...
}public class NacosServiceRegistry implements ServiceRegistry<Registration> {...@Overridepublic void deregister(Registration registration) {log.info("De-registering from Nacos Server now...");if (StringUtils.isEmpty(registration.getServiceId())) {log.warn("No dom to de-register for nacos client...");return;}NamingService namingService = namingService();String serviceId = registration.getServiceId();String group = nacosDiscoveryProperties.getGroup();try {//调用NacosNamingService.deregisterInstance()方法namingService.deregisterInstance(serviceId, group, registration.getHost(), registration.getPort(), nacosDiscoveryProperties.getClusterName());} catch (Exception e) {log.error("ERR_NACOS_DEREGISTER, de-register failed...{},", registration.toString(), e);}log.info("De-registration finished.");}...
}@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule")
public class NacosNamingService implements NamingService {private NamingClientProxy clientProxy;...public NacosNamingService(Properties properties) throws NacosException {init(properties);}private void init(Properties properties) throws NacosException {...this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, properties, changeNotifier);}@Overridepublic void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException {Instance instance = new Instance();instance.setIp(ip);instance.setPort(port);instance.setClusterName(clusterName);deregisterInstance(serviceName, groupName, instance);}@Overridepublic void deregisterInstance(String serviceName, String groupName, Instance instance) throws NacosException {//调用NamingClientProxyDelegate.deregisterService()方法clientProxy.deregisterService(serviceName, groupName, instance);}...
}public class NamingClientProxyDelegate implements NamingClientProxy {private final NamingHttpClientProxy httpClientProxy;private final NamingGrpcClientProxy grpcClientProxy;...public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, Properties properties, InstancesChangeNotifier changeNotifier) throws NacosException {...this.httpClientProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, properties, serviceInfoHolder);this.grpcClientProxy = new NamingGrpcClientProxy(namespace, securityProxy, serverListManager, properties, serviceInfoHolder);}@Overridepublic void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException {getExecuteClientProxy(instance).deregisterService(serviceName, groupName, instance);}private NamingClientProxy getExecuteClientProxy(Instance instance) {return instance.isEphemeral() ? grpcClientProxy : httpClientProxy;}...
}public class NamingGrpcClientProxy extends AbstractNamingClientProxy {private final NamingGrpcRedoService redoService;...@Overridepublic void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException {NAMING_LOGGER.info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}", namespaceId, serviceName, instance);redoService.instanceDeregister(serviceName, groupName);doDeregisterService(serviceName, groupName, instance);}//Execute deregister operation.public void doDeregisterService(String serviceName, String groupName, Instance instance) throws NacosException {//创建请求参数对象  InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName, NamingRemoteConstants.DE_REGISTER_INSTANCE, instance);//向服务端发起请求requestToServer(request, Response.class);redoService.removeInstanceForRedo(serviceName, groupName);}private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass) throws NacosException {try {request.putAllHeader(getSecurityHeaders(request.getNamespace(), request.getGroupName(), request.getServiceName()));//实际会调用RpcClient.request()方法发起gRPC请求Response response = requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout);if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) {throw new NacosException(response.getErrorCode(), response.getMessage());}if (responseClass.isAssignableFrom(response.getClass())) {return (T) response;}NAMING_LOGGER.error("Server return unexpected response '{}', expected response should be '{}'", response.getClass().getName(), responseClass.getName());} catch (Exception e) {throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e);}throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response");}...
}

(2)服务端处理服务下线请求的源码

服务注册时请求参数InstanceRequest的类型是REGISTER_INSTANCE,服务下线时请求参数InstanceRequest的类型是DE_REGISTER_INSTANCE。

处理服务注册和服务下线的入口是InstanceRequestHandler的handle()方法,这个方法会触发调用InstanceRequestHandler的deregisterInstance()方法,也就是调用EphemeralClientOperationServiceImpl的deregisterInstance()方法。

在EphemeralClientOperationServiceImpl的deregisterInstance()方法中,会在移除Client对象中的instance信息时,发布ClientChangedEvent事件,然后接着发布客户端注销服务实例的事件ClientDeregisterServiceEvent。

其中ClientChangedEvent事件是用来同步数据给集群节点的,ClientDeregisterServiceEvent事件是用来移除注册表 + 订阅表的服务实例。移除注册表 + 订阅表的服务实例时,还会发布ServiceChangeEvent事件,ServiceChangeEvent事件是用来通知订阅了该服务的Nacos客户端的。同理,服务注册时其实也会发布类似的三个事件。

@Component
public class InstanceRequestHandler extends RequestHandler<InstanceRequest, InstanceResponse> {private final EphemeralClientOperationServiceImpl clientOperationService;public InstanceRequestHandler(EphemeralClientOperationServiceImpl clientOperationService) {this.clientOperationService = clientOperationService;}@Override@Secured(action = ActionTypes.WRITE)public InstanceResponse handle(InstanceRequest request, RequestMeta meta) throws NacosException {//根据请求信息创建一个Service对象,里面包含了:命名空间、分组名、服务名Service service = Service.newService(request.getNamespace(), request.getGroupName(), request.getServiceName(), true);switch (request.getType()) {case NamingRemoteConstants.REGISTER_INSTANCE://注册实例return registerInstance(service, request, meta);case NamingRemoteConstants.DE_REGISTER_INSTANCE://注销实例return deregisterInstance(service, request, meta);default:throw new NacosException(NacosException.INVALID_PARAM, String.format("Unsupported request type %s", request.getType()));}}private InstanceResponse registerInstance(Service service, InstanceRequest request, RequestMeta meta) {//调用EphemeralClientOperationServiceImpl的注册方法registerInstance(),这里需要注意如下参数;//参数service:根据请求信息创建的一个Service对象,里面有命名空间、分组名、服务名//参数request.getInstance():这个参数就对应了客户端的实例对象,里面包含IP、端口等信息//参数meta.getConnectionId():这个参数很关键,它是连接IDclientOperationService.registerInstance(service, request.getInstance(), meta.getConnectionId());return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE);}private InstanceResponse deregisterInstance(Service service, InstanceRequest request, RequestMeta meta) {//调用EphemeralClientOperationServiceImpl的注销方法deregisterInstance()clientOperationService.deregisterInstance(service, request.getInstance(), meta.getConnectionId());return new InstanceResponse(NamingRemoteConstants.DE_REGISTER_INSTANCE);}
}@Component("ephemeralClientOperationService")
public class EphemeralClientOperationServiceImpl implements ClientOperationService {private final ClientManager clientManager;...@Overridepublic void deregisterInstance(Service service, Instance instance, String clientId) {if (!ServiceManager.getInstance().containSingleton(service)) {Loggers.SRV_LOG.warn("remove instance from non-exist service: {}", service);return;}//从ServiceManager中根据由请求信息创建的Service对象获取一个已注册的Service对象Service singleton = ServiceManager.getInstance().getSingleton(service);//从ClientManagerDelegate中根据请求参数中的connectionId获取一个Client对象,即IpPortBasedClient对象Client client = clientManager.getClient(clientId);if (!clientIsLegal(client, clientId)) {return;}//调用AbstractClient.removeServiceInstance()方法//移除Client对象中的instance信息并发布ClientChangedEvent事件来同步集群节点InstancePublishInfo removedInstance = client.removeServiceInstance(singleton);client.setLastUpdatedTime();if (null != removedInstance) {//发布客户端注销服务实例的事件NotifyCenter.publishEvent(new ClientOperationEvent.ClientDeregisterServiceEvent(singleton, clientId));NotifyCenter.publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, removedInstance.getMetadataId(), true));}}...
}public abstract class AbstractClient implements Client {//publishers其实就是记录该客户端提供的服务和服务实例,一个客户端可提供多个服务//存储客户端发送过来的请求中的Instance信息,当然这些信息已封装为InstancePublishInfo对象//key为已注册的Service,value是根据请求中的instance实例信息封装的InstancePublishInfo对象protected final ConcurrentHashMap<Service, InstancePublishInfo> publishers = new ConcurrentHashMap<>(16, 0.75f, 1);...@Overridepublic InstancePublishInfo removeServiceInstance(Service service) {InstancePublishInfo result = publishers.remove(service);if (null != result) {MetricsMonitor.decrementInstanceCount();//发布客户端改变事件,用于处理集群间的数据同步NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(this));}Loggers.SRV_LOG.info("Client remove for service {}, {}", service, getClientId());return result;}...
}

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

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

相关文章

[手写系列]Go手写db — — 完整教程

[手写系列]Go手写db ZiyiDB是一个简单的内存数据库实现&#xff0c;支持基本的SQL操作&#xff0c;包含create、insert、delete、select、update、drop。目前一期暂支持int类型以及字符类型数据&#xff0c;后续会支持更多数据结构以及能力。本项目基于https://github.com/eato…

十三、动态对象创建(Dynamic Object Creation)

十三、动态对象创建&#xff08;Dynamic Object Creation&#xff09; 目录 13.1 对象创建&#xff08;Object creation&#xff09;13.2 new / delete 操作符13.3 数组的 new 与 delete13.4 总结 背景说明 有时候我们需要知道程序中对象的数量、类型和声明周期&#xff0c;…

一、网络基础

IPv4&#xff1a;32位二进制 -- 点分十进制标识 192.168.1.1&#xff08;连续的32位&#xff0c;为了好看方便每8位一段&#xff09; IPv6&#xff1a;128位二进制 IP&#xff08;Internet协议&#xff09; 洪泛&#xff1a;除流量进入接口外的所有接口的复制 OSI模型&#…

前端面试测试题目(一)

一、Vue的双向绑定机制&#xff08;v-model底层实现原理&#xff09; Vue的双向绑定核心由 响应式系统 和 指令语法糖 共同实现&#xff0c;具体原理如下&#xff1a; 响应式系统 Vue通过数据劫持和依赖收集实现数据变化到视图的同步&#xff1a; • 数据劫持&#xff1a;在Vue…

我用Deepseek + 亮数据爬虫神器 1小时做出輿情分析器

我用Deepseek 亮数据爬虫神器 1小时做出輿情分析器 一、前言二、Web Scraper API 实战&#xff08;1&#xff09;选择对应的URL&#xff08;2&#xff09;点击进入对应url界面&#xff08;3&#xff09;API结果实例和爬取结果展示&#xff08;4&#xff09;用户直接使用post请…

机器学习实战:归一化与标准化的选择指南

在机器学习实战中——是否需要归一化&#xff08;Normalization&#xff09;或标准化&#xff08;Standardization&#xff09;&#xff0c;取决于所使用的模型类型。 ✅ LightGBM / XGBoost 是否需要归一化或标准化&#xff1f; 不需要。 &#x1f527; 原因&#xff1a; L…

磁珠特点,原理与应用

什么是磁珠&#xff1f; 磁珠在1930年由日本东京工业大学的加藤与五郎和武井武两位教授发明&#xff0c;TDK首次生产&#xff0c;是电感的一种&#xff0c;区别就是&#xff1a;电感外面包裹着铁氧体材质。 因铁氧体具有高电阻率&#xff0c;低涡流损耗&#xff0c;高频时依旧…

【连载14】基础智能体的进展与挑战综述-多智能体系统设计

基础智能体的进展与挑战综述 从类脑智能到具备可进化性、协作性和安全性的系统 【翻译团队】刘军(liujunbupt.edu.cn) 钱雨欣玥 冯梓哲 李正博 李冠谕 朱宇晗 张霄天 孙大壮 黄若溪 在基于大语言模型的多智能体系统&#xff08;LLM-MAS&#xff09;中&#xff0c;合作目标和合…

React Native踩坑实录:解决NativeBase Radio组件在Android上的兼容性问题

React Native踩坑实录&#xff1a;解决NativeBase Radio组件在Android上的兼容性问题 问题背景 在最近的React Native项目开发中&#xff0c;我们的应用在iOS设备上运行良好&#xff0c;但当部署到Android设备时&#xff0c;进入语言设置和隐私设置页面后应用崩溃。我们遇到了…

[Windows] 网络检测工具InternetTest v8.8.2.2503 单文件版_支持查询IP_DNS_WIFI密码一键恢复

InternetTest&#xff08;详情请戳 官网 / 作者项目地址&#xff09;是一款免费开源的网络检测实用工具&#xff0c;其可实现监控、诊断互联网网络连接&#xff0c;例如进行 ping 测试、延迟测试、WiFi 密码查看、IP 地址或域名信息查询等算是搭建网站及服务器的实用维护工具。…

配置Hadoop集群-集群配置

以下是 Hadoop 集群的核心配置步骤&#xff0c;基于之前的免密登录和文件同步基础&#xff0c;完成 Hadoop 分布式环境的搭建&#xff1a; 1. 集群规划 假设集群包含 3 个节点&#xff1a; master&#xff1a;NameNode、ResourceManagerslave1&#xff1a;DataNode、NodeMana…

Spring Bean有哪几种配置方式?

大家好&#xff0c;我是锋哥。今天分享关于【Spring Bean有哪几种配置方式&#xff1f;】面试题。希望对大家有帮助&#xff1b; Spring Bean有哪几种配置方式&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring Bean的配置方式主要有三种&#xff…

Webpack中Compiler详解以及自定义loader和plugin详解

Webpack Compiler 源码全面解析 Compiler 类图解析&#xff1a; 1. Tapable 基类 Webpack 插件系统的核心&#xff0c;提供钩子注册&#xff08;plugin&#xff09;和触发&#xff08;applyPlugins&#xff09;能力。Compiler 和 Compilation 均继承此类&#xff0c;支持插件…

HAProxy + Keepalived + Nginx 高可用负载均衡系统

1. 项目背景 在现代Web应用中&#xff0c;高可用性和负载均衡是两个至关重要的需求。本项目旨在通过HAProxy实现流量分发&#xff0c;通过Keepalived实现高可用性&#xff0c;通过Nginx提供后端服务。该架构能够确保在单点故障的情况下&#xff0c;系统仍然能够正常运行&#…

Kubernetes控制平面组件:Kubelet详解(一):API接口层介绍

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…

VIC-2D 7.0 为平面样件机械试验提供全视野位移及应变数据软件

The VIC-2D系统是一个完全集成的解决方案&#xff0c;它基于优化的相关算法为平面试样的力学测试提供非接触、全场的二维位移和应变数据&#xff0c;可测量关注区域内的每个像素子集的面内位移&#xff0c;并通过多种张量选项计算全场应变。The VIC-2D 系统可测量超过 2000%变形…

多线程访问Servlet如何谨慎处理共享资源

1. 避免共享状态&#xff08;最佳实践&#xff09; 核心思想&#xff1a;Servlet 本身应设计为无状态&#xff08;Stateless&#xff09;&#xff0c;不依赖实例变量存储请求相关数据。 实现方式&#xff1a; 将变量声明在方法内部&#xff08;局部变量&#xff09;&#xff0…

从Windows到Mac的过渡:学习笔记与心得

作为一名长期使用Windows操作系统的用户&#xff0c;当我决定转换到Mac时&#xff0c;心中充满了期待与好奇。Mac以其独特的操作系统和设计风格著称&#xff0c;虽然有许多相似之处&#xff0c;但仍有不少差异需要适应。为了帮助其他有类似转换需求的朋友&#xff0c;我总结了一…

TestNG接口自动化

第一章、 Rest assured接口测试框架 一、概述 接口自动化的框架&#xff0c;主要是用来做接口自动化测试&#xff0c;返回的报文都是JSON 语法比较简单&#xff0c;只需要掌握常用的方法 用例运行的速度非常快 断言的机制 Json 封装相关方法&#xff0c;jsonpath&#xff0c;x…

【速写】KV-cache与解码的再探讨(以束搜索实现为例)

文章目录 1 Beam Search 解码算法实现2 实现带KV Cache的Beam Search解码3 关于在带kv-cache的情况下的use_cache参数 1 Beam Search 解码算法实现 下面是一个使用PyTorch实现的beam search解码算法&#xff1a; 几个小细节&#xff1a; 束搜索可以加入length_penalty&#…