仿写一个tomcat(含线程池配置)超详细!!

news2024/11/23 3:29:08

目录

工作原理

整体项目结构

自定义注解

创建servlet类

创建启动类

线程池配置

测试阶段


工作原理

首先看流程图,搞清楚tomcat的工作原理

工作原理如下:

  1. Tomcat使用一个叫作Catalina的核心组件来处理HTTP请求和响应。Catalina包含了一个HTTP连接器(Connector),负责监听和接收客户端请求。

  2. 当接收到一个HTTP请求时,Tomcat会解析请求头和请求体,提取出请求的URL、请求方法、请求参数等信息。

  3. Tomcat中的Servlet容器负责管理Servlet的生命周期和处理Servlet请求。Servlet是以Java类的形式编写的服务器端程序,用于处理动态内容。

  4. 在Tomcat中,Servlet类需要使用注解(如@WebServlet)进行标记,以指定Servlet的URL映射和其他配置信息。

  5. 当Tomcat启动时,它会扫描应用程序中包含的所有类,找到带有Servlet注解的类,并将它们的URL映射和全类名存储在一个映射表中。

  6. 当接收到请求时,Tomcat会根据请求的URL在映射表中查找对应的Servlet类。利用反射机制,Tomcat可以实例化Servlet类并调用其相应的方法来处理请求。

  7. 处理请求的Servlet可以通过请求对象(HttpServletRequest)获取请求的URL、参数、请求头等信息,并通过响应对象(HttpServletResponse)来生成响应内容和设置响应头。

  8. Tomcat使用Socket与浏览器进行通信,通过监听在8080端口(默认)来接收客户端的HTTP请求。

人话:它是利用注解对servlet类进行标记,之后将它标记中的请求地址和全类名存入map中,利用反射拿到类信息,调用类的方法,利用socket和浏览器交互,监听8080端口,之后将请求信息拿到,处理其中的信息拿到请求url和参数信息

整体项目结构

自定义注解

需要用到java中的元注解实现,如果不清楚的小伙伴可以参考这一篇:

java 元注解||自定义注解的使用_ADRU的博客-CSDN博客

import java.lang.annotation.*;

/**
 * @author 小如
 *
 * @date 2023/08/15
 */

/** 请求映射该注解可以应用于类、接口(包括注解类型)、枚举*/
@Target(ElementType.TYPE)
/**该注解标记的元素可以被Javadoc 或类似的工具文档化*/
@Documented
/**该注解的生命周期,由JVM 加载,包含在类文件中,在运行时可以被获取到*/
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMap {
    String path() default "";
}

创建servlet类

1.创建接收参数信息类

import java.util.HashMap;

/**
 * http请求参数仿写
 *
 * @author 小如
 * @date 2023/08/15
 */
public class HttpRequestDemo {

    public HashMap<String, String> map = new HashMap<>();

    public String getParameter(String key) {
        return map.get(key);
    }

}
import java.io.OutputStream;

public class HttpResponseDemo {

    public OutputStream outputStream;

    public static final String responsebody="HTTP/1.1 200+\r\n"+"Content-Type:text/html+\r\n"
            +"\r\n";
    public HttpResponseDemo(OutputStream outputStream){
        this.outputStream=outputStream;
    }

}

2.自定义两个servlet类(模拟请求处理)


import Tools.HttpRequestDemo;
import Tools.HttpResponseDemo;
import annotation.RequestMap;
import java.io.IOException;

/**
 * 请求servlet
 *
 * @author 小如
 * @date 2023/08/15
 */
@RequestMap(path = "hello")
public class RequestServlet {

    public void doGet(HttpRequestDemo request, HttpResponseDemo response) throws IOException {
        System.out.println("hello GET响应:");
        System.out.println("a="+request.getParameter("a"));
        String resp= HttpResponseDemo.responsebody+"<!DOCTYPE html>\n" +
                "<html>\n" +
                "<head>\n" +
                "    <meta charset=\"utf-8\" />\n" +
                "<link rel=\"icon\" href=\"data:image/ico;base64,aWNv\">\n"+
                "</head>\n" +
                "<body>\n" +
                " \n" +
                "    <form name=\"my_form\" method=\"POST\">\n" +
                "        <input type=\"button\" value=\"按下\" onclick=\"alert('你按下了按钮')\">\n" +
                "    </form>\n" +
                " \n" +
                "</body>\n" +
                "</html>";
        response.outputStream.write(resp.getBytes());
        response.outputStream.flush();
        response.outputStream.close();

    }

