手写RPC框架

RPC框架核心组件

对于RPC框架简洁模式下,主要有以下角色,暂且抛开心跳机制以及负载均衡等复杂策略,我们先来自己实现一个RPC框架,后面我们再深入理解。
在这里插入图片描述

注册中心

RegisterServiceVo

package com.cover.rpc.remote.vo;

import java.io.Serializable;

// 注册中心注册服务的实体类
public class RegisterServiceVo implements Serializable {

    // 服务提供者的ip地址
    private final String host;
    
    // 服务提供者的端口
    private final int port;
    
    public RegisterServiceVo(String host, int port) {
        this.host = host;
        this.port = port;
    }
    
    public String getHost() {
        return host;
    }
    
    public int getPort() {
        return port;
    }
}

RegisterCenter

package com.cover.rpc.rpc.reg.service;

import org.springframework.stereotype.Component;
import com.cover.rpc.remote.vo.RegisterServiceVo;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

// 服务注册中心,服务提供者在启动时需要注册中心登记自己的信息
@Component
public class RegisterCenter {
    // key表示服务名,value代表
    private static final Map<String, Set<RegisterServiceVo>> serviceHolder 
            = new HashMap<>();
    // 注册服务端口号
    private int port;

    /**
     * 服务注册,考虑到可能有多个提供者同时注册,进行加索
     */
    private static synchronized void registerService(String serviceName, String host, int port) {
        // 获得当前服务的已有地址集合
        Set<RegisterServiceVo> serviceVoSet = serviceHolder.get(serviceName);
        if (serviceVoSet == null) {
            // 已有地址集合为空,新增集合
            serviceVoSet = new HashSet<>();
            serviceHolder.put(serviceName, serviceVoSet);
        }
        
        // 将新的服务提供者加入集合
        serviceVoSet.add(new RegisterServiceVo(host,port));

        System.out.println("服务已注册[" + serviceName + "]," + "地址[ " + host + "], 端口[" + port + "]" );
    }


    /**
     * 取出服务提供者
     */
    private static Set<RegisterServiceVo> getService(String serviceName) {
        return serviceHolder.get(serviceName);
    }

    /**
     * 处理服务请求的任务,无非就是两种服务
     * 1.服务注册服务
     * 2.服务查询服务
     */
    private static class ServerTask implements Runnable {

        private Socket client = null;
        
        
        public ServerTask(Socket client) {
            this.client = client;
        }
        @Override
        public void run() {
            try (
                    ObjectInputStream inputStream = new ObjectInputStream(client.getInputStream());
                    ObjectOutputStream outputStream = new ObjectOutputStream(client.getOutputStream())
            ) {
                
                // 检查当前请求是注册服务还是获取服务
                boolean isGetService = inputStream.readBoolean();
                // 服务查询服务,获取服务提供者
                if (isGetService) {
                    String serviceName = inputStream.readUTF();
                    // 取出服务提供者
                    Set<RegisterServiceVo> result = getService(serviceName);
                    // 返回给客户端
                    outputStream.writeObject(result);
                    outputStream.flush();
                    System.out.println("将已注册的服务[" + serviceName + "提供给客户端");
                } else {
                    // 服务注册服务
                    //取得新服务提供方的ip和端口
                    String serviceName = inputStream.readUTF();
                    String host = inputStream.readUTF();
                    int port = inputStream.readInt();
                    // 注册中心保存
                    registerService(serviceName, host, port);
                    outputStream.writeBoolean(true);
                    outputStream.flush();

                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
//                    throw new RuntimeException(e);
                }
            }
        }
    }

