zookeeper实现RMI服务,高可用,HA
这可不是目录
- 1.RMI原理与说明
- 1.1含义
- 1.2流程
- 1.3rmi的简单实现
- 1.4RMI的局限性
- 2.zookeeper实现RMI服务(高可用、HA)
- 2.1实现原理
- 2.2高可用分析
- 2.3zookeeper实现
- 2.3.1代码分析
- 2.3.2公共部分
- 2.3.3服务端
- 2.3.4客户端
- 2.3.5运行与部署
- 2.3.6效果展示与说明
1.RMI原理与说明
1.1含义
远程方法调用
仅适用于JAVA
RMI是一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制。使用这种机制,某一台计算机上的对象可以调用另外一台计算机上的对象来获取远程数据。
1.2流程
1.3rmi的简单实现
客户端:RmiClient.java
package com.rmi.client;import com.rmi.common.HelloService;import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;public class RmiClient {public static void main(String[] args) throws MalformedURLException, NotBoundException, RemoteException {System.out.println("rmi client running");//定义urlString url ="rmi://127.0.0.1:1099/com.rmi.server.HelloServiceImpl";//寻找发布的服务Remote lookup = Naming.lookup(url);//强制类型转换HelloService helloService = (HelloService) lookup;//调用目标方法String result = helloService.sayHello("wunaiieq");System.out.println("result:"+result);}
}
远程接口:HelloService.java
package com.rmi.common;import java.rmi.Remote;
import java.rmi.RemoteException;public interface HelloService extends Remote {String sayHello(String name) throws RemoteException;
}
远程接口的实现类:HelloServiceImpl.java
package com.rmi.server;import com.rmi.common.HelloService;import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {protected HelloServiceImpl() throws RemoteException {}@Overridepublic String sayHello(String name) throws RemoteException {return "Hello"+name;}
}
rmi服务:RmiServer
package com.rmi.server;import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;public class RmiServer {public static void main(String[] args) throws Exception{//定义发布RMi服务的端口int port = 1099;String url = "rmi://127.0.0.1:1099/com.rmi.server.HelloServiceImpl";//注册服务:相当于在JNDI中创建了一个注册表LocateRegistry.createRegistry(port);//绑定服务:将RMI服务的实现类对象和url绑定Naming.rebind(url,new HelloServiceImpl());}
}
运行说明
- 先启动RmiServer,注册远程对象
- 再启动RmiCllient,查找此对象,并调用远程方法
1.4RMI的局限性
- 只能使用Java,不支持跨语言
- RMI使用了Java默认的序列化方式,对于要求较高的系统,可能需要其他的序列化方案进行解决(Protobuf)
- RMI服务在运行时可能会出现单点故障的问题,因此需要配置实现高可用HA
2.zookeeper实现RMI服务(高可用、HA)
2.1实现原理
服务注册与发现:RMI服务端在启动后,可以在ZooKeeper上注册一个临时节点(Ephemeral Node),并将自己的服务地址写入该节点。客户端在需要调用RMI服务时,可以监听ZooKeeper上的这些临时节点,以获取服务地址。由于ZooKeeper会监控这些节点的状态,一旦服务端节点宕机或断开连接,对应的临时节点就会被自动删除。客户端在感知到这一变化后,可以重新获取有效的服务地址,从而实现了服务的自动发现和故障切换。
负载均衡:ZooKeeper还可以作为服务注册中心,为多个RMI服务端实例提供统一的注册和发现接口。客户端在调用RMI服务时,可以通过ZooKeeper获取到多个服务端的地址,并根据一定的策略(如轮询、随机等)选择一个进行调用。这样可以实现负载的均衡分配,避免单个服务端过载。注意,此处只能实现相对均衡,原则上不等同于负载均衡服务器
服务状态监控:ZooKeeper可以监控RMI服务端的状态信息(如CPU使用率、内存占用率等),并将这些信息反馈给客户端或系统管理员。当发现某个服务端状态异常时,可以及时采取措施(如重启服务、扩展资源等)来恢复服务的正常运行。
zookeeper在此处相当于一个注册表
2.2高可用分析
1. rmi服务高可用
首先一个服务端只能运行于一台服务中心上,提供的同一个服务可以存在多个,这样当某一个服务宕机时,服务中心仍存在其他相同的服务。
因此,这样的同名服务,同时运行,但是端口不一致,客户端在调用这样的服务时,随机选取(自定义选取也可以)一个znode节点,调用rmi服务
2. 服务注册中心高可用
上述的服务已经保证了不会宕机,但服务中心仍存在宕机的可能。
因此配置zookeeper高可用,在每个zookeeper上运行相应的服务,以保证当一个服务中心宕机,仍然可以提供服务。
2.3zookeeper实现
2.3.1代码分析
将设置6个代码块,红框表示客户端程序,黄框表示服务器端,中间为公共部分
2.3.2公共部分
客户端和服务端应共同规定这个包中所有的接口
Constant.java
这个接口主要是规定一些常量,以便于服务端和客户端的使用
package com.zkrmi.common;public interface Constant {//zk集群的地址String ZK_CONNECTION_STRING="192.168.80.111:2181,192.168.80.112:2181,192.168.80.113:2181";//连接超时时间int ZK_SESSION_TIMEOUT = 5000;//服务列表对应的临时节点的parent节点String ZK_REGISTRY_PATH="/registry";//临时节点的路径,注意创建的是临时顺序节点,因此最后显示的是provider0,provider1....String ZK_provider_PATH=ZK_REGISTRY_PATH+"/provider";
}
HelloService.java
远程接口,这是提前定义好,应当被客户端和服务端同时知晓的,共同规定的。
package com.zkrmi.common;import java.rmi.Remote;
import java.rmi.RemoteException;public interface HelloService extends Remote {String sayHello(String name) throws RemoteException;
}
2.3.3服务端
任务1. 实现远程接口
任务2. 在zookeeper上注册远程方法服务
HelloServiceImpl.java
这串代码主要用于实现远程接口,没有什么特殊的点
package com.zkrmi.server;import com.rmi.common.HelloService;import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {protected HelloServiceImpl() throws RemoteException {}@Overridepublic String sayHello(String name) throws RemoteException {//注意,在不同的zk集群上,请修改以下说明,在实际生产中,不同节点上的实现类应保持一致return "Hello_ZK112"+name;}
}
ServiceProvider.java
在zookeeper上发布可以提供的服务,即远程对象,这里进行了封装,后续的调用将在主函数中进行。
package com.zkrmi.server;import com.zkrmi.common.Constant;
import com.zookeeper.ZooKeeperFactory;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.concurrent.CountDownLatch;public class ServiceProvider {private CountDownLatch latch = new CountDownLatch(1);/*** 发布RMI服务** @param remote 远程对象,即HelloServiceImpl的实例* @param host 192.168.80.113,zookeeper的地址* @param port 11214,11215,11216,这个表示端口* @return rmi地址 rmi://192.168.80.113:11214/com.rmi.server.HelloServiceImpl*/private String publishServer(Remote remote, String host, int port) {String url = null;try {//设置发布服务的rmi地址 rmi://192.168.80.113:11214/com.rmi.server.HelloServiceImplurl = String.format("rmi://%s:%d/%s", host, port, remote.getClass().getName());//注册服务:相当于在JNDI中创建了一个注册表LocateRegistry.createRegistry(port);//绑定服务:将RMI服务的实现类对象和url绑定Naming.rebind(url, remote);} catch (RemoteException | MalformedURLException e) {e.printStackTrace();}return url;}/*** 创建临时节点** @param zk:传入一个zookeeper对象* @param url:url是临时节点的数据,表示rmi地址*/private void createNode(ZooKeeper zk, String url) {try {byte[] data = url.getBytes();//创建一个临时有序的节点String result = zk.create(Constant.ZK_provider_PATH, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);System.out.println("节点创建成功"+result);} catch (Exception e) {e.printStackTrace();}}/*** @param remote 调用时填入一个远程对象,这里使用remote进行标识,表示此对象可以从服务器端调用* */public void publish(Remote remote, String host, int port) throws Exception {//调用publishServer发布rmi服务,获取rmi的地址String url = publishServer(remote, host, port);//连接zookeeper集群if (url!=null){ZooKeeper zk =ZooKeeperFactory.create(Constant.ZK_CONNECTION_STRING);if (zk!=null){createNode(zk,url);}else {System.out.println("zk==null,节点创建失败");}}else {System.out.println("url==null,发布失败");}}}
Server.java
作为服务器端的主类,值得说明的是,远程服务端的端口自拟即可(建议在1024到49151)
package com.zkrmi.server;import com.rmi.common.HelloService;import java.rmi.RemoteException;//作为服务端的main类
public class Server {public static void main(String[] args) throws Exception {//zk节点String host ="192.168.80.112";//第一个端口System.out.println("server 10100 start");int port =Integer.parseInt("10100");//创建服务的生产者对象ServiceProvider provider0 = new ServiceProvider();//创建远程对象,客户端将使用此对象进行远程方法调用HelloService helloService =new HelloServiceImpl();//发布rmi服务provider0.publish(helloService,host,port);//第二个端口System.out.println("server 10101 start");int port1 =Integer.parseInt("10101");ServiceProvider provider1 = new ServiceProvider();provider1.publish(helloService,host,port1);}
}
2.3.4客户端
任务1:获取所有的rmi地址
任务2:当rmi地址更新,或者某个服务器异常时,需要重新获取
ServiceConsumer.java
客户端的方法支持,实现上述任务1和任务2的要求
package com.zkrmi.client;import com.zkrmi.common.Constant;
import com.zookeeper.ZooKeeperFactory;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;import java.rmi.ConnectException;
import java.rmi.Naming;
import java.rmi.Remote;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;public class ServiceConsumer {private CountDownLatch latch =new CountDownLatch(1);/**保存最新的rmi地址*/private volatile List<String> urlList =new ArrayList<>();/**构造器,用于观察/registry节点的所有子节点,并更新urlList*/public ServiceConsumer() throws Exception {ZooKeeper zk = ZooKeeperFactory.create(Constant.ZK_CONNECTION_STRING);if (zk!=null){watchNode(zk);}}/**观察/registry节点下所有子节点是否有变化* <br>初始化:构造器中调用一次,获取所有rmi地址* <br>若有:重新调用此方法,更新rmi地址* @param zk 用final定义,防止后续调用时获取的节点是新节点,监听不到变化* */private void watchNode(final ZooKeeper zk){try {//获取所有子节点名称,并设置监听List<String> nodeList = zk.getChildren(Constant.ZK_REGISTRY_PATH, new Watcher() {//当子节点发生变化时,再次调用这个watchNode方法@Overridepublic void process(WatchedEvent event) {if (event.getType()==Event.EventType.NodeChildrenChanged){watchNode(zk);}}});List<String> dataList =new ArrayList<>();//根据子节点名称进行遍历,获取所有子节点的数据,即rmi的地址for (String node:nodeList){byte[] data =zk.getData(Constant.ZK_REGISTRY_PATH+"/"+node,false,null);dataList.add(new String(data));}//获取所有rmi地址,更新-->重新调用此方法-->再度获取urlList =dataList;} catch (InterruptedException e) {throw new RuntimeException(e);} catch (KeeperException e) {throw new RuntimeException(e);}}/**查找rmi服务* */public <T extends Remote> T lookup(){T service =null;//由于前面的更新,因此urlList始终保持的是最新的int size =urlList.size();if (size>0){String url;if (size ==1){url = urlList.get(0);System.out.println("只获取到一个url:"+url);}else {url =urlList.get(ThreadLocalRandom.current().nextInt(size));System.out.println("获取一个随机的url:"+url);}System.out.println("当前url:"+url);service = lookupService(url);}return service;}@SuppressWarnings("unchecked")private <T> T lookupService(String url){T remote =null;try {remote=(T) Naming.lookup(url);}catch (Exception e){if (e instanceof ConnectException){System.out.println("连接中断,重试");if (urlList.size()!=0){url=urlList.get(0);return lookupService(url);}}}return remote;}
}
Client.java
作为客户端的主类,实现远程方法调用
package com.zkrmi.client;import com.rmi.common.HelloService;public class Client {public static void main(String[] args) throws Exception {ServiceConsumer consumer =new ServiceConsumer();while (true){HelloService helloService =consumer.lookup();String result =helloService.sayHello("wunaiieq");System.out.println(result);Thread.sleep(3000);}}
}
2.3.5运行与部署
- 建议部署于虚拟机上
- 为测试zookeeper效果,请将服务端部署于不同的zookeeper节点上
- 客户端可以运行于非zookeeper集群的主机
- 上述代码中缺少zookeeperFactory.java可以去博客中复制(不放这,太乱了)
打包
pom.xml
为实现如下效果,请打3个包,2个服务端,1个客户端,打包时修改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"><modelVersion>4.0.0</modelVersion><groupId>com.wunaiieq</groupId><artifactId>zookeeper02</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.7.1</version></dependency></dependencies><build><plugins><plugin><!--声明--><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>3.3.0</version><!--具体配置--><configuration><archive><manifest><!--jar包的执行入口--><mainClass>com.zkrmi.client.Client</mainClass></manifest></archive><descriptorRefs><!--描述符,此处为预定义的,表示创建一个包含项目所有依赖的可执行 JAR 文件;允许自定义生成jar文件内容--><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs></configuration><!--执行配置--><executions><execution><!--执行配置ID,可修改--><id>make-assembly</id><!--执行的生命周期--><phase>package</phase><goals><!--执行的目标,single表示创建一个分发包--><goal>single</goal></goals></execution></executions></plugin></plugins></build>
</project>
2.3.6效果展示与说明
112 , 113为连接到zookeeper集群的主机,作为服务中心
114为客户端,调用rmi服务
检查114的输出,可以看到,服务能正常调用
代码其他解释
zk集群中,113状态为leader,112follower,同时运行服务端jar包,均正常创建了rmi服务对象
ZK高可用
当一个zk节点宕机,另一个仍可以正常运行。保证服务不中断
服务高可用
由于服务的端口号不一致,因此当关闭一个服务时,仍存在rmi服务供客户端使用
客户端选择
在上述代码中,设置的时随机选择,这个不重要,自行设置即可
相关文章:

zookeeper实现RMI服务,高可用,HA
这可不是目录 1.RMI原理与说明1.1含义1.2流程1.3rmi的简单实现1.4RMI的局限性 2.zookeeper实现RMI服务(高可用、HA)2.1实现原理2.2高可用分析2.3zookeeper实现2.3.1代码分析2.3.2公共部分2.3.3服务端2.3.4客户端2.3.5运行与部署2.3.6效果展示与说明 1.RM…...

通过Express + Vue3从零构建一个用户认证与授权系统(一)项目结构设计
项目背景 本文基于 TypeScript Express Vue3 ,从零构建一个用户认证与授权管理系统。这个系统的核心部分包括前端、后端和数据库。我们需要确保各部分合理分层、易于维护和扩展,让我们一步步去实现我们的系统。 一、项目结构设计 1. 前端 (Vue 3 E…...

JavaScript 第13章:Ajax 与异步请求
在Web开发中,异步请求是一种非常重要的技术,它可以让网页在不重新加载的情况下与服务器交互。本章将介绍两种常用的异步请求技术:XMLHttpRequest 和 Fetch API,以及它们如何用于处理JSON数据交换,并通过一个实战案例—…...

速卖通商品详情接口技术解析及Python代码示例
速卖通商品详情接口技术解析及Python代码示例 速卖通(AliExpress)作为全球知名的跨境电商平台,其开放平台提供了丰富的API接口,允许开发者集成速卖通的各项功能,实现商品搜索、详情查询、订单管理等一系列操作。本文将…...

邻接表的有向网(C语言代码)
#include <stdio.h> #include <stdlib.h> #define MVNum 100 //最大顶点数 //边表结构体 typedef struct ArcNode { //表结点 int adjvex; //邻接点的位置 struct ArcNode* nextarc; //指向下一个…...

大模型生成PPT大纲优化方案:基于 nVidia NIM 平台的递归结构化生成
大模型生成PPT大纲优化方案:基于 nVidia NIM 平台的递归结构化生成 待解决的问题 生成PPT大纲是一种大模型在办公场景下应用的常见需求。 然而: 目前直接让大模型生成大纲往往是非结构化的,输出格式多样,难以统一和规范&#…...

MRSO算法(JCR2区)
原论文摘要:智能技术的快速发展促使利用自然行为来解决复杂问题的优化算法得以发展。其中,鼠群优化算法(Rat Swarm Optimizer,RSO)受老鼠的社会和行为特征启发,在各个领域已展现出潜力,但其收敛…...

最新Spring Boot3框架入门教程,基础知识讲解(参考官方文档),同时基于MybatisPlus+MYSQL搭建后台管理系统基础流程(附源码)
本文所涉及的代码以及相关文件均上传至仓库:GitHub - yang66-hash/XDPropertyManagementSystemDemo: This is a demo template based on SpringBoot3 in the background of property management system. Spring Boot 是由 Pivotal 团队开发的一款开源框架,它可以帮助…...

导数的概念及在模型算法中的应用
一. 导数概念与计算 1. 导数的物理意义: 瞬时速率。一般的,函数yf(x)在x处的瞬时变化率是 2. 导数的几何意义: 曲线的切线,当点趋近于P时,直线 PT 与曲线相切。容易知道,割线的斜率是当点趋近于 P 时&…...

获取首日涨停封盘后第二次交易日上涨/下跌的概率
有许多投资者喜欢在股票涨停封盘后,跟进买入。普通股民会认为一个能在今日涨停封盘的股票,证明其上市公司正有十分重大的利好信息,只需要跟进购买便可以获取短期利益。 我们用数据来看一下在当日涨停封盘后,第二次交易日是上涨还…...

shell $ 用法
Shell脚本中$符号的几种用法小结_linux shell_脚本之家 Shell 传递参数 | 菜鸟教程 $ 符号说明$0Shell 的命令本身1到9表示 Shell 的第几个参数$?显示最后命令的执行情况$#传递到脚本的参数个数$$脚本运行的当前进程 ID 号$*以一个单字符串显示所有向脚本传递的参数$!后台运行…...

如何用支付宝实现靠脸吃饭
还记得上学时,每当下课铃声响起,我们就会像一群脱缰的野马一样,浩浩荡荡地冲向食堂。最令人崩溃的时刻莫过于终于到达打饭窗口前排时,却发现饭卡忘带了!但现在,这种情况将不再发生。许多学校食堂已经配备了…...

Visual Studio的实用调试技巧总结
对于很多学习编程的老铁们来说,是不是也像下面这张图一样写代码呢? 那当我们这样编写代码的时候遇到了问题?大家又是怎么排查问题的呢?是不是也像下面这张图一样,毫无目的的一遍遍尝试呢? 这篇文章我就以 V…...

graphrag学习总结
学习视频:b站链接 项目链接 GraphRAG 的基本概念 Document(文档):系统中的输入文档。这些文档要么代表CSV中的单独行,要么代表单独的txt文件。 TextUnit(文本块):要分析的文本块。…...

专题:贪心算法(已完结)
1.分发饼干 方法一:用最大的胃口 找到最大的饼干(先遍历胃口) class Solution { public:int findContentChildren(vector<int>& g, vector<int>& s) {// 主要思路 用最大的饼干找最大的胃口sort(g.begin(),g.end());so…...

Hadoop的三种运行模式:单机模式、伪分布式模式和完全分布式模式
单机模式 单机模式是Hadoop最简单的运行模式。在单机模式下,所有Hadoop组件都运行在单个机器上,包括HDFS、MapReduce等。由于只有一个节点参与计算,单机模式适用于开发和测试阶段,不适合用于处理大规模数据。在单机模式下…...

JavaScript将array数据下载到Excel中
具体代码如下: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widt…...

【前端】Bootstrap:快速开始
Bootstrap 是一个功能强大且易于使用的前端框架,专门用于创建响应式和移动优先的网页。学习Bootstrap不仅可以帮助你快速构建现代网页,还可以提升你对前端开发流程的理解。本教程将从基础概念开始,逐步引导你掌握Bootstrap,并通过…...

文献阅读(222) VVQ协议死锁
题目:VVQ: Virtualizing Virtual Channel for Cost-Efficient Protocol Deadlock Avoidance时间:2023会议:HPCA研究机构:KAIST request-reply协议死锁如下图所示,每个node收到request之后发送reply,但是想…...

Node.js管理工具NVM
nvm(Node Version Manager)是一个用于管理多个 Node.js 版本的工具。以下是 nvm 的使用方法和一些常见命令: 一、安装 nvm 下载 nvm: 地址:https://github.com/coreybutler/nvm-windows/releases访问 nvm 的 GitHub 仓…...

云原生后端
云原生后端(Cloud-Native Backend)是指在云计算环境中,利用云原生技术(如容器、微服务、服务网格等)构建和部署后端应用程序的一种方法。以下是对云原生后端的详细讲解: 1. 定义 云原生是一种设计和构建应…...

充电宝哪个品牌值得买?2024年五款靠谱充电宝推荐
哪个品牌充电宝值得买?用过这么多款充电宝,个人还是觉得充电快、小巧便携的充电宝使用会更加的方便!在当今快节奏的生活中,手机已成为我们不可或缺的伙伴。然而,随着智能手机功能的日益强大,电池续航问题也…...

YOLOv11对比YOLOV8网络结构变化分析,帮助你真正的理解和学习yolo框架
本文在大佬的文章YOLOv11 | 一文带你深入理解ultralytics最新作品yolov11的创新 | 训练、推理、验证、导出 (附网络结构图)基础上做了一些补充。 一、YOLOv11和YOLOv8对比 二、YOLOv11的网络结构图 下面的图片为YOLOv11的网络结构图。 三、YOLOv11…...

弃用RestTemplate,RestClient真香!
在Spring框架的发展历程中,RestTemplate作为发起HTTP请求的同步API,曾经扮演着举足轻重的角色。然而,随着技术的不断进步和微服务架构的普及,RestTemplate的局限性逐渐显现,尤其是在处理高并发和异步请求时。因此&…...

electron-vite_10electron-updater软件更新
网很多electron-updater更新文章,这里只简单写一下演示代码; 为什么选择 electron-updater插件可以自动更新应用程序,同时支持多个平台;比官方要强; 官方的autoUpdater仅支持macOS 和 Windows 自动更新; 注意是自动,直接更新那种; 脚手架中是…...

React native之全局变量存储AsyncStorage
AsyncStorage是React native中对变量,对象进行全局存储,读取的异步使用对象。以key值进行存储。但是只能存储字符串数据,想存储对象,可把对象JSON进行序列化存储,读取的时候再转成JSON对象。 AsyncStorage.getItem()-…...

获取vue实例
需要注意的是,无论通过哪种方式获取元素,如果元素为 vue 组件,则需要在子组件中使用 defineExpose 进行暴露。 在父组件中,我们静态绑定 childRef: 在子组件中,我们需要通过defineExpose函数,手…...

基于Python实现电影推荐系统
电影推荐系统 标签:Tensorflow、矩阵分解、Surprise、PySpark 1、用Tensorflow实现矩阵分解 1.1、定义one_batch模块 import numpy as np import pandas as pddef read_and_process(filename, sep ::):col_names [user, item, rate, timestamp]df pd.read_cs…...

【linux】进程理解
🔥个人主页:Quitecoder 🔥专栏:linux笔记仓 目录 01.进程的基本概念进程的组成部分进程的特性进程的状态 02.PCBPCB的组成部分task_structtask_struct 的主要组成部分 03.进程属性查看进程 04.通过系统调用创建进程-fork初识工作…...

文件IO练习1
题目一: 1、使用fread和fwrite完成两个文件的拷贝,要求源文件和目标文件由外界输入 实现代码: #define LEN_BUF 256int main(int argc, const char *argv[]) {if(argc ! 3){fprintf(stderr,"程序入参输入有误\n");return -1;}FILE…...