    public void doPost(HttpRequestDemo request,HttpResponseDemo response) throws IOException {
        System.out.println("\n响应的http如下:");
        String resp= HttpResponseDemo.responsebody+
                "{\"sorry\":\"we only respond to method GET now\"},\r\n"+
                "";
        System.out.println(resp);
        response.outputStream.write(resp.getBytes());
        response.outputStream.flush();
        response.outputStream.close();
    }

}
import Tools.HttpRequestDemo;
import Tools.HttpResponseDemo;
import annotation.RequestMap;

import java.io.IOException;

/**
 * 请求servlet
 *
 * @author 小如
 * @date 2023/08/15
 */
@RequestMap(path = "nihao")
public class RequestServlet2 {

    public void doGet(HttpRequestDemo request, HttpResponseDemo response) throws IOException {
        System.out.println("nihao GET响应:");
        System.out.println("a="+request.getParameter("a"));

        String resp= HttpResponseDemo.responsebody+
                "{\"success\":\"200\"},\r\n"+
                "";
        response.outputStream.write(resp.getBytes());
        response.outputStream.flush();
        response.outputStream.close();

    }

    public void doPost(HttpRequestDemo request,HttpResponseDemo response) throws IOException {
        System.out.println("\n响应的http如下:");
        String resp= HttpResponseDemo.responsebody+
                "{\"sorry\":\"we only respond to method GET now\"},\r\n"+
                "";
        System.out.println(resp);
        response.outputStream.write(resp.getBytes());
        response.outputStream.flush();
        response.outputStream.close();
    }
}

3.定义一个异常servlet,当请求异常时,打到这个servlet

import Tools.HttpRequestDemo;
import Tools.HttpResponseDemo;
import annotation.RequestMap;

import java.io.IOException;

/**
 * 未找到返回的页面
 *
 * @author 小如
 * @date 2023/08/17
 */
@RequestMap(path = "404")
public class Html404 {
    public void err(HttpRequestDemo request, HttpResponseDemo response) throws IOException {

        String resp= HttpResponseDemo.responsebody+"<!DOCTYPE html>\n" +
                "<html>\n" +
                "<head>\n" +
                "<link rel=\"icon\" href=\"data:image/ico;base64,aWNv\">\n"+
                "    <meta charset=\"utf-8\" />\n" +
                "    <title>404 - Not Found</title>\n" +
                "</head>\n" +
                "<body>\n" +
                "    <h1>404 - Not Found</h1>\n" +
                "    <p>Sorry, the page you are looking for does not exist.</p>\n" +
                "</body>\n" +
                "</html>";
        response.outputStream.write(resp.getBytes());
        response.outputStream.flush();
        response.outputStream.close();
    }
}

创建启动类

1.启动类框架


public class start {
    private static ArrayList<String> arr = new ArrayList<>();
    public static HashMap<String, Class> map = new HashMap<>();

2.在启动类中定义方法遍历文件,找到所有的java文件

   /**
     * 寻找带有RequestMap的注解,拿到相应的数据
     *
     * @param file 文件
     */
    private static void func(File file) {
        File[] fs = file.listFiles();
        for (File f : fs) {
            if (f.isDirectory()){    //若是目录,则递归打印该目录下的文件
                func(f);
            }
            if (f.isFile()) {        //若是文件,直接打印
                String filepath = f.toString();
                filepath = filepath.split("src")[1];
                filepath = filepath.substring(1,filepath.length());
                if( filepath.endsWith(".java")) {
                    arr.add(filepath.replace("\\", ".").replace(".java", "").replace("main.",""));
                }

            }
        }
    }

3.在找到的java文件中筛选出含有自定义注解的类,并将path和全类名存入map中