    // 启动注册服务
    public void startService() throws IOException {
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(port));
        System.out.println("服务注册中心 on :" + port + ": 运行");
        try {
            while (true) {
                new Thread(new ServerTask(serverSocket.accept())).start();
            }
        } finally {
            serverSocket.close();
        }
    }

    @PostConstruct
    public void init() {
        this.port = 9999;
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    startService();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

文件结构
在这里插入图片描述

服务提供者

UserInfo

package com.cover.rpc.remote.vo;

import java.io.Serializable;

public class UserInfo implements Serializable {

    private final String name;
    
    private final String phone;
    
    public UserInfo(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }

    public String getName() {
        return name;
    }

    public String getPhone() {
        return phone;
    }
}

SendSms

package com.cover.rpc.remote;


import com.cover.rpc.remote.vo.UserInfo;

// 短信息发送接口
public interface SendSms {
    
    boolean sendMail(UserInfo userInfo);
}

RegisterServiceWithRegCenter

package com.cover.rpc.rpc.base;

import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

// 注册服务,引入了服务的注册和发现机制
@Service
public class RegisterServiceWithRegCenter {
    
    // 本地提供服务的一个名单,用缓存实现
    private static final Map<String, Class> serviceCache = new ConcurrentHashMap<>();
    
    // 往远程注册服务器注册本服务,同时在本地注册本服务
    public void regRemote(String serviceName, String host, int port, Class impl) throws IOException {
        // 登记到注册中心
        Socket socket = null;
        ObjectOutputStream output = null;
        ObjectInputStream input = null;
        try {
            socket = new Socket();
            socket.connect(new InetSocketAddress("127.0.0.1", 9999));
            
            output = new ObjectOutputStream(socket.getOutputStream());
            // 注册服务
            output.writeBoolean(false);
            // 提供的服务名
            output.writeUTF(serviceName);
            // 服务提供方的IP
            output.writeUTF(host);
            // 服务提供方的端口
            output.writeInt(port);
            
            output.flush();
            
            input = new ObjectInputStream(socket.getInputStream());
            if (input.readBoolean()) {
                System.out.println("服务[" + serviceName + "]注册成功!");
            }
            
            // 可提供服务放入本地缓存
            serviceCache.put(serviceName, impl);
        } catch (Exception e) {
            e.printStackTrace();
            if (socket != null) {
                socket.close();
            }
            
            if (output != null) {
                output.close();
            }
            
            if (input != null) {
                input.close();
            }
        }
        
    }
    
    
    // 获取服务
    public Class getLocalService(String serviceName) {
        return serviceCache.get(serviceName);
    }
}

RpcServerFrame

package com.cover.rpc.rpc.base;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

// RPC框架的服务端部分
@Service
public class RpcServerFrame {
    
    @Autowired
    private RegisterServiceWithRegCenter registerServiceWithRegCenter;
    
    // 服务的端口号
    private int port;
    
    
    // 处理服务请求任务
    private static class ServerTask implements Runnable {

        private Socket socket;
        
        private RegisterServiceWithRegCenter registerServiceWithRegCenter;
        
        public ServerTask(Socket client, RegisterServiceWithRegCenter registerServiceWithRegCenter) {
            this.socket = client;
            this.registerServiceWithRegCenter = registerServiceWithRegCenter;
        }
        
        @Override
        public void run() {
            try (
                    ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
                    ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream())
            ) {

                // 方法所在类名接口名
                String serviceName = inputStream.readUTF();
                // 方法的名字
                String methodName = inputStream.readUTF();
                // 方法的入参  类型
                Class<?>[] paramTypes = (Class<?>[]) inputStream.readObject();
                // 方法的入参的值
                Object[] args = (Object[]) inputStream.readObject();

                // 从容器中拿到服务的Class对象
                Class serviceClass = registerServiceWithRegCenter.getLocalService(serviceName);
                if (serviceClass == null) {
                    throw new ClassNotFoundException(serviceName + "not found");
                }

                // 通过反射,执行实际的服务
                Method method = serviceClass.getMethod(methodName, paramTypes);
                Object result = method.invoke(serviceClass.newInstance(), args);

                // 将服务的执行结果通知调用者
                outputStream.writeObject(result);
                outputStream.flush();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public void startService(String serviceName, String host, int port, Class impl) throws IOException {
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(port));
        System.out.println("RPC server on :" + port + ":运行");
        registerServiceWithRegCenter.regRemote(serviceName, host, port, impl);
        try {
            while (true) {
                new Thread(new ServerTask(serverSocket.accept(), registerServiceWithRegCenter)).start();
            }
        } finally {
            serverSocket.close();
        }
    }
}

SendSmsImpl

package com.cover.rpc.rpc.sms;

import com.cover.rpc.remote.SendSms;
import com.cover.rpc.remote.vo.UserInfo;

public class SendSmsImpl implements SendSms {
    @Override
    public boolean sendMail(UserInfo userInfo) {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("已发送短信息给 :" + userInfo.getName() + "到[" + userInfo.getPhone());
        return true;
    }
}

SmsRpcServer

package com.cover.rpc.rpc.sms;

import com.cover.rpc.remote.SendSms;
import com.cover.rpc.rpc.base.RpcServerFrame;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Random;

@Service
public class SmsRpcServer {
    
    @Autowired
    private RpcServerFrame rpcServerFrame;
    
    @PostConstruct
    public void server() throws IOException {
        Random r = new Random();
        int port = 8778 + r.nextInt(100);
        rpcServerFrame.startService(SendSms.class.getName(), "127.0.0.1", port, SendSmsImpl.class);
    }
}

文件结构
在这里插入图片描述

服务消费者

BeanConfig

package com.cover.rpc.client.config;

import com.cover.rpc.client.rpc.RpcClientFrame;
import com.cover.rpc.remote.SendSms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class BeanConfig {
    
    @Autowired
    private RpcClientFrame rpcClientFrame;

    @Bean
    public SendSms getSmsService() throws IOException, ClassNotFoundException {
        return rpcClientFrame.getRemoteProxyObject(SendSms.class);
    }

}

RpcClientFrame

package com.cover.rpc.client.rpc;

import com.cover.rpc.remote.vo.RegisterServiceVo;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;

// RPC框架的客户端代理部分
@Service
public class RpcClientFrame {

    // 远程服务的代理对象,参数为客户端要调用的服务
    public static <T> T getRemoteProxyObject(final Class<?> serviceInterface) throws IOException,
            ClassNotFoundException {
        // 获取远程服务的一个网络地址
        InetSocketAddress addr = getService(serviceInterface.getName());
        // 拿到一个代理对象,由这个代理对象通过网络进行实际的服务调用    
        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface},
                new DynProxy(serviceInterface, addr));
    }


    // 动态代理,实现对远程服务的访问
    private static class DynProxy implements InvocationHandler {

        private Class<?> serviceInterface;

        private InetSocketAddress addr;

        public DynProxy(Class<?> serviceInterface, InetSocketAddress addr) {
            this.serviceInterface = serviceInterface;
            this.addr = addr;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            Socket socket = null;
            ObjectInputStream inputStream = null;
            ObjectOutputStream outputStream = null;
            try {
                socket = new Socket();

                socket.connect(addr);
                outputStream = new ObjectOutputStream(socket.getOutputStream());

                // 方法所在类名接口名
                outputStream.writeUTF(serviceInterface.getName());
                // 方法名
                outputStream.writeUTF(method.getName());
                // 方法入参类型
                outputStream.writeObject(method.getParameterTypes());
                // 方法入参的值
                outputStream.writeObject(args);

                outputStream.flush();

                inputStream = new ObjectInputStream(socket.getInputStream());
                // 接受服务器的输出
                System.out.println(serviceInterface + " remote exec susccess!");
                return inputStream.readObject();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (socket != null) {
                    socket.close();
                }

                if (outputStream != null) {
                    outputStream.close();
                }

                if (inputStream != null) {
                    inputStream.close();
                }
            }

            return null;
        }


    }

    //------------------------------以下和动态获得服务提供者有关
    private static Random r = new Random();

    // 获取远程服务的地址
    private static InetSocketAddress getService(String serviceName) throws IOException, ClassNotFoundException {
        // 获得服务提供者的地址列表
        List<InetSocketAddress> serviceList = getServiceList(serviceName);
        System.out.println("serviceList =" + serviceList.toString());
        InetSocketAddress addr = serviceList.get(r.nextInt(serviceList.size()));
        System.out.println("本次选择了服务器: " + addr);
        return addr;
    }

    private static List<InetSocketAddress> getServiceList(String serviceName) throws IOException,
            ClassNotFoundException {
        Socket socket = null;
        ObjectOutputStream output = null;
        ObjectInputStream input = null;
        List<InetSocketAddress> services = new ArrayList<>();

        try {
            socket = new Socket();
            socket.connect(new InetSocketAddress("127.0.0.1", 9999));

            output = new ObjectOutputStream(socket.getOutputStream());
            // 需要获得服务提供者
            output.writeBoolean(true);
            // 告诉注册中心服务名
            output.writeUTF(serviceName);
            output.flush();

            input = new ObjectInputStream(socket.getInputStream());
            Object object = input.readObject();
            System.out.println("从注册中心读取到的数据是" + object.toString());
            Set<RegisterServiceVo> result = (Set<RegisterServiceVo>) object;
            for (RegisterServiceVo serviceVo : result) {
                // 获取服务提供者
                String host = serviceVo.getHost();
                int port = serviceVo.getPort();
                InetSocketAddress serviceAddr = new InetSocketAddress(host, port);
                services.add(serviceAddr);
            }

            System.out.println("获得服务[" + serviceName + "] 提供者的地址列表[" + services + "],准备调用");

            return services;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                socket.close();
            }

            if (output != null) {
                output.close();
            }

            if (input != null) {
                input.close();
            }
        }

        return services;
    }
}

