zookeeper实现RMI服务,高可用,HA

news2024/11/30 10:34:08

这可不是目录

  • 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");
        //定义url
        String 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 {
    }

    @Override
    public 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());
    }
}

运行说明

  1. 先启动RmiServer,注册远程对象
  2. 再启动RmiCllient,查找此对象,并调用远程方法
    在这里插入图片描述

1.4RMI的局限性

  1. 只能使用Java,不支持跨语言
  2. RMI使用了Java默认的序列化方式,对于要求较高的系统,可能需要其他的序列化方案进行解决(Protobuf)
  3. 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 {
    }

    @Override
    public 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.HelloServiceImpl
            url = 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方法
                @Override
                public 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运行与部署

  1. 建议部署于虚拟机上
  2. 为测试zookeeper效果,请将服务端部署于不同的zookeeper节点上
  3. 客户端可以运行于非zookeeper集群的主机
  4. 上述代码中缺少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服务供客户端使用
客户端选择
在上述代码中,设置的时随机选择,这个不重要,自行设置即可

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

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

相关文章

Spring Boot: 构建高效中小型医院网站

1 绪论 1.1研究背景 随着计算机技术的成熟、普及&#xff0c;现代信息技术革命的迅猛发展,正冲击并进而改变着经济和社会结构。信息化的程度已经成为一个国家&#xff0c;一个企业&#xff0c;一个组织仍至一个人发展的基础和竞争成败的关键。 在实际的生活中&#xff0c;用户都…

软件评测CNAS资质获取流程

软件评测实验室如有意向申请 CNAS 检验机构认可&#xff0c;首先需要依据 CNAS 的认可准则建立管理体系&#xff0c;正式运行6个月以上&#xff0c;自我评估满足 CNAS 认可条件后可向 CNAS 提交申请。软件评测实验室CNAS认可的整体流程如图所示&#xff0c;后面的内容针对每个环…

数据结构之单链表详解:从原理到C语言实现

一、 什么是单链表&#xff1f; 单链表&#xff08;Singly Linked List&#xff09;是一种线性数据结构&#xff0c;它的特点是每个节点通过指针链接到下一个节点。不同于顺序表&#xff08;数组&#xff09;&#xff0c;链表的每个元素&#xff08;节点&#xff09;并不存储在…

【简单版】通过 Window.performance 实现前端页面(性能)监控

1 背景 前端监控系统告警xx接口fetchError 问题&#xff1a;前端监控系统没有更多的错误信息&#xff0c;查询该fetch请求对应的接口日志返回200状态码、无请求异常记录&#xff0c;且后台能查到通过该fetch请求成功发送的数据。那是前端页面的错误还是前端监控系统的问题&…

yjs机器学习常见算法01——KNN(1)(K—近邻算法)

1.K—近邻算法 的含义&#xff1a; 简单来说就是通过你的邻居的“类别”&#xff0c;来推测你的“类别” 定义&#xff1a;如果一个样本在特征空间中的k个最相似&#xff08;即特征空间中最临近&#xff09;的样本中大多数属于某一类别&#xff0c;则该样本也属于这个类别。 2.…

【Python爬虫系列】_028.Python玩Redis

课 程 推 荐我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)教程合集 👈👈

Redis原理篇之网络模型

Redis原理篇之网络模型 文章目录 Redis原理篇之网络模型1 用户空间和内核空间2 阻塞IO3 非阻塞IO4 IO多路复用4.1 IO多路复用-select4.2 IO多路复用-poll4.3 IO多路复用-epoll4.4 总结 5 信号驱动IO6 异步IO7 同步和异步8 Redis网络模型8.1 Redis是单线程吗&#xff1f;为什么要…

基于Opencv中的DNN模块实现图像/视频的风格迁移

一、DNN模块的介绍 1、简介 OpenCV中的DNN&#xff08;Deep Neural Network&#xff09;模块是一个功能强大的组件&#xff0c;它支持深度学习网络模型的加载和推理。虽然DNN模块不提供模型的训练功能&#xff0c;但它可以与主流的深度学习框架&#xff08;如TensorFlow、Caf…

tigeR免疫治疗数据分析工具学习和整理

tigeR整合了多个肿瘤的数据集&#xff0c;用于探索生物标志物和构建预测免疫治疗反应模型。 该工具内置了 11 个黑色素瘤数据集、3 个肺癌数据集、2 个肾癌数据集、1 个胃癌数据集、1 个低级别胶质瘤数据集、1 个胶质母细胞瘤数据集和 1 个头颈鳞状细胞癌数据集的 1060 例具有…