   /**
     * 找到servlet,将信息放入map
     *
     * @throws ClassNotFoundException 类没有发现异常
     */
    public static void choseServlet() throws ClassNotFoundException {
        for(int i = 0; i < arr.size(); i++) {
            String path = arr.get(i);
            Class<?> servletClass = Class.forName(path);
            //System.out.println(servletClass);
            //判断Class对象上是否有RequestMap的注解
            if (servletClass.isAnnotationPresent(RequestMap.class)) {
                //System.out.println("找到了一个Servlet,路径是:" + path);
                //获取SystemConfig注解
                RequestMap config = servletClass.getAnnotation(RequestMap.class);
                //System.out.println("它的请求路径是:" + config.path() );
                map.put( config.path(), servletClass);
            }
        }
    }

以上扫描文件的操作应该在项目启动时就开始处理,当我们发送请求的时候可以直接调用。因此我们将以上两个方法调用放入static块中


    static {
        String inputPath = "F:\\javaDemo\\simple-tomcat2\\src\\servlet";		//要遍历的路径,改成自己的
        File file = new File(inputPath);		//获取其file对象
        func(file);//遍历指定目录下的所有子文件以及子目录下的文件名字
        try {
            choseServlet();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }//  根据注解筛选出servlet并存储到hashmap中
    }

4.socket监听8080端口

 //注册端口
            InetAddress localHost = InetAddress.getLocalHost();
            System.out.println("localhost:" + localHost);
            ServerSocket serverSocket = new ServerSocket(8080, 10, localHost);
            startUp(serverSocket);

5.建立连接,解析http请求

 private static void startUp(ServerSocket serverSocket) throws IOException, InterruptedException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
        ExecutorService executorService = new ThreadPoolConfigPlus().executorService();
        System.out.println("等待建立连接");
        for(int i =0;i<Max_Request_Count;i++){
            Socket server = serverSocket.accept();
            while (true) {
                // 尝试加锁
                try {
                    synchronized (server) {
                        // 获取到锁
                        HttpAcceptThreadPlus httpAcceptThreadPlus = new HttpAcceptThreadPlus(server, executorService,new HttpAcceptThreadPlus.OnFinishListener() {
                            @Override
                            public void onFinish(List<String> strings) throws IOException, InterruptedException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
                                //处理请求
                                if(!strings.get(0).equals("GET /favicon.ico HTTP/1.1")){
                                    requestHttp(server,strings.get(0));
                                }
                            }
                        });
                        httpAcceptThreadPlus.start();
                        break;
                    }
                }catch (Exception e){
                    // 加锁失败,线程睡眠两秒
                    Thread.sleep(2000);
                }
            }
        }
    }

6.处理http请求,判断是GET还是POST请求,调用相应的servlet方法

 private static void requestHttp(Socket socket,String http) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, IOException, InterruptedException {

        //获取请求方式
        String requestStyle=http.split(" ")[0];

        if(requestStyle.equals("GET")){

            String httpPathAndParameter=http.split(" ")[1];
            String httpPath;

            //创建HttpRequest对象
            HttpRequestDemo httpRequestDemo=new HttpRequestDemo();
            if(httpPathAndParameter.indexOf("?")!=-1){
                httpPath=httpPathAndParameter.substring(1);
                httpPath=httpPath.split("\\?")[0];
                String parameterString=httpPathAndParameter.split("\\?")[1];
                String[] parameters=parameterString.split("&");
                for (int i=0;i<parameters.length;i++){
                    httpRequestDemo.map.put(parameters[i].split("=")[0],parameters[i].split("=")[1]);
                }
            }else{
                httpPath=httpPathAndParameter.substring(1);
            }

            //创建HttpResponse对象
            OutputStream outputStream=socket.getOutputStream();
            HttpResponseDemo httpResponseDemo=new HttpResponseDemo(outputStream);

            //反射调用doGet
            if(map.containsKey(httpPath)){
                Class<?> servletClass=map.get(httpPath);
                Method method=servletClass.getMethod("doGet",HttpRequestDemo.class,HttpResponseDemo.class);
                method.invoke(servletClass.newInstance(),httpRequestDemo,httpResponseDemo);
            }else {
               httpPath="404";
                Class<?> servletClass=map.get(httpPath);
                Method method=servletClass.getMethod("err",HttpRequestDemo.class,HttpResponseDemo.class);
                method.invoke(servletClass.newInstance(),httpRequestDemo,httpResponseDemo);
                throw new RuntimeException("无效的请求路径!");
            }
        }else{
            String httpPath=http.split(" ")[1];
            httpPath=httpPath.substring(1);

            HttpRequestDemo httpRequestDemo=new HttpRequestDemo();

            OutputStream outputStream=socket.getOutputStream();
            HttpResponseDemo httpResponseDemo=new HttpResponseDemo(outputStream);

            Class<?> servletClass=map.get(httpPath);
            Method method=servletClass.getMethod("doPost",HttpRequestDemo.class,HttpResponseDemo.class);
            method.invoke(servletClass.newInstance(),httpRequestDemo,httpResponseDemo);
        }

    }