NormalBusi

package com.cover.rpc.client.service;

import org.springframework.stereotype.Service;

/**
 * @author xieh
 * @date 2024/02/03 17:46
 */
@Service
public class NormalBusi {
    public void business() {
        System.out.println("其他的业务操作。。。。");
    }
}

RegisterServiceVo

package com.cover.rpc.remote.vo;

import java.io.Serializable;

// 注册中心注册服务的实体类
public class RegisterServiceVo implements Serializable {

    // 服务提供者的ip地址
    private final String host; 
    
    // 服务提供者端口
    private final int port;
    
    public RegisterServiceVo (String host, int port) {
        this.host = host;
        this.port = port;
    }

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }
}

UserInfo

package com.cover.rpc.remote.vo;

import java.io.Serializable;

/**
 * @author xieh
 * @date 2024/02/03 17:48
 */
public class UserInfo implements Serializable {

    private final String name;

    private final String phone;

    public UserInfo(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }

    public String getName() {
        return name;
    }

    public String getPhone() {
        return phone;
    }
}

SendSms

package com.cover.rpc.remote;

import com.cover.rpc.remote.vo.UserInfo;

/**
 * @author xieh
 * @date 2024/02/03 17:47
 */
// 短信发送接口
public interface SendSms {
    boolean sendMail(UserInfo userInfo);
}