网络资源模板--Android Studio 实现简易新闻App

目录 一、项目演示 二、项目测试环境 三、项目详情 四、完整的项目源码 一、项目演示 网络资源模板--基于Android studio 实现的简易新闻App 二、项目测试环境 三、项目详情 登录页 用户输入&#xff1a; 提供账号和密码输入框&#xff0c;用户可以输入登录信息。支持“记…

2022年10月自考《操作系统概论》02323试题

目录 一.选择题 二.填空题 三.简答题 四.综合体 一.选择题 1.以下各种操作系统中&#xff0c;对可靠性要求最高的是 &#xff08;书中&#xff09;P25页 A.分时操作系统 B.实时操作系统 C.多道批处理系统 D.单道批处理系统 2.一个进程正常执行完毕时&#xff0c;需要对其…

简述光密度仪日常中的用途及光密度测量方法

光密度仪在日常中的用途 光密度仪在众多领域发挥着重要作用。在医疗领域&#xff0c;它常用于检测生物样本中的物质浓度&#xff0c;如血液中特定成分的含量测定。在化学分析中&#xff0c;可精确测量溶液的浓度&#xff0c;为实验和研究提供准确数据。在工业生产中&#xff0…

go+bootstrap实现简单的注册登录和管理

概述 使用&#xff0c;gomysql实现了用户的登录&#xff0c;注册&#xff0c;和管理的简单功能&#xff0c;不同用户根据不同权限显示不同的内容 实战要求&#xff1a; 1、用户可以注册、登录&#xff1b; 2、登录后可以查看所有的注册的用户&#xff1b; 3、管理员操作对用…

PHP(一)从入门到放弃

参考文献&#xff1a;https://www.php.net/manual/zh/introduction.php PHP 是什么&#xff1f; PHP&#xff08;“PHP: Hypertext Preprocessor”&#xff0c;超文本预处理器的字母缩写&#xff09;是一种被广泛应用的开放源代码的多用途脚本语言&#xff0c;它可嵌入到 HTML…

Qt/C++编写的mqtt调试助手使用说明

一、使用说明 第一步&#xff0c;选择协议前缀&#xff0c;可选mqtt://、mqtts://、ws://、wss://四种&#xff0c;带s结尾的是走ssl通信&#xff0c;ws表示走websocket通信。一般选默认的mqtt://就好。第二步&#xff0c;填写服务所在主机地址&#xff0c;可以是IP地址也可以…

使用LSPatch+PlusNE修改手机软件

一、问题概述 国内使用一些软件&#xff0c;即使科学上网&#xff0c;打开都是网络错误&#xff0c;更换节点同样如此。 二、软件下载 通过官网或者正规商店(如Google play)下载并且安装。 是的&#xff0c;先要下载一个无法使用的版本&#xff0c;后续对其进行修改。 三、下…

代码随想录(七) —— 二叉树部分

1. 二叉树的四种遍历方式的理解 前序遍历&#xff0c;中序遍历&#xff0c;后序遍历&#xff1b;层次遍历 结合另一篇博客&#xff0c;关于灵神的题单刷题 二叉树刷题记录-CSDN博客 理解&#xff1a; 在二叉树类型题目中&#xff0c;遍历顺序的选择需要根据具体问题来确定…

算法笔记day04

目录 1. 在字符串中找出连续最长的数字串 2.岛屿数量 3.拼三角 1. 在字符串中找出连续最长的数字串 字符串中找出连续最长的数字串_牛客题霸_牛客网 (nowcoder.com) 算法思路&#xff1a; 这是一道简单的双指针题目&#xff0c;首先用i遍历数组&#xff0c;当遍历到数字的时…

春日技术辅导:Spring Boot课程答疑

3系统分析 3.1可行性分析 通过对本课程答疑系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本课程答疑系统采用JAVA作为开发语言&#xff0c;Spring Boot框…

数据驱动,漫途能耗管理系统打造高效节能新生态!

在我国能源消耗结构中&#xff0c;工业企业所占能耗比例相对较大。为实现碳达峰、碳中和目标&#xff0c;工厂需强化能效管理&#xff0c;减少能耗与成本。高效的能耗管理系统通过数据采集与分析&#xff0c;能实时监控工厂能源使用及报警情况&#xff0c;为节能提供数据。构建…