完整的启动类如下:


//省略n个包

public class start {
    private static ArrayList<String> arr = new ArrayList<>();
    public static HashMap<String, Class> map = new HashMap<>();


    public static void main(String[] args) throws Exception {
        //启动Socket --->  获取字符串协议数据(HTTP协议 --> requestHttp()  )
        try {
            //注册端口
            InetAddress localHost = InetAddress.getLocalHost();
            System.out.println("localhost:" + localHost);
            ServerSocket serverSocket = new ServerSocket(8080, 10, localHost);
            startUp(serverSocket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static void startUp(ServerSocket serverSocket) throws IOException, InterruptedException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
        ExecutorService executorService = new ThreadPoolConfigPlus().executorService();
        System.out.println("等待建立连接");
        for(int i =0;i<Max_Request_Count;i++){
            Socket server = serverSocket.accept();
            while (true) {
                // 尝试加锁
                try {
                    synchronized (server) {
                        // 获取到锁
                        HttpAcceptThreadPlus httpAcceptThreadPlus = new HttpAcceptThreadPlus(server, executorService,new HttpAcceptThreadPlus.OnFinishListener() {
                            @Override
                            public void onFinish(List<String> strings) throws IOException, InterruptedException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
                                //处理请求
                                if(!strings.get(0).equals("GET /favicon.ico HTTP/1.1")){
                                    requestHttp(server,strings.get(0));
                                }
                            }
                        });
                        httpAcceptThreadPlus.start();
                        break;
                    }
                }catch (Exception e){
                    // 加锁失败,线程睡眠两秒
                    Thread.sleep(2000);
                }
            }
        }
    }


    private static void requestHttp(Socket socket,String http) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, IOException, InterruptedException {

        //获取请求方式
        String requestStyle=http.split(" ")[0];

        if(requestStyle.equals("GET")){

            String httpPathAndParameter=http.split(" ")[1];
            String httpPath;

            //创建HttpRequest对象
            HttpRequestDemo httpRequestDemo=new HttpRequestDemo();
            if(httpPathAndParameter.indexOf("?")!=-1){
                httpPath=httpPathAndParameter.substring(1);
                httpPath=httpPath.split("\\?")[0];
                String parameterString=httpPathAndParameter.split("\\?")[1];
                String[] parameters=parameterString.split("&");
                for (int i=0;i<parameters.length;i++){
                    httpRequestDemo.map.put(parameters[i].split("=")[0],parameters[i].split("=")[1]);
                }
            }else{
                httpPath=httpPathAndParameter.substring(1);
            }

            //创建HttpResponse对象
            OutputStream outputStream=socket.getOutputStream();
            HttpResponseDemo httpResponseDemo=new HttpResponseDemo(outputStream);

            //反射调用doGet
            if(map.containsKey(httpPath)){
                Class<?> servletClass=map.get(httpPath);
                Method method=servletClass.getMethod("doGet",HttpRequestDemo.class,HttpResponseDemo.class);
                method.invoke(servletClass.newInstance(),httpRequestDemo,httpResponseDemo);
            }else {
               httpPath="404";
                Class<?> servletClass=map.get(httpPath);
                Method method=servletClass.getMethod("err",HttpRequestDemo.class,HttpResponseDemo.class);
                method.invoke(servletClass.newInstance(),httpRequestDemo,httpResponseDemo);
                throw new RuntimeException("无效的请求路径!");
            }
        }else{
            String httpPath=http.split(" ")[1];
            httpPath=httpPath.substring(1);

            HttpRequestDemo httpRequestDemo=new HttpRequestDemo();

            OutputStream outputStream=socket.getOutputStream();
            HttpResponseDemo httpResponseDemo=new HttpResponseDemo(outputStream);

            Class<?> servletClass=map.get(httpPath);
            Method method=servletClass.getMethod("doPost",HttpRequestDemo.class,HttpResponseDemo.class);
            method.invoke(servletClass.newInstance(),httpRequestDemo,httpResponseDemo);
        }

    }

    /**
     * 找到servlet,将信息放入map
     *
     * @throws ClassNotFoundException 类没有发现异常
     */
    public static void choseServlet() throws ClassNotFoundException {
        for(int i = 0; i < arr.size(); i++) {
            String path = arr.get(i);
            Class<?> servletClass = Class.forName(path);
            //System.out.println(servletClass);
            //判断Class对象上是否有RequestMap的注解
            if (servletClass.isAnnotationPresent(RequestMap.class)) {
                //System.out.println("找到了一个Servlet,路径是:" + path);
                //获取SystemConfig注解
                RequestMap config = servletClass.getAnnotation(RequestMap.class);
                //System.out.println("它的请求路径是:" + config.path() );
                map.put( config.path(), servletClass);
            }
        }
    }
    /**
     * 寻找带有RequestMap的注解,拿到相应的数据
     *
     * @param file 文件
     */
    private static void func(File file) {
        File[] fs = file.listFiles();
        for (File f : fs) {
            if (f.isDirectory()){    //若是目录,则递归打印该目录下的文件
                func(f);
            }
            if (f.isFile()) {        //若是文件,直接打印
                String filepath = f.toString();
                filepath = filepath.split("src")[1];
                filepath = filepath.substring(1,filepath.length());
                if( filepath.endsWith(".java")) {
                    arr.add(filepath.replace("\\", ".").replace(".java", "").replace("main.",""));
                }

            }
        }
    }

    static {
        String inputPath = "F:\\javaDemo\\simple-tomcat2\\src\\servlet";		//要遍历的路径,改成自己的
        File file = new File(inputPath);		//获取其file对象
        func(file);//遍历指定目录下的所有子文件以及子目录下的文件名字
        try {
            choseServlet();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }//  根据注解筛选出servlet并存储到hashmap中
    }

}

线程池配置

socket有个弊端,当我们建立一次连接处理完http请求之后,连接断开就会导致项目停止运行,因此我们加入线程池配置,开多个线程去接收建立连接

import java.util.concurrent.*;

import static Tools.config.*;

/**
 * 自定义的线程池配置
 *
 * @author 小如
 * @date 2023/08/17
 */
public class ThreadPoolConfigPlus {
    /**
     * 核心线程数量
     */
    private int corePoolSize=Core_PoolSize;
    /**
     * 最大线程数量
     */
    private int maxPoolSize=Max_PoolSize;
    /**
     * 队列容量
     */
    private int queueCapacity=Queue_Capacity;