RpcClientApplicationTests

package com.example.rpcclient;

import com.cover.rpc.client.service.NormalBusi;
import com.cover.rpc.remote.SendSms;
import com.cover.rpc.remote.vo.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class RpcClientApplicationTests {

    @Test
    void contextLoads() {
    }



    @Autowired
    private NormalBusi normalBusi;

    @Autowired
    private SendSms sendSms;

//    @Test
//    void contextLoads() {
//
//    }

    @Test
    public void rpcTest() {
        long start = System.currentTimeMillis();
        normalBusi.business();

        // 发送邮件
        UserInfo userInfo = new UserInfo("Cover", "181");
        System.out.println("Send mail" + sendSms.sendMail(userInfo));
        System.out.println("共耗时:" + (System.currentTimeMillis() - start));
    }
}

文件结构
在这里插入图片描述

结果展示

服务注册中心
在这里插入图片描述

服务消费者
在这里插入图片描述
服务提供者
在这里插入图片描述

总结分析

1.我们在当前项目中的序列化框架选择是JDK自带的序列化,注意,你这个时候不能给上面提到的实体类添加唯一的serialId,否则通信过程中则将视为不一样的对象,导致序列化失败,还有就是要注意自己的目录结构,因为如果客户端和服务端中的实体类目录结构不一样,也是不行的,在实际业务中,往往会抽成一个公共的服务来使用,这里为了简洁
2.网络通信模型采用的也是JDK自带的Socket模式,它是阻塞式的,它式无法支撑高并发的网络连接的

如果需要这个项目源码的,可以在下方评论

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

J6 - ResNeXt50模型的实现

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 环境代码在之前的章节都有&#xff0c;我后续只贴出模型设计构建过程如下打印模型结构打印参数量 训练过程与结果总结 环境 系统: L…

聊聊并发编程,另送5本Golang并发编程新书

大家好&#xff0c;我是飞哥&#xff01; 并发编程并不是一个新话题&#xff0c;但是我觉得在近几年以及未来的时间里&#xff0c;并发编程将显得越来越重要。 为什么这样讲&#xff0c;让我们先回到一个基本的问题上来&#xff0c;为什么我们要采用并发编程&#xff1f;关于这…

数据分析基础之《pandas(2)—基本数据操作》

一、读取一个真实的股票数据 1、读取数据 # 基本数据操作 data pd.read_csv("./stock_day.csv")data# 删除一些列&#xff0c;使数据简洁点 data data.drop([ma5,ma10,ma20,v_ma5,v_ma10,v_ma20], axis1)data 二、索引操作 1、numpy当中我们已经讲过使用索引选取…

Unity 读取指定目录所占内存大小

public static class TxxTool{#region 读取文件大小private static List<string> DirList new List<string>();public static long GetFileSize(string path){DirList new List<string>();DirList.Add(path);GetAllDirecotries(path);long fileSize 0;for…

蓝桥杯省赛无忧 课件87 01tire

01 算法概述 02 问题引入 03 算法分析 04 例题

ReactNative实现的横向滑动条

OK,我们先看下效果图 注意使用到了两个库 1.react-native-linear-gradient 2.react-native-gesture-handler ok,我们看下面的代码 import {Image, TouchableWithoutFeedback, StyleSheet, View} from react-native; import LinearGradient from react-native-linear-grad…

开源软件:技术创新与应用的推动力量

文章目录 每日一句正能量前言开源软件如何推动技术创新开源软件的历史开源软件的开发模式开源软件与闭源软件源代码和开发许可维护特点、支持和成本开源软件的优势减少开支可定制性快速创新发展透明度和安全性 开源软件的应用 常见问题后记 每日一句正能量 不好等待运气降临&am…

vue3-自定义指令

自定义指令 vue 除了内置的制指令&#xff08;v-model v-show 等&#xff09;之外&#xff0c;还允许我们注册自定义的指令。 vue 复用代码的方式&#xff1a; 组件&#xff1a;主要是构建模块。 组合式函数&#xff1a;侧重有状态的逻辑。 自定义指令&#xff1a;主要是为…

大模型ReAct提示工程详解【2023】

普林斯顿大学的教授和谷歌的研究人员最近发表了一篇论文&#xff0c;描述了一种新颖的提示工程方法&#xff0c;该方法使大型语言模型&#xff08;例如 ChatGPT&#xff09;能够在模拟环境中智能地推理和行动。 这种 ReAct 方法模仿了人类在现实世界中的运作方式&#xff0c;即…

欧洲的编程语言三巨头,只剩下一位了!

能把三位大牛的名字都叫出来的人恐怕不多吧&#xff1a; 这三位都是图灵奖获得者&#xff0c;他们的名字和发明散布在各种教科书中&#xff0c;从左到右&#xff0c;依次是&#xff1a; 尼克劳斯沃斯 (Niklaus Wirth) 瑞士人&#xff0c;一生发明了8种编程语言&#xff0c;其中…

CTF赛三层内网渗透

CTF赛三层内网渗透 前言 2019某CTF线下赛真题内网结合WEB攻防题库&#xff0c;涉及WEB攻击&#xff0c;内网代理路由等技术&#xff0c;每台服务器存在Flag&#xff0c;获取一个Flag对应一个积分&#xff0c;获取三个Flag结束。 第一关 Taget1_centos7 1、访问目标网页 发现…

AIGC 为何能火爆全网,赋能智能时代?

Hi&#xff0c;大家好&#xff0c;我是半亩花海。2023年&#xff0c;人工智能新浪潮涌起&#xff0c;AIGC 火爆全网&#xff0c;不断赋能各大行业。从短视频平台上火爆的“AI 绘画”&#xff0c;到智能聊天软件 ChatGPT&#xff0c;都引起了大家的广泛关注。那么 AIGC 到底是什…

浙政钉访接口:k8s+slb容器日志报错(:Temporary failure in name resolution。)

在此我只能说兄弟&#xff0c;浙政钉的扫码接口和用户详情返回这两个接口是不需要白名单的&#xff0c; 我们文明人先确定一件事就是&#xff0c;你代码本地能调用到浙政钉返回。ecs服务器curl浙政钉也通的&#xff1a; 这时候和你说要开通白名单的&#xff0c;请放开你的道德…

r0下进程保护

简介 SSDT 的全称是 System Services Descriptor Table&#xff0c;系统服务描述符表。这个表就是一个把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系起来。SSDT 并不仅仅只包含一个庞大的地址索引表&#xff0c;它还包含着一些其它有用的信息&#xff0c;诸如地址索引的基地…

如何强制关掉系统或应用程序?这里提供详细方法

总的来说,Windows相当可靠,但有时会挂断并崩溃。我们如何在最少麻烦的情况下重返工作或游戏?为此,我们需要强制退出操作系统。 在本教程中,我们将向你展示如何在最坏的情况下安全关闭或重新启动计算机。我们还将向你展示如何在不触摸鼠标的情况下强制关闭应用程序和快速关…

【51单片机】开发板和单片机的介绍(2)

前言 大家好吖&#xff0c;欢迎来到 YY 滴单片机系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过单片机的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

挖矿系列:细说Python、conda 和 pip 之间的关系

继续挖矿&#xff0c;挖金矿&#xff01; 1. Python、conda 和 pip Python、conda 和 pip 是在现代数据科学和软件开发中常用的工具&#xff0c;它们各自有不同的作用&#xff0c;但相互之间存在密切的关系&#xff1a; Python&#xff1a;是一种解释型、面向对象的高级程序设…

【数据集】全国地级市-平均受教育年限-男、女数据集(2000-2020年)

平均受教育年限用以衡量地区的人力资本&#xff0c;指对一定时期、一定区域某一人口群体接受学历教育的年数总和的平均值。参考陈熠辉&#xff08;2023&#xff09;等人的计算方式&#xff0c;根据第五次人口普查、第六次人口普查、第七次人口普查结果整理了地级市的平均受教育…

CentOS下安装vlc

一、引言 vlc是一跨多媒体播放器&#xff0c;可以播放本地媒体文件和网络串流&#xff0c;帮助我们排查音视频开发过程中遇到的问题。大部分情况下&#xff0c;我们只需要在Windows系统下安装vlc就可以了。但有一种情况是需要在Linux下安装vlc的&#xff1a;我们的音视频拉流软…

2024美赛C题完整解题教程及代码 网球运动的势头

2024 MCM Problem C: Momentum in Tennis &#xff08;网球运动的势头&#xff09; 注&#xff1a;在网球运动中&#xff0c;"势头"通常指的是比赛中因一系列事件&#xff08;如连续得分&#xff09;而形成的动力或趋势&#xff0c;这可能对比赛结果产生重要影响。球…