    public ExecutorService executorService(){
        //线程工厂
        ThreadFactory factory=new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread=new Thread(r);
                return thread;
            }
        };
        //手动创建线程池
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(corePoolSize, maxPoolSize, 10, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(queueCapacity), factory,
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        return threadPoolExecutor;
    }
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;

/**
 * @author 小如
 */
public class HttpAcceptThreadPlus {
    private Socket socket;
    private ExecutorService executorService;
    private OnFinishListener onFinishListener; // 回调接口

    public interface OnFinishListener {
        void onFinish(List<String> strings) throws IOException, InterruptedException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException;
    }
    public HttpAcceptThreadPlus(Socket server, ExecutorService executorService, OnFinishListener onFinishListener) {
        this.socket = server;
        this.executorService = executorService;
        this.onFinishListener = onFinishListener;
    }
    public void start(){
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    String s = null;
                    ArrayList<String> strings = new ArrayList<>();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                    while ((s = reader.readLine()).length() != 0) {
                        //每次循环接收一行的Http数据
                        try {
                            strings.add(s);
                        } catch (Exception e) {
                            System.out.println("接收Http进程结束");
                            break;
                        }
                    }
                    if(!strings.get(0).equals("GET /favicon.ico HTTP/1.1")){
                        System.out.println(Thread.currentThread().getName()+"连接成功");
                        System.out.println(Thread.currentThread().getName()+"接收Http进程结束,获取的请求为:"+strings.get(0));
                    }
                    onFinishListener.onFinish(strings);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

线程池的配置文件

public class config {
    /**
     * 核心线程数量
     */
    public static final int Core_PoolSize = 8;
    /**
     * 最大线程数量
     */
    public static final int Max_PoolSize = 16;
    /**
     * 队列容量
     */
    public static final int Queue_Capacity =2000;

    public static final int Max_Request_Count = 2000;
}

测试阶段

至此,项目已经完成,启动start,开始测试

 浏览器输入请求

 测试多次请求,观察后台

 

 发送错误的请求:

 后台提示,抛出异常

 我已将项目开源至Github

https://github.com/adpanru/easy-tomcat

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

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

相关文章

匈牙利算法相关介绍

重要说明&#xff1a;本文从网上资料整理而来&#xff0c;仅记录博主学习相关知识点的过程&#xff0c;侵删。 一、参考资料 匈牙利算法匹配问题? Exactly how the Hungarian Algorithm works 多目标跟踪数据关联之匈牙利算法 五分钟小知识&#xff1a;什么是匈牙利算法 论文…

改进YOLO系列:3.添加SOCA注意力机制

添加SOCA注意力机制 1. SOCA注意力机制论文2. SOCA注意力机制原理3. SOCA注意力机制的配置3.1common.py配置3.2yolo.py配置3.3yaml文件配置1. SOCA注意力机制论文 暂未找到 2. SOCA注意力机制原理 3. SOCA注意力机制的配置 3.1common.py配置 ./models/common.p…

SpringBoot部署到腾讯云

SpringBoot部署到腾讯云 此处默认已经申请到腾讯云服务器&#xff0c;因为本人还没有申请域名&#xff0c;所以就直接使用的ip地址 XShell连接到腾讯云 主机中填写腾讯云的公网ip地址 公网ip地址在下图中找到 接下来填写服务器的用户名与密码 一般centOS用户名为root&#xff…

ZLMediakit-method ANNOUNCE failed: 401 Unauthorized

使用ffmpeg推流&#xff1a; nohup ffmpeg -stream_loop -1 -re -i "/usr/local/mp4/test.mp4" -vcodec h264 -acodec aac -f rtsp -rtsp_transport tcp rtsp://10.55.134.12/live/test &[rootlocalhost ~]# ffmpeg -stream_loop -1 -re -i "/usr/local/mp…

HCIP的交换机实验

题目 拓扑图 PC1/3接口用access 创建WLAN LSW1 创建WLAN [lsw1]vlan batch 2 to 6[lsw1-Ethernet0/0/1]p [lsw1-Ethernet0/0/1]port l [lsw1-Ethernet0/0/1]port link- [lsw1-Ethernet0/0/1]port link-flap [lsw1-Ethernet0/0/1]port link-type acc [lsw1-Ethernet0/0…

Java实现微信小程序V3支付 (完整demo)

1. 微信小程序支付-开发者文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml 2. 导入依赖 <!--小程序支付 v3--> <dependency><groupId>com.github.wechatpay-apiv3</groupId><artifactId>wechatpay-apache-httpclient<…

精密、CMOS、轨到轨输入/输出、宽带运算放大器MS8601/8602/8604

产品简述 MS8601/MS8602/MS8604 分别是单 / 双 / 四通道、轨到轨输入和输出、 单电源放大器&#xff0c;具有极低的失调电压和宽信号带宽。它采用 1.8V 至 5V 单电 源&#xff08; 0.9 V 至 2.5 V 双电源&#xff09;供电。 MS8601/MS8602/MS8604 低失调、极低的输入偏置…

02__models

LangChain提供两种封装的模型接口 1.大规模语言模型&#xff08;LLM&#xff09;&#xff1a;输入文本字符串&#xff0c;返回文本字符串 2.聊天模型&#xff1a;基于一个语言模型&#xff0c;输入聊天消息列表&#xff0c;返回聊天消息 Langchain的支持OpenAI、ChatGLM、Hu…

Minjourney 参数详解(MJ参数)

官方地址&#xff1a; Midjourney Parameter ListParameters are added to a prompt to change how an image generates. Parameters can change an images aspect ratios, model version, upscaler, and more.https://docs.midjourney.com/docs/parameter-list官方原文&#…

更改计算机睡眠时间

控制面板–>系统和安全–>电源选项下的更改计算机睡眠时间 如果关闭显示器时间小于使计算机进入睡眠状态时间&#xff0c;时间先到达关闭显示器时间&#xff0c;显示器关闭&#xff0c;这时电脑还在正常工作状态。如果此时敲击键盘显示器出现画面&#xff0c;无需输入密…

最小化安装移动云大云操作系统--BCLinux-R8-U8-Server-x86_64-230802版

CentOS 结束技术支持&#xff0c;转为RHEL的前置stream版本后&#xff0c;国内开源Linux服务器OS生态转向了开源龙蜥和开源欧拉两大开源社区&#xff0c;对应衍生出了一系列商用Linux服务器系统。BC-Linux V8.8是中国移动基于龙蜥社区Anolis OS 8.8版本深度定制的企业级X86服务…

Unable to create tempDir. java.io.tmpdir is set to ./tmp

项目场景&#xff1a; 程序启动时报错&#xff1a;Application run failed org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to create tempDir…

第07天 Static关键字作用及用法

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a;每天一个知识点 ✨特色专栏&#xff1a…

小航助学Python编程NOC模拟卷(第1套)(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSDN博客 需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSD…

C++ STL序列式容器(详解)

STL基础 C STL基本组成&#xff08;6大组件13个头文件&#xff09; 通常认为&#xff0c;STL 是由容器、算法、迭代器、函数对象、适配器、内存分配器这 6 部分构成&#xff0c;其中后面 4 部分是为前 2 部分服务的&#xff0c;它们各自的含义如表 1 所示。 ​ 表 1 STL 组成…

石头IT

石头是地球上最常见的矿石之一&#xff0c;它由天然矿物颗粒组成。石头可以有不同的形状&#xff0c;大小和颜色&#xff0c;取决于其中的矿物组成和地质过程。石头可以从地球表面的岩石中形成&#xff0c;也可以从火山活动或陨石撞击中形成。 石头是一种非常坚固和耐用的材料…

BifroMQ:五分钟了解百度开源旗下消息中间件

BifroMQ 并不是一个独立的公司&#xff0c;而是由一家名为 "Bifrost" 的公司开发的一款产品。Bifrost 公司成立于 2014 年&#xff0c;总部位于中国北京&#xff0c;是一家专注于开源技术的公司。当时 Bifrost 公司的创始人陈明发起了开源项目 "iProven"&a…

综合能源系统(7)——综合能源综合评估技术

综合能源系统关键技术与典型案例  何泽家&#xff0c;李德智主编 综合能源系统是多种能源系统非线性耦合的、多时间与空间尺度耦合的“源-网-荷一储”一体化系统&#xff0c;通过能源耦合、多能互补&#xff0c;能够实现能源的高效利用&#xff0c;并提高新能源的利用水平。对…

Power Basic 入门-4

前面我们叙述了power的一些基本概念&#xff0c;其中&#xff0c;对于switching power计算的部分&#xff0c;我们发现了一些correlation问题。 接下来&#xff0c;解释一下这个问题 首先&#xff0c;我们要报一下cell power pt_shell> report_power -cell_power inst_q_…

openLayers实战(八):坐标系及其转换

坐标系介绍 EPSG: 3857 --web地图&#xff0c;基于球体的、web墨卡托投影&#xff08;伪墨卡托投影Pseudo-Mercator&#xff09;的投影坐标系&#xff0c;范围为纬度85度以下&#xff0c;由于google地图最先使用而成为事实标准。至今&#xff0c;大多互联网地图都使用EPSG3857&…