代码拉取完成,页面将自动刷新
/*
Navicat Premium Data Transfer
Source Server : blog
Source Server Type : MySQL
Source Server Version : 80032
Source Host : localhost:3306
Source Schema : newblog
Target Server Type : MySQL
Target Server Version : 80032
File Encoding : 65001
Date: 22/07/2024 08:37:29
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for b_admin
-- ----------------------------
DROP TABLE IF EXISTS `b_admin`;
CREATE TABLE `b_admin` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`password` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`avtar` varchar(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '管理员头像',
`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL,
`role_id` int NOT NULL COMMENT '管理员等级,0(超级管理员),1(普通管理员)',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_admin
-- ----------------------------
INSERT INTO `b_admin` VALUES (2, 'admin', '21232f297a57a5a743894a0e4a801fc3', 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png', '管理员2号', 1);
INSERT INTO `b_admin` VALUES (4, 'user777', 'e10adc3949ba59abbe56e057f20f883e', 'http://localhost:8080/image/1712657448478-CL.jpg', '@随时WIFI', 6);
-- ----------------------------
-- Table structure for b_collect
-- ----------------------------
DROP TABLE IF EXISTS `b_collect`;
CREATE TABLE `b_collect` (
`id` int NOT NULL AUTO_INCREMENT,
`content_id` int NOT NULL,
`user_id` int NOT NULL,
`create_time` datetime NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_collect
-- ----------------------------
INSERT INTO `b_collect` VALUES (15, 30, 6, '2024-05-05 21:33:12');
INSERT INTO `b_collect` VALUES (34, 30, 5, '2024-05-06 14:32:50');
-- ----------------------------
-- Table structure for b_community
-- ----------------------------
DROP TABLE IF EXISTS `b_community`;
CREATE TABLE `b_community` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL COMMENT '发布问题内容',
`create_time` datetime NOT NULL,
`review_count` bigint UNSIGNED NOT NULL COMMENT '评论数量',
`like_count` bigint UNSIGNED NOT NULL COMMENT '点赞数量',
`status` int NOT NULL COMMENT '审核状态,0(审核中),1(已发布),2(未通过)',
`community_id` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`tags_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL,
`look` bigint NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_community
-- ----------------------------
INSERT INTO `b_community` VALUES (6, 5, '<p>我觉得你就是个憨批,没用的东西,老子弄死你</p>', '2024-05-05 22:46:53', 0, 0, 1, '0de729a5d7a6428b9fe08a60886d5753', '2', 2);
INSERT INTO `b_community` VALUES (7, 5, '<div>\n<pre>***,qwdhjkwd</pre>\n</div>', '2024-05-10 09:52:10', 0, 0, 0, '833ae3662d3d45c3a9e2dcbc01e9c70f', '16', 0);
-- ----------------------------
-- Table structure for b_community_examine
-- ----------------------------
DROP TABLE IF EXISTS `b_community_examine`;
CREATE TABLE `b_community_examine` (
`id` int NOT NULL,
`community_id` int NOT NULL,
`user_id` int NOT NULL,
`create_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_community_examine
-- ----------------------------
-- ----------------------------
-- Table structure for b_community_review
-- ----------------------------
DROP TABLE IF EXISTS `b_community_review`;
CREATE TABLE `b_community_review` (
`id` int NOT NULL AUTO_INCREMENT,
`community_review_content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '社区评论表',
`parent_id` int NULL DEFAULT NULL COMMENT '父级id(用户id)',
`community_id` int NOT NULL COMMENT '社区id',
`create_time` datetime NOT NULL,
`status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '评论状态,0(审核中),1(已发布)',
`like_count` bigint UNSIGNED NULL DEFAULT NULL,
`user_id` int NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_community_review
-- ----------------------------
-- ----------------------------
-- Table structure for b_content
-- ----------------------------
DROP TABLE IF EXISTS `b_content`;
CREATE TABLE `b_content` (
`id` int NOT NULL AUTO_INCREMENT,
`create_time` datetime NOT NULL,
`like` bigint NULL DEFAULT NULL COMMENT '获得的点赞数量',
`user_id` int NOT NULL COMMENT '发布者id',
`collect` bigint NULL DEFAULT NULL COMMENT '收藏数量',
`status` int NOT NULL COMMENT '当前状态0(草稿),1(审核中),2(已发布),3(未通过),4(延时发布)',
`is_delete` int UNSIGNED NOT NULL COMMENT '是否删除',
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL COMMENT '文章内容',
`type_id` int NOT NULL COMMENT '分类id',
`tags_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '标签',
`update_time` datetime NOT NULL,
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL COMMENT '文章描述',
`first_picture` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '文章首图',
`is_review` int NOT NULL COMMENT '是否开启评论',
`model` int NOT NULL COMMENT '文章类型0(原创),1(转载),2(翻译)',
`history_people_count` bigint NULL DEFAULT NULL COMMENT '历史观看人数',
`content_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '文章附属id',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `content_id`(`content_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 37 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_content
-- ----------------------------
INSERT INTO `b_content` VALUES (30, '2021-01-05 21:28:48', 2, 6, 2, 2, 0, '# AJAX(Asynchronous JavaScript And Xml)\n\n## 传统请求及缺点\n\n- 传统的请求都有哪些?\n - 直接在浏览器的地址栏输入url\n - 点击超链接\n - 提交form表单\n - 使用JS代码发送请求\n - window.open(url)\n - document.location.href=url\n - window.location.href=url\n - ...\n - 传统请求存在的问题\n - 页面全部刷新导致用户的体验感较差\n - 传统请求导致用户体验有空白期\n\n## AJAX概述\n\n![image-20230409120237884](E:\\programming\\AJAX\\笔记2023-4-9\\image-20230409120237884.png)\n\n- AJAX不能称为一种技术,他是多种技术的综合产物\n- AJAX可以让浏览器发送一种特殊的请求,这种请求可以是异步的\n- 什么是异步,什么是同步?\n - 假设有t1和t2线程,t1和t2是线程并发的,就是异步\n - 假设有t1和t2线程,t2在执行的时候回,必须等t1线程执行到某个位置的时候t2才能执行,那么t2在等t1,显然它们是排队的,排队的时候就是同步\n - AJAX可以发送异步请求,也就是说,在同一个浏览器页面中,Ajax可以发送多个请求,这些请求之间不需要等待\n- Ajax代码属于WEB前端的JS代码。和后端没有关系\n- Ajax应用程序可能使用XML来传输数据,但将数据作为纯文本或JSON文本传输也同样常见\n\n## XMLHttpRequest对象\n\n- XMLHttpRequest对象是Ajax的核心对象,发送请求以及接收服务器数据的返回都是靠他\n\n- XMLHttpRequest对象,现代浏览器都是支持的,都内置了该对象,直接使用即可\n\n- 创建XMLHttpRequest对象\n\n - var xhr=new XMLHttpRequest();\n\n- XMLHttpRequest对象的方法\n\n - | 方法 | 描述 |\n | ------------------------------- | ------------------------------------------------------------ |\n | abort() | 取消当前请求 |\n | getAllResponseHeaders() | 返回头部信息 |\n | getResponseHeader() | 返回特定头部信息 |\n | send() | 将请求发送到服务器,用于GET请求 |\n | send(String) | 将请求发送到服务器,用于POST请求 |\n | setRequestHeader() | 向要发送的报头添加标签/值对 |\n | open(method,url,async,user,psw) | 规定请求method:请求类型GET或POST。url:文件位置。async:true(异步)或false(同步)。user:可选用户名称。psw:可选的密码 |\n\n- XMLHttpRequest对象的属性\n\n - | 属性 | 描述 |\n | ----------------- | ------------------------------------------------------------ |\n | onredystatechange | 定义当readyState属性发生变化时被调用的函数 |\n | readyState | 保存XMLHttpRequest的状态。0:请求未初始化。1:服务器连接已建立。2:请求已收到。3:正在处理请求。4:请求已完成且响应已就绪 |\n | responseText | 以字符串返回响应数据 |\n | responseXML | 以XML数据返回响应数据 |\n | status | 返回请求的状态号200:“OK”。403:”Forbidden“。404:”Not Found“ |\n | statusText | 返回状态文本(比如:“OK” 或 “Not Found”) |\n\n## AJAX GET请求\n\n- html代码\n\n - ```html\n <%--\n Created by IntelliJ IDEA.\n User: object\n Date: 2023/4/9\n Time: 13:07\n To change this template use File | Settings | File Templates.\n --%>\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n <html>\n <head>\n <title>ajax的get请求</title>\n </head>\n <body>\n <script type=\"text/javascript\">\n window.onload=function (){\n document.getElementById(\"hello\").onclick=function (){\n //发送ajax的get请求\n /*console.log(\"发送Ajax get请求\")*/\n //第一步:创建Ajax核心对象XMLHttpRequest对象\n var xhr=new XMLHttpRequest();\n //第二步:注册回调函数,当readystate状态值发生改变的时候,执行回调函数\n xhr.onreadystatechange=function (){\n //这里的回调函数会被调用多次\n /*\n 0->1 调用一次\n 1->2 调用一次\n 2->3 调用一次\n 3->4 调用一次\n */\n //console.log(xhr.readyState)\n if (xhr.readyState==4) {\n //响应结束\n /*\n 响应结束之后,一般会有一个Http状态码\n Http状态码常见的包括:200表示访问成功,404表示资源不存在,500表示服务器内部错误\n Http状态码是HTTP协议的一部分,HTTP协议中规定,服务器响应之后都会有一个状态码\n */\n //console.log(\"响应结束\")\n //获取状态码\n //console.log(\"Http响应状态码\"+this.status)\n if(this.status==404){\n alert(\"不好意思,访问的资源不存在\")\n }else if (this.status==500){\n alert(\"服务器内部出现错误\")\n }else if (this.status==200){\n alert(\"访问成功\")\n /*\n 通过XMLHttpRequest对象获取响应信息\n 通过XMLHttpRequest对象的responseText属性来获取响应信息\n */\n //alert(this.responseText)\n //将响应信息放到div图层中渲染\n <!--\n innerHTML属性是javascript中的语法,和ajax的XMLHttpRequest对象无关\n innerHTML可以设置元素内部的HTML代码。(innerHTML可以将后面的内容当做一段普通的HTML代码来解释并运行)\n -->\n document.getElementById(\"mydiv\").innerHTML=this.responseText\n <!--\n innerText也不是ajax中的,是JavaScript中的元素属性\n 他也是可以设置元素中的内容,但是即使后面是HTML代码,也是将其看成一个普通的字符串来处理\n -->\n document.getElementById(\"mydiv\").innerText=this.responseText\n }\n }\n }\n //第三步:开启通道(open只是浏览器和服务器建立通道,并不会发送请求)\n /*\n 规定请求method:请求类型GET或POST。\n url:文件位置。\n async:true(异步)或false(同步)。\n user:可选用户名称。用户名和密码是进行身份认证的,说明访问这个服务器上的资源,可能需要提供一些口令才能访问。需不需要主要看服务器\n psw:可选的密码\n */\n xhr.open(\"GET\",\"/first/request1\",true)\n //第四步:发送请求\n xhr.send()\n }\n }\n </script>\n \n <input type=\"button\" value=\"hello ajax\" id=\"hello\">\n \n <div id=\"mydiv\"></div>\n </body>\n </html>\n ```\n\n- servlet代码\n\n - ```java\n @WebServlet(\"/request1\")\n public class ajaxRequest extends HttpServlet {\n @Override\n protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n response.setContentType(\"text/html\");\n PrintWriter out = response.getWriter();\n \n out.print(\"你好\");\n }\n }\n ```\n\n- AJAX get请求如何提交数据\n\n - get请求提交数据是在 “请求行” 上提交,格式是:url?name=value&name=value\n\n - 其实这个get请求提价数据的格式是HTTP协议中规定的,遵循协议即可\n\n - ```xml\n //获取到用户填写的username和password\n var username=document.getElementById(\"username\").value\n var password=document.getElementById(\"password\").value\n //开启通道\n xhr.open(\"GET\",\"/first/request1?username=\"+username+\"&password=\"+password+\"\",true)\n \n username<input type=\"text\" id=\"username\"><br>\n password<input type=\"text\" id=\"password\"><br>\n ```\n\n - ```java\n String username = request.getParameter(\"username\");\n String password = request.getParameter(\"password\");\n out.print(\"username=\"+username+\"<br>\"+\"password=\"+password+\"<br>\");\n ```\n\n## AJAX GET请求的缓存问题\n\n- 对应低版本 的IE浏览器来说,AJAX的get请求可能会走缓存。存在缓存问题。对于现代的浏览器来说,大部分浏览器都已经不存在AJAX get缓存问题了。\n\n- 什么是AJAX GET请求缓存问题呢?\n\n - 在HTTP协议中是这样规定get请求的:get请求不会被缓存起来\n - 发送AJAX GET请求时,在同一个浏览器上,前后发送的AJAX请求路径一样的话,对于低版本的IE来说,第二次的AJAX GET请求会走缓存,不走服务器\n\n- POST请求在HTTP协议中规定的是:POST请求不会被浏览器缓存\n\n- GET请求缓存的优缺点\n\n - 优点:直接从浏览器缓存中获取资源,不需要从服务器上重新加载资源,速度快,用户体验好\n - 缺点:无法实时获取最新的服务器资源\n\n- 浏览器什么时候会走缓存?\n\n - 第一:是一个GET请求\n - 第二:请求路径已经被浏览器缓存过了。第二次发送请求的时候,这个路径没有变化,会走浏览器缓存·\n\n- 如果是低版本的IE浏览器,怎么解决AJAX GET请求的缓存问题呢?\n\n - 可以在请求路径后面添加一个时间戳,这个时间戳是随时间变化的。所以每一次发送的请求的请求路径都是不一样的,这样就不会走浏览器的缓存问题了\n\n - ```html\n xhr.open(\"GET\",\"/first/request1?t=\"+new Date().getTime()+\"&username=\"+username+\"&password=\"+password+\"\",true)\n ```\n\n - 当然也可以随机数加时间戳的方式\n\n## AJAX POST请求\n\n- ```html\n <!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <title>发送Ajax POST请求</title>\n </head>\n <body>\n \n <script type=\"text/javascript\">\n \n window.onload=function (){\n document.getElementById(\"mybtn\").onclick=function (){\n //发送ajax post请求\n //第一步:创建AJAX核心对象\n var xhr=new XMLHttpRequest();\n //注册回调函数\n xhr.onreadystatechange = function (){\n if (this.readyState == 4) {\n if (this.status == 200) {\n document.getElementById(\"mydiv\").innerHTML=this.responseText\n }\n else {\n alert(\"发送失败!!!\")\n }\n }\n }\n //开启通道\n xhr.open(\"POST\",\"/first/request2\",true)\n //发送请求\n xhr.send()\n }\n }\n </script>\n <button id=\"mybtn\">ajax</button>\n <div id=\"mydiv\"></div>\n </body>\n </html>\n ```\n\n- ```java\n @WebServlet(\"/request2\")\n public class ajaxRequestDoPOST extends HttpServlet {\n @Override\n protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n response.setContentType(\"text/html\");\n PrintWriter out = response.getWriter();\n \n out.print(\"post请求已发送!!!\");\n }\n }\n ```\n\n- AJAX POST请求图和提交数据?\n\n - 通过send()方法来提交数据,在这个方法中的数据,会自动在请求体重提交数据\n\n - ```html\n <script type=\"text/javascript\">\n \n window.onload=function (){\n document.getElementById(\"mybtn\").onclick=function (){\n //发送ajax post请求\n //第一步:创建AJAX核心对象\n var xhr=new XMLHttpRequest();\n //注册回调函数\n xhr.onreadystatechange = function (){\n if (this.readyState == 4) {\n if (this.status == 200) {\n document.getElementById(\"mydiv\").innerHTML=this.responseText\n }\n else {\n alert(\"发送失败!!!\")\n }\n }\n }\n \n //开启通道\n xhr.open(\"POST\",\"/first/request2\",true)\n \n //发送请求\n //怎么模拟ajax提交form表单呢?设置请求头的内容类型(这行代码非常关键,是模拟form表单提交数据的关键)\n //注意这行代码不能放在open之前\n xhr.setRequestHeader(\"Content-Type\",\"application/x-www-form-urlencoded\")\n // xhr.send()\n //使用js代码获取用户填写的用户名和密码\n var username=document.getElementById(\"username\").value\n var password=document.getElementById(\"password\").value\n //注意格式 xhr.send(name=value&name=value)\n xhr.send(\"username=\"+username+\"&password=\"+password)\n }\n }\n </script>\n \n 用户名:<input type=\"text\" id=\"username\"><br>\n 密码:<input type=\"password\" id=\"password\"><br>\n ```\n\n###### 实现案例\n\n- 使用AJAX OPST请求实现用户注册的时候,用户名是否可用(用户名是否重复)\n - 在前端,用户名输入用户名之后,失去焦点事件blur发生,然后发生AJAX POST请求,提交用户名\n - 在后端接收用户名之后,连接数据库,根据用户名去表中搜索\n - 如果用户名存在\n - 后端响应消息:对不起,用户名已存在(在前端页面以红色字体显示)\n - 如果用户名不存在\n - 后端响应消息:用户名可以使用(在前端以绿色字体显示)\n\n```html\n<script type=\"text/javascript\">\n window.onload=function (){\n document.getElementById(\"username\").onfocus=function (){\n //获得焦点\n //清空提示\n document.getElementById(\"tip\").innerHTML=\"\"\n }\n document.getElementById(\"username\").onblur=function (){\n //失去焦点\n console.log(\"发送ajax请求\")\n //发送post请求\n //1 创建ajax核心对象\n var xhr=new XMLHttpRequest()\n //注册回调函数\n xhr.onreadystatechange=function (){\n if (this.readyState == 4) {\n if (this.status == 200) {\n document.getElementById(\"tip\").innerHTML=this.responseText\n }\n else {\n alert(\"注册失败\")\n }\n }\n }\n //打开通道\n xhr.open(\"POST\",\"/first/zhuce\",true)\n //设置请求头的内容类型\n xhr.setRequestHeader(\"Content-Type\",\"application/x-www-form-urlencoded\")\n //转发数据\n var username=document.getElementById(\"username\").value\n var password=document.getElementById(\"password\").value\n xhr.send(\"username=\"+username+\"&password=\"+password)\n }\n }\n</script>\n<div align=\"center\">\n 用户名:<input type=\"text\" id=\"username\"><span id=\"tip\"></span> <br>\n 密码:<input type=\"password\" id=\"password\"><br>\n <button id=\"btn\">注册</button>\n</div>\n```\n\n\n\n## 基于JSON的数据交换\n\n- ```html\n <script type=\"text/javascript\">\n /*\n 在JavaScript中怎么创建JSON对象呢?\n var jsonbf={\n \"属性名1\" : 属性值,\n \"属性名2\" : 属性值,\n \"属性名3\" : 属性值,\n \"属性名4\" : 属性值\n }\n 注意:\n 属性值的数据类型,是随意的,可能是数字,也可能是字符串\n */\n // var jaon={\n // \"usercode\":1234,\n // \"username\":\"张三\",\n // \"sex\":\'男\',\n // \"age\" :20,\n // \"aihao\":[\"抽烟\",\"喝酒\",\"爱好\"],\n // \"addr\":addres\n // }\n // var addres={\n // \"city\": \"北京\",\n // \"street\": \"大兴区\",\n // \"zipcode\":\"12345号\"\n // }\n \n var jaon={\n \"usercode\":1234,\n \"username\":\"张三\",\n \"sex\":\'男\',\n \"age\" :20,\n \"aihao\":[\"抽烟\",\"喝酒\",\"爱好\"],\n \"addr\":{\n \"city\": \"北京\",\n \"street\": \"大兴区\",\n \"zipcode\":\"12345号\"\n }\n }\n \n //访问json对象\n //第一种方式\n console.log(jaon.usercode)\n console.log(jaon.usercode)\n console.log(jaon.sex?\"男\":\"女\")\n console.log(jaon.age)\n console.log(jaon.addr)\n \n for (let i = 0; i <jaon.aihao.length ; i++) {\n console.log(jaon.aihao[i])\n }\n \n //第二种方式\n console.log(jaon[\"usercode\"])\n console.log(jaon[\"username\"])\n console.log(jaon[\"sex\"]?\"男\":\"女\")\n console.log(jaon[\"age\"])\n </script>\n ```\n\n- WEB前端中如何将json格式字符串转换成JSON对象\n\n - ```html\n <script type=\"javascript\">\n /*\n 从后端java程序中响应回来的是JSON格式的字符串,怎么把他转换成json对象呢?\n 有两种方式\n 第一种:使用eval函数\n 第二种:调用JavaScript语言中的内置对象JSon的一个方法parse\n */\n var fromJavaServerStr=\"{\\\"usercode\\\":111,\\\"age\\\":20}\"\n \n var jsonb=JSON.parse(fromJavaServerStr)\n alert(jsonb.usercode+\"=\"+jsonb.age)\n </script>\n ```\n\n\n\n- 前后端使用JSON实现数据交互\n\n - 后端\n\n - ```java\n @WebServlet(\"/student\")\n public class StudentAjaxGet extends HttpServlet {\n @Override\n protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n response.setContentType(\"text/html\");\n PrintWriter out = response.getWriter();\n \n Connection conn = null;\n ResultSet rs = null;\n PreparedStatement ps = null;\n \n StringBuilder html = new StringBuilder();\n try {\n Class.forName(\"com.mysql.cj.jdbc.Driver\");\n conn = DriverManager.getConnection(\"jdbc:mysql://localhost:3306/bank\");\n \n String sql = \"select id,name,age,addrts from student\";\n ps = conn.prepareStatement(sql);\n rs = ps.executeQuery();\n html.append(\"[\");\n if (rs.next()) {\n int id = rs.getInt(\"id\");\n String name = rs.getString(\"name\");\n Long age = rs.getLong(\"age\");\n String addrts = rs.getString(\"addrts\");\n \n html.append(\"{\\\"id\\\":\"+id+\",\\\"name\\\":\"+name+\",\\\"addr\\\":\"+addrts+\"},\");\n }\n String ass=html.substring(0,html.length()-1)+ \"]\";//如果是最后一个 “,” 那么就裁去\n } catch (Exception e) {\n throw new RuntimeException(e);\n }finally {\n if (rs!=null) {\n try {\n rs.close();\n } catch (SQLException e) {\n throw new RuntimeException(e);\n }\n }\n if (ps!=null) {\n try {\n ps.close();\n } catch (SQLException e) {\n throw new RuntimeException(e);\n }\n }\n if (conn!=null) {\n try {\n conn.close();\n } catch (SQLException e) {\n throw new RuntimeException(e);\n }\n }\n }\n out.print(html);\n }\n }\n ```\n\n - 前端\n\n - ```html\n <script type=\"text/javascript\">\n window.onload=function (){\n document.getElementById(\"btn\").onclick=function(){\n //创建核心对象\n var xhr=new XMLHttpRequest()\n //注册回调函数\n xhr.onreadystatechange=function(){\n if (this.readyState == 4) {\n if (this.status == 200) {\n var html=\"\"\n var list=JSON.parse(this.responseText)\n for (var i = 0; i < list.length; i++) {\n var student=list[i]\n html+=\"<tr>\"\n html+=\"<td>\"+(i)+\"</td>\"\n html+=\"<td>\"+student.name+\"</td>\"\n html+=\"<td>\"+student.age+\"</td>\"\n html+=\"<td>\"+student.addr+\"</td>\"\n html+=\"</tr>\"\n }\n document.getElementById(\"stu\").innerHTML=html\n }\n else alert(this.status)\n }\n }\n //打开通道\n xhr.open(\"GET\",\"/first/student\",true)\n //发送请求\n xhr.send()\n }\n }\n </script>\n </head>\n <body>\n \n <input type=\"button\" id=\"btn\" value=\"显示学生列表\">\n <table width=\"50%\" border=\"1px\">\n <tr>\n <th>序号</th>\n <th>姓名</th>\n <th>年龄</th>\n <th>住址</th>\n </tr>\n <tbody id=\"stu\"></tbody>\n </table>\n ```\n\n\n\n## 基于XML的数据交换(较少、了解)\n\n- ```java\n @WebServlet(\"/student\")\n public class AjaxRequestServlet extends HttpServlet {\n @Override\n protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n //注意返回类型是xml\n response.setContentType(\"text/xml;charset=UTF-8\");\n PrintWriter out = response.getWriter();\n \n StringBuilder xml=new StringBuilder();\n xml.append(\"<students>\");\n xml.append(\"<student>\");\n xml.append(\"<name>zhangsan</name>\");\n xml.append(\"<age>20</age>\");\n xml.append(\"</student>\");\n xml.append(\"<student>\");\n xml.append(\"<name>lisi</name>\");\n xml.append(\"<age>22</age>\");\n xml.append(\"</student>\");\n xml.append(\"</studnets>\");\n \n out.print(xml);\n }\n }\n ```\n\n- ```html\n <script type=\"javascript\">\n window.onload=function (){\n document.getElementById(\"mybtnss\").onclick=function (){\n var xhr=new XMLHttpRequest();\n xhr.onreadystatechange=function (){\n if (this.readyState == 4) {\n if (this.status == 200) {\n //document.getElementById(\"mytal\").innerHTML=this.responseText\n //获取服务器端响应的一个xml字符串\n //使用xml属性接收返回自后可以自动封装成document文档对象\n var responseXML = this.responseXML;\n console.log(responseXML)\n var elementsByName = responseXML.getElementsByTagName(\"student\");\n console.log(elementsByName)\n }\n else{ alert(this.status)}\n }\n }\n xhr.open(\"GET\",\"/ajax/student?t=\"+new Date().getTime(),true)\n xhr.send()\n }\n }\n </script>\n </head>\n <body>\n \n <input type=\"button\" id=\"mybtnss\" value=\"刷新\">\n <table width=\"500px\" border=\"1px\">\n <thead>\n <tr>\n <td>序号</td>\n <td>姓名</td>\n <td>年龄</td>\n <td>地址</td>\n </tr>\n </thead>\n <tbody id=\"mytal\">\n <!-- <tr>-->\n <!-- <td>1</td>-->\n <!-- <td>张三</td>-->\n <!-- <td>20</td>-->\n <!-- <td>贵州省遵义市</td>-->\n <!-- </tr>-->\n <!-- <tr>-->\n <!-- <td>2</td>-->\n <!-- <td>李四</td>-->\n <!-- <td>22</td>-->\n <!-- <td>贵州省黔东南</td>-->\n <!-- </tr>-->\n </tbody>\n </table>\n ```\n\n\n\n## AJAX乱码问题\n\n- 测试内容:\n\n - Tomcat10\n - 发送ajax get请求\n - 发送数据到服务器,服务器获取的数据是否乱码\n - 不会\n - 服务器响应给前端中文,会不会乱码\n - 不会\n - 发送ajax post请求\n - 发送数据到服务器,服务器获取的数据是否乱码\n - 不会\n - 服务器响应给前端中文,会不会乱码\n - 不会\n\n - Tomcat9\n - 发送ajax get请求\n - 发送数据到服务器,服务器获取的数据是否乱码\n - 不会\n - 服务器响应给前端中文,会不会乱码\n - 会,需要后端添加:response.setContentType(“text/html;charset=UTF-8”);\n - 发送ajax post请求\n - 发送数据到服务器,服务器获取的数据是否乱码\n - 会,后端添加代码:request.setCharacterEncoding(“UTF-8”);\n - 服务器响应给前端中文,会不会乱码\n - 会,添加后端代码:response.setContentType(“text/html;charset=UTF-8”);\n\n\n\n## AJAX异步与同步\n\n- 什么是异步?什么是同步?\n\n - ajax请求1和ajax请求2同时并发,谁也不等谁就是异步\n\n - ajax请求1在发送的时候需要等待ajax请求2结束之后才能发送,那么就是同步\n\n - 异步和同步在代码上怎么实现?\n\n - ```javascript\n //ajax请求1\n //如果第三个参数是false:这个就表示ajax请求1不支持异步,也就是说请求发送之后,会影响其他的ajax请求,只有当这个请求结束之后,其他的请求才能发送\n xhr.open(\"请求方式\",\"URL\",false)\n \n //ajax请求2\n //如果第三个参数是true:就表示ajax请求2支持异步,也就是说请求发送之后,不影响其他的ajax请求发送\n xhr.open(\"请求方式\",\"URL\",true)\n ```\n\n\n\n- 异步具体代码实现\n\n - 请求1的后端\n\n - ```java\n @WebServlet(\"/student2\")\n public class AjaxRequestServletText extends HttpServlet {\n @Override\n protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n response.setContentType(\"text/html;charset=UTF-8\");\n PrintWriter out = response.getWriter();\n \n try {\n Thread.sleep(10*1000);\n } catch (InterruptedException e) {\n throw new RuntimeException(e);\n }\n out.print(\"ajax 异步请求1\");\n }\n }\n ```\n\n - 请求2的后端\n\n - ```java\n @WebServlet(\"/stu\")\n public class AjaxRequestServlet2 extends HttpServlet {\n @Override\n protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n response.setContentType(\"text/html;charset=UTF-8\");\n PrintWriter out = response.getWriter();\n \n out.print(\"ajax 异步请求2\");\n }\n }\n ```\n\n - 前端\n\n - ```html\n <script>\n window.onload=function (){\n document.getElementById(\"btn1\").onclick=function (){\n var xhr=new XMLHttpRequest()\n xhr.onreadystatechange=function (){\n if (this.readyState == 4) {\n if (this.status == 200) {\n document.getElementById(\"dv1\").innerHTML=this.responseText\n }\n }\n }\n xhr.open(\"GET\",\"/ajax/student2?t=\"+new Date().getTime(),true)\n xhr.send()\n }\n \n document.getElementById(\"btn2\").onclick=function (){\n var xhr1=new XMLHttpRequest()\n xhr1.onreadystatechange=function (){\n if (this.readyState == 4) {\n if (this.status == 200) {\n document.getElementById(\"dv2\").innerHTML=this.responseText\n }\n }\n }\n xhr1.open(\"GET\",\"/ajax/stu?t=\"+new Date().getTime(),true)\n xhr1.send()\n }\n }\n </script>\n <button id=\"btn1\">ajax请求1</button>\n <div id=\"dv1\"></div>\n \n <button id=\"btn2\">ajax请求2</button>\n <div id=\"dv2\"></div>\n ```\n\n - 当程序运行之后,点击请求1,紧接着点击请求2,会发现,当请求1睡眠的时间里,请求2是能执行的\n\n\n- 同步实现\n - 更改为false即可\n - 当程序运行之后,在请求1睡眠的时间中,请求2 无法执行\n\n## AJAX代码封装\n\n- AJAX请求相关的代码都是类似的,有很多重复的代码,这些重复的代码,能不能封装成一个类。要发送ajax请求的话,就直接调用这个类的相关函数即可\n\n- 接下来,手动封装一个工具类,这个工具类我们可以把他看做是一个js的库,叫做JQuery。(这里封装的jQuery只是前端的库,和后端没有关系)\n\n - 初步代码\n\n - ```html\n <script>\n \n /*\n 封装一个函数,通过这个函数获取到html页面中的节点\n 要封装的代码是:根据id来获取元素:document.getElementById(\"btn1\")\n 设计思路来自于css代码\n */\n function jQuery(selector){\n /* \n selector 可能是#id,也可能是其他选择器,例如类选择器: .id\n */\n if(typeof selector==\"string\"){\n //selector是一个id选择器\n var domObj=document.getElementById(selector.substring(1))\n return domObj\n }\n \n if(typeof selector==\"function\"){//如果selector是一个函数类型\n window.onload=selector\n }\n }\n $=jQuery//将jQuery赋给变量 $\n /* window.onload=function(){\n $(\"#btn1\").onclick=function(){\n $(\"#dv1\").innerHTML=\"<font color=\'red\'>用户名不可用!!!</font>\"\n //$(\".myclass\").innerHTML=\"我是span\"\n }\n } */\n \n $(function(){\n $(\"#btn1\").onclick=function(){\n $(\"#dv1\").innerHTML=\"<font color=\'red\'>用户名不可用!!!</font>\"\n }\n })\n </script>\n </head>\n <body>\n <button id=\"btn1\">显示信息</button>\n <div id=\"dv1\"></div>\n <span class=\"myclass\"></span>\n <span class=\"myclass\"></span>\n <span class=\"myclass\"></span>\n <span class=\"myclass\"></span>\n </body>\n ```\n\n \n\n- JS代码中,如何定义一个类\n\n - ```javascript\n <script>\n //JS中怎么定义一个类\n function User(username,password){\n this.username=username\n this.password=password\n //实例方法,通过对象调用\n this.doSom=function(){\n console.log(username+\"doSom....\")\n }\n //静态方法,通过类名调用\n User.doOther=function(){\n console.log(\"user doOther.....\")\n }\n }\n \n //创建对象,访问对象属性,访问对象方法,访问静态方法\n //User():直接这样调用,只是把他当做一个普通的函数去执行,不会创建这个对象,堆中也没有这个对象\n //new User():这样调用的话,其实就是在调用这个类的构造方法,创建对象,并在堆中分配空间\n var user=new User(\"1111\",\"zhangsan\")\n //访问属性\n alert(user.username+\",\"+user.password)\n //调用方法(实例方法)\n user.doSom()\n //调用静态方法\n User.doOther()\n \n //如果想后期给某个类型扩展方法,可以使用prototype属性\n User.prototype.getUsername=function(){\n console.log(\"扩展方法:getUsername\")\n }\n user.getUsername()\n </script>\n ```\n\n\n\n\n- 手写jQuery库的基本实现代码\n\n - ```html\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>手动封装JS库jQuery</title>\n <script>\n \n \n function jQuery(selector){\n if(typeof selector==\"string\"){\n //selector是一个id选择器\n //全局变量,即不加var的变量\n domObj=document.getElementById(selector.substring(1))\n //return domObj\n //如果是返回的domObj对象,那么就无法在$(\"#dv1\").html(\"<font color=\'red\'>用户名不可用!!!</font>\")这个代码中使用html方法\n //因为这个html方法是JQuery的内置方法,只能通过类名来调用\n return new jQuery\n }\n \n if(typeof selector==\"function\"){//如果selector是一个函数类型\n window.onload=selector\n }\n \n //定义一个html方法,代替innerHtml=\"\"\n this.html=function(htmlStr){\n domObj.innerHTML=htmlStr\n }\n //定义一个函数,代替onclick属性\n this.cllick=function(clicle){\n domObj.onclick=clicle\n }\n \n \n this.onchange=function(htmlStr){\n domObj.onchange=htmlStr\n }\n //定义一个函数代替value属性\n this.val=function(v){\n if(v==undefined){//判断是否有修改的值\n return domObj.value \n }else{\n domObj.value=v\n }\n }\n }\n \n \n $=jQuery//将jQuery赋给变量 $\n \n $(function(){\n //注意:这里就不能使用onclick属性了,因为jQuery对象没有这个属性\n //$(\"#btn1\").onclick=function(){}\n $(\"#btn1\").cllick(function(){\n $(\"#dv1\").html(\"<font color=\'red\'>用户名不可用!!!</font>\")\n \n //获取文本框的value\n var username=$(\"#username\").val\n \n //修改文本框的value\n //document.getElementById(\"username\").value=\"呵呵\"\n var username=$(\"#username\").val(\"aasd\")\n alert(username)\n })\n })\n </script>\n </head>\n <body>\n 用户名:<input type=\"text\" id=\"username\">\n <button id=\"btn1\">显示信息</button>\n <div id=\"dv1\"></div>\n \n <select onchange=\"xz\">\n <option id=\"op\">大学</option>\n <option id=\"gz\">高中</option>\n <option id=\"cz\">初中</option>\n <option id=\"xx\">小学</option>\n </select>\n <div id=\"dx\"></div>\n </body>\n </html>\n ```\n\n \n\n- 将jQuery单独写到js文件中,使用时引入库\n\n - 单独写到js文件中\n\n - ```js\n function jQuery(selector){\n if(typeof selector==\"string\"){\n domObj=document.getElementById(selector.substring(1))\n return new jQuery\n }\n if(typeof selector==\"function\"){\n window.onload=selector\n }\n this.html=function(htmlStr){\n domObj.innerHTML=htmlStr\n }\n this.click=function(clicle){\n domObj.onclick=clicle\n }\n this.onchange=function(htmlStr){\n domObj.onchange=htmlStr\n }\n this.val=function(v){\n if(v==undefined){\n return domObj.value \n }else{\n domObj.value=v\n }\n }\n } \n $=jQuery\n ```\n\n - 测试\n\n - ```html\n <script type=\"text/javascript\" src=\"jQuery.-1.0.0.js\"></script>//引入jQuery库\n \n <script>\n $(function(){\n $(\"#btn\").click(function(){\n //alert(\"hrllo\")\n alert($(\"#te\").val(\"hello\"))\n })\n })\n </script>\n <button id=\"btn\">哈喽</button>\n <input type=\"text\" id=\"te\"/>\n <div id=\"dv\"></div>\n ```\n\n \n\n- 封装ajax的代码\n\n - ```js\n function jQuery(selector){\n if(typeof selector==\"string\"){\n domObj=document.getElementById(selector.substring(1))\n return new jQuery\n }\n if(typeof selector==\"function\"){\n window.onload=selector\n }\n this.html=function(htmlStr){\n domObj.innerHTML=htmlStr\n }\n this.click=function(clicle){\n domObj.onclick=clicle\n }\n this.onchange=function(htmlStr){\n domObj.onchange=htmlStr\n }\n this.val=function(v){\n if(v==undefined){\n return domObj.value \n }else{\n domObj.value=v\n }\n }\n \n //定义一个静态方法,发送ajax请求\n /* \n 分析:使用ajax函数,需要传什么参数\n 请求的方式(type):GET/POST\n 请求的URL(url):URL\n 请求时提交的数据(data):data\n 请求时发送异步还是同步(async):true、false\n 可以使用JSON的方式传参数\n {\n type : \"\"\n url : \"\"\n data : \"\" \n asnyc : \"\"\n }\n */\n jQuery.ajax=function(jsonArge){\n var xhr=new XMLHttpRequest()\n xhr.onreadystatechange=function(){\n if(this.readyState==4){\n if(this.status==200){\n //我们这个工具在封装的时候,向不考虑这么多,假设服务器返回的都是JSON格式的字符串\n var jsonObj = JSON.parse(this.responseText)\n //调用函数\n jsonArge.success(jsonObj) \n }\n }\n }\n if(jsonArge.type.toUpperCase()==\"POST\"){//判断是否是POST请求,以防止是小写,所以全部转大写\n xhr.open(\"POST\",jsonArge.url,jsonArge.async)\n xhr.setRequestHeader(\"Content-Type\",\"application/x-www-form-urlencoded\")\n xhr.send(jsonArge.data)\n }\n if(jsonArge.type.toUpperCase()==\"GET\"){\n xhr.open(\"GET\",jsonArge.url + \"?\" +jsonArge.data,jsonArge.async)\n xhr.send\n }\n }\n } \n $=jQuery\n //注意:静态方法虽然可以直接使用类名去调用,但是也需要new对象之后才能调用\n new jQuery()\n ```\n\n - 测试\n\n - ```html\n <script type=\"text/javascript\" src=\"jQuery.-1.0.0.js\"></script>\n <script>\n $.ajax({\n type : \"POST\",\n url : \"/ajax/student\",\n data : \"username=\" + username,\n async : true,\n //程序响应完成之后,对于客户端来说要拿到响应的数据,然后解析这个数据,响应到页面上\n success : function(json){\n document.getElementById(\"dv\").innerHTML=json.uname\n }\n })\n </script>\n <button id=\"btn\">发送ajax请求</button>\n <div id=\"dv\"></div>\n ```\n\n \n\n## AJAX实现省市联动\n\n- 什么是省市联动?\n\n - 在网页上,选择对应的省份之后,动态的关联出该省份对应的市,选择对应的市之后,动态关联出对应的县或者区\n\n- 进行数据表的设计\n\n - ```\n t_area\n id(PK-自增) code name pcode\n ------------------------------------------------------\n 1 001 河北省 null\n 2 002 河南省 null\n 3 003 石家庄 001\n 4 004 邯郸 001\n 5 005 郑州 002\n 6 006 洛阳 002\n 7 007 丛台区 004\n ```\n\n- 首先实现第一个功能\n\n - 页面加载完毕之后,将省份加载完成\n\n- 后端代码\n\n - ```java\n @WebServlet(\"/sheng\")\n public class AjaxHttpServlet extends HttpServlet {\n @Override\n protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n response.setContentType(\"text/html\");\n PrintWriter out = response.getWriter();\n String pcode = request.getParameter(\"pcode\");\n \n List<Area> list=new ArrayList<>();\n //连接数据库,获取所有的对应区域\n Connection conn=null;\n PreparedStatement ps=null;\n ResultSet rs=null;\n \n try {\n Class.forName(\"com.mysql.cj.jdbc.Driver\");\n conn= DriverManager.getConnection(\"jdbc:mysql://localhost:3306/powerndoe\",\"root\",\"root\");\n \n String sql=null;\n if (pcode==null){\n sql=\"select code,name from t_area where pcode is null\";\n ps=conn.prepareStatement(sql);\n }\n if (pcode!=null){\n sql=\"select code,name from t_area where pcode=?\";\n ps=conn.prepareStatement(sql);\n ps.setString(1,pcode);\n }\n rs=ps.executeQuery();\n if (rs.next()){\n String code = rs.getString(\"code\");\n String name = rs.getString(\"name\");\n \n Area area=new Area(code,name);\n list.add(area);\n }\n } catch (Exception e) {\n throw new RuntimeException(e);\n }finally {\n if (rs!=null) {\n try {\n rs.close();\n } catch (SQLException e) {\n throw new RuntimeException(e);\n }\n }\n if (ps!=null) {\n try {\n ps.close();\n } catch (SQLException e) {\n throw new RuntimeException(e);\n }\n }\n if (conn!=null) {\n try {\n conn.close();\n } catch (SQLException e) {\n throw new RuntimeException(e);\n }\n }\n }\n String json=JSON.toJSONString(list);//将字符串转换成JSON格式的字符串\n out.print(json);\n }\n }\n ```\n\n\n\n- 前端代码\n\n - ```html\n <script type=\"javascript\" src=\"jQuery.js\"></script>\n <script>\n //页面加载完毕之后,显示省份\n </script>\n $(function(){\n $.ajax({\n type : \"GET\",\n url : \"/ajax003/sheng\",\n data : \"t=\" + new Data().getTime(),\n async : true,\n success : function(json){\n //[{\"code\" : \"001\" , \"name\" : \"河南省\"},{\"code\" : \"002\" , \"name\" : \"河北省\"}]\n //以上,是希望系统返回的信息\n for(var i=0; i<json.length ;i++){\n var area=json[i]\n //这里拼接字符串\n }\n //关联市\n $(\"#city\").html(html)\n }\n })\n //只要change事件发生就发生请求\n $(\"#province\").change(function(){\n //alert(\"123\")\n //alert(this.value)//获取select下拉框的子框的value属性\n \n })\n })\n <select id=\"province\" onchange=\"\">\n <!-- <option value=\"\">--请选择省份--</option>-->\n <!-- <option value=\"001\">河北省</option>-->\n <!-- <option value=\"002\">河南省</option>-->\n </select>\n <select id=\"city\">\n \n </select>\n ```\n\n\n\n## AJAX跨域问题\n\n##### 跨域\n\n- 跨域是指从一个域名的网页去请求另一个域名的资源。比如从百度页面去请求京东的资源\n\n - ![image-20230415161048054](E:\\programming\\AJAX\\picture\\image-20230415161048054.png)\n\n- 通过超链接或者form表单的提交或者window.location.href的方式去进行跨域是不存在的问题。但在下一个域名的网页中的一段js代码发送ajax请求去访问另一个域名中的资源,由于同源策略的存在导致无法跨域访问,那么ajax就存在这种跨域问题\n\n- 同源策略(CORS) 是指一段脚本中只能读取来自同一来源的窗口和文档的属性,同源就是协议、域名、端口都相同\n\n- 同源策略有什么用?\n\n - 如果刚刚在网银输入账号密码,查看了自己还有1w钱,紧接着访问一些不规矩的网站,这个网站可以访问刚刚的网银站点,并且获取账号密码,那么后果可想而知。所以,从安全方面来说,同源策略是有利于保护网站信息的\n\n- 解决跨域问题\n\n - 超链接:跨域\n\n - 准备:\n\n - 编写两个模块:a应用和b应用\n - 准备两个服务器:Tomcat1(port : 8080)和Tomcat2(port : 8081),注意:两个服务器的端口要不同\n\n - 编写index页面\n\n - a应用\n\n - ```html\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>a应用的index</title>\n </head>\n <body>\n <h1>a页面的index</h1>\n <a href=\"htpp://localhost:8081/b/index.html\">b的index页面(跨域访问)</a>\n //访问b应用的资源,这个路径填写是关键\n </body>\n </html>\n ```\n\n - b应用\n\n - ```html\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>b应用的index</title>\n </head>\n <body>\n <h1>b页面的index</h1>\n </body>\n </html>\n ```\n\n - form表单跨域\n\n - a应用\n\n - ```html\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>a应用的index</title>\n </head>\n <body>\n <form action=\"htpp://localhost:8081/b/user/reg\" method=\"post\">\n 用户名:<input type=\"text\" name=\"username\">\n 密码 :<input type=\"password\" name=\"password\">\n <input type=\"submit\" value=\"注册\">\n </form>\n </body>\n </html>\n ```\n\n - b应用\n\n - ```html\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>b应用的index</title>\n </head>\n <body>\n <form action=\"\" method=\"post\">\n 用户名:<input type=\"text\" name=\"username\">\n 密码 :<input type=\"password\" name=\"password\">\n <input type=\"submit\" value=\"注册\">\n </form>\n </body>\n </html>\n ```\n\n - 通过js代码中的window.location.href / document.location.href跨域\n\n - a应用\n\n - ```html\n <button onclick=\"window.location.href=\'http://localhost:8081/b/index.html\'\">b页面的index页面(跨域访问)</button>\n \n <button onclick=\"document.location.href=\'http://localhost:8081/b/index.html\'\">b页面的index页面(跨域访问)</button>\n ```\n\n - 怎么在a应用里面使用b应用的js文件\n\n - a应用的index页面\n\n ```javascript\n <script type=\"javascript\" src=\"http://localhost:8081/b/my.js\"></script>\n ```\n\n##### 同源还是不同源\n\n- 区分同源的三要素\n\n - 协议\n - 端口\n - 域名\n\n- 协议一致、域名一致、端口一致,三个要素都一致,才是同源,其他的一律不同源\n\n- | URL1 | URL2 | 是否同源 | 描述 |\n | ---------------------------------- | ------------------------------- | -------- | ------------------ |\n | http://localhost:8080/a/index.html | http://localhost:8080/a/first | 同源 | 协议 域名 端口一致 |\n | http://localhost:8080/a/index.html | http://localhost:8080/b/first | 同源 | 协议 域名 端口一致 |\n | http://www.myweb.com:8080/a.js | https://www.myweb.com:8081/b.js | 不同源 | 协议不同 |\n | http://www.myweb.com/a.js | http://crm.myweb.com/b.js | 不同源 | 子域名不同 |\n\n##### AJAX跨域解决方案\n\n###### 在后端设置响应头\n\n- ```java\n //允许某个\n response.setHeader(\"Access-Control-Allow-Origin\",\"http://localhost:8080\");\n 或者\n //允许所有\n response.setHeader(\"Access-Control-Allow-Origin\",\"*\");\n ```\n\n###### jsonp\n\n- jsonp:json with padding(带填充的json)\n\n- jsonp不是一个真正的ajax请求。只不过可以完成ajax的局部刷新效果。可以说jsonp是一种类aja的请求机制\n\n- jsonp不是ajax请求,但是可以完成局部刷新效果,并且可以解决跨域问题\n\n- ```html\n <!--\n 超链接也可以实现跨域,为什么不用呢?因为超链接点击之后会跳转页面,无法做到局部刷新效果\n script标签也是可以实现跨域的。src属性可以是xxx.js文件,那这个路径可以是一个servlet路径吗?可以\n -->\n <!--防止后端将函数名写死,所以将函数名传到后端-->\n <script type=\"javascript\" src=\"http://localhost:8081/b/jsonp1?funname=sayHello\"\n <script>\n function sayHrllo(fun){\n //alert(\"sahello\")\n alert(\"hello,\" + fun.name + \"! your mima is\" + fun.mima)\n }\n </script>\n ```\n\n- 后端\n\n - ```java\n System.out.println(\"jsonp方式完成跨域访问\")\n //向前端响应一段js代码,这个alert函数是JS内置的函数,可以直接使用\n out.print(\"alert(\"hello\")\")\n \n //这也是响应一段js代码,只不过这个函数是程序员自己定义的\n //注意:不要误以为是后端java调用了sayHello()函数,实际上后端只负责响应一个字符串回去\n //真正的调用者,还是浏览器,浏览器接受到这个字符串之后,会自动将这个字符串当做一段js代码去执行\n out.print(\"sayHello()\")\n String funname=request.getParameter(\"funname\");\n out.print(funname+\"({\"name\" : \"zhangsan\" , \"mima\" : \"1234\"})\");\n ```\n\n- 注意:jsonp解决跨域的时候,只支持GET请求。不支持POST请求\n\n- 如何使用jsonp实现局部刷新达到ajax的效果\n\n - ```html\n <!-- 如何实现点击按钮,才执行以下代码 -->\n <!-- <script type=\"text/javascript\" src=\"http://localhost:8081/jsonp1?fun=sayHello\"></script> -->\n <script>\n function sayHello(data){\n document.getElementById(\"dv\").innerHTML=data.username\n }\n window.onload=()=>{\n document.getElementById(\"btn\").onclick=()=>{\n //加载script元素\n //创建script元素对象\n const htmlScriptElement=document.createElement(\"script\")\n //设置script的type属性\n htmlScriptElement.type=\"text/javascript\"\n //设置script的src属性\n htmlScriptElement.src=\"http://localhost:8081/jsonp1?fun=sayHello\"\n //将script对象添加到body标签中(这一步就是加载script)\n //将标签放到body下的 0 位置的子标签中\n document.getElementsByTagName(\"body\")[0].appendChild(htmlScriptElement)\n }\n }\n </script>\n <button id=\"btn\">josn解决跨域问题,达到ajax局部刷新的效果</button>\n <div id=\"dv\"></div>\n ```\n\n\n\n###### jQuery封装的jsonp\n\n- 以及存在着封装好的jQuery库,只需要引入js文件即可\n\n - ```html\n <!-- 引入jQuery库:这个jQuery是官网的 -->\n <!-- 在 a 根目录下的js目录中 -->\n <script type=\"text/javascript\" src=\"/a/js/jquery-3.6.0.min.js\"></script>\n <script>\n function sayHello(data){\n alert(\"欢迎你\" + data.username)\n }\n $(function(){\n $(\"bt\").click(function(){\n //发送所谓的ajax请求(本质上并不是ajax请求,只是被看做ajax请求)\n $.ajax({\n type : \"GET\",\n url : \"http://localhost:8081/b/jsonp1\",\n dataType : jsonp,//指定的数据类型是jsonp [最关键的就是这个]\n /* \n 还有很多参数\n jsonp : \"fun\",指定参数的名字,即不使用callback这个默认的参数名\n jsonpCallback : function(data){\n //不采用指定的回调函数,用这个属性来指定具体的回调函数\n //当采用这种方式的时候,后端获取回调函数名就不再是通过callback了,而是使用自定义的\n //data变量用来接收服务器端的响应(data是一个json:{\"username\" : \"zhangsan\"})\n $(\"dv\").html(\"欢迎你\" + data.username)\n },\n */\n \n success : function(data){\n //data变量用来接收服务器端的响应(data是一个json:{\"username\" : \"zhangsan\"})\n $(\"dv\").html(\"欢迎你\" + data.username)\n }\n })\n })\n })\n </script>\n <button id=\"btn\">jquery-3封装的库</button>\n <div id=\"dv\"></div>\n ```\n\n - 后端\n\n - ```java\n String callback=request.getParameter(\"callback\");\n response.getWriter().print(callback+\"({\"username\" : \"lisi\"})\");\n ```\n\n \n\n###### 代理机制(httpclient)\n\n![image-20230415162254024](E:\\programming\\AJAX\\picture\\image-20230415162254024.png)\n\n- 使用java程序怎么去发送 get/post 请求呢?\n\n - 第一种方法:使用JDK内置的API【java.net.URL】,这些API是可以发送HTTP请求的\n - 第二种方法:使用第三方的开源组件,例如:Apache的httpclient组件。(httpclient组件是开源免费的)\n\n- 使用httpclient组件发送http请求\n\n - 可以不做深入了解,具体代码可以去网上查\n\n - ```java\n public static void main(String[] args) {\n //使用java代码去发送http的get请求\n // 目标地址\n //String url = \"https://www.baidu.com\";\n String usrl=\"http://localhost:8081/b/jsonp1\";\n HttpGet httpGet = new HttpGet(url);//发送http get请求\n // 设置类型 \"application/x-www-form-urlencoded\" \"application/json\"\n httpGet.setHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\n System.out.println(\"调用URL: \" + httpGet.getURI());\n //httpClient实例化\n CloseableHttpClient httpClient = HttpClients.createDefault();\n // 执行请求并获取返回\n HttpResponse response = httpClient.execute(httpGet);\n //System.out.println(\"Response toString()\" + response.toString());\n HttpEntity entity = response.getEntity();//获取响应数据\n System.out.println(\"返回状态码:\" + response.getStatusLine());\n // 显示结果\n BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), \"UTF-8\"));\n String line = null;\n StringBuffer responseSB = new StringBuffer();\n while ((line = reader.readLine()) != null) {\n responseSB.append(line);\n }\n System.out.println(\"返回消息:\" + responseSB);\n reader.close();\n httpClient.close();\n }\n ```\n\n ###### Nginx反向代理\n\n## AJAX实现搜索联想 自动补全\n\n- 什么是搜索联想?什么是自动补全?\n - 百度是一个很典型的代表。在百度搜索框中输入相关信息的时候,会有搜索联想以及自动补全\n - 搜索联想和自动补全:实际上是为了方便用户使用。让用户体验更好\n - 搜索联想:当用户输入一些单词之后,自动联想出用户要搜索的信息,给一个提示\n - 自动补全:当联想出一些内容之后,用户点击某个联想的单词之后,然后将这个单词自动补全到搜索框中\n - 搜索联想和自动补全功能,因为是页面布局刷新效果,所以需要使用ajax请求来完成\n- 搜索联想、自动补全功能实现的核心\n - 当键盘事件发生之后,比如:keyup:键盘弹起事件\n - 发生ajax请求,请求中提交用户输入的搜索内容,例如:北京(发生ajax请求,携带 “北京” 两个字)\n - 后端接收ajax请求,接收到 “北京” 两个字,执行select语句进行模糊查询。返回查询结果\n - 将查询的结果封装成JSON格式的字符串,将JSON格式的字符串响应到前端\n - 前端接收JSON格式的字符串之后,解析这个JSON字符串,动态显示页面\n\n\n\n- 前端\n\n - ```html\n <title>ajax实现搜索联想和自动补全</title>\n <style>\n .uerInput{\n width: 300px;\n height: 25px;\n font-size: 20px;\n padding-left: 5px;\n }\n .showDataDiv{\n width: 300px;\n padding-left: 3px;\n border: 1px solid lightgray;\n background-color: azure;\n /*隐藏这个div*/\n display: none;\n }\n .showDataDiv p{\n margin-top: 5px;\n margin-bottom: 5px;\n }\n .showDataDiv p:hover{\n /*当鼠标移动到p标签上时,变成小手*/\n cursor: pointer;\n /*添加边框*/\n border: 1px blue solid;\n /*改变背景色*/\n background-color: antiquewhite;\n }\n </style>\n </head>\n <script>\n /*不使用jQuery,使用原生的ajax实现搜索联想和自动补全*/\n window.onload=()=>{\n document.getElementById(\"ss\").onkeyup=function (){\n if (this.value = \"\") {\n document.getElementById(\"dv\").style.display=\"none\"\n }\n //console.log(this.value)\n /*发送ajax请求*/\n var xhr=new XMLHttpRequest()\n xhr.onreadystatechange=function (){\n if (this.readyState==4){\n if (this.status==200){\n /*将后端的数据转换为JSON字符串*/\n const json=JSON.parse(xhr.responseText)\n /*遍历数组*/\n let html=\"\"\n for (let i=0;i<json.length;i++){\n html += \"<p onclick=\'setInput(\\\"\"+json[i].content+\"\\\")\'>\" + json[i].content + \"</p>\"\n }\n /*将html放到div标签里面*/\n document.getElementById(\"dv\").innerHTML=html\n /*将div显示出来*/\n document.getElementById(\"dv\").style.display=\"block\"\n }\n }\n }\n xhr.open(\"GET\",\"http://localhost:8080/zdbq/autocomplete?t=\"+new Date().getTime()+\"&value=\"+this.value,true)\n xhr.send()\n }\n }\n function setInput(content){\n //点击p之后,将p的value传到input 标签中\n document.getElementById(\"ss\").value=content\n //隐藏p标签\n document.getElementById(\"dv\").style.display=\"block\"\n }\n </script>\n <body>\n <input type=\"text\" id=\"ss\" class=\"uerInput\">\n \n \n <div id=\"dv\" class=\"showDataDiv\">\n <!-- <p>北京市的最新消息</p>-->\n <!-- <p>北京天气</p>-->\n <!-- <p>北京地名</p>-->\n <!-- <p>北京美食</p>-->\n <!-- <p>北京的那些事</p>-->\n </div>\n ```\n\n- 后端\n\n - ```java\n response.setContentType(\"text/html;charset=UTS-8\");\n StringBuilder sb=null;\n Connection conn=null;\n PreparedStatement ps=null;\n ResultSet rs=null;\n \n try {\n Class.forName(\"com.mysql.cj.jdbc.Driver\");\n conn= DriverManager.getConnection(\"jdbc:mysql://localhost:3306/powerndoe\",\"root\",\"root\");\n \n String sql=\"select log from powernode where log like ?\";\n ps=conn.prepareStatement(sql);\n ps.setString(1,value+\"%\");\n rs= ps.executeQuery();\n sb.append(\"[\");\n if (rs.next()) {\n String log = rs.getString(\"log\");\n sb.append(\"{\\\"log\\\" : \\\"\"+log+\"\\\"},\");\n }\n \n } catch (Exception e) {\n throw new RuntimeException(e);\n }finally {\n if (rs!=null) {\n try {\n rs.close();\n } catch (SQLException e) {\n throw new RuntimeException(e);\n }\n }\n if (ps!=null) {\n try {\n ps.close();\n } catch (SQLException e) {\n throw new RuntimeException(e);\n }\n }\n if (conn!=null) {\n try {\n conn.close();\n } catch (SQLException e) {\n throw new RuntimeException(e);\n }\n }\n }\n //去除最后一个 “,”\n response.getWriter().print(sb.substring(0,sb.length()-1)+\"]\");\n ```', 1, 'html,测试', '2024-05-05 21:28:48', '# AJAX(Asynchronous JavaScript And Xml)', 'h1AJAX(Asynchronous JavaScript And Xml)h1\nh2传统请求及缺点h2\nul\nli传统的请求都有哪些ul\nli直接在浏览器的地址栏输入urlli\nli点击超链接li\nli提交form表单li\nli使用JS代码发送请求ul\nliwindow.openurlli\nlidocument.location.hrefurlli\nliwindow.location.hrefurlli\nli...li\nul\nli\nli传统请求存在的问题ul\nli页面全部刷新导致用户', 'http://localhost:8080/image/1714915727991-Ajax.png', 1, 0, 1, '0138d16249034830b9bd09a805327e68');
INSERT INTO `b_content` VALUES (31, '2023-01-05 21:35:46', 0, 6, 0, 2, 0, '# 入门篇\n\n## Docker的常用命令\n\n### 帮助命令\n\n```shell\ndocker version #显示docker的版本信息\ndocker info #显示docker的系统信息,包括镜像和容器的数量\ndocker 命令 --help #帮助命令\n```\n\n帮助文档地址:[Docker run reference | Docker Docs](https://docs.docker.com/engine/reference/run/)\n\n### 镜像命令\n\n```shell\n#查看本地主机上的所有镜像\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker images\nREPOSITORY TAG IMAGE ID CREATED SIZE\nhello-world latest 9c7a54a9a43c 4 months ago 13.3kB\nmysql latest 3218b38490ce 20 months ago 516MB\ncentos latest 5d0da3dc9764 24 months ago 231MB\n\n\n#解释\nREPOSITORY 镜像的仓库源\nTAG 镜像的标签\nIMAGE ID 镜像的id\nCREATED 镜像的创建时间\nSIZE 镜像的大小\n\n\n#可选项\n-a,-all # 列出所有的镜像\n-q,--quiet #只显示镜像的id\n\n#显示所有镜像的id\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker images -aq\n9c7a54a9a43c\n3218b38490ce\n5d0da3dc9764\n```\n\n#### 搜索镜像\n\n```shell\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker search mysql\n\n# 可选项\n--filter=STARS=3000 #搜索出来的镜像就是STARS大于3000的\n\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker search mysql --filter=STARS=3000\nNAME DESCRIPTION STARS OFFICIAL AUTOMATED\nmysql MySQL is a widely used, open-source relation… 14427 [OK] \nmariadb MariaDB Server is a high performing open sou… 5508 [OK] \n```\n\n![image-20230906112419651](E:\\programming\\docker\\image\\docker.md)\n\n \n\n#### 下载镜像\n\n```shell\n#docker pull 镜像名[:tag]\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker pull mysql\nUsing default tag: latest #如果不写tag,默认就是latest\nlatest: Pulling from library/mysql\n72a69066d2fe: Already exists #分层下载,docker images的核心,联合文件系统,如果以及下载,则显示已存在\n93619dbc5b36: Already exists \n99da31dd6142: Already exists \n626033c43d70: Already exists \n37d5d7efb64e: Already exists \nac563158d721: Already exists \nd2ba16033dad: Already exists \n688ba7d5c01a: Already exists \n00e060b6d11d: Already exists \n1c04857f594f: Already exists \n4d7cfa90e6ea: Already exists \ne0431212d27d: Already exists \nDigest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709\nStatus: Downloaded newer image for mysql:latest\ndocker.io/library/mysql:latest #真实地址\n#docker pull mysql 等价于 docker pull docker.io/library/mysql:latest\n\n#指定版本下载\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker pull mysql:5.7\n5.7: Pulling from library/mysql\n72a69066d2fe: Already exists \n93619dbc5b36: Already exists \n99da31dd6142: Already exists \n626033c43d70: Already exists \n37d5d7efb64e: Already exists \nac563158d721: Already exists \nd2ba16033dad: Already exists \n0ceb82207cd7: Pull complete \n37f2405cae96: Pull complete \ne2482e017e53: Pull complete \n70deed891d42: Pull complete \nDigest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94\nStatus: Downloaded newer image for mysql:5.7\ndocker.io/library/mysql:5.7\n\n\n```\n\n#### 删除镜像\n\n```shell\n#删除指定的镜像\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker rmi -f 3218b38490ce #这里写镜像id或者镜像名称\nUntagged: mysql:latest\nUntagged: mysql@sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709\nDeleted: sha256:3218b38490cec8d31976a40b92e09d61377359eab878db49f025e5d464367f3b\n\n#删除多个镜像\ndocker rmi -f 镜像id 镜像id 镜像id\n\n\n#删除所有镜像\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker rmi -f $(docker images -aq)\nUntagged: hello-world:latest\nUntagged: hello-world@sha256:dcba6daec718f547568c562956fa47e1b03673dd010fe6ee58ca806767031d1c\nDeleted: sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d\nUntagged: centos:latest\nUntagged: centos@sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177\nDeleted: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6\nDeleted: sha256:74ddd0ec08fa43d09f32636ba91a0a3053b02cb4627c35051aff89f853606b59\n\n\n```\n\n### 容器命令\n\n```shell\n#下载centos镜像\ndocker pull centos\n```\n\n新建容器并启动\n\n```shell\ndocker run [可选参数] image\n\n#参数说明\n--name=\"name\" #容器名字,Tomcat01 Tomcat02,用来区分容器\n-d #后台的方式运行\n-it #使用交互方式运行,进入容器查看内容\n-p 指定容器的端口 -p 8080:8080\n -p ip:主机端口:容器端口\n -p 主机端口:容器端口(常用)\n -p 容器端口\n 容器端口\n-p #随机指定端口\n\n\n#测试,启动并进入主机\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker run -it centos /bin/bash\n[root@7bde97a7f285 /]# ls\nbin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var\n\n#从容器中退出主机\n[root@7bde97a7f285 /]# exit\nexit\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# ls\nbin boot dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var\n```\n\n**列出所有的运行的容器**\n\n```shell\n#docker ps [可选参数]\n #列出当前正在运行的容器\n-a #列出当前正在运行的容器以及历史运行过的容器 \n-n=? #列出最近创建的容器\n-q #列出正在运行的容器的编号\n-aq #列出所有正在运行的容器编号\n\n\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker ps\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n\n\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker ps -a\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n7bde97a7f285 centos \"/bin/bash\" 2 minutes ago Exited (0) About a minute ago intelligent_aryabhata\n2c150cae4477 3218b38490ce \"docker-entrypoint.s…\" 6 hours ago Exited (1) 6 hours ago elated_mirzakhani\n14e965d2fa0d 9c7a54a9a43c \"/hello\" 2 days ago Exited (0) 2 days ago great_shaw\n```\n\n\n\n**退出容器**\n\n```shell\nexit #停止并退出容器\nCtrl + P + Q #容器不停止退出\n```\n\n\n\n**删除容器**\n\n```shell\ndocker rm 容器id #删除指定的容器。不能删除正在运行的容器,如果要强制删除使用docker rm -f\ndocker rm -f $(docker ps -aq) #删除所有的容器\ndocker ps -a -q|xargs docker rm #删除所有的容器\n\n#强制删除正在运行的容器\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker rm -f 9db514403bc6\n9db514403bc6\n```\n\n\n\n**启动和停止容器的操作**\n\n```shell\ndocker start 容器id #启动容器\ndocker restart 容器id #重启容器\ndocker stop 容器id #停止当前正在运行的容器\ndocker kill 容器id #强制停止当前容器\n```\n\n### 其他命令\n\n**后台启动命令**\n\n```shell\n#docker run -d 镜像名\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker run -d centos\n444146c98d0dcd4bbedbe8581c0d65da74f2d3952250d5f8aa5a5b397aa3ba08\n\n#注意:当使用docker ps查看运行容器的时候,发现centos停止了\n#这是因为容器使用后台,就必须有一个前台进程,docker发现没有应用就会自己停止容器\n\n#\"while true;do echo bin;sleep 1;done\" 脚本文件,循环执行打印bin文件,每隔一秒打印一次\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker run -d centos /bin/bash -c\n\"while true;do echo bin;sleep 1;done\"\n2afcfeb84973310bcba85c00e7d1ce516c4ada9460979ab88264f2db1503556a\n\n\n#显示日志\n-tf #显示日志,-t -f 的组合,-f为时间戳\n--tail number #显示的日志条数\ndocker logs -tf --tail 10 容器id\n\n```\n\n**查看容器中进程的信息**\n\n```shell\ntop命令\ndocker top 容器id\n\n```\n\n**查看镜像的元数据**\n\n```shell\ndocker inspect 容器id\n```\n\n**进入当前正在运行的容器**\n\n```shell\n#进入容器打开一个新的终端,可以在里面操作\ndocker exec -it 容器id /bin/bash\n\n#进入容器正在执行的终端,不会启动新的进程\ndocker attach 容器id\n```\n\n**从容器内拷贝文件到主机上**\n\n```shell\ndocker cp 容器id:容器内路径 目的主机路径\n\n#进入docker容器内部\ndocker attach 容器id\n#在容器内新建一个目录\ncd /home\ntouch text.java\n#退出容器\ndocker cp 容器id:容器路径(/home/text.java) 目的主机路径(/home)\n\n# 拷贝是一个手动过程,未来我们使用 -v 卷的技术,可以实现主机和容器的目录同步\n```\n\n![image-20230906204634145](E:\\programming\\docker\\image\\image-20230906204634145.png)\n\n## 部署服务器\n\n### 部署Nginx\n\n**搜索镜像**\n\n```shell\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker search nginx\nNAME DESCRIPTION STARS OFFICIAL AUTOMATED\nnginx Official build of Nginx. 18972 [OK] \n```\n\n**下载镜像**\n\n```shell\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker pull nginx\nUsing default tag: latest\nlatest: Pulling from library/nginx\na2abf6c4d29d: Pull complete \na9edb18cadd1: Pull complete \n589b7251471a: Pull complete \n186b1aaa4aa6: Pull complete \nb4df32aa5a72: Pull complete \na0bcbecc962e: Pull complete \nDigest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31\nStatus: Downloaded newer image for nginx:latest\ndocker.io/library/nginx:latest\n```\n\n**启动镜像**\n\n```shell\n# --name nginx01 给这台Nginx服务器起别名\n# -p 8081:80 Nginx自己端口号为80 ,主机通过访问8081来访问容器中的Nginx服务器\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker run -d --name nginx01 -p 8081:80 nginx\ncd0f28ecd84d5d765950196363b2e80c0e01bdb5bd137fe8cfbab452414cd573\n```\n\n**本机访问服务器**\n\n```shell\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# curl localhost:8081\n<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\nhtml { color-scheme: light dark; }\nbody { width: 35em; margin: 0 auto;\nfont-family: Tahoma, Verdana, Arial, sans-serif; }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n```\n\n**进入容器**\n\n```shell\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker exec -it 35a0d79c26ea /bin/bash\nroot@35a0d79c26ea:/# ls\nbin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var\n```\n\n**停止容器**\n\n```shell\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker stop 35a0d79c26ea\n35a0d79c26ea\n```\n\n\n\n**端口暴露的概念**\n\n![image-20230907092535497](E:\\programming\\docker\\image\\image-20230907092535497.png)\n\n这就是通过本机端口访问容器端口的基本原理,类似于映射\n\n\n\n### 问题解决\n\n> **在开启服务器后,例如开启Nginx,是可以通过访问服务器,加上配置的映射地址来在外部浏览器访问服务器的,但是这里不行**\n\n![image-20230907100613050](E:\\programming\\docker\\image\\image-20230907100613050.png)\n\n**解决办法:**\n\n```shell\n#之前的启动服务器命令\ndocker run -d --name nginx01 -p 8081:80 nginx\n#修改之后的启动命令\ndocker run -itd --name nginx01 -p 8081:80 nginx\n```\n\n但是修改之后还是不行,查了办法说docker的版本和Linux的内核版本有冲突,需要升级版本\n\n```shell\n#查看Linux内核版本\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# uname -sr\nLinux 3.10.0-1062.18.1.el7.x86_64\n\n#查看docker版本\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker version\nClient: Docker Engine - Community\n Version: 24.0.5\n API version: 1.43\n Go version: go1.20.6\n Git commit: ced0996\n Built: Fri Jul 21 20:39:02 2023\n OS/Arch: linux/amd64\n Context: default\n\nServer: Docker Engine - Community\n Engine:\n Version: 24.0.5\n API version: 1.43 (minimum version 1.12)\n Go version: go1.20.6\n Git commit: a61e2b4\n Built: Fri Jul 21 20:38:05 2023\n OS/Arch: linux/amd64\n Experimental: false\n containerd:\n Version: 1.6.22\n GitCommit: 8165feabfdfe38c65b599c4993d227328c231fca\n runc:\n Version: 1.1.8\n GitCommit: v1.1.8-0-g82f18fe\n docker-init:\n Version: 0.19.0\n GitCommit: de40ad0\n\n#升级Linux内核版本\nyum update -y#这只是小版本的更新\n\nCentOS 允许使用 ELRepo,这是一个第三方仓库,可以将内核升级到最新版本。\n\n在 CentOS 7 上启用 ELRepo 仓库,运行如下命令:\n\nrpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org#导入该源的秘钥\n\nrpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm#启用该源仓库\n\nyum --disablerepo=\"*\" --enablerepo=\"elrepo-kernel\" list available#查看有哪些内核版本可供\n\nyum --enablerepo=elrepo-kernel install kernel-lt -y #安装的长期稳定版本,稳定可靠,版本为4.4.238\n```\n\n**升级大版本之后,还是无法访问,然后对防火墙进行配置**\n\n```shell\n#开启防火墙\nsystemctl start firewalld.service\n#查看防火墙的开放端口\nfirewall-cmd --list-ports#默认为空\nfirewall-cmd --list-ports --permanent\n\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# firewall-cmd --list-ports --permanent\n8081/tcp\n#如果没有开放端口, 添加端口\nsudo firewall-cmd --add-port=8080/tcp --permanent\n#刷新\nfirewall-cmd --reload\n```\n\n**之后进入Nginx容器启动服务**\n\n```shell\n#启动Nginx服务\n/etc/init.d/nginx start\n\n\n#查看服务是否启动\nps -aux|grep nginx\n```\n\n\n\n### 部署Tomcat\n\n大部分都是和Nginx一样的,docker hub地址:[hub.docker.com](http://hub.docker.com/)\n\n```shell\n# 官方给的运行方法(在运行的时候,没有下载会自动查找然后下载)\ndocker run -it --rm tomcat:9.0\n\n# 这个--rm 代表着运行停止之后,自动删除镜像,如果需要再次使用就需要重新下载\n\n#进入容器\ndocker exec -it tomcat /bin/bash\n#进去之后,发现没有Linux命令少了,没有webapps,阿里云镜像默认是最小镜像,所有不必要的都删除了,保证最小可运行环境\n```\n\n### 部署ES + Kibana\n\n##### <font color=\'#blue\'>部署elasticsearch</font>\n\n```shell\n# 安装运行elasticsearch\n#es 暴露的端口很多\n#es 十分的消耗内存\n# es的数据一般需要放置到安全目录\n# --net somenetwork 网络配置\n\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e \"discovery.type=single-node\" elasticsearch:7.6.2\n\n#docker stats 查看cpu状态\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker stats\n\nCONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS\n308bb45360b5 elasticsearch 4.77% 1.252GiB / 1.831GiB 68.38% 0B / 0B 99.3MB \n\n#测试 curl localhost:9200\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# curl localhost:9200\n{\n \"name\" : \"308bb45360b5\",\n \"cluster_name\" : \"docker-cluster\",\n \"cluster_uuid\" : \"8Vo9YsZLQxubIBVrSpJfmQ\",\n \"version\" : {\n \"number\" : \"7.6.2\",\n \"build_flavor\" : \"default\",\n \"build_type\" : \"docker\",\n \"build_hash\" : \"ef48eb35cf30adf4db14086e8aabd07ef6fb113f\",\n \"build_date\" : \"2020-03-26T06:34:37.794943Z\",\n \"build_snapshot\" : false,\n \"lucene_version\" : \"8.4.0\",\n \"minimum_wire_compatibility_version\" : \"6.8.0\",\n \"minimum_index_compatibility_version\" : \"6.0.0-beta1\"\n },\n \"tagline\" : \"You Know, for Search\"\n}\n\n```\n\n![image-20230907212455109](E:\\programming\\docker\\image\\image-20230907212455109.png)\n\n**由于es非常的消耗内存,所以,会在启动的时候添加一个内存限制**\n\n```shell\n# -e EX_JAVA_OPTS=\"-Xms64m -Xmx512m\" es最小64MB,最大512MB\ndocker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e \"discovery.type=single-node\" -e EX_JAVA_OPTS=\"-Xms64m -Xmx512m\" elasticsearch:7.6.2\n```\n\n![image-20230907213004714](E:\\programming\\docker\\image\\image-20230907213004714.png)\n\n##### 结合kibana\n\n\n\n![image-20230907213331674](E:\\programming\\docker\\image\\image-20230907213331674.png)\n\n\n\n\n\n## 可视化\n\n**portainer**\n\n```shell\n# 安装命令\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer\n```\n\n**外网访问**\n\n![image-20230907215159233](E:\\programming\\docker\\image\\image-20230907215159233.png)\n\n**会出现一个登录界面**\n\n![image-20230908090702173](E:\\programming\\docker\\image\\image-20230908090702173.png)\n\n**选择本地**\n\n![image-20230908090741653](E:\\programming\\docker\\image\\image-20230908090741653.png)\n\nRancher(CI/CD 持续化集成,持续化部署的时候使用)\n\n## Docker 镜像讲解\n\n### 镜像是什么\n\n镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境的开发软件,它包含运行某个软件所需的所有内容,包括代码,运行时、库、环境变量、配置文件\n\n### Docker镜像加载原理\n\n<h4><font color=\"red\">UnionF(联合文件系统)</font></h4>\n\nUnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem )。Union文件系统是Docker镜像的基础,镜像可以通过分层来进行基继承,基于基础镜像(没有父镜像),可以制造各种具体的应用镜像\n\n<font color=\"green\">特性</font>:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包括所有底层的文件和目录\n\n<h4><font color=\"red\">Docker镜像加载原理</font></h4>\n\ndocker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFs\n\n<font color=\"blue\">bootfs(boot file system)主要包括了bootloader和kernel,BootLoader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/unix 系统是一样的,包含boot加载器和内核,当boot完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核了,此时系统也会卸载bootfs</font>\n\n<font color=\"purple\">rootfs(root file system),在bootfs之上,包含的就是典型Linux系统中 /dev,/proc,/bin,/etc标准目录和文件。rootfs就是各种不同操作系统的发行版,比如Ubuntu、centOS等</font>\n\n![image-20230908100717561](E:\\programming\\docker\\image\\image-20230908100717561.png)\n\n<font color=\"red\">问题</font>:question:为什么平时我们安装到CentOS都是好几个G,Docker里面才几百mb\n\n- 对于一个精简的OS,roofs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的Linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs\n\n### 分层理解\n\n#### <font color=\"red\">分层的镜像</font>\n\n下载一个镜像,他是一层一层的下载的\n\n![image-20230908104022720](E:\\programming\\docker\\image\\image-20230908104022720.png)\n\n<font color=\"blue\">思考</font>:thinking:为什么Docker镜像要采用这种分层的结构呢?\n\n- 最大的好处,我觉得莫过于资源贡献了,比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需要在磁盘上保留一份base镜像,同时内存中也只需要加载一根base镜像,这样就可以为所有的容器服务 了,而且镜像的每一层都可以被贡献\n- 查看镜像分层的方式可以通过docker image inspect 命令\n\n```shell\n\"RootFS\": {\n \"Type\": \"layers\",\n \"Layers\": [\n \"sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f\",\n \"sha256:9b24afeb7c2f21e50a686ead025823cd2c6e9730c013ca77ad5f115c079b57cb\",\n \"sha256:4b8e2801e0f956a4220c32e2c8b0a590e6f9bd2420ec65453685246b82766ea1\",\n \"sha256:529cdb636f61e95ab91a62a51526a84fd7314d6aab0d414040796150b4522372\",\n \"sha256:9975392591f2777d6bf4d9919ad1b2c9afa12f9a9b4d260f45025ec3cc9b18ed\",\n \"sha256:8e5669d8329116b8444b9bbb1663dda568ede12d3dbcce950199b582f6e94952\"\n ]\n}\n```\n\n<font color=\"blue\">理解</font>:comet::所有的Docker都起始于一个基础的镜像层,当进行修改或增加的新的内容时,就会在当前镜像层之上,创建一个新的镜像层。\n\n<font color=\"purple\">例如</font>:exclamation::基于CentOS创建一个新的镜像,这就是新镜像的第一层,如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层,如果继续添加一个安全补丁,就会创建第三个镜像层。\n\n![image-20230908105019026](E:\\programming\\docker\\image\\image-20230908105019026.png)\n\n在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合\n\n<font color=\"purple\">例如</font>:exclamation::下图的每一个镜像层包含三个文件,而镜像包括了来自两个镜像层 的6个文件\n\n![image-20230908105508548](E:\\programming\\docker\\image\\image-20230908105508548.png)\n\n下图外部镜像来看,只有6个文件,这是因为最上层的文件7是文件5的一个更新版本,他不会替换文件5,而是新建一个镜像层来覆盖之前的文件\n\n![image-20230908105915435](E:\\programming\\docker\\image\\image-20230908105915435.png)\n\n这种情况下,上层镜像中的文件覆盖了底层镜像中的文件,这样就使得文件的更新版本作为一个新镜像层添加到镜像当中,Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层的堆栈,并保证多镜像层对外展示为统一的文件系统\n\nLinux上可用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义,每中存储引擎都基于Linux中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点\n\nDocker在Windows上仅支持WindowsFilter一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW\n\n下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图\n\n![image-20230908110645191](E:\\programming\\docker\\image\\image-20230908110645191.png)\n\n<font color=\"blue\">特点</font>:peach::Docker 镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部\n\n这一层就是容器层,容器之下的都是镜像层!\n\n![image-20230908110758792](E:\\programming\\docker\\image\\image-20230908110758792.png)\n\n### Commit镜像\n\n```shell\ndocker commit 命令\ndocker commit -m=\"提交的容器的描述信息\" -a=\"作者\" 容器id 目标容器\n```\n\n\n\n<font color=\"red\">实例</font>:exclamation::\n\n```shell\n# 启动Tomcat\ndocker run -d -p 8080:8080 tomcat\n# 进入Tomcat容器\ndocker exec -it 容器id /bin/bash\n# 将webapps.dist里面的内容复制到webapps目录\ncp -r webapps.dist/* webapps\n# 退出容器,提交容器\nexit \n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker commit -m=\"这是使用过后的Tomcat,添加了webapps内容\" -a=\"付成梁\" 1c03a808a407 tomcat01\nsha256:e6681698a154c750100d884eeadbeba0d2723256847e9ddb23035ac006ab33de\n```\n\n```shell\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker images\nREPOSITORY TAG IMAGE ID CREATED SIZE\ntomcat01 latest e6681698a154 6 seconds ago 684MB\nnginx latest 605c77e624dd 20 months ago 141MB\ntomcat latest fb5657adc892 20 months ago 680MB\nredis latest 7614ae9453d1 20 months ago 113MB\ncentos latest 5d0da3dc9764 24 months ago 231MB\nportainer/portainer latest 580c0e4e98b0 2 years ago 79.1MB\nelasticsearch 7.6.2 f29a1ee41030 3 years ago 791MB\n```\n\n\n\n# 穿透内网服务\n\n```shell\n$docker run -it -e NGROK_AUTHTOKEN=<token> ngrok/ngrok http 80\n```\n\n\n\n# 进阶篇\n\n## 容器数据卷\n\n### 什么是容器数据卷\n\n**docker理念回顾**\n\n将应用和环境导包成一个镜像\n\n数据?如果数据都在容器中,那么容器删除,数据也会丢失 <font color=\"red\">需求</font>:数据可以持久化\n\nMySQL,容器删除,等同于删库跑路 <font color=\"red\">需求</font> :MySQL数据可以存储在本地\n\n容器之间可以有一个数据共享的技术,Docker容器中产生的数据,同步到本地,这就是卷技术,目录的挂载,将容器内的目录挂载到Linux上面\n\n![image-20230909122238243](E:\\programming\\docker\\image\\image-20230909122238243.png)\n\n\n\n### 使用数据卷\n\n> 方式一:直接使用命令来挂载 -v\n\n```shell\ndocker run -it -v 主机目录:容器内目录\n```\n\n<font color=\"red\">例子</font>\n\n```shell\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker run -it -v /home/test:/home centos\n\n#在容器内创建文件,会直接同步到主机的挂载目录下\ntouch hello.java\n\n# 当关闭容器后,修改主机内的目录,再开启容器,也会再次同步到容器内的目录\n#所以,这种同步是双向的,\n```\n\n![image-20230909123044578](E:\\programming\\docker\\image\\image-20230909123044578.png)\n\n![image-20230909123320314](E:\\programming\\docker\\image\\image-20230909123320314.png)\n\n![image-20230909123831160](E:\\programming\\docker\\image\\image-20230909123831160.png)\n\n\n\n###### 测试:mysql的安装挂载数据\n\n```shell\n# 获取镜像\ndocker pull mysql\n#运行容器 需要做数据挂载\n# 安装启动mysql需要配置密码,这是需要注意的\n\n#启动mysql\ndocker run --name mysql -d -p 3306:3306 -v /home/mysql/data:/var/lib/mysql -v /home/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 mysql\n\n# 启动之后,进入mysql,使用本地的Navicat连接数据库\n# 连接之后发现,报1130错误,这是由于用户连接权限问题,依次执行以下命令\nmysql -u root -p\nmysql>use mysql;\nmysql>select \'host\' from user where user=\'root\';\nmysql>update user set host = \'%\' where user =\'root\';\nmysql>flush privileges;\nmysql>select \'host\' from user where user=\'root\';\n\n第一句是以权限用户root登录\n第二句:选择mysql库\n第三句:查看mysql库中的user表的host值(即可进行连接访问的主机/IP名称)\n第四句:修改host值(以通配符%的内容增加主机/IP地址),当然也可以直接增加IP地址\n第五句:刷新MySQL的系统权限相关表\n第六句:再重新查看user表时,有修改\n\n# 再次执行登录即可\n```\n\n当在本地的Navicat中添加数据时,也会同步到服务器、容器中\n\n![image-20230909145917025](E:\\programming\\docker\\image\\image-20230909145917025.png)\n\n![image-20230909150242748](E:\\programming\\docker\\image\\image-20230909150242748.png)\n\n![image-20230909150259975](E:\\programming\\docker\\image\\image-20230909150259975.png)\n\n\n\n<font color=\"blue\">注意</font>:ice_hockey::删除掉mysql镜像之后,主机内的数据文件依旧存在,不会因为镜像的删除而删除\n\n\n\n#### 具名挂载和匿名挂载\n\n##### 匿名挂载\n\n```shell\n# 匿名挂载\n-v 容器内id\n#使用匿名挂载启动容器\n\n-P # 随机分配端口\n[root@iZ0jlhf9yhz0qhyklzjp4tZ first]# docker run -d -P --name nginx01 -v /etc/nginx nginx \n# 这里可以发现,不写容器内的路径,就是匿名挂载\n# 查看所有卷的情况\n[root@iZ0jlhf9yhz0qhyklzjp4tZ first]# docker volume ls\n```\n\n![image-20230909185108466](E:\\programming\\docker\\image\\image-20230909185108466.png)\n\n当匿名挂载时,会自动生成一个卷名,如上图\n\n##### 具名挂载\n\n```shell\n#具名挂载\n[root@iZ0jlhf9yhz0qhyklzjp4tZ first]# docker run -d -P --name nginx02 -v newNginx:/etc/nginx nginx\n# 注意:这里的newNginx不是路径,是具体的卷名,这样的就是具名挂载\n```\n\n<font color=\"red\">查看具体的卷信息</font>\n\n```shell\ndocker volume inspect 卷名\n```\n\n![image-20230909185728968](E:\\programming\\docker\\image\\image-20230909185728968.png)\n\n<span style=\"background:green\">注意:</span>这个路径是在没有设定的情况下默认的路径,并不是一定的\n\n```shell\n-v 容器内路径 #匿名挂载\n-v 卷名:容器内路径 #具名挂载\n-v 容器内路径:容器外路径 #指定路径挂载\n```\n\n###### 拓展\n\n```shell\n# 这里的ro、rw是用来设定卷的读写权限的\nro #只读\nrw #可读可写\n\ndocker run -d -P --name nginx -v /etc/nginx:ro nginx\ndocker run -d -P --name nginx -v /etc/nginx:rw nginx\n```\n\n\n\n### 初识DockerFile\n\n> 方式二:编写一个脚本,在创建的时候,自动执行脚本,来挂载卷\n\n```shell\n# 进入home目录,创建一个目录\nmkdir docker-test-volume\n# 进入到创建的目录中,编写一个脚本\nvim dockerfile1\n# 文件内容\nFROM centos # 镜像来源于centos\n\nVOLUME [\"volume1\",\"volume2\"] # 添加两个数据卷\n\nCMD echo \"----end----\"\n\nCMD /bin/bash # 默认执行bash命令\n# 创建镜像 这里的fu/centos 前面不能有 / 因为镜像不是路径,后面是版本号\n[root@iZ0jlhf9yhz0qhyklzjp4tZ docker-test-volume]# docker build -f /home/docker-test-volume/dockerfile01 -t fu/centos:1.0 . \n```\n\n<font color=\"blue\">自己创建的镜像</font>\n\n![image-20230909211040515](E:\\programming\\docker\\image\\image-20230909211040515.png)\n\n<font color=\"red\">启动镜像</font>\n\n```shell\n[root@iZ0jlhf9yhz0qhyklzjp4tZ docker-test-volume]# docker run -it cff771a3c31e /bin/bash\n```\n\n<font color=\"red\">查看镜像详细信息</font>\n\n```shell\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker inspect fdb3d661a127\n```\n\n![image-20230909211502091](E:\\programming\\docker\\image\\image-20230909211502091.png)\n\n<span style=\"background:black;color:white\">注意:这里生成的数据卷是匿名挂载</span>\n\n### 数据卷容器\n\n```shell\n# 启动docker01容器\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker run -it --name docker01 cff771a3c31e\n\n# 启动docker02容器,同步docker01的数据卷\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker run -it --name docker02 --volumes-from docker01 cff771a3c31e\n\n# 启动 docker03数据同步docker02的数据\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker run -it --name docker03 --volumes-from docker02 cff771a3c31e\n\n# 启动docker04 同步docker01数据卷\n[root@iZ0jlhf9yhz0qhyklzjp4tZ ~]# docker run -it --name docker04 --volumes-from docker01 cff771a3c31e\n```\n\n![image-20230910103254734](E:\\programming\\docker\\image\\image-20230910103254734.png)\n\n![image-20230910103429059](E:\\programming\\docker\\image\\image-20230910103429059.png)\n\n![image-20230910103723418](E:\\programming\\docker\\image\\image-20230910103723418.png)\n\n<span style=\"background:black;color:white;\">由上可知:一个容器可以被多个容器所继承同步,并且所继承的子容器还可以继续拥有子容器</span>\n\n<span style=\"background:black;color:white;\">数据卷容器同步直接的关系是一种拷贝的关系</span>\n\n![image-20230910104151400](E:\\programming\\docker\\image\\image-20230910104151400.png)\n\n\n\n<font color=\"blue\">例子</font>:expressionless::多个数据库共享一个数据\n\n```shell\n# 启动父容器SQL\ndocker run --name mysql01 -d -p 3306:3306 -v /var/lib/mysql -v /etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 mysql\n\n# 启动同步继承容器\ndocker run --name mysql02 -d -p 3307:3306 -v /var/lib/mysql -v /etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 --volumes-from mysql01 mysql\n```\n\n\n\n## DockerFile\n\n### DockerFile介绍\n\ndockerfile是用来构建docker镜像文件的,是一个命令参数脚本\n\n构建步骤:\n\n1. 编写一个dockerfile文件\n2. docker build 构建成一个镜像\n3. docker run 运行镜像\n4. docker push 发布镜像(DockerHub、GitHub)\n\n### Docker构建过程\n\n基础知识:\n\n- 每个保留关键字(指令)都必须是大写字母\n- 执行从上到下的顺序执行\n- “#” 表示注释\n- 每一个指令都会构建一个新的镜像层,并提交\n\n![image-20230910112012342](E:\\programming\\docker\\image\\image-20230910112012342.png)\n\ndockerfile是面向开发的,在发布项目,做镜像,就需要dockerfile文件\n\ndockerfile:构建文件,定义了一切的步骤,源代码\n\ndockerImages:通过DockerFile 构建生成的镜像,最终发布和运行的产品\n\nDocker容器:容器就是镜像运行起来提供的服务器\n\n\n\n### DockerFIle基础指令\n\n```shell\nFROM # 基础镜像,一切从这里开始构建 scratch是最基础的镜像\nMAINTAINER # 镜像是谁写的,姓名 + 邮箱\nRUN # 镜像构建的时候需要运行的命令\nADD # 添加其他的镜像,例如Tomcat、jdk的压缩包等\nWORLDIR # 镜像的工作目录\nVOLUME # 挂载的目录\nEXPOSE # 暴露端口号\nCMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代,例如文件中:CMD [\"ls -a\"],容器运行之后,docker -l,这里的-l就会替代ls -a\nENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令,例如文件中:CMD [\"ls -a\"],容器运行之后,docker -l,这里的ls -a变成ls -a -l\nONBUILD # 当构建一个被继承的DockerFile时,就会运行ONBUILD命令。属于触发型命令\nCOPY # 类似ADD,将文件拷贝到镜像中\nENV # environment 构建的时候设置环境变量\n```\n\n\n\n> 创建自己的CentOs\n\n```shell\n# 编写自己的DockerFile 文件\nFROM centos # 基础镜像\n\nMAINTAINER fuchengliang<19923807615@163.com> #作者信息\n\nENV MYPATH /usr/local # 配置初始目录\nWORKDIR $MYPATH #工作目录\n\nRUN yum -y install vim # 安装vim目录\nRUN yum -y install net-tools # 安装ifconfig命令\n\nEXPOSE 7777 #暴露端口号\n\nCMD echo $MYPATH # 输出工作目录\nCMD echo \"----END----\" # 输出字符串\nCMD /bin/bash # 默认执行命令\n```\n\n```shell\n# 创建镜像\n-f dockerfile文件名称 \n-t 创建的镜像名称\ndocker build -f dockerfile-centos -t first-centos:1.0 .\n```\n\n<span style=\"background:red;\">**执行之后报错:**</span>\n\n```shell\nFailed to download metadata for repo \'appstream\': Cannot prepare internal mirrorlist: No URLs in mirrorlist\n```\n\n- 有一下情况:\n\n - 网络原因:ping以下,查看是否有丢包的情况\n\n - CentOS 已经停止维护:在20年官方宣布停止维护Linux centOS,针对这种情况有解决办法如下\n\n - ```shell\n FROM centos MAINTAINER fuchengliang<19923807615@163.com>\n \n ENV MYPATH /usr/local\n WORKDIR $MYPATH\n RUN cd /etc/yum.repos.d/ # 执行 进入到 yum 的 repos 目录\n RUN sed -i \'s/mirrorlist/#mirrorlist/g\' /etc/yum.repos.d/CentOS-* #修改文件内容\n RUN sed -i \'s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g\' /etc/yum.repos.d/CentOS-*\n RUN yum makecache #生成缓存\n RUN yum update -y #更新配置\n RUN yum -y install vim\n RUN yum -y install net-tools\n \n EXPOSE 7777\n \n CMD echo $MYPATH\n CMD echo \"----END----\"\n CMD /bin/bash\n ```\n\n 以上完成之后,再次执行build命令即可\n\n```shell\ndocker build -f dockerfile-centos -t first-centos:1.0 .\n```\n\n![image-20230910122405193](E:\\programming\\docker\\image\\image-20230910122405193.png)\n\n![image-20230910122430683](E:\\programming\\docker\\image\\image-20230910122430683.png)\n\n\n\n对此还可以通过 docker history 命令查看它的历史更新\n\n```shell\ndocker history 287494ecba2f\n```\n\n![image-20230910122601632](E:\\programming\\docker\\image\\image-20230910122601632.png)\n\n<font color=\"blue\">进入容器</font>\n\n```shell\ndocker run -it first-centos:1.0\n```\n\n![image-20230910122944597](E:\\programming\\docker\\image\\image-20230910122944597.png)\n\n<span style=\"background:yellow\">CMD 和 ENTRYPOINT的区别</span>\n\n> CMD\n\n```shell\n# 编辑dockerfile-cmd-test文件\nFROM centos\nCMD [\"ls\",\"-a\"]\n#提交镜像\ndocker build -f dockerfile-cmd-text -t cmd-test .\n#执行镜像\ndocker run 7d202bdf002b\n#执行之后会直接执行CMD里面的命令(图一)\n#再次执行以下命令 出现错误\ndocker run 7d202bdf002b -l\n\n[root@iZ0jlhf9yhz0qhyklzjp4tZ dockerfile]# docker run 7d202bdf002b -l\ndocker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: \"-l\": executable file not found in $PATH: unknown.\nERRO[0000] error waiting for container: \n\n# 这是因为添加 -l 之后会替换CMD [\"ls\",\"-a\"],而-l不是一条命令,所以抛错 \ndocker run 7d202bdf002b ls -al #这样即可\n```\n\n图1![image-20230910145949004](E:\\programming\\docker\\image\\image-20230910145949004.png)\n\n\n\n\n\n> ENTRYPOINT\n\n```shell\n# 编辑dockerfile-entrypoint-test文件\nFROM centos\nENTRYPOINT [\"ls\",\"-a\"]\n#提交镜像\ndocker build -f dockerfile-entrypoint-text -t entrypoint-test .\n# 与CMD不同的是,他可以直接添加命令\ndocker run b325f5b97233 -l #这样也不会抛错,而是可以直接运行\n```\n\n### 实战:配置Tomcat服务器\n\n步骤:\n\n- 准备Tomcat、jdk的相关压缩文件,这里的文件是下载到/home/build/目录下\n\n ```shell\n # jdk 下载\n wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz\n # Tomcat下载去官网,然后传到Linux上\n wget https://mirrors.bfsu.edu.cn/apache/tomcat/tomcat-9/v9.0.44/bin/apache-tomcat-9.0.44.tar.gz\n ```\n\n- 编写Dockerfile文件,注:官方的规定是名称为Dockerfile,因为执行之后,会自动去找这个文件,不需要再通过 -f 来指定文件夹\n\n```shell\nFROM centos\nMAINTAINER fuchengliang<19923807615@163.com>\n\nCOPY readme.txt /usr/local/readme.txt\n\nADD apache-tomcat-10.1.13.tar.gz /usr/local # 添加解压的路径\nADD jdk-17_linux-x64_bin.tar.gz /usr/local\n\nRUN cd /etc/yum.repos.d/\nRUN sed -i \'s/mirrorlist/#mirrorlist/g\' /etc/yum.repos.d/CentOS-*\nRUN sed -i \'s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g\' /etc/yum.repos.d/CentOS-*\nRUN yum makecache\nRUN yum update -y\n\nRUN yum -y install vim\nENV MYPATH /usr/local\nWORKDIR $MYPATH\n\nENV JAVA_HOME /usr/local/jdk-17.0.8 # 这里填写的是解压之后的文件名,不知道就解压看一下是啥\nENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar\nENV CATALINA_HOME /usr/local/apache-tomcat-10.1.13\nENV CATALINA_BASE /usr/local/apache-tomcat-10.1.13\nENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin\n\nEXPOSE 8080\nCMD /usr/local/apache-tomcat-10.1.13/bin/startup.sh && tail -F /usr/local/apache-tomcat-10.1.13/bin/logs/catalina.out\n```\n\n- 创建镜像\n\n```shell\ndocker build -t diytomcat .\n```\n\n![image-20230910161738126](E:\\programming\\docker\\image\\image-20230910161738126.png)\n\n\n\n- 运行镜像(我这里直接使用了交互模式运行)\n\n ```shell\n docker run -it -p 8080:8080 --name divtomcat -v /home/build/test:/usr/local/apache-tomcat-10.1.13/webapps/test -v /home/build/tomcatlogs:/usr/local/apache-tomcat-10.1.13/logs diytomcat\n ```\n\n![image-20230910165128141](E:\\programming\\docker\\image\\image-20230910165128141.png)\n\n- 在test目录下创建项目\n\n```shell\n# 在test下创建WEB-INF、index.html文件\nmkdir WEB-INF\ntouch index.html\n# 在WEB-INF 目录下创建web.xml文件\ntouch web.xml\n```\n\nweb.xml模板\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<web.app xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd\"\n version=\"4.0\">\n \n</web-app>\n```\n\nindex.xml文件\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>付成梁的tomcat</title>\n</head>\n<body>\n <p>\n hello,world!\n </p>\n <p>\n 这是我的部署的第一个Tomcat项目,通过dockerfile实现了\n </p>\n</body>\n</html>\n```\n\n#重新启动Tomcat服务器,然后外网访问服务器路径:http://8.130.70.149:8080/test/\n\n![image-20230910174748586](E:\\programming\\docker\\image\\image-20230910174748586.png)\n\n![image-20230910175604193](E:\\programming\\docker\\image\\image-20230910175604193.png)\n\n### 发布镜像\n\n##### 发布到DockerHub\n\n前提:拥有自己的dockerhub账号\n\n```shell\n# 登录hub\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker login --help\n\nUsage: docker login [OPTIONS] [SERVER]\n\nLog in to a registry.\nIf no server is specified, the default is defined by the daemon.\n\nOptions:\n -p, --password string Password\n --password-stdin Take the password from stdin\n -u, --username string Username\n \n#登录\n# docker login -u fuchengliang\n#由于现在无法访问dockerhub,就没有账号\n\n#登录之后,直接push镜像即可,这里加上了作者信息,以及版本号\ndocker push fuchengliang/diytomcat:1.0\n```\n\n<font color=\"red\">可能出现的错误</font>\n\n```shell\n# 可能由于之前已经发布过,所以版本号冲突,只需要往上加几个版本即可\n#也可以重新添加tag,在发布\ndocker tag 镜像id 新的镜像名:版本号\n# 原本的镜像名:diytomcat\ndocker tag 61a294af93f4 fuchengliang/divtomcat:1.0\n```\n\n##### 阿里云镜像发布\n\n1. 登录阿里云\n2. 找到容器镜像服务\n3. 创建命名空间(一个账号只能创建三个命名空间)\n\n![image-20230910200020425](E:\\programming\\docker\\image\\image-20230910200020425.png)\n\n4. 创建镜像仓库(注意选择本地仓库,当然其他的也可以)\n\n5. 在docker中操作\n\n ```shell\n # 登录阿里云\n [root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker login --username=aliyun6402195717 registry.cn-wulanchabu.aliyuncs.com\n Password: \n WARNING! Your password will be stored unencrypted in /root/.docker/config.json.\n Configure a credential helper to remove this warning. See\n https://docs.docker.com/engine/reference/commandline/login/#credentials-store\n \n Login Succeeded\n \n \n #提交镜像\n docker tag [ImageId] registry.cn-wulanchabu.aliyuncs.com/fublog/tomcat:[镜像版本号] # 生成镜像,这里生成过了\n docker push registry.cn-wulanchabu.aliyuncs.com/fublog/tomcat:[镜像版本号]# 提交镜像\n # 这里需要将自己的镜像更改为registry.cn-wulanchabu.aliyuncs.com/fublog/tomcat\n docker trg diytomcat registry.cn-wulanchabu.aliyuncs.com/fublog/tomcat\n # 提交 fublog 命名空间 fublog 镜像仓库\n docker push registry.cn-wulanchabu.aliyuncs.com/fublog/tomcat:1.0\n ```\n\n ![image-20230910203233504](E:\\programming\\docker\\image\\image-20230910203233504.png)\n\n\n\n## Docker网络\n\n### Docker0\n\n使用命令查看网卡地址\n\n```shell\nip addr\n```\n\n![image-20230911091123894](E:\\programming\\docker\\image\\image-20230911091123894.png)\n\n<font color=\"blue\">测试</font>\n\n```shell\n# 启动容器\ndocker run -d -P --name tomcat01 tomcat\n\n# 进入容器\ndocker exec -it tomcat01 ip addr # 这里不知道是因为版本原因还是什么,ip addr无法执行\ndocker exec -it tomcat01 /bin/bash\n\n# 执行之后发现,网卡多了一个(图一)\nip addr\n# 查看容器ip(图二)\ndocker inspect e43438c9eca4\n# 能够发现,容器的ip和docker0的地址是在同一个网段,在容器外ping容器也是可以ping通的\n```\n\n图一![image-20230911094353810](E:\\programming\\docker\\image\\image-20230911094353810.png)\n\n图二![image-20230911094620918](E:\\programming\\docker\\image\\image-20230911094620918.png)\n\n![image-20230911094850979](E:\\programming\\docker\\image\\image-20230911094850979.png)\n\n> <font color =“blue\">原理</font>\n\n1. 每启动一个docker容器,docker就会给docker容器分配一个ip,只要安装了docker,就会有一个网卡docker0\n\n 1. 桥接模式,使用的技术是veth-pair 技术\n\n2. 再启动一个容器\n\n 1. ![image-20230911095857415](E:\\programming\\docker\\image\\image-20230911095857415.png)\n\n 2. ```shell\n # 发现这个容器带来的网卡都是一对一对的\n # veth-pair 就是一对一对的虚拟设备接口,他们都是成对出现的,一端连接协议,一端彼此相连\n # 正因为如此,veth-pair才能充当桥梁,连接多个虚拟网络设备\n ```\n\n3. 测试 tomcat01 和 tomcat02 是否能ping通\n\n 1. ```shell\n root@35baf5ed7a1c:/usr/local/tomcat# ping 172.17.0.3\n ```\n\n 2. 容器和容器之间是相连的,可以ping通\n\n\n\n网络桥接图:当01 ping 02 时,并不是直接相连,而是通过docker0来桥接 02,01 和02 是公用一个路由器docker0\n\n所有的容器在不指定网络的情况下,都是docker0路由的,docker0会给容器自动分配一个默认的可用ip\n\n![image-20230911100837813](E:\\programming\\docker\\image\\image-20230911100837813.png)\n\nDocker中所有的网络接口都是虚拟的,虚拟的转发效率高\n\n只要容器删除,对应的网桥就没了\n\n![image-20230911101433063](E:\\programming\\docker\\image\\image-20230911101433063.png)\n\n\n\n> <span style=\"background:#a52a2a;color:white;border-radius:10px;\"> --link </span>\n\n```shell\n# 启动tomcat01 ,再启动tomcat02 \n# 通过tomcat02 ping你tomcat 01 直接ping是不行的,必须在启动tomcat02 的时候,使用 --link 连通tomcat01,这样才能通过容器名称来ping\ndocker run -it --name tomcat02 --link tomcat01 tomcat\n\n# 反向可以ping通吗?\n# 是不行的,--link 只支持单行绑定,除非在启动tomcat01的时候也使用 --link绑定\n\n# 列出所有网卡\ndocker network ls\n\n# 查看网卡信息\ndocker network inspect 网卡id\n```\n\n![image-20230911144346422](E:\\programming\\docker\\image\\image-20230911144346422.png)\n\n![image-20230911144518048](E:\\programming\\docker\\image\\image-20230911144518048.png)\n\n![image-20230911144651157](E:\\programming\\docker\\image\\image-20230911144651157.png)\n\n总的来说,就是tomcat02 在本地绑定了tomcat01\n\n```shell\ndocker exec -it tomcat02 cat /etc/hosts\n```\n\n![image-20230911145311084](E:\\programming\\docker\\image\\image-20230911145311084.png)\n\n本质:--link 其实就是在hosts 配置文件中增加了 172.17.0.3 tomcat01 2a36989c9dbe \n\n现在不建议使用,因为自定义网络不支持,docker0:不支持容器名连接容器\n\n\n\n### 自定义网络\n\n查看所有网络\n\n```shell\ndocker network ls\n```\n\n![image-20230911150347989](E:\\programming\\docker\\image\\image-20230911150347989.png)\n\n**网络模式**\n\n- bridge:桥接docker(默认,自己创建也是使用这个)\n- none :不配置网络\n- host :主机模式,和宿主机共享网络\n- container:容器网络连通(用的少,局限很大)\n\n**测试**\n\n```shell\n# 直接启动命令,--net bridge,这个就是docker0\ndocker run -d -P --name tomcat01 tomcat # 等价于\ndocker run -d -P --name tomcat01 --net bridge tomcat\n\n# docker0 特点:默认,域名不能访问,使用--link可以打通\n\n# 自定义网络\n--driver 选择网络模式\n--subnet 子网 192.168.0.0/16 这里写16 表示可以创建255*255个ip,如果是24 则能创建255个ip\n 00000000.00000000.00000000.00000000 十进制为 255.255.255.255\n--geteway 默认网关\ndocker network create --driver bridge --subnet 192.168.0.0/16 --geteway 192.168.0.1 mynet\n\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet\n52792d18d635b7cde17f40de8d1e5342db4d8d66276318e9e77e1fd780d03ad7\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker network ls\nNETWORK ID NAME DRIVER SCOPE\n930e5f6ffa16 bridge bridge local\n578b1ac484f4 host host local\n52792d18d635 mynet bridge local\n```\n\n这样就创建好了\n\n![image-20230911151813233](E:\\programming\\docker\\image\\image-20230911151813233.png)\n\n<span style=\"background:#a36a3a;border-radius:10px\">启动容器到自己创建的网络中</span>\n\n```shell\ndocker run -d -P --name tomcat-net01 --net mynet tomcat\n\n# 查看网卡信息\ndocker network inspect mynet\n \n[\n {\n \"Name\": \"mynet\",\n \"Id\": \"52792d18d635b7cde17f40de8d1e5342db4d8d66276318e9e77e1fd780d03ad7\",\n \"Created\": \"2023-09-11T15:15:10.075615064+08:00\",\n \"Scope\": \"local\",\n \"Driver\": \"bridge\",\n \"EnableIPv6\": false,\n \"IPAM\": {\n \"Driver\": \"default\",\n \"Options\": {},\n \"Config\": [\n {\n \"Subnet\": \"192.168.0.0/16\",\n \"Gateway\": \"192.168.0.1\"\n }\n ]\n },\n \"Internal\": false,\n \"Attachable\": false,\n \"Ingress\": false,\n \"ConfigFrom\": {\n \"Network\": \"\"\n },\n \"ConfigOnly\": false,\n \"Containers\": {\n \"85d8163d378a035a4a7be3d6b3231bfbf110579022405badc978be7feb81abc2\": {\n \"Name\": \"tomcat-net01\",\n \"EndpointID\": \"57a9ce41e1eb24cb56ac6049065dbb09ed976d52497ba5b8b591f1900a47ce52\",\n \"MacAddress\": \"02:42:c0:a8:00:02\",\n \"IPv4Address\": \"192.168.0.2/16\",\n \"IPv6Address\": \"\"\n }\n },\n \"Options\": {},\n \"Labels\": {}\n }\n]\n```\n\n自定义网络的好处:\n\n- 自定义的网络会帮助我们管理好容器间的关系,例如,两个容器间ping,不需要再使用--link,可以直接ping通\n- 不同的集群使用不同的网络,并且通过网络连通,也可以使得不同集群互相连通\n\n### 网络连通\n\n![image-20230911154511703](E:\\programming\\docker\\image\\image-20230911154511703.png)\n\n![image-20230911154704711](E:\\programming\\docker\\image\\image-20230911154704711.png)\n\n<span style=\"background:#a36a3a;border-radius:10px\">测试打通不同网段</span>\n\n```shell\n# tomcat01 连接 mynet网络\ndocker network connect mynet tomcat01\n\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker network connect mynet tomcat01\n# 查看网络详细信息\n[root@iZ0jlhf9yhz0qhyklzjp4tZ /]# docker network inspect mynet\n# 由下图看出,connect之后就直接将tomcat01添加到了mynet的网络里面\n# 也就是一个容器两个地址\n```\n\n![image-20230911155135691](E:\\programming\\docker\\image\\image-20230911155135691.png)\n\n当然,docker0里面的其他的容器依旧是不能连接的,需要使用connect来再次连接\n\n### 实战:打包SrringBoot微服务发布\n\n![image-20230911164733081](E:\\programming\\docker\\image\\image-20230911164733081.png)\n\n在docker的home下面新建,blog文件,将打包好的jar包和Dockerfile文件上传到blog文件中\n\n```shell\nFROM openjdk:17-jdk-alpine\n\nCOPY *.jar /app.jar\n\nCMD echo [\"--server.port=8080\"]\n\nEXPOSE 8080\nENTRYPOINT [\"java\",\"-jar\",\"/app.jar\"]\n```\n\n然后创建镜像,运行镜像,测试访问镜像即可', 6, '测试,java', '2024-05-05 21:35:46', '# 关于docker的学习与记录', 'h1入门篇h1\nh2Docker的常用命令h2\nh3帮助命令h3\nprecode classlanguageshelldocker version 显示docker的版本信息\ndocker info 显示docker的系统信息包括镜像和容器的数量\ndocker 命令 help 帮助命令\ncodepre\np帮助文档地址:a hrefhttpsdocs.docker.comenginereferencerunDocker run reference Docker Docsap\n', 'http://localhost:8080/image/1714916146139-Docker.png', 1, 0, 0, 'c5e2313f768245a5aacc6d26a4f8f743');
INSERT INTO `b_content` VALUES (32, '2024-05-05 21:38:07', 0, 6, 0, 2, 0, '# Dubbo\n\n## Dubbo简介\n\n### 基础理论\n\n#### 分布式基础\n\n- 什么是分布式系统?\n - 分布式系统是若干独立计算机的集合,这些计算机对于用户来书就像是单个相关系统\n- 分布式系统是建立在网络之上的软件系统\n\n#### 发展演变\n\n- 单一应用架构:ORM\n- 垂直应用架构:MVC\n- 分布式服务架构:RPC--->远程过程调用\n- 流动计算架构:SOA\n\n#### 什么是RPC\n\n- RPC(Remote Procedure Call) 是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。它允许程序调用有另一个地址空间(通常是共享网络的另一台机器上)的过程和函数,而不是程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。\n- 也就是A服务器和B服务器建立连接,两个服务器进行通信\n- 常见的RPC框架\n - Dubbo、gRPC、Thrift、HSF\n\n### Dubbo核心\n\n#### Dubbo的特性\n\n- 面向接口代理的高性能RPC调用\n- 智能负载均衡\n- 服务自动注册与发现\n- 高度扩展能力\n- 运行期流量调度\n- 可视化的访问治理与运维\n\n#### Dubbo支持的协议\n\n- Dubbo、hessian、rmi、http、webservice、thrift、memcached、redis\n\n- dubbo官方推荐使用dubbo协议,默认端口是20880\n\n- 使用dubbo协议,spring配置文件中加入:\n\n - ```xml\n <dubbo :protocol name=\"dubbo\" port=\"20880\"/>\n ```\n\n#### 直连方式(不使用注册中心)\n\n- 依赖\n\n - ```xml\n <dependencies>\n <!--spring依赖-->\n <dependency>\n <groupId>org.springframework</groupId>\n <artifactId>spring-context</artifactId>\n <version>6.0.4</version>\n </dependency>\n <dependency>\n <groupId>org.springframework</groupId>\n <artifactId>spring-webmvc</artifactId>\n <version>6.0.4</version>\n </dependency>\n \n <!--dubbo依赖-->\n <dependency>\n <groupId>com.alibaba</groupId>\n <artifactId>dubbo</artifactId>\n <version>2.6.12</version>\n </dependency>\n </dependencies>\n ```\n\n\n\n- spring配置文件\n\n - ```xml\n <mvn:component-scan base-package=\"com.bjpowernode.dubbo\"></mvn:component-scan>\n <!--服务提供者声明名称:必须保证服务名称的唯一性-->\n <dubbo:application name=\"001-link-userservice-provider\"/>\n \n <!--访问服务协议的名称及端口号-->\n <!--\n name:协议的名称\n port:协议端口号\n -->\n <dubbo:protocol name=\"dubbo\" port=\"20880\"/>\n \n <!--\n interface:暴露服务接口\n ref:接口使用的实现类,在spring容器里面表示\n registry:如果不使用注册中心,则值为:N/A\n -->\n <dubbo:service interface=\"com.bjpowernode.dubbo.UserService\" ref=\"userService\" registry=\"N/A\"/>\n \n <!--将接口的实现类加载到spring容器中-->\n ```\n\n- web配置文件\n\n - ```xml\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <web-app xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd\"\n version=\"4.0\">\n <context-param>\n <param-name>contextConfigLocation</param-name>\n <param-value>classpath:springDubbo.xml</param-value>\n </context-param>\n \n <listener>\n <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n </listener>\n </web-app>\n ```\n\n- 创建一个新的模块,来使用第一个模块的功能\n\n - 怎么才能定位到第一个工程呢?\n\n - 将该工程打包成jar包,注意:需要将packaging注释掉,因为是web应用,打包方式是war\n\n - 然后在第二个模块中引入jar包\n\n - ```xml\n <dependency>\n <groupId>com.bjpowernode</groupId>\n <artifactId>dubbo-001-allongconnection</artifactId>\n <version>1.0-SNAPSHOT</version>\n </dependency>\n ```\n\n - 配置dubbo配置文件\n\n - ```xml\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <beans xmlns=\"http://www.springframework.org/schema/beans\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dubbo=\"http://code.alibabatech.com/schema/dubbo\"\n xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd\">\n \n <!--声明服务消费者的名称,保证唯一性-->\n <dubbo:application name=\"dubbo-002-consumer\"/>\n \n <!--\n 引用远程服务接口\n id:远程服务接口对象名称\n interface:调用远程接口的全限定类名\n url:访问服务接口的地址\n registry:不使用注册中心,值为:N/A\n -->\n <dubbo:reference id=\"UserService\"\n interface=\"com.bjpowernode.dubbo.UserService\"\n url=\"dubbo://localhost:20880\"\n registry=\"N/A\"/>\n </beans>\n ```\n\n - 实现类Controller\n\n - ```java\n @Controller\n public class ConsumerController {\n @Autowired\n private UserService userService;\n \n @RequestMapping(value = \"/user\")\n public String userDetail(Model model, Integer id){\n User user=userService.queryUserById(id);\n model.addAttribute(\"user\",user);\n return \"userDetail\";\n }\n }\n ```\n\n#### dubbo服务化最佳实践\n\n##### 分包\n\n- 建议将服务接口、服务模型等均放到公共包中\n\n##### 粒度\n\n- 服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤\n- 服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸\n- 不建议使用过于抽象的通用接口,如:Map query(Map),这样的接口没有明确语义,会给后期维护带来不便\n\n##### 版本\n\n- 每个接口都应定义版本号,区分同一个接口的不同实现\n- 如:<dubbo:service interface=“com.xxx.XxxService” version=“1.0”>\n\n##### 优化直连\n\n![image-20230510201542241](E:\\programming\\Dubbo\\img\\image-20230510201542241.png)\n\n- 003模块提供接口和实现类\n\n - ![image-20230510202031343](E:\\programming\\Dubbo\\img\\image-20230510202031343.png)\n\n - 不需要配置jar包\n\n- 004模块提供依赖等相关配置\n\n - pom文件\n\n - ```xml\n <dependency>\n <groupId>org.springframework</groupId>\n <artifactId>spring-context</artifactId>\n <version>4.3.30.RELEASE</version>\n </dependency>\n <dependency>\n <groupId>org.springframework</groupId>\n <artifactId>spring-webmvc</artifactId>\n <version>4.3.30.RELEASE</version>\n </dependency>\n \n <dependency>\n <groupId>com.alibaba</groupId>\n <artifactId>dubbo</artifactId>\n <version>2.6.12</version>\n </dependency>\n <!--接口工程-->\n <dependency>\n <groupId>com.bjpowernode</groupId>\n <artifactId>dubbo-003-interface</artifactId>\n <version>1.0-SNAPSHOT</version>\n </dependency>\n \n <dependency>\n <groupId>io.netty</groupId>\n <artifactId>netty-all</artifactId>\n <version>4.1.32.Final</version>\n </dependency>\n ```\n\n - 接口实现类\n\n - ```java\n public class UserServiceimpl implements UserService {\n \n \n @Override\n public List<User> sselectAll() {\n User user1=new User(1,\"zhangsan\",23);\n User user2=new User(2,\"lisi\",22);\n List<User> list=new ArrayList<>();\n list.add(user1);\n list.add(user2);\n return list;\n }\n }\n ```\n\n - dubbo配置文件\n\n - ```xml\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <beans xmlns=\"http://www.springframework.org/schema/beans\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dubbo=\"http://dubbo.apache.org/schema/dubbo\"\n xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd\">\n \n <!--声明服务提供者,保证唯一性-->\n <dubbo:application name=\"dubbo-004-userservice\"/>\n \n <!--设置dubbo协议和名称-->\n <dubbo:protocol name=\"dubbo\" port=\"20880\"/>\n \n <!--暴露服务接口-->\n <dubbo:service interface=\"com.powernode.service.UserService\" registry=\"N/A\" ref=\"UserService\"/>\n \n <!--加载业务接口的实现类到spring容器中-->\n <bean id=\"UserService\" class=\"com.power.service.impl.UserServiceimpl\"/>\n </beans>\n ```\n\n - web配置文件\n\n - ```xml\n <context-param>\n <param-name>contextConfigLocation</param-name>\n <param-value>classpath:dubbo-service.xml</param-value>\n </context-param>\n \n <listener>\n <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n </listener>\n ```\n\n- 005模块访问服务\n\n - pom配置\n\n - ```xml\n <dependency>\n <groupId>org.springframework</groupId>\n <artifactId>spring-context</artifactId>\n <version>4.3.30.RELEASE</version>\n </dependency>\n \n <dependency>\n <groupId>org.springframework</groupId>\n <artifactId>spring-webmvc</artifactId>\n <version>4.3.30.RELEASE</version>\n </dependency>\n \n <dependency>\n <groupId>com.alibaba</groupId>\n <artifactId>dubbo</artifactId>\n <version>2.6.12</version>\n </dependency>\n \n <dependency>\n <groupId>com.bjpowernode</groupId>\n <artifactId>dubbo-003-interface</artifactId>\n <version>1.0-SNAPSHOT</version>\n </dependency>\n ```\n\n - dubbo配置文件\n\n - ```xml\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <beans xmlns=\"http://www.springframework.org/schema/beans\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dubbo=\"http://dubbo.apache.org/schema/dubbo\"\n xmlns:dubbp=\"http://dubbo.apache.org/schema/dubbo\"\n xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd\">\n \n <!--声明服务消费者名称,保证唯一性-->\n <dubbo:application name=\"dubbo-005-consumer\"/>\n \n <!--引用远程接口-->\n <dubbp:reference id=\"userService\"\n interface=\"com.powernode.service.UserService\"\n url=\"dubbo://localhost:20880\"\n registry=\"N/A\"/>\n </beans>\n ```\n\n - spring配置文件\n\n - ```xml\n <context:component-scan base-package=\"com.powernode\"/>\n <mvc:annotation-driven/>\n <bean class=\"org.springframework.web.servlet.view.InternalResourceViewResolver\">\n <property name=\"prefix\" value=\"/\"/>\n <property name=\"suffix\" value=\".jsp\"/>\n </bean>\n ```\n\n - web配置文件\n\n - ```xml\n <context-param>\n <param-name>contextConfigLocation</param-name>\n <param-value>classpath:dubbo-service.xml</param-value>\n </context-param>\n \n <listener>\n <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n </listener>\n ```\n\n - controller实现类\n\n - ```java\n @Controller\n public class UserController {\n private UserService userService;\n \n @RequestMapping(\"/user\")\n public String testUserDetail(Model model){\n List<User> list = userService.sselectAll();\n model.addAttribute(\"list\",list);\n return \"userDetail\";\n }\n }\n ```\n\n##### dubbo常用标签\n\n- dubbo中常用的标签分为三个类别:公用标签、服务提供者标签、服务消费者标签\n\n- 公用标签\n\n - ```xml\n <dubbo:application/> \n <dubbo:registry/>\n ```\n\n - 配置应用信息\n\n - ```xml\n <dubbo:application name=\"访问的名称,也就是模块名\"/> \n ```\n\n - 配置注册中心\n\n - ```xml\n <dubbo:registry address=\"ip:port\" protocol=\"协议\"/>\n ```\n\n- 访问提供者标签\n\n - 配置服务消费引用远程服务\n\n - ```xml\n <dubbo:reference id=\"服务引用bean的id\" interface=\"服务接口名\">\n ```\n\n \n\n#### 注册中心\n\n##### 注册中心概述\n\n- 对于服务的提供方,他需要发布服务,而且由于应用系统的复杂性,访问的数量,类型也不断膨胀;对于服务消费方,它最关心如何获取到他需要的访问,而面对复杂的应用系统,需要管理大量的服务调用。而且,对于服务提供方和服务消费方来说,他们还有可能兼具这两种角色,即既需要提供消费,也需要提分服务。通过服务统一管理起来,可以有效的优化内部应用对服务发布/使用的流程和管理。服务注册中心可以通过特定协议来完成服务对外的统一。Dubbo提供的注册中心有一下几种类型:\n - Multicast注册中心:组播方式\n - Redis注册中心:使用Redis作为注册中心\n - Simple注册中心:就是一个dubbo服务,作为注册中心。提供查找服务的功能\n - Zookeeper注册中心\n\n##### windows安装zookeeper\n\n- [Index of /dist/zookeeper (apache.org)](https://archive.apache.org/dist/zookeeper/)\n\n- 开启zookeeper\n - bin--->zkServer.cmd\n\n##### 使用zookeeper注册中心\n\n- zookeeper-userservice-provider模块的dubbo配置文件\n\n - ```xml\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <beans xmlns=\"http://www.springframework.org/schema/beans\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dubbo=\"http://dubbo.apache.org/schema/dubbo\"\n xmlns:dubbp=\"http://dubbo.apache.org/schema/dubbo\"\n xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd\">\n \n <dubbo:application name=\"zookeeper-interface\"/>\n \n <dubbo:protocol name=\"dubbo\" port=\"20880\"/>\n \n <!--指定注册中心地址和端口号-->\n <dubbp:registry address=\"zookeeper://localhost:2181\"/>\n \n <dubbo:service interface=\"com.fcl.dubbo.service.UserService\" ref=\"UserService\"></dubbo:service>\n \n <bean id=\"UserService\" class=\"com.fcl.dubbo.service.impl.UserServiceImpl\"/>\n </beans>\n ```\n\n- 模块zookeeper-consumer的dubbo\n\n - ```xml\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <beans xmlns=\"http://www.springframework.org/schema/beans\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:dubbo=\"http://dubbo.apache.org/schema/dubbo\"\n xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd\">\n \n <dubbo:application name=\"zookeeper-interface\"/>\n \n <!--指定注册中心-->\n <dubbo:registry address=\"zookeeper:loaclhsot:2181\"/>\n \n <dubbo:reference id=\"UserService\" interface=\"com.fcl.dubbo.service.UserService\"/>\n </beans>\n ```\n\n- 其他的配置文件不变\n\n\n\n\n\n##### 版本号version的使用\n\n- 作为服务者:当存在多个实现类impl的时候,在dubbo配置文件中,就会只用version来配置多个interface\n\n - ```xml\n <dubbo:service interface=\"com.fcl.dubbo.service.UserService\" ref=\"UserService1\" version=\"1.0.0\"/>\n <dubbo:service interface=\"com.fcl.dubbo.service.UserService\" ref=\"UserService2\" version=\"2.0.0\"/>\n \n \n <bean id=\"UserService1\" class=\"com.fcl.dubbo.service.impl.UserService2Impl\"/>\n <bean id=\"UserService2\" class=\"com.fcl.dubbo.service.impl.UserService1Impl\"/>\n ```\n\n - 不管是否一个接口有多个实现类,只要服务提供者服务接口服务的时候指定了版本号,那作为消费者引用远程接口服务的时候就必须指定版本号\n\n- 作为消费者:\n\n - dubbo配置\n\n - ```xml\n <dubbo:reference id=\"UserService1\" interface=\"com.fcl.dubbo.service.UserService\" version=\"1.0.0\"/>\n ```\n\n - spring配置文件不变\n\n - Controller\n\n - ```java\n @Controller\n public class UserController {\n \n @Autowired\n private UserService userService1;\n @Autowired\n private UserService userService2;\n \n @RequestMapping(\"/zookeeper\")\n public String zookeeper(Integer id, String name, Model model){\n // User user=new User(id,name);\n \n User user1 = userService1.selectById(id,name);\n User user2 = userService2.selectById(id, name);\n model.addAttribute(\"user2\",user2);\n model.addAttribute(\"user1\",user1);\n return \"userDetail\";\n }\n }\n ```\n\n#### 监控中心\n\n# 补充\n\n### RFC和HTTP的区别\n### Dubbo的底层原理和源码剖析', 4, 'java,测试', '2024-05-05 21:38:07', '# 关于RPC框架Dubbo的详解', 'h1Dubboh1\nh2Dubbo简介h2\nh3基础理论h3\nh4分布式基础h4\nul\nli什么是分布式系统ul\nli分布式系统是若干独立计算机的集合这些计算机对于用户来书就像是单个相关系统li\nul\nli\nli分布式系统是建立在网络之上的软件系统li\nul\nh4发展演变h4\nul\nli单一应用架构:ORMli\nli垂直应用架构:MVCli\nli分布式服务架构:RPCgt远程过程调用li\nli流动计算架构:SOAli\nul\nh4什么是RPCh4\nul\nliRPC(Remote Proc', 'http://localhost:8080/image/1714916286672-dubbo.png', 1, 0, 0, '449cbc3e7bf244cebf243f6c2277553d');
INSERT INTO `b_content` VALUES (33, '2019-01-01 21:40:15', 0, 6, 0, 2, 0, '# 对比\n\n## ElasticSearch\n\nElasticsearch是一个实时分布式搜索引擎和分析引擎。可以用于全文搜索、结构化搜索、分析以及将三者混合使用\n\nElasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎,无论在开源还是专有领域,Lucene可以被认为迄今为止最先进、性能最好、功能最全的搜索引擎库。\n\n但是Lucene只是一个工具库,如果想要使用,必须使用Java来作为开发语言,并将其直接集成到应用中,重要的是Lucene非常复杂,需要深入了解检索的相关知识来理解其工作原理\n\nElasticsearch也使用java 开发,并使用Lucene作为核心来实现所有的索引和搜索的功能,但是他的目的是通过简单的RESTFul API 来隐藏Lucene的复杂性,从而让全文搜索变得简单。\n\n## Solr\n\nSolr是Apache下的一个顶级开源项目,采用java开发,他是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化\n\nSolr可以独立运行,运行咋Jetty、Tomcat等这些Servlet容器中,Solr索引的实现方法很简单,用POST方法向Solr服务器发送一个描述Field及其内容的XML文档,Solr根据XML文档CRUD索引。Solr搜索只需要发送HTTP GET请求,然后对Solr返回XML、JSON等格式的查询结果进行解析,组织页面布局。Solr不提供构建UI的功能,SOlr提供了一个管理界面。通过管理界面可以查询Solr的配置和运行情况。\n\nSolr是基于Lucene开发企业级搜索服务器,实际上就是封装了Lucene。\n\nSolr是一个独立的企业级搜索应用服务器,他对外提供类似于Web-service的API接口。用户可以通过HTTP请求,向搜索引擎服务器提交一定格式的文件,生成索引,也可以通过提出查找请求,并得到返回结果。\n\n## Lucene\n\nLucene是Apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文搜索引擎工具包,但是他不是一个完整的全文搜索引擎,而是一个全文搜索引擎的架构,提供了完整的查询引擎和搜索引擎,部分文本分析引擎。\n\nLucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文搜索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会提供和支持。Lucene提供了简单却强大的应用程式接口,能够做到全文检索和搜寻。在Java开发环境李Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。\n\n\n\n## 操作步骤\n\n### 安装\n\n#### linux \n\n```bash\n# 安装\nwget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.13.2-linux-x86_64.tar.gz\n# 解压\ntar -zxvf elasticsearch-7.13.2-linux-x86_64.tar.gz -C /usr/local\n```\n\n#### window\n\n官网下载:[Download Elasticsearch | Elastic](https://www.elastic.co/cn/downloads/elasticsearch)\n\n### 目录\n\n![image-20240428184514553](E:\\programming\\ELasticsearch\\img\\image-20240428184514553.png)\n\n```bash\nbin 启动文件\nconfig 配置文件\n log4j2 日志配置文件\n jvm.options java虚拟机相关配置\n elasticserach.yml elasticsearch配置文件。默认9200端口---> 跨域\nlib 相关jar包\nlogs 日志\nmodules 功能模块\nplugins 插件\n```\n\n### 启动\n\nbin目录下的elasticsearch.bat 文件\n\n### 错误解析\n\n##### jdk问题\n\n安装jdk\n\n```bash\nwget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz\n```\n\njvm.options 启动内存设置,这里需要注意的是不能空格,也就是-Xms256m必须顶格。\n\n配置环境\n\n```bash\nJAVA_HOME=/home/myjdk/jdk-17.0.11\nPATH=/home/myjdk/jdk-17.0.11/bin:$PATH\nexport JAVA_HOME PATH\n```\n\n\n\n### 实现外网访问\n\n修改elasticsearch.yml\n\n```bash\n# 任意地址\nnetword.host = 0.0.0.0\ncluster.initial_master_nodes: [\"node-1\"]\n\n# 在root用户下执行\n# 添加配置\nsudo vim /etc/sysctl.conf\n# 在配置文件中添加es\nvm.max_map_count=655360\n\n# 查看是否添加成功\nsudo sysctl -p\n\n# 再次重启即可\n```\n\n\n\n![image-20240430110347120](E:\\programming\\ELasticsearch\\img\\image-20240430110347120.png)\n\n### 安装可视化界面\n\n地址:https://github.com/mobz/elasticsearch-head/\n\n- 解压之后,安装环境:node.js\n- 安装依赖:npm install\n- 启动:npm run start \n\n![image-20240430111601731](E:\\programming\\ELasticsearch\\img\\image-20240430111601731.png)\n\n\n\n![image-20240430112556342](E:\\programming\\ELasticsearch\\img\\image-20240430112556342.png)\n\n### 跨域配置\n\n在elasticsearch.yml配置文件中\n\n```bash\nhttp.cors.enabled: true\nhttp.cors.allow-origin: \"*\"\n```\n\n\n\n### ELK详解\n\nELK 是Elasticsearch、Logstash、Kibana三大开源框架的首字母缩写简称。市面上也被称为Elastic Stack。其中Elasticsearch是基于Lucene、分布式、通过Restful方式进行交互的进实时搜索平台框架。Logstash是ELK的中央数据流引擎,用于从不同目标(文件/数据存储/MQ)收集不同格式数据,经过过滤后支持输出到不同目的地。Kibana可以将elasticsearch的数据通过友好的页面展示出来,提供实时分析的功能。\n\n### Kibana下载\n\n地址:[Kibana 7.13.2 | Elastic](https://www.elastic.co/cn/downloads/past-releases/kibana-7-13-2)\n\n> Kibana 启动\n\n将压缩包解压之后, 点击bin目录下的kibana.bat文件,由于我是使用的云服务器,所以还需要修改配置文件,让他能够访问云服务器的elasticsearc\n\n> 修改kinana的config目录下的kibana.yml文件\n\n```bash\nelasticsearch.hosts: [\"http://47.97.50.171:9200/\"]\n```\n\n保存再次启动即可\n\n![image-20240430122936357](E:\\programming\\ELasticsearch\\img\\image-20240430122936357.png)\n\n\n\n![image-20240430123234548](E:\\programming\\ELasticsearch\\img\\image-20240430123234548.png)\n\n> 汉化\n\n在kibana.yml文件中修改\n\n```bash\ni18n.locale: \"zh-CN\"\n```\n\n 该汉化包是存在于kibana目录:x-pack\\plugins\\translations\\translations\n\n## Kibana核心概念\n\n### 概述\n\n在之前的学习中,已经知道了es是什么,同时也完成了es的安装和启动,那么es是怎么去存储数据的,数据结构又是什么,又是怎么实现搜索的\n\n<span style=\"color:red\">集群、节点、索引、类型、文档、分片、映射是什么?</span>\n\nelasticsearch 是面向文档的,关系行数据库和elasticsearch的客观对比,在elasticsearch中,一切都是json\n\n| Relational DB | Elasticsearch |\n| ------------------ | ------------------------ |\n| 数据库(database) | 索引(indices) |\n| 表(tables) | 类型(types) 慢慢会弃用 |\n| 行(rows) | 文档(documents) |\n| 字段(columns) | 字段(fields) |\n\nelasticsearch(集群)可以包含多个索引,每个索引可以包含多个类型,每个类型又可以包含多个文档,每个文档包含多个字段\n\n**物理设计**:\n\nElasticsearch在后台将每个索引分成多个分片,每个分片可以在集群中的不同服务器之间进行迁移,注意:Elasticsearch一个就是一个集群\n\n![image-20240430130030233](E:\\programming\\ELasticsearch\\img\\image-20240430130030233.png)\n\n**逻辑设计**:\n\n一个索引类型中,包含多个文档,比如文档1、文档2。当索引一篇文章时,可以通过一个顺序去查询:索引->类型->文档id。\n\n### 文档\n\nElasticsearch是面向文档的。也就意味着索引和搜索数据的最小的单位是文档,在es中,文档有几个重要属性:\n\n- 自我包含,一篇文章同时包含字段和对应的值,也就是同时包含key:value\n- 可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的\n- 灵活的结构,文档不依赖预先定义的模式,在关系型数据库中,要提前定义字段才能使用,而在es中,对于字段是非常灵活的,有时候可以忽略该字段,或者动态添加一个新的字段\n\n尽管可以随意的新增或忽略某个字段,但是,每个字段的类型非常重要,比如一个年龄字段类型,可以是字符串也可以是整形,意味es会保存字段之间的映射及其他设置,这种映射具体到每个映射的每种类型,这也是为什么在es中,类型也被称为映射类型。\n\n### 类型\n\n类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。类型中对于字段的定义称为映射,比如name映射为字符串类型。文档是无模式的,他不需要拥有映射中所定义的所有字段,比如新增一个字段,那么es会自动将新的字段加入映射,但是这个字段不确定是什么类型的,es就会根据自动去判定类型并且定义,但是es可能会定义出错,所以最安全的方式就是提前定义好所需要的映射\n\n\n\n### 索引\n\n索引是映射类型的容器,es中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他-配置。然后他们被存储到了各个分片上。\n\n**物理设计:节点和分片是如何工作的**:\n\n- 一个集群至少有一个节点,而一个节点就是一个es进程,节点可以有多个索引默认的,如果创建索引,那么索引将会有五个分片(primary shard,又称主分片)构成的,每一个主分片会又一个副片(replica shard,又称复制分片)\n\n**![image-20240430131444312](E:\\programming\\ELasticsearch\\img\\image-20240430131444312.png)**\n\n![image-20240430131714132](E:\\programming\\ELasticsearch\\img\\image-20240430131714132.png)\n\n上图是一个由3个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于防止某个节点宕机,导致的数据丢失。实际上,一个分片是一个Lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得es在不扫描全部文档的情况下,就能告诉那些文档包含特定的关键字。\n\n#### 倒排索引\n\nes使用的是一种称为倒排索引的结构,采用Lucene倒排索引作为底层。这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含他的文档列表\n\n![image-20240430132506829](E:\\programming\\ELasticsearch\\img\\image-20240430132506829.png)\n\n### ik分词器\n\n#### 安装\n\n地址:https://github.com/infinilabs/analysis-ik/releases/download/v7.13.2/elasticsearch-analysis-ik-7.13.2.zip\n\n```bash\n# 将文件上传到elasticsearch的plugins目录下,创建一个ik文件夹,将压缩包放入ik目录中,解压\nunzip 压缩包 -d 解压目的地\n# 删除压缩包,重启服务器\n```\n\n#### 概述\n\n分词:即把一段中文或者别的数据划分成一个个的关键字,在搜索的时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词器是将每一个字看作一个词,比如:“我是张三”,会分为:“我”,“是”,“张”,“三”,这显然是不符合要求的,所以需要安全分词器来解决\n\nik分词器提供了两种分词算法:ik_smart和ik_max_word,其中ik_smart为最少切分,ik_max_word为最细粒度划分\n\n#### 测试分词器\n\n> ik_smart\n\n![image-20240430141535421](E:\\programming\\ELasticsearch\\img\\image-20240430141535421.png)\n\n\n\n\n\n> ik_max_word\n\n![image-20240430141609451](E:\\programming\\ELasticsearch\\img\\image-20240430141609451.png)\n\n注意:两者的区别在于smart是不会存在重复词,而max_ward会重复分词\n\n\n\n#### 自定义分词词典\n\n![image-20240430142717009](E:\\programming\\ELasticsearch\\img\\image-20240430142717009.png)\n\n在插件的config目录下,可以添加自己的分词词典,如下:\n\n```dic\n付成梁\n```\n\n\n\n### Restful 风格说明\n\n![image-20240430142929799](E:\\programming\\ELasticsearch\\img\\image-20240430142929799.png)\n\n#### 关于索引的基本操作\n\n##### 创建索引\n\n```bash\n# 指定id\nPUT /索引名称(数据库名称)/类型(以后会慢慢省略)/文档id\n{请求体}\n# 随机id\nPOST /test/test1\n{\n \"name\":\"张三\",\n \"age\":12\n}\n```\n\n![image-20240430143531658](E:\\programming\\ELasticsearch\\img\\image-20240430143531658.png)\n\n![image-20240430143658420](E:\\programming\\ELasticsearch\\img\\image-20240430143658420.png)\n\n![image-20240430143906893](E:\\programming\\ELasticsearch\\img\\image-20240430143906893.png)\n\n##### 删除文档\n\n```bash\nDELETE /test/test1/1\n```\n\n##### 指定字段类型创建索引\n\n```bash\nPUT /test2\n{\n \"mappings\": {\n \"properties\": {\n \"name\":{\n \"type\": \"text\"\n },\n \"age\":{\n \"type\": \"long\"\n },\n \"birth\":{\n \"type\": \"date\"\n }\n }\n }\n}\n```\n\n![image-20240430144803114](E:\\programming\\ELasticsearch\\img\\image-20240430144803114.png)\n\n##### 查看索引信息\n\n###### 自定义索引\n\n```bash\nGET 索引名称\n```\n\n![image-20240430145712656](E:\\programming\\ELasticsearch\\img\\image-20240430145712656.png)\n\n###### 默认索引\n\n```bash\n# 创建默认类型的索引:_doc\nPUT /test4/_doc/1\n{\n \"name\":\"张三\",\n \"age\":12,\n \"birth\":\"2002-09-10\"\n}\n\nGET test4\n```\n\n![image-20240430150314577](E:\\programming\\ELasticsearch\\img\\image-20240430150314577.png)\n\n##### 扩展\n\n通过_cat 命令可以查看很多信息_\n\n![image-20240430150443914](E:\\programming\\ELasticsearch\\img\\image-20240430150443914.png)\n\n\n\n##### 修改\n\n###### PUT修改\n\n```bash\nPUT /test4/_doc/1\n{\n \"name\":\"张三123\",\n \"age\":12,\n \"birth\":\"2002-09-10\"\n}\n```\n\n![image-20240430150856420](E:\\programming\\ELasticsearch\\img\\image-20240430150856420.png)\n\n问题:当缺少某一个字段时,那么也会执行修改,但是会改变原来的结构\n\n###### POST修改\n\n```bash\nPOST /test4/_doc/1/_update\n{\n \"doc\":{\n \"name\":\"万恶的人\"\n }\n}\n```\n\n\n\n![image-20240430151225561](E:\\programming\\ELasticsearch\\img\\image-20240430151225561.png)\n\n#### 关于文档的基本操作\n\n对于简单的增、删、改是和索引是一样的操作\n\n##### 简单的搜索\n\n```java\nGET blog/_search?q=title:Java\n```\n\n![image-20240430153923008](E:\\programming\\ELasticsearch\\img\\image-20240430153923008.png)\n\n\n\n##### 复杂的查询\n\n```bash\nGET blog/_search\n{\n \"query\": {\n \"match\": {\n \"title\": \"Springboot\"\n }\n },\n \"_source\": [\"title\",\"type\"]\n}\n```\n\n![image-20240430155347037](E:\\programming\\ELasticsearch\\img\\image-20240430155347037.png)\n\n###### 分页查询\n\n```bash\nGET blog/_search\n{\n \"query\": {\n \"match\": {\n \"type\": \"后\"\n }\n },\n \"from\": 0, # 从第几条数据开始\n \"size\": 1 # 查询的size\n}\n```\n\n\n\n\n\n###### 排序\n\n```bash\nGET blog/_search\n{\n \"query\": {\n \"match\": {\n \"type\": \"后\"\n }\n },\n \"sort\": [\n {\n \"_id\": { # 需要排序的字段\n \"order\": \"desc\"\n }\n }\n ]\n}\n```\n\n![image-20240430160301281](E:\\programming\\ELasticsearch\\img\\image-20240430160301281.png)\n\n###### 布尔查询(多条件查询)\n\n> must 命令(相对于and)\n\n```bash\nGET blog/_search\n{\n \"query\": {\n \"bool\": {\n \"must\": [\n {\n \"match\": {\n \"title\": \"springboot\"\n }\n },\n {\n \"match\": {\n \"type\": \"后端\"\n }\n }\n ]\n }\n }\n}\n```\n\n> should(相对于or命令)\n\n```bash\nGET blog/_search\n{\n \"query\": {\n \"bool\": {\n \"should\": [\n {\n \"match\": {\n \"title\": \"springboot\"\n }\n },\n {\n \"match\": {\n \"type\": \"后端\"\n }\n }\n ]\n }\n }\n}\n```\n\n> must_not(不等于)\n\n```bash\nGET blog/_search\n{\n \"query\": {\n \"bool\": {\n \"must_not\": [\n {\n \"match\": {\n \"type\": \"后端\"\n }\n }\n ]\n }\n }\n}\n```\n\n> 过滤查询\n\n```bash\nGET blog/_search\n{\n \"query\": {\n \"bool\": {\n \"must\": [\n {\n \"match\": {\n \"title\": \"springboot\"\n }\n }\n ],\n \"filter\": {\n \"range\": {\n \"FIELD\": { \n \"gte\": 10, # 大于等于\n \"lte\": 20 # 小于等于\n }\n }\n }\n }\n }\n}\n```\n\n> 多条件查询\n\n```bash\nGET blog/_search\n{\n \"query\": {\n \"match\": {\n \"type\": \"前 后\" # 这里多个条件使用空格隔开\n }\n }\n}\n```\n\n\n\n', 4, 'java,测试', '2024-05-05 21:40:15', '# 基于Lucene的搜索引擎 “Elasticsearch”', 'h1对比h1\nh2ElasticSearchh2\npElasticsearch是一个实时分布式搜索引擎和分析引擎可以用于全文搜索、结构化搜索、分析以及将三者混合使用p\npElasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎无论在开源还是专有领域Lucene可以被认为迄今为止最先进、性能最好、功能最全的搜索引擎库p\np但是Lucene只是一个工具库如果想要使用必须使用Java来作为开发语言并将其直接集成到应用中重要的是Lucene非常复杂需要深入了解检索的相', 'http://localhost:8080/image/1714916414803-elasticsearch.png', 1, 0, 0, '2d8f8d331fce4998ad5686a0f4598e67');
INSERT INTO `b_content` VALUES (34, '2024-05-05 21:50:25', 0, 6, 0, 2, 0, 'Git\n\n## Git与svn的对比\n\n#### Svn\n\n- SVN是集中式版本控制系统,版本库是及中国农放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度快,如果在互联网下,如果网速慢,就不好了\n- 集中管理方式在一定程度上看到其他开发人员在干什么,而管理员也可以很轻松的掌握每个人开发的权限\n- 缺点:\n - 服务器单点故障\n - 容错性查\n\n#### Git(去中心化)\n\n- 什么是去中心化思想?\n - 去中心化,不是不要中心,而是由节点来自由选择中心、自由决定中心。简单地说,中心化的意思,是中心决定节点。节点必须依赖中心,节点离开了中心就无法生存。在去中心化系统中,任何人都是一个节点,任何人也都可以成为一个中心。任何中心都不是永久的,而是阶段性的,任何中心对节点都不具有强制性。\n- 中心化的弊端:\n - Git是分布式版本控制系统,那么他就没有中央服务器,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要要联网了,因为版本都是在自己电脑上的。既然每个人的电脑都有一个完整的版本库,那么多个人怎么协作呢?比如说自己的电脑上修改了文件A,其他人也在自己的电脑上修改了文件A,这时,两台电脑只需要将各自修改的推送给对方,就可以互相看到修改的部分了\n\n#### Git和Svn的区别\n\n- 1)Git是分布式的,而svn不是:这是Git和其他非分布式的版本控制系统,例如svn、cvs等,最核心的区别\n- 2)Git把内容按元数据方式存储,而svn是按文件:所有的资源控制系统都是把文件的元信息隐藏在一个类似 .svn 、.cvs等的文件夹里\n- 3)Git没有一个全局的版本号,而svn有\n- 4)Git的内容完整性要优于svn:Git的内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏\n\n## Git的工作流程\n\n- 一般的工作流程:\n - 1)从远程仓库中克隆Git资源作为本地仓库\n - 2)从本地仓库中checkout代码然后进行代码修改\n - 3)在提交前先将代码提交到暂存区\n - 4)提交修改。提交到本地仓库。本地仓库中保存修改的各个历史版本\n - 5)在修改完成后,需要和团队成员共享代码时,可以将代码push到远程仓库\n - ![image-20230513204722879](E:\\programming\\Git\\img\\image-20230513204722879.png)\n\n## Git的安装\n\n- Git最开始只支持Linux,到后面支持window、Unix、Mac等操作系统\n- Git环境包、UI界面程序、语言包\n- ![image-20230514112815313](E:\\programming\\Git\\img\\image-20230514112815313.png)\n- Git Bash:Unix与Linux风格的命令行,使用最多,推荐最多\n- Git CMD:Window风格的命令行\n- Git GUI:图形界面的Git\n\n#### 基本的Linux命令\n\n```linux\ncd:改变目录\ncd ..:回退到上一个目录,直接cd进入默认目录\npwd:显示当前所在的目录路径\nls(ll):都是列出当前目录中的所有文件,只不过ll列出的内容更加详细\ntouch:新建一个文件,rm index.js就会把文件删除\nmkdir:新建一个目录\nrm -r:删除一个文件夹,rm -r src删除src目录\nrm -rf /:删除所有目录\nmv:移动文件,mv index.html src----index.html是要移动的文件,src是目标文件夹,当然,这样写必须要在同一个目录下\nreset:从新初始化终端/清屏\nclear:清屏\nhistory:查看命令历史\nhelp:帮助\nexit:退出\n# :表示注释\n```\n\n\n\n## Git配置\n\n- Git提供了一个叫做git config的工具,专门用来配置或读取响应的工作环境变量。这些环境变量,决定了Git在各个环节的具体工作方式和行为\n\n#### 用户信息\n\n- 配置个人用户名称和电子邮件地址\n\n - ```txt\n $git config --global user.name \"fcl\"\n $git config --global user.email 5174812@qq.com\n ```\n\n#### 查看配置信息\n\n- 要检查已有的配置信息,可以使用git config --list命令\n- ![image-20230514111923250](E:\\programming\\Git\\img\\image-20230514111923250.png)\n\n#### 差异分析工具\n\n- 有一个比较常用的是,在解决合并冲突时使用那种差异分析工具。比如要改用vimdiff的话:\n\n - ```\n git config --global merge.tool vimdiff\n ```\n\n \n\n## Git的基本理论(核心)\n\n![image-20230514115539130](E:\\programming\\Git\\img\\image-20230514115539130.png)\n\n- Git本地有三个工作区域:工作目录(Working Directory)、暂存区(Stage/Index)、资源库(Repository或Git Directory)。如果在加上远程的git仓库(Remote Directory)就可以分为四个工作区。\n- Workspace:工作区,就是平时存放目录代码的地方\n- Index/Stage:暂存区,用于临时存放你的改动,事实上他只是一个文件,保存即将提交到文件列表信息\n- Repository:仓库区(或本地仓库),就是安全存放数据的位置,这里有提交到所有版本的数据。其中HEAD指向最新放入仓库的版本\n- Remote:远程仓库,托管代码的服务器,可以简单认为是项目组中的一台电脑用于远程数据交换\n\n![image-20230514120831546](E:\\programming\\Git\\img\\image-20230514120831546.png)\n\n- Directory:使用Git管理的一个目录,也就是一个仓库,包含我们的工作空间和Git管理的空间\n- WorkSpace:需要通过Git进行版本控制的目录和文件,这些目录和文件组成了工作空间\n- .git:存放Git管理信息的目录,初始化仓库的时候自动创建\n- Index/Stage:暂存区,或者叫做待提交更新区,在提交进入repo之前,可以把所有的更新放在暂存区\n- Local Repo:本地仓库,一个存放在本地的版本库,HEAD只是当前的开发分支(branch)\n- Stash:隐藏,是一个工作状态保护栈,用于保存/恢复WorkSpace中的临时状态\n\n#### 工作流程\n\n- 1)在工作目录中添加、修改文件(UserMapper.xml)\n- 2)将需要进行版本管理的文件放入暂存区域(git add ..)\n- 3)将暂存区的文件提交到git仓库(git commit)\n\n\n\n## Git项目创建\n\n#### 创建工作目录的常用指令\n\n- 工作目录(WorkSpace)一般就是你希望Git帮助你管理的文件夹,就是你项目的目录,也可以是一个空目录,建议不要有中文。日常使用只需要记住以下常用命令即可\n - ![image-20230514122321285](E:\\programming\\Git\\img\\image-20230514122321285.png)\n\n#### 本地仓库搭建\n\n- 创建本地仓库的方法有两种:\n\n - 创建一个全新的仓库\n - 克隆一个远程仓库\n\n- 创建一个新的仓库\n\n - ```\n git init //执行之后会出现一个隐藏文件:.git\n ```\n\n- 克隆一个远程仓库\n\n - ```\n git clone [url]\n ```\n\n - 去gitee或者github上面克隆\n\n## Git文件操作\n\n#### 文件的四种状态\n\n- 版本控制就是对文件的版本控制,要对文件进行修改、提交等操作,首先要知道文件当前在什么状态,不然可能会提交了现在还不想提交的文件,或者套提交的文件没有提交上\n- Untracked:未跟踪,此文件在文件夹中,但并没有加入到git库不参与版本控制。通过 **git add**状态变为**Staged。**\n- Unmodify:文件已经入库,未修改,即版本库中的文件快照内容与文件夹中完全一致。这种类型的文件有两种去处,如果他被修改,而变为**Modified**。如果使用 **git rm** 移出版本库,则称为**Untracked**文件\n- Modified:文件已经修改,仅仅是修改,并没有进行其他的操作,这个文件也有两种去处,通过 **git add** 可以进入暂存 **staged** 状态,使用**git checkout**则丢弃修改过,返回到**unmodify**状态,这个**git checkout**即从库中取出文件,覆盖当前修改\n- Staged:暂存状态。执行 **git commit**则将修改同步到库中,这时库中的文件和本地文件又变为一致,文件为**unmodify**状态。执行 **git reset HEAD filename**取消暂存,文件状态问**Modified**\n\n\n\n#### 查看文件状态\n\n- 在git里面添加一个hello.txt 文件\n- ![image-20230514133135596](E:\\programming\\Git\\img\\image-20230514133135596.png)\n\n- 文件没有被跟踪Untracked状态\n\n![image-20230514133036961](E:\\programming\\Git\\img\\image-20230514133036961.png)\n\n- 文件执行git add命令,变为Staged状态\n\n![image-20230514133317653](E:\\programming\\Git\\img\\image-20230514133317653.png)\n\n- 使用git commit -m命令提交到仓库\n\n![image-20230514133539721](E:\\programming\\Git\\img\\image-20230514133539721.png)\n\n```bash\n#查看文件指定状态\ngit status [filename]\n\n#查看所有文件状态\ngit status\n\n#添加所有文件到暂存区\ngit add . \n\n#提交暂存区中的内容到本地仓库 -m 提交信息\ngit commit -m [提交信息]\n```\n\n#### 忽略文件\n\n- 有时候我们不想把某些文件纳入版本控制中,比如数据库文件,临时文件,设计文件等\n\n- 在主目录下建立 “.gitignore” 文件,此文件有如下规则:\n\n - 1)忽略文件中的空行或以 # 号开始的行将会被忽略\n - 2)可以使用Linux通配符。例如:“ * ” 代表任意多个字符,“ ?”代表一个字符,“[abc]” 代表可选字符范围,“{string1,string2...}” 代表可选的字符串等\n - 3)如果名称最前面有一个感叹号,表示例外规则,将不被忽略\n - 4)如果名称的最前面是一个路径分隔符(/),表示要忽略的文件在次目录下,而子目录中的文件不被忽略\n - 5)如果名称的最后面是一个路径分隔符(/),表示要忽略的是此目录下该名称的子目录,而非文件\n\n ```bash\n #为注释\n *.txt #忽略所有 .txt 结尾的文件,这样的话上传就不会被选中\n !lib.txt #但lib.txt除外\n /temp #仅忽略项目根目录下的TODO文件,但不包括其他目录temp\n build/ #忽略build/目录下的所有文件\n doc/*.txt #会忽略doc/notes.txt,但不包括doc/server/arch.txt\n ```\n\n\n\n#### 使用码云(Gitee)\n\n- 1)注册登录码云,完善个人信息\n\n- 2)设置本机绑定SSH公钥,实现免密码登录\n\n - ```bash\n #进入:C:\\user\\Administrator\\.ssh 目录\n #生成公钥\n ssh-keygen -t rsa #加密\n ```\n\n ![image-20230514160500623](E:\\programming\\Git\\img\\image-20230514160500623.png)\n\n- 3)将公钥信息public key添加到码云账户中即可\n\n ![image-20230514160533318](E:\\programming\\Git\\img\\image-20230514160533318.png)\n\n- 4)使用码云创建一个自己的仓库\n\n - ![image-20230514160658531](E:\\programming\\Git\\img\\image-20230514160658531.png)\n\n![image-20230514161014084](E:\\programming\\Git\\img\\image-20230514161014084.png)\n\n- 克隆该仓库\n - ![image-20230514161347126](E:\\programming\\Git\\img\\image-20230514161347126.png)\n\n\n\n## 使用idea集成git\n\n- 1)新建项目,绑定git\n - 将克隆下来的仓库复制到module中即可\n - ![image-20230514162500596](E:\\programming\\Git\\img\\image-20230514162500596.png)\n - ![image-20230514162757312](E:\\programming\\Git\\img\\image-20230514162757312.png)\n\n- 2)修改文件,使用IDEA操作git\n - 添加到暂存区\n - commit提交\n - push到远程仓库\n\n- 3)提交测试\n - ![image-20230514163202072](E:\\programming\\Git\\img\\image-20230514163202072.png)\n\n- 手动提交\n - ![image-20230514163308870](E:\\programming\\Git\\img\\image-20230514163308870.png)\n\n- push到远程仓库\n - \n\n![image-20230514163959568](E:\\programming\\Git\\img\\image-20230514163959568.png)\n\n![image-20230514164023204](E:\\programming\\Git\\img\\image-20230514164023204.png)\n\n## Git分支(多人操作)\n\n![image-20230514181314003](E:\\programming\\Git\\img\\image-20230514181314003.png)\n\n- git分支中常用的指令:\n\n - ```bash\n #列出所有本地分支\n git branch\n \n #列出所有远程分支\n git branch -r\n \n #新建一个分支,但依然停留当前的分支\n git branch [branch-name]\n \n #新建一个分支,并切换到该分支\n $git checkout -b [branch]\n \n #合并指定分支当当前分支\n $git merge [branch]\n \n #删除分支\n git branch -d [branch-name]\n \n #删除远程分支\n git push origin --delete [branch-name]\n git branch -dr [remote/branch]\n ```\n\n- 如果同一个文件在合并分支时都被修改了则会引起冲突:解决方法是我们可以修改冲突文件后重新提交,选择要保留某一个修改的代码即可\n\n- master主分支应该非常稳定,用来发布新版本,一般情况下不允许在上面工作,工作一般在新建的dev分支上工作,工作完后,需要发布,或者说dev分支代码稳定后可以合并到master分支上', 6, '测试', '2024-05-05 21:50:25', '# 代码仓库的操作语言----git', 'pGitp\nh2Git与svn的对比h2\nh4Svnh4\nul\nliSVN是集中式版本控制系统版本库是及中国农放在中央服务器的而干活的时候用的都是自己的电脑所以首先要从中央服务器哪里得到最新的版本然后干活干完后需要把自己做完的活推送到中央服务器集中式版本控制系统是必须联网才能工作如果在局域网还可以带宽够大速度快如果在互联网下如果网速慢就不好了li\nli集中管理方式在一定程度上看到其他开发人员在干什么而管理员也可以很轻松的掌握每个人开发的权限li\nli缺点:ul\nli服务器单点故障li\nl', 'http://localhost:8080/image/1714917024925-git.png', 1, 0, 0, '570d4d29c24f435aa1f29db9fc48cf99');
INSERT INTO `b_content` VALUES (35, '2023-01-05 22:17:27', 0, 5, 0, 2, 0, '# JavaWeb\n\n## Servlet\n\n### ##静态资源\n\n##### [127.0.0.1](http://locahost:8080/oa/userList.html)\n\n 访问这个地址可以显示一个用户列表页面,但是这个用户列表页面时写死在HTML文件当中的。这种资源称作静态资源。当链接数据库时会变成动态资源\n\n 链接数据库需要JDBC程序,也就是说需要编写Java程序连接数据库,数据库中有多少记录 就显示多少记录,这种技术称作动态网页技术(动态网页技术并不是说网页中有flash动画,动态网页技术是说页面中的数据是动态的,根据数据库中的数据的变化而变化)。\n\n```html\n<!doctype html>\n<html>\n<head>\n<title>User List</title>\n</head>\n<body>\n<table border=\"1px\" align=\"center\" width=\"50%\">\n <tr>\n <th>no</th>\n <th>username</th>\n </tr>\n <tr>\n <td>1</td><td>zhangsan</td>\n </tr>\n <tr>\n <td>2</td><td>lisi</td>\n </tr>\n <tr>\n <td>3</td><td>wangwu</td>\n </tr>\n <tr>\n <td>4</td><td>zhaoliu</td>\n </tr>\n </table>\n</body>\n</html>\n```\n\n\n\n\n\n### ##对于一个动态的WEB应用来说一个请求和响应的过程有多少个角色参与,角色与角色之间有多少个协议\n\n![](C:\\Users\\object\\Pictures\\Saved Pictures\\QQ图片20230128111046.png)\n\n###### —有哪些角色(在整个BS结构的结构系统中,有哪些参与进去了)\n\n (1)浏览器软件的开发团队(谷歌浏览器、火狐浏览器、IE浏览器...)\n\n (2)WEB Server的开发团队(Tomcat、Jetty、WebLogic、JBOSS、WebSphere...)\n\n (3)DB Server的开发团队(Oracle、MySQL、SqlServer...) \n\n (4)webAPP的开发团队(Java程序员开发)\n\n###### 角色和角色之间应该遵守哪些规范和协议\n\n (1)webAPP的开发团队 和 WEB Server的开发团队 之间有一套规范:JavaEE规范之一——Servlet规范\n\n 1、Servlet规范的作用。\n\n ①WEB Server 和 webAPP 解耦合。\n\n (2)Browser 和 WebServer 之间有一套传输协议:HTTP协议(超文本传输协议)。\n\n (3)webAPP开发团队 和 DB Server的开发团队有一套规范:JDBC规范。\n\n ![](C:\\Users\\object\\Pictures\\Saved Pictures\\QQ图片20230128115907.png)\n\n###### Servlet是一个什么规范?\n\n* 遵循Servlet规范的webAPP,就可以放在不同的web服务器中运行。\n* Servlet规范包括:\n * 规定哪些接口\n * 规范哪些类\n * 规范了一个web应用应该有哪些配置文件\n * 规范了一个web应用中配置文件的名字\n * 规范了一个web应用中配置文件存放路径\n * 规范了一个web应用中配置问价的内容\n * 规范了一个合法的web应用他的目录结构是怎样\n * .....\n\n### 使用文本工具开发一个带有Servlet(Java小程序)的webAPP(重点)\n\n* 开发步骤: \n\n * 第一步:在webapps目录下创建一个目录,起名crm(这个crm就是webAPP的名字)。\n\n * 注意:crm就是这个webAPP的根 \n\n * 第二步:在webapp的根下新建一个目录:WEB-INF\n\n * 注意:这个目录的名字是Servlet中规定的,必须全部大写,必须一模一样。\n\n * 第三步:在WEB-INF目录下新建一个目录:classes\n\n * 注意:这个目录的名字必须全部是小写的classes。这也是Servlet规范中规定。另外这个目录下一定存放的是java程序编译之后的class文件(这里存放的是字节码文件)\n\n * 第四步:在WEB-INF目录下新建一个目录:lib\n\n * 注意:这个目录不是必须的,但是如果webapp需要第三方jar包的情况下,这个jar包要放到lib目录下,这个目录的名字也不能随便编写,必须是全部小写的lib。例如:java语言链接数据库需要数据库的驱动jar包。那么这个jar包一定要放在lib目录下。这也是Servlet规范中规定的。\n\n * 第五步:在WEB-INF目录下新建一个文件:web.xml\n\n * 注意:这个文件是必须的,这个文件名叫做web.xml。这个文件必须放在这里。一个合法的webapp,web.xml文件是必须的,这个web.xml文件就是一个配置文件,在这个配置文件中描述了请求路径和Servlet类之间的对照关系。\n\n * 这个文件最好从其他的webapp中拷贝,最好别手写。\n\n * ```xml\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <web-app xmlns=\"https://jakarta.ee/xml/ns/jakartaee\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"https://jakarta.ee/xml/ns/jakartaee\n https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd\"\n version=\"5.0\"\n metadata-complete=\"true\">\n </web-app>\n ```\n\n * 第六步:编写一个小Java程序,这个小程序也不能随便开发,必须实现Servlet接口\n\n * 这个Servlet不在jdk当中(因为Servlet属于JavaEE,jdk在JavaSE中)\n * Servlet接口(Servlet.class文件)是Oracle公司提供的(最原始的是SUN公司提供的)\n * Servlet接口是JavaEE的规范中的一员\n * Tomcat服务器实现了Servlet规范,所以Tomcat服务器也需要使用Servlet接口。Tomcat服务器中应该由这个接口,Tomcat服务器的CATALINA_HOME/lib目录下有个Servlet-api.jar,解压这个文件之后会看见里面有一个Servlet.class 文件,属于Jakarta包.\n * **重点**:从JakartaEE9开始,Servlet接口全名变了:Jakarta.servlet.Servlet\n * *注意*:编写Java小程序的时候,java源代码可以在任意位置,只需要将编译后的class文件放到classes目录下即可。\n\n * 第七步:编译我们写的HelloSrevlet\n\n * **重点** :如何让编译通过呢 ? 配置环境变量\n\n CLASSPATH=.;D:\\java\\apache-tomcat-10.0.27\\lib\\servlet-api.jar\n\n * 思考问题:以上配置的CLASSPATH配置对于Tomcat服务器运行有没有关系?\n\n * 没有任何关系,以上配置这个环境变量只是为了让文件能正常编译成class文件\n\n * ```java\n package com.bjpowernode.servlet;\n \n import jakarta.servlet.Servlet;\n import jakarta.servlet.ServletException;\n import jakarta.servlet.ServletConfig;\n import jakarta.servlet.ServletRequest;\n import jakarta.servlet.ServletResponse;\n import java.io.IOException;\n \n public class HelloServlet implements Servlet{\n public void init(ServletConfig config) throws ServletException{}\n public void service(ServletRequest request,ServletResponse response) throws ServletException,IOException{\n System.out.println(\"My First Servlet,Hello Servlet\");\n \n }\n public void destroy(){\n \n }\n public String getServletInfo(){\n return \"\";}\n public ServletConfig getServletConfig(){\n return null;\n }\n }\n ```\n\n \n\n 将信息直接传输到浏览器:\n\n * ```java\n package com.bjpowernode.servlet;\n \n import jakarta.servlet.Servlet;\n import jakarta.servlet.ServletException;\n import jakarta.servlet.ServletConfig;\n import jakarta.servlet.ServletRequest;\n import jakarta.servlet.ServletResponse;\n import java.io.IOException;\n import java.io.PrintWriter;\n \n public class HelloServlet implements Servlet{\n public void init(ServletConfig config) throws ServletException{}\n public void service(ServletRequest request,ServletResponse response) throws ServletException,IOException{\n System.out.println(\"My First Servlet,Hello Servlet\");\n /*怎么将信息直接传输到浏览器上\n 需要使用ServletResponse接口:response\n response表示响应:从服务器向浏览器发送数据叫做响应*/\n //设置响应内容为普通文本或者HTML,这个设置只能在获取流之前设置\n response.setContentType(\"Text/html\")\n \n PrintWriter out=response.getWriter();\n out.print(\"HelloServlet, this is my first web\");\n \n \n //浏览器是可以识别HTML代码的,所以:\n out.print(\"<h1>HelloServlet, this is my first web</h1>\");\n \n /*这是一个输出流,负责输出字符串到浏览器\n 这个输出流不需要我们刷新,也不需要关闭,这些都由Tomcat服务器来维护*/\n /*\n out.flush();\n out.close();\n */\n \n }\n public void destroy(){\n \n }\n public String getServletInfo(){\n return \"\";}\n public ServletConfig getServletConfig(){\n return null;\n }\n }\n ```\n\n \n\n * 第八步:将编译的class文件剪切到classes目录下\n\n * 第九步:在web.xml文件中编写路径,让“请求路径”和“Servlet类名”关联在一起\n\n * 这一步用专业术语描述:在web.xml文件中注册Servlet类\n\n * ```xml\n <description>\n Tomcat Documentation.\n </description>\n <!--Servlet的描述信息-->\n <!--任何一个Servlet都对应一个servlet-mapping-->\n <servlet>\n <servlet-name>adfasdf</servlet-name>\n <servlet-class>com.bjpowernode.servlet.HelloServlet</servlet-class>\n </servlet>\n <!--Servlet的映射信息-->\n <servlet-mapping>\n <!--这个随便写,但是要和上面一样-->\n <servlet-name>adfasdf</servlet-name>\n <!--这里需要一个路径,这个路径唯一要求是以“/”开始-->\n <!--当前这个路径可以随便写-->\n <url-pattern>/ads/ad/ad/dsads/ddas</url-pattern>\n </servlet-mapping>\n </web-app>\n ```\n\n * 第十步:启动Tomcat服务器\n\n * 第十一步:打开浏览器,在地址栏上输入一个URL,这个URL必须是:http://127.0.0.1:8080/crm/ads/ad/ad/dsads/ddas\n\n * *注意*:之后端口之后的crm是文件名,文件名之后的是web.xml中填写的路径,即url-pattern标签中的路径\n\n * 浏览器编写路径太复杂,可以使用超链接(注:HTML页面只能放在WEB-INF目录外面)\n\n * 以后不需要编写main方法,Tomcat服务器负责调用main方法,Tomcat服务器启动的时候执行的就是main方法。我们Javaweb程序员只需要编写Servlet接口实现类,然后注册到web.xml文件中即可\n\n**关于JavaEE的版本:**\n\n* JavaEE目前最高版本是JavaEE8\n* JavaEE被Oracle公司捐献了,Oracle将JavaEE规范捐给了Apache\n* Apache将JavaEE换名了,改成JakartaEE\n* JavaEE8升级之后的JavaEE9改为“JakartaEE9”\n* JavaEE8的时候对应得Servlet类名是:javax.servlet.Servlet\n* JakartaEE9的时候对应得Servlet类名是:jakarta.servlet.Servlet\n\nC:\\Program Files\\Common Files\\Oracle\\Java\\javapath\n\n**解决Tomcat服务器在DOS命令窗口中乱码问题**\n\n将CATALINA_HOME\\conf\\logging.properties文件中的内容修改成java.util.logging.ConsoleHandler.encoding =GBK\n\n#### 向浏览器响应一段HTML代码\n\n```java\npublic void service(ServletRequest request,ServletResponse response){\n response.setContentType(\"Text/html\");\n PrintWriter print=response.getWriter();\n print.print(\"<h1>你好</h1>\");\n}\n```\n\n#### 在Servlet中链接数据库\n\n```java\npackage com.bjpowernode.servlet;\n\nimport jakarta.servlet.Servlet;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletConfig;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.sql.*;\n\npublic class StudentServlet implements Servlet{\n public void init(ServletConfig config) throws ServletException{}\n public void service(ServletRequest request,ServletResponse response) throws ServletException,IOException{\n //编写JDBC代码连接数据库\n Connection conn=null;\n PrepareStatement ps=null;\n Resultset rs=null;\n try{\n //注册驱动\n Class.forName(\"com.mysql.cj.jdbc.Driver\");\n //获取连接\n String url=\"jdbc:mysql://localhost:3306/student\";\n String user=\"root\";\n String pass=\"root\";\n conn=DriverManager.getConnection(url,user,pass);\n //获取编译sql语句对象\n String sql=\"insert into student(ID) values(\"20112840105\")\";\n ps=conn.prepareStatement(sql);\n //执行sql\n rs=ps.executeQuery();\n //处理查询结果集 \n while(rs.next()){\n String ID=rs.getString(\"ID\");\n System.out.println(ID);\n }\n }\n catch(Exception e){\n e.printStackTrace();\n\n }\n\n finally{\n //释放资源\n if(rs!=null){\n try{rs.close();}catch(Exception e){\n e.printStackTrace();\n }\n if(conn!=null){\n try{conn.close();}catch(Exception e){\n e.printStackTrace();\n }\n if(ps!=null){\n try{ps.close();}catch(Exception e){\n e.printStackTrace();\n } \n } \n }\n }\n public void destroy(){}\n public String getServletInfo(){\n return \"\";}\n public ServletConfig getServletConfig(){\n return null;\n }\n}\n```\n\n### 使用集成环境开发Servlet程序\n\n##### 开发步骤:\n\n* 第一步:New Project(尽量使用Empty Project,也可以直接创建Project),起名:Javaweb\n\n* 第二步:新建模块(File->New->Module...)\n\n * 这里新建的是一个普通的JavaSE模块(不用Java Enterprise)\n * 这个Module会自动放到Javaweb的project下面,起名servlet01\n\n* 第三步:让模块变成JavaEE的模块(让Modle变成Javaweb的模块。符合Javaweb规范,符合Servlet规范的模块)\n\n * 在Module上点右键:Add Application(选择web Application)\n * 选择这个webapp支持之后,idea会自动生成一个符合Servlet规范的webapp目录结构\n * 重点:在idea工具中根据web Application模板生成的目录中有一个web目录,这个目录就代表webapp的根\n\n* 第四步(非必须):根据web Application生成的资源中有index.jsp文件,可以删除\n\n* 第五步:编写Servlet(StudentServlet)\n\n * class StudentServlet implements Servlet\n * 这个时候发现Servlet.claa 文件没有。将CATALINA_HOME/lib/servlet-api.jar和jsp-api.jar添加到classpath当中\n * file->Project Structure->Modules->+->Add JARS\n * 实现Jakarta.servlet.Servlet接口中的五个方法\n\n* 第六步:在Servlet中编写业务代码(连接数据库了)\n\n* 第七步:在WEB-INF下新建一个lib目录,并将连接数据库的jar包放进去\n\n* 第八步:在web.xml文件中完成StudentServlet类的注册(请求路径和Servlet之间对应起来)\n\n * ```xml\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <web-app xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd\"\n version=\"4.0\">\n <servlet>\n <servlet-name>studentServlet</servlet-name>\n <servlet-class>com.bjpowernode.javaweb.servlet.StudentServlet</servlet-class>\n </servlet>\n <servlet-mapping>\n <servlet-name>studentServlet</servlet-name>\n <url-pattern>/servlet/student</url-pattern>\n </servlet-mapping>\n </web-app>\n ```\n\n* 第九步:给一个html文件中编写一个超链接,用户点击这个连接之后,发送请求,Tomcat执行后台的StudentServlet\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Student page</title>\n</head>\n<body>\n<a href=\"/web/servlet/student\">Stuednt Servlet</a>\n</body>\n</html>\n```\n\n* 第十步:让idea工具去关联Tomcat服务器。关联的过程中将webapp部署到Tomcat服务器\n * IDEA工具右上角,绿色小锤子右边有一个:Add Configuration\n * 左上角点击 “+” ,点击Tomcat Server->local\n * 在弹出的界面中设置服务器Server的参数(基本不用动)\n * 在当前窗口中有一个Deployment(点击这个用来部署webapp),继续点击 “+” ,部署即可\n * 修改Application context为项目根名:/web\n* 第十一步:启动Tomcat服务器\n * 右上角的debug旁边的绿色小虫子,点击这个绿色小虫子可以采用debug的方式启动Tomcat服务器\n* (第十二步:打开浏览器在导航栏输入地址:http://127.0.0.1:8080/web/student.html\n\n### Servlet对象的生命周期\n\n* 什么是Servlet对象的生命周期?\n * 一个Servlet对象从创建到销毁的整个过程\n* Servlet对象由谁来维护?\n * Servlet对象的创建,对象方法上的调用,对象的最终销毁,Javaweb程序员是无法干预的\n * Servlet对象的生命周期是由Tomcat服务器(web Server)全权负责的\n * Tomcat服务器我们又称为:WEB 容器(WEB Container)\n * WEB容器来管理Servlet对象的生命\n* *思考?*:我们自己new的Servlet对象受WEB容器的管理吗\n * *答:*是不受管理的\n * WEB容器创建的Servlet对象,都会被放到一个集合当中(HashMap),只有在这个集合当中的对象才会被管理\n\n![](C:\\Users\\object\\Pictures\\Saved Pictures\\QQ图片20230129151123.png)\n\n* 研究:服务器在启动的Servlet对象有没有被创建出来?\n * 在Servlet中提供了一个无参数的构造方法,启动的时候看看构造方法是否执行\n * 经过测试:默认情况下,服务器在启动的时候Servlet对象并不会被实例化\n * 这个设计是合理的。用户没有发送请求之前,如果提前创建出所有的Servlet对象,必然是耗费内存的,并且创建出来的Servlet对象如果一直没有用户访问,显然这个对象是在浪费内容,没必要先创建\n* 怎么让服务器启动的时候创建Servlet对象呢?\n * 在Servlet标签中添加<load-on-startup>子标签,在该标签中填写整数,越小的整数优先级越高\n\n```xml\n<servlet>\n <servlet-name>aservlet</servlet-name>\n <servlet-class>com.bjpowernode.javaweb.servlet.AServlet</servlet-class>\n <!--在启动的时候去加载这个对象,为整数-->\n <load-on-startup>0</load-on-startup>\n\n </servlet>\n <servlet-mapping>\n <servlet-name>aservlet</servlet-name>\n <url-pattern>/a</url-pattern>\n </servlet-mapping>\n```\n\n- Servlet对象生命周期\n\n - 默认情况下服务器启动的时候AServlet对象并没有被实例化\n - 用户发送第一次请求的时候,控制台输出以下内容\n\n ```\n AServlet 无参数构造方法\n init 方法开始执行\n service 开始执行\n ```\n\n - 根据输出内容得出结论\n - 用户在第一次请求的时候Servlet对象被实例化(AServlet的构造方法被执行了)。并且执行的是无参数构造方法\n - AServlet对象被创建之后,Tomcat服务器马上调用了AServlet对象的init方法。(init方法在执行的时候,AServlet已经存在了。)\n - 用户发送第一次请求的时候,init执行之后,Tomcat服务器马上调用AServlet对象的service方法。\n\n```\nservice 开始执行 \n```\n\n 根据以上输出结果得知,用户在发送第二次,第三次....请求的时候,Servlet对象并没有新建,还是使用前创建的Servlet对象,直接调用该Servlet对象的service方法,这说明:\n\n- 第一:Servlet对象是单例的(单实例的。但是要注意:Servlet对象是单实例的,但是Servlet类并不符合单例模式。我们称之为假单例。之所以单例是因为Servlet对象的创建我们Javaweb程序员管不了,这个对象只能是Tomcat说了算,Tomcat创建了一个,所以导致单例,但是属于假单例。真单例模式,构造方法是私有化的。)\n\n- 第二:无参数构造方法、init方法只在第一次用户发送请求的时候执行。也就是说无参数构造方法只执行一次。init方法也只执行一次\n\n- 第三:只要用户发送一次请求service必然会被调用一次,发送100次,service调用100次。\n\n- 关闭服务器时,控制台输出以下内容:\n\n - ```\n destroy 开始执行\n ```\n\n - 通过以上输出内容,可以的出以下结论:\n\n - Servlet的destroy方法只被Tomcat服务器执行一次\n - destroy方法什么时候调用呢?\n - 在服务器关闭的时候\n - 因为服务器关闭的时候要销毁AServlet对象的内存\n - 服务器销毁AServlet对象前,会自动调用destroy方法\n\n- 当我们Servlet类中编写一个有参数的构造方法,如果没有手动编写无参数构造方法会出现什么问题?\n\n - 当执行Tomcat服务器打开浏览器会报错:500错误 --> HTTP协议的错误状态码\n - 所以,在Servlet开发当中,不建议Javaweb程序员定义构造方法,因为定义不当会导致Servlet对象无法实例化\n\n- *思考*:Servlet的无参数构造方法是在对象第一次创建的时候执行,并且只执行一次。init方法也是在对象第一次创建的时候执行,也执行一次。那么这个无参数构造方法能代替init方法吗/\n\n - 不能。Servlet规范中有要求,作为Javaweb程序员,编写Servlet类的时候,不建议手动编写构造方法,因为编写构造方法很容易让无参数构造方法小时,这个操作很容易导致Servlet对象无法实例化。所以init方法是由存在的必要(init方法更安全)\n\n### 适配器模式改造Servlet\n\n- 适配器,将代码变得美观,将最常用的接口方法改成抽象,就可以只重写这个方法\n\n - ```java\n package com.bjpowernode.javaweb.servlet;\n import jakarta.servlet.*;\n import java.io.IOException;\n public abstract class AServlet implements Servlet{\n //无参数构造方法\n public AServlet(){\n System.out.println(\"AServlet 无参数构造方法\");\n }\n @Override\n public void init(ServletConfig servletConfig) throws ServletException {\n System.out.println(\"init 方法开始执行\");\n }\n @Override\n public ServletConfig getServletConfig() {\n return null;\n }\n \n /*\n 将service方法改为抽象方法,再让另外的方法来继承这个抽象类重写service方法————适配器\n */\n @Override\n public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException ;\n \n @Override\n public String getServletInfo() {\n return null;\n }\n @Override\n public void destroy() {\n System.out.println(\"destroy 开始执行\");\n }\n }\n \n ```\n\n- 实现类\n\n- ```java\n package com.bjpowernode.javaweb.servlet;\n \n import jakarta.servlet.ServletException;\n import jakarta.servlet.ServletRequest;\n import jakarta.servlet.ServletResponse;\n import java.io.IOException;\n public class UseAServlet extends AServlet{\n @Override\n public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {\n System.out.print(\"适配器开始执行\");\n }\n }\n ```\n\n - 编写UseAServlet类来单独继承抽象的AServlet类service方法\n\n - *思考:*AServlet类是否需要改造一下?怎么改造?更利于子类的编写?\n\n - 将局部变量转换为成员变量\n\n ```java\n private ServletConfig config;\n //无参数构造方法\n public AServlet(){\n System.out.println(\"AServlet 无参数构造方法\");\n }\n @Override\n public void init(ServletConfig servletConfig) throws ServletException {\n this.config=config;\n System.out.println(\"init 方法开始执行\");\n }\n \n @Override\n public ServletConfig getServletConfig() {\n return config;\n }//返回私有成员变量\n \n ```\n\n - 子类调用ServletConfig对象config\n\n ```java\n ServletConfig config=this.getServletConfig();\n System.out.println(\"service方法可以获取ServletConfig方法的对象?\"+config);\n ```\n\n - *思考:*是否有可能子类会重写init方法?\n\n - 可能。但是因此config对象返回为空。所以会将init方法添加final\n - 当子类必须重写方法时,可以重载init方法\n\n ```java\n @Override\n public final void init(ServletConfig servletConfig) throws ServletException {\n this.config=config;\n System.out.println(\"init 方法开始执行\");\n this.init();\n }\n public void init(){\n //给子类重写\n }\n ```\n\n- **注:**适配器,官方已经写了,location:jakarta.servlet.GenericServlet类\n\n### ServletConfig接口详解\n\n###### 序列化\n\n- 序列化:序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。通俗的说就是将就是将对象转化成字节序列的过程。\n- 反序列化:就是讲字节序列转化成对象的过程。 \n- 序列化有什么作用? \n - 在序列化期间,对象将其当前状态写入到临时或持久性存储区。 以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。 序列化使其他代码可以查看或修改,那些不序列化便无法访问的对象实例数据。\n- 序列化的原因?\n - 持久化:对象是存储在JVM中的堆区的,但是如果JVM停止运行了,对象也不存在了。序列化可以将对象转化成字节序列,可以写进硬盘文件中实现持久化。在新开启的JVM中可以读取字节序列进行反序列化成对象。\n - 网络传输:网络直接传输数据,但是无法直接传输对象,可在传输前序列化,传输完成后反序列化成对象。所以所有可在网络上传输的对象都必须是可序列化的。\n\n###### transient关键字\n\n- java 的**transient关键字**的作用是**需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient**,序列化对象的时候,这个属性就不会序列化到指定的目的地中。\n- 如GenericServlet接口中的config属性:\n\n```java\nprivate transient ServletConfig config;\n```\n\n以下代码作用为区分类,即类名相同时,通过序列化版本号来区分类。在以后修改类名之后仍然可以通过序列化版本号反编译类\n\n```java\nprivate static final long serialVersionUID = 1L;//版本号,可以是任意数字\n```\n\n###### ServletConfig\n\n- ServletConfig是什么?\n - jakarta.servlet.ServletConfig,显然是Servlet规范中的一员,是一个接口\n- 谁去创建/实现ServletConfig接口?\n - 创建:在创建Servlet对象的同时,Tomcat服务器创建了ServletConfig对象\n - 实现:public class org.apache.catalina.core.StandardWrapperFacade implements ServletConfig{}\n - 结论:Tomcat服务器实现了这个接口\n- 一个Servlet对象中有一个ServletConfig。他们是一对一关系。\n- ServletConfig接口的作用?\n - configuration:配置\n - Servlet对象的配置信息对象,即一个Servlet对象就有一个配置信息对象\n- ServletConfig对象包装了什么什么信息?\n - web.xml文件中<servlet></servlet>标签中的配置信息\n- <init-param></init-param> 标签是初始化参数。这个初始化参数信息会被Tomcat自动封装到ServletConfig对象当中。\n\n```java\n <servlet>\n <servlet-name>servlet03</servlet-name>\n <servlet-class>com.bjpowernode.javaweb.servlet.ConfigTestServlet</servlet-class>\n <!--这里可以配置一个Servlet对象的初始化信息的-->\n <init-param>\n <param-name>driver</param-name>\n <param-value>com.mysql.cj.jdbc.Driver</param-value>\n </init-param>\n <init-param>\n <param-name>url</param-name>\n <param-value>jdbc:mysql://127.0.0.1:3306/bjpowernode</param-value>\n </init-param>\n</servlet>\n```\n\n- 在ServletConfig接口中的方法:\n\n - **getInitParameterNames()**:用于获取xml文件中的init-param中的name数组\n\n ```java\n ServletConfig config=this.getServletConfig();\n Enumeration<String> initname=config.getInitParameterNames();\n while (initname.hasMoreElements()){//便历initname\n String name=initname.nextElement();//取元素\n out.print(name+\"<br>\");\n }\n ```\n\n 浏览器显示:driver url\n\n - **getInitParameter()**:可以通过获取初始化参数的name获取value\n\n ```java\n String driver=config.getInitParameter(\"driver\");\n out.print(driver);\n ```\n\n 浏览器显示:com.mysql.cj.jdbc.Driver\n\n - getServletName():获取Servlet的name\n - getServletContext():获取ServletContext对象\n\n### ServletContext接口详解\n\n###### ServletContext\n\n- 只要在同一个webapp当中,只要在同一个应用中,所有的Servlet对象都是共享同一个ServletContext对象的\n\n- ServletContext对象在服务器启动阶段创建,在服务器关闭的时候销毁。这就是ServletContext的生命周期。ServletContext对象是应用级对象\n\n- 一个webapp肯定只有一个ServletContext对象\n\n- ServletContext被称为Servlet上下文对象(Servlet对象的四周环境对象)\n\n- 通常一个ServletContext对象对应一个web.xml文件\n\n - *eg:*一个教室有多个学生,那么每一个学生就是一个Servlet对象,而教室就是ServletContext对象。也就是说放在这个ServletContext对象当中的数据,在同一个教室当中是共享的。\n\n- ServletContext接口的常用方法\n\n - ```java\n public String getInitParameter(String name); //通过初始化参数的name获取value\n ```\n\n ```java\n /**调试*/\n ServletContext application=this.getServletContext();\n out.print(application);\n Enumeration<String> stringEnumeration=application.getInitParameterNames();\n while (stringEnumeration.hasMoreElements()){\n String name=stringEnumeration.nextElement();\n String value=application.getInitParameter(name);\n out.print(\"<br>\"+name+\"<br>\"+value);\n }\n \n //输出:pageSize=10 indexpage=0\n ```\n\n - ```java\n public Enumeration<String> getInitParameterNames(); //获取所有的初始化参数的额name\n ```\n\n - 以上两个方法是ServletContext对象的方法,这个方法获取的是什么信息?是以下的配置信息\n\n ```xml\n <!--上下文的初始化参数,可以在Servlet内部,也可以在外部-->\n <context-param>\n <param-name>pageSize</param-name>\n <param-value>10</param-value>\n </context-param>\n <context-param>\n <param-name>indexpage</param-name>\n <param-value>0</param-value>\n </context-param>\n ```\n\n *注:*以上的配置信息属于应用级的配置信息,一般一个项目中共享的配置信息会放到以上的标签当中。如果配置信息只想给某一个Servlet作为参考,那么可以将其放到对应的Servlet标签当中,使用ServletContext获取即可。\n\n - ```java\n public abstract String getContextPath(); //动态获取应用的根路径\n ```\n\n ```java\n //getcontextpath()方法的应用\n String contextpath=application.getContextPath();\n out.print(contextpath);\n \n //输出:/servlet03\n ```\n\n - ```java\n public abstract String getRealPath(String s); //获取文件的绝对路径\n ```\n\n ```java\n //获取文件的绝对路径\n String realpath=application.getRealPath(\"/indextext.html\");\n out.print(realpath);\n \n //输出: E:\\JavaWeb\\javaweb\\out\\artifacts\\servlet03_war_exploded\\indextext.html\n ```\n\n - 注:不添加 “/” 也可以 \n\n - ```java\n //通过ServletContext对象也是可以记录日志的\n public void log(String message);\n public void log(String message,Throwable t);\n ```\n\n ```java\n //测试log方法,这个日志会记录到CATALINA_HOME/logs\n application.log(\"大家好,今天是学校javaweb的第四天\");\n \n \n int age=17;\n if(age<18){\n application.log(\"对不起您未成年,不能访问\",new RuntimeException(\"小屁孩,快走开\"));\n }\n ```\n\n - 这些日志文件将会放到当前项目的logs文件中\n\n - ServletContext对象还有另一个名字:应用域(还有其他域:会话域,请求域)\n\n - 如果所有的用户共享一份数据,并且这个数据很少的被修改,数据量也很少,可以将这些数据放到ServletContext这个应用域当中\n\n - 为什么是所有用户共享的数据?\n\n - 不是共享的没有意义。因为ServletContext这个对象只有一个。只有共享的数放进去才有意义\n\n - 为什么数据量要小?\n\n - 因为数据量比较大的话,太占用堆内存,并且这个对象的生命周期比较长,服务器关闭的时候这个对象才会被销毁。大数据量会影响服务器的性能。占用内存较小的数据量可以考虑放进去\n\n - 为什么这些共享数据很少的修改,或者说几乎不修改?\n\n - 所有用户共享的数据,如果涉及到修改操作,必然会存在线程并发所带来的安全问题。所以放在ServletContext对象中的数据一般都是只读的\n\n - 数据量小,所有用户共享,又不修改,这样的数据放到ServletContext这个应用域当中,会大大提升效率。因为应用域相当于一个缓存,放到缓存中的数据,下次在用的时候,不需要从数据库中再次取出,大大提升执行效率\n\n - 存(怎么向ServletContext应用域中存数据)\n\n - ```java\n public void setAttribute(String name,object value); //map.put(k,v)\n ```\n\n - 取(怎么从ServletContext应用域中取数据)\n\n - ```java\n public Object getAttribute(String name);//Object v=map.get(k)\n ```\n\n - 删(怎么删除ServletContext应用域中的数据)\n\n - ```java\n public void removeAttribute(String name);//map.remove(k)\n ```\n\n ```java \n UseAServlet user=new UseAServlet(\"jacke\",\"123\");\n //存数据\n application.setAttribute(\"value\",user);\n //取数据\n Object value=application.getAttribute(\"value\");\n //输出到浏览器\n out.print(value+\"<br>\");\n ```\n\n - 注意:以后编写Servlet类的时候,实际上是不会直接继承GenericServlet类的,因为我们是B/S系统,这种系统是基于HTTP超文本传输协议对的,在Servlet规范中,提供了一个类叫做HttpServlet,他是专门为HTTP协议准备的一个Servlet类。我们编写的Servlet类要继承HttpServlet。(HttpServlet是HTTP专用的)。使用HttpServlet处理HTTP协议更便捷。继承结构如下:\n\n ```java\n jakarta.servlet.Servlet(接口)【爷爷】\n jakarta.servlet.GenericServlet implements Servlet(抽象类)【儿子】\n jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)【孙子】\n ```\n\n ', 4, 'java,测试', '2024-05-05 22:17:27', '# Java web---Servlet', 'h1JavaWebh1\nh2Servleth2\nh3静态资源h3\nh5a hrefhttplocahost8080oauserList.html127.0.0.1ah5\np 访问这个地址可以显示一个用户列表页面但是这个用户列表页面时写死在HTML文件当中的这种资源称作静态资源当链接数据库时会变成动态资源p\np 链接数据库需要JDBC程序也就是说需要编写Java程序连接数据库数据库中有多少记录 就显示多少记录这种技术称作动态网页技术(动态网页技术并不是说网页中有flash动画动态网页', 'http://localhost:8080/image/1714918646563-Java.png', 1, 0, 0, '79002f0a562148a183a0da78db81d8fe');
INSERT INTO `b_content` VALUES (36, '2024-05-10 09:48:22', 0, 5, 0, 1, 0, '# Zookeeper\n\n## zookeeper基本知识\n\n### zookeeper的工作机制\n\nZookeeper从设计者角度来理解:\n\n- 是一个基于观察者模式设计的分布式服务管理框架,她负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据发生变化,Zookeeper就将负责通知已经在zookeeper上注册的那些观察者做出相应的反应\n\n### Zookeeper特点\n\n![image-20231111154937627](E:\\programming\\zookeeper\\img\\image-20231111154937627.png)\n\n1. zookeeper:一个领导者(Leader),多个跟随着(Follower)组成的集群\n2. 集群中只要有**半数以上**节点存活,zookeeper集群就能正常服务。所以zookeeper适合安装奇数台服务器\n3. 全局数据一致:每个Server 保存一份相同的数据副本,Client 无论连接到那个Server,数据都是一致的\n4. 更新请求顺序执行,来自同一份Client的更新请求按照发送顺序依次执行\n5. 数据更新原子性,一次数据更新要么成功,要么失败\n6. 实时性:在一定时间范围内,Client读到的数据一定是最新的\n\n### 应用场景\n\n提供的服务包括:\n\n1. 统一命名服务\n2. 统一配置管理服务\n3. 统一集群服务\n4. 服务器节点动态上下线服务\n5. 软负载均衡\n\n#### 统一命名服务\n\n在分布式环境下,经常需要对应用/服务器统一命名,便于识别\n例如:IP记不住,而域名容易记住\n\n![image-20231111200146519](E:\\programming\\zookeeper\\img\\image-20231111200146519.png)\n\n#### 统一配置管理\n\n![image-20231111200356265](E:\\programming\\zookeeper\\img\\image-20231111200356265.png)\n\n#### 统一集群管理\n\n![image-20231111200644534](E:\\programming\\zookeeper\\img\\image-20231111200644534.png)\n\n#### 服务器的动态上下线\n\n![image-20231111200806587](E:\\programming\\zookeeper\\img\\image-20231111200806587.png)\n\n也就是服务器在zk中注册信息,以方便zk能动态的监听服务器的状态,在服务器宕机后及时的通知到客户端,并重新安排\n\n#### 软负载均衡\n\n![image-20231111201050143](E:\\programming\\zookeeper\\img\\image-20231111201050143.png)\n\n### zookeeper的安装\n\n准备:\n\n- jdk\n\n - ```bash\n # 查询jdk\n yum -y list java*\n # 下载jdk\n yum install -y java-1.8.0-openjdk.x86_64 \n # 查看版本\n java -version\n ```\n\n- zk安装包([Apache Download Mirrors](https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.8.3/apache-zookeeper-3.8.3-bin.tar.gz))\n\n - ```bash\n #解压zk\n tar -zxvf apache-zookeeper-3.8.3-bin.tar.gz \n ```\n\n配置\n\n- 进入zk的conf目录\n\n - ```bash\n # 更改目录名称\n mv zoo_sample.cfg zoo.cfg\n ```\n\n- 启动zk\n\n - ```bash\n # 启动服务端\n bin/zkServer.sh start\n # 启动客户端\n bin/zkCli.sh\n # 退出zk\n quit\n # 查看zk状态\n bin/zkServer.sh status\n ```\n\n ![image-20231111212135731](E:\\programming\\zookeeper\\img\\apache-zookeeper-3.8.3-bin.tar.gz)\n\n![image-20231111212531231](E:\\programming\\zookeeper\\img\\image-20231111212531231.png)\n\n![image-20231111212937975](E:\\programming\\zookeeper\\img\\image-20231111212937975.png)\n\n退出zk:quit\n\n#### 配置文件详解\n\n```bash\n#The number of milliseconds of each tick\n# 通信心跳时间,zk服务器与客户端心跳时间\ntickTime=2000 \n# The number of ticks that the initial\n# synchronization phase can take\n# Leader 和 Follower 初始通信时限,这里的10 表示最多十个心跳连接,否则超时\ninitLimit=10\n# The number of ticks that can pass between\n# sending a request and getting an acknowledgement\n# LF同步通信时限\n# LF之间的通信如果超过 syncLimit * tickTime,L就会认为F死掉了,然后从服务器列表删除F\nsyncLimit=5\n# the directory where the snapshot is stored.\n# do not use /tmp for storage, /tmp here is just\n# example sakes.\n# 存储数据的目录\ndataDir=/home/zk/apache-zookeeper-3.8.3-bin/zkData\n# the port at which the clients will connect\n# 客户端的端口号 \nclientPort=2181\n# the maximum number of client connections.\n# increase this if you need to handle more clients\n#maxClientCnxns=60\n#\n# Be sure to read the maintenance section of the\n# administrator guide before turning on autopurge.\n#\n# https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance\n#\n# The number of snapshots to retain in dataDir\n#autopurge.snapRetainCount=3\n```\n\n#### 集群操作\n\n1. 解压安装zk\n\n2. 配置服务器编号\n\n - 在根目录下创建zkData目录\n - 在zkData目录下创建myid文件\n - 在myid文件中添加与server对应的编号(进入文件直接写编号即可,如:1、2、3...)\n\n3. 将zk的文件传到其他服务器即可,然后修改对应的编号\n\n4. 打开zoo.cfg文件\n\n - 修改数据存储位置,也就是dataDir\n\n - 增加配置(每一台都需要配置)\n\n - ```bash\n #server.A=B:C:D\n server.1=192.168.186.137:2888:3888\n server.2=192.168.186.138:2888:3888\n server.3=192.168.186.139:2888:3888\n ```\n\n - A是一个数字,代表第几号服务器,也就是myid中配置的编号\n\n - B是服务器地址\n\n - C是服务器Follower 与集群中的Leader服务器交换信息的端口\n\n - D是万一集群中的Leader宕机了,需要一个端口来重新进行选举出新的Leader\n\n#### 选举机制(面试重点)\n\n##### zk选举机制----第一次启动\n\n![image-20231113152930278](E:\\programming\\zookeeper\\img\\image-20231113152930278.png)\n\n环境:五台zk服务器\n\n1. 服务器1 启动的时候,发起一次选举,投自己一票。不够半数以上的票数,选举无法完成,服务器1 保持LOOKING 状态,此时客户端无法启动\n2. 服务器2 启动,再发起一次选举。服务器1 和 2 分别投自己一票并交换选票信息:此时服务器1 发现服务器2 的myid 比自己目前投票选举(服务器1)的大,更改选票未推举服务器2。此时服务器1 票数为0,服务器2 票数为2,没有半数以上结果,选举无法完成,服务器1、2 保持LOOKING状态\n3. 服务器3 启动,发起选举,此时服务器1、2更改选票为服务器3。此时服务器3 获得半数以上结果,当选Leader,服务器1、2更改状态为FOLLOWING,服务器3 更改状态为LEADING\n4. 服务器4 启动,发起一次选举,投票自己,此时的服务器1、2、3状态不是LOOKING,不会更改选票信息,服务器4发现服务器3 为LEADER ,所以更改选票信息,选举服务器3,并更改状态为FOLLOWING\n\n\n\n\n\n##### zk选举机制----非第一次启动\n\n![image-20231113154511154](E:\\programming\\zookeeper\\img\\image-20231113154511154.png)\n\n#### zk启动停止脚本\n\n```bash\n# zk 服务停止\n/bin/zkServer.sh stop\n```\n\n以上是一台服务器的停止,如果有一百台呢?也需要一台一台的去关闭吗?\n\n```bash\n#!/bin/bash\ncase $1 in\n\"start\"){\n for i in 192.168.186.137 192.168.186.138 192.168.186.139\\\n do\n ssh $i \"/home/zk/apache-zookeeper-3.8.3-bin/bin/zkServer.sh start\"\n done\n}\n;;\n\"stop\"){\n for i in 192.168.186.137 192.168.186.138 192.168.186.139\\\n do\n ssh $i \"/home/zk/apache-zookeeper-3.8.3-bin/bin/zkServer.sh stop\"\n done\n}\n;;\n\"status\"){\n for i in 192.168.186.137 192.168.186.138 192.168.186.139\\\n do\n ssh $i \"/home/zk/apache-zookeeper-3.8.3-bin/bin/zkServer.sh status\"\n done\n}\n;;\nesac\n```\n\n之后在zk的bin目录新建zk.sh文件,将其填入\n\n![image-20231113162009917](E:\\programming\\zookeeper\\img\\image-20231113162009917.png)\n\n在当前目录下输入目录:chmod 777 zk.sh 即可将其更改为可执行文件\n\n![image-20231113162211681](E:\\programming\\zookeeper\\img\\image-20231113162211681.png)\n\n\n\n### zk基本操作\n\n#### 客户端命令行操作\n\n###### 命令行语法\n\n| 命令基础语法 | 功能描述 |\n| ------------ | ------------------------------------------------------------ |\n| help | 显示所有操作命令 |\n| ls path | 使用ls 命令来查看当前znode 的字节的 [可监听] -w 监听字节的变化 -s 附加次级信息 |\n| create | 普通创建 -s 含有序列 -e 临时(重启或者超时消失) |\n| get path | 获得节点的值 [可监听] -w 监听节点内容变化 -s 附加刺激信息 |\n| set | 设置节点的具体值 |\n| stat | 查看节点的状态 |\n| delete | 删除节点 |\n| deleteall | 递归删除节点 |\n\n###### 服务器启动\n\n```bash$$\n\n$$\n\n# 启动服务端\nbin/zkServer.sh start\n# 启动客户端\nbin/zkCli.sh -server 192.168.186.137:2181\n```\n\n###### 节点信息\n\n```bash\nls /\nls -s /\n```\n\n![image-20231113165324724](E:\\programming\\zookeeper\\img\\image-20231113165324724.png)\n\n1. czxid: 创建节点的事务 zxid\n - 每次修改ZK 状态都会产生一个ZK事务id。事务id 是ZK 中所有修改总的次序,每次修改都有唯一的zxid,如果zxid1 小于 zxid2,那么zxid1 在zxid 2 之前发生\n2. ctime:znode 被创建的毫秒数(从1970 年开始)\n3. mzxid:znode 最后更新的事务zxid\n4. mtime:znode 最后修改的毫秒数(从1979年开始)\n5. pZxid:znode 最后更新的字节的zxid\n6. cversion:znode 字节点变化号,znode 字节点修改次数\n7. dataversion:znode数据变化号\n8. aclVersion:znode 访问控制列表的变化号\n9. ephemeralOwner:如果是临时节点,这个是znode 拥有者的session id。如果不是临时节点则是0\n10. dataLength:znode 的数据长度\n11. numChildren:znode字节点数量', 4, 'java,elasticsearch,es,redis,后端开发', '2024-05-10 09:48:22', '# zookeeper', 'h1Zookeeperh1\nh2zookeeper基本知识h2\nh3zookeeper的工作机制h3\npZookeeper从设计者角度来理解:p\nul\nli是一个基于观察者模式设计的分布式服务管理框架她负责存储和管理大家都关心的数据然后接受观察者的注册一旦这些数据发生变化Zookeeper就将负责通知已经在zookeeper上注册的那些观察者做出相应的反应li\nul\nh3Zookeeper特点h3\npimg srcE5Cprogramming5Czookeeper5Cimg5Cimage', 'http://localhost:8080/image/1715305701451-Java.png', 1, 0, 0, '50ae1a850b124e99a81e2bec1765e8c2');
INSERT INTO `b_content` VALUES (37, '2024-05-10 09:49:30', 0, 5, 0, 1, 0, '# Zookeeper\n\n## zookeeper基本知识\n\n### zookeeper的工作机制\n\nZookeeper从设计者角度来理解:\n\n- 是一个基于观察者模式设计的分布式服务管理框架,她负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据发生变化,Zookeeper就将负责通知已经在zookeeper上注册的那些观察者做出相应的反应\n\n### Zookeeper特点\n\n![image-20231111154937627](E:\\programming\\zookeeper\\img\\image-20231111154937627.png)\n\n1. zookeeper:一个领导者(Leader),多个跟随着(Follower)组成的集群\n2. 集群中只要有**半数以上**节点存活,zookeeper集群就能正常服务。所以zookeeper适合安装奇数台服务器\n3. 全局数据一致:每个Server 保存一份相同的数据副本,Client 无论连接到那个Server,数据都是一致的\n4. 更新请求顺序执行,来自同一份Client的更新请求按照发送顺序依次执行\n5. 数据更新原子性,一次数据更新要么成功,要么失败\n6. 实时性:在一定时间范围内,Client读到的数据一定是最新的\n\n### 应用场景\n\n提供的服务包括:\n\n1. 统一命名服务\n2. 统一配置管理服务\n3. 统一集群服务\n4. 服务器节点动态上下线服务\n5. 软负载均衡\n\n#### 统一命名服务\n\n在分布式环境下,经常需要对应用/服务器统一命名,便于识别\n例如:IP记不住,而域名容易记住\n\n![image-20231111200146519](E:\\programming\\zookeeper\\img\\image-20231111200146519.png)\n\n#### 统一配置管理\n\n![image-20231111200356265](E:\\programming\\zookeeper\\img\\image-20231111200356265.png)\n\n#### 统一集群管理\n\n![image-20231111200644534](E:\\programming\\zookeeper\\img\\image-20231111200644534.png)\n\n#### 服务器的动态上下线\n\n![image-20231111200806587](E:\\programming\\zookeeper\\img\\image-20231111200806587.png)\n\n也就是服务器在zk中注册信息,以方便zk能动态的监听服务器的状态,在服务器宕机后及时的通知到客户端,并重新安排\n\n#### 软负载均衡\n\n![image-20231111201050143](E:\\programming\\zookeeper\\img\\image-20231111201050143.png)\n\n### zookeeper的安装\n\n准备:\n\n- jdk\n\n - ```bash\n # 查询jdk\n yum -y list java*\n # 下载jdk\n yum install -y java-1.8.0-openjdk.x86_64 \n # 查看版本\n java -version\n ```\n\n- zk安装包([Apache Download Mirrors](https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.8.3/apache-zookeeper-3.8.3-bin.tar.gz))\n\n - ```bash\n #解压zk\n tar -zxvf apache-zookeeper-3.8.3-bin.tar.gz \n ```\n\n配置\n\n- 进入zk的conf目录\n\n - ```bash\n # 更改目录名称\n mv zoo_sample.cfg zoo.cfg\n ```\n\n- 启动zk\n\n - ```bash\n # 启动服务端\n bin/zkServer.sh start\n # 启动客户端\n bin/zkCli.sh\n # 退出zk\n quit\n # 查看zk状态\n bin/zkServer.sh status\n ```\n\n ![image-20231111212135731](E:\\programming\\zookeeper\\img\\apache-zookeeper-3.8.3-bin.tar.gz)\n\n![image-20231111212531231](E:\\programming\\zookeeper\\img\\image-20231111212531231.png)\n\n![image-20231111212937975](E:\\programming\\zookeeper\\img\\image-20231111212937975.png)\n\n退出zk:quit\n\n#### 配置文件详解\n\n```bash\n#The number of milliseconds of each tick\n# 通信心跳时间,zk服务器与客户端心跳时间\ntickTime=2000 \n# The number of ticks that the initial\n# synchronization phase can take\n# Leader 和 Follower 初始通信时限,这里的10 表示最多十个心跳连接,否则超时\ninitLimit=10\n# The number of ticks that can pass between\n# sending a request and getting an acknowledgement\n# LF同步通信时限\n# LF之间的通信如果超过 syncLimit * tickTime,L就会认为F死掉了,然后从服务器列表删除F\nsyncLimit=5\n# the directory where the snapshot is stored.\n# do not use /tmp for storage, /tmp here is just\n# example sakes.\n# 存储数据的目录\ndataDir=/home/zk/apache-zookeeper-3.8.3-bin/zkData\n# the port at which the clients will connect\n# 客户端的端口号 \nclientPort=2181\n# the maximum number of client connections.\n# increase this if you need to handle more clients\n#maxClientCnxns=60\n#\n# Be sure to read the maintenance section of the\n# administrator guide before turning on autopurge.\n#\n# https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance\n#\n# The number of snapshots to retain in dataDir\n#autopurge.snapRetainCount=3\n```\n\n#### 集群操作\n\n1. 解压安装zk\n\n2. 配置服务器编号\n\n - 在根目录下创建zkData目录\n - 在zkData目录下创建myid文件\n - 在myid文件中添加与server对应的编号(进入文件直接写编号即可,如:1、2、3...)\n\n3. 将zk的文件传到其他服务器即可,然后修改对应的编号\n\n4. 打开zoo.cfg文件\n\n - 修改数据存储位置,也就是dataDir\n\n - 增加配置(每一台都需要配置)\n\n - ```bash\n #server.A=B:C:D\n server.1=192.168.186.137:2888:3888\n server.2=192.168.186.138:2888:3888\n server.3=192.168.186.139:2888:3888\n ```\n\n - A是一个数字,代表第几号服务器,也就是myid中配置的编号\n\n - B是服务器地址\n\n - C是服务器Follower 与集群中的Leader服务器交换信息的端口\n\n - D是万一集群中的Leader宕机了,需要一个端口来重新进行选举出新的Leader\n\n#### 选举机制(面试重点)\n\n##### zk选举机制----第一次启动\n\n![image-20231113152930278](E:\\programming\\zookeeper\\img\\image-20231113152930278.png)\n\n环境:五台zk服务器\n\n1. 服务器1 启动的时候,发起一次选举,投自己一票。不够半数以上的票数,选举无法完成,服务器1 保持LOOKING 状态,此时客户端无法启动\n2. 服务器2 启动,再发起一次选举。服务器1 和 2 分别投自己一票并交换选票信息:此时服务器1 发现服务器2 的myid 比自己目前投票选举(服务器1)的大,更改选票未推举服务器2。此时服务器1 票数为0,服务器2 票数为2,没有半数以上结果,选举无法完成,服务器1、2 保持LOOKING状态\n3. 服务器3 启动,发起选举,此时服务器1、2更改选票为服务器3。此时服务器3 获得半数以上结果,当选Leader,服务器1、2更改状态为FOLLOWING,服务器3 更改状态为LEADING\n4. 服务器4 启动,发起一次选举,投票自己,此时的服务器1、2、3状态不是LOOKING,不会更改选票信息,服务器4发现服务器3 为LEADER ,所以更改选票信息,选举服务器3,并更改状态为FOLLOWING\n\n\n\n\n\n##### zk选举机制----非第一次启动\n\n![image-20231113154511154](E:\\programming\\zookeeper\\img\\image-20231113154511154.png)\n\n#### zk启动停止脚本\n\n```bash\n# zk 服务停止\n/bin/zkServer.sh stop\n```\n\n以上是一台服务器的停止,如果有一百台呢?也需要一台一台的去关闭吗?\n\n```bash\n#!/bin/bash\ncase $1 in\n\"start\"){\n for i in 192.168.186.137 192.168.186.138 192.168.186.139\\\n do\n ssh $i \"/home/zk/apache-zookeeper-3.8.3-bin/bin/zkServer.sh start\"\n done\n}\n;;\n\"stop\"){\n for i in 192.168.186.137 192.168.186.138 192.168.186.139\\\n do\n ssh $i \"/home/zk/apache-zookeeper-3.8.3-bin/bin/zkServer.sh stop\"\n done\n}\n;;\n\"status\"){\n for i in 192.168.186.137 192.168.186.138 192.168.186.139\\\n do\n ssh $i \"/home/zk/apache-zookeeper-3.8.3-bin/bin/zkServer.sh status\"\n done\n}\n;;\nesac\n```\n\n之后在zk的bin目录新建zk.sh文件,将其填入\n\n![image-20231113162009917](E:\\programming\\zookeeper\\img\\image-20231113162009917.png)\n\n在当前目录下输入目录:chmod 777 zk.sh 即可将其更改为可执行文件\n\n![image-20231113162211681](E:\\programming\\zookeeper\\img\\image-20231113162211681.png)\n\n\n\n### zk基本操作\n\n#### 客户端命令行操作\n\n###### 命令行语法\n\n| 命令基础语法 | 功能描述 |\n| ------------ | ------------------------------------------------------------ |\n| help | 显示所有操作命令 |\n| ls path | 使用ls 命令来查看当前znode 的字节的 [可监听] -w 监听字节的变化 -s 附加次级信息 |\n| create | 普通创建 -s 含有序列 -e 临时(重启或者超时消失) |\n| get path | 获得节点的值 [可监听] -w 监听节点内容变化 -s 附加刺激信息 |\n| set | 设置节点的具体值 |\n| stat | 查看节点的状态 |\n| delete | 删除节点 |\n| deleteall | 递归删除节点 |\n\n###### 服务器启动\n\n```bash\n# 启动服务端\nbin/zkServer.sh start\n# 启动客户端\nbin/zkCli.sh -server 192.168.186.137:2181\n```\n\n###### 节点信息\n\n```bash\nls /\nls -s /\n```\n\n![image-20231113165324724](E:\\programming\\zookeeper\\img\\image-20231113165324724.png)\n\n1. czxid: 创建节点的事务 zxid\n - 每次修改ZK 状态都会产生一个ZK事务id。事务id 是ZK 中所有修改总的次序,每次修改都有唯一的zxid,如果zxid1 小于 zxid2,那么zxid1 在zxid 2 之前发生\n2. ctime:znode 被创建的毫秒数(从1970 年开始)\n3. mzxid:znode 最后更新的事务zxid\n4. mtime:znode 最后修改的毫秒数(从1979年开始)\n5. pZxid:znode 最后更新的字节的zxid\n6. cversion:znode 字节点变化号,znode 字节点修改次数\n7. dataversion:znode数据变化号\n8. aclVersion:znode 访问控制列表的变化号\n9. ephemeralOwner:如果是临时节点,这个是znode 拥有者的session id。如果不是临时节点则是0\n10. dataLength:znode 的数据长度\n11. numChildren:znode字节点数量', 2, 'dubbo,zookeeper', '2024-05-10 09:49:30', '# zookeeper', 'h1Zookeeperh1\nh2zookeeper基本知识h2\nh3zookeeper的工作机制h3\npZookeeper从设计者角度来理解:p\nul\nli是一个基于观察者模式设计的分布式服务管理框架她负责存储和管理大家都关心的数据然后接受观察者的注册一旦这些数据发生变化Zookeeper就将负责通知已经在zookeeper上注册的那些观察者做出相应的反应li\nul\nh3Zookeeper特点h3\npimg srcE5Cprogramming5Czookeeper5Cimg5Cimage', 'http://localhost:8080/image/1715305769744-Java.png', 1, 0, 0, 'cb15f7ba533f47cea58f1c6ac2e0cccd');
-- ----------------------------
-- Table structure for b_content_examine
-- ----------------------------
DROP TABLE IF EXISTS `b_content_examine`;
CREATE TABLE `b_content_examine` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '文章审核表',
`admin_id` int NULL DEFAULT NULL,
`content_id` int NOT NULL,
`user_id` int NOT NULL,
`content_type_id` int NOT NULL,
`msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '不通过的理由',
`status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '状态',
`time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 29 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_content_examine
-- ----------------------------
INSERT INTO `b_content_examine` VALUES (17, 2, 30, 6, 1, '通过了', '通过', '2024-05-05 21:30:14');
INSERT INTO `b_content_examine` VALUES (18, 2, 31, 6, 6, '通过了', '通过', '2024-05-05 22:21:42');
INSERT INTO `b_content_examine` VALUES (19, 2, 32, 6, 4, '通过了', '通过', '2024-05-05 22:22:07');
INSERT INTO `b_content_examine` VALUES (20, 2, 34, 6, 6, '通过了', '通过', '2024-05-05 22:22:58');
INSERT INTO `b_content_examine` VALUES (21, 2, 35, 5, 4, '通过了', '通过', '2024-05-05 22:27:38');
INSERT INTO `b_content_examine` VALUES (22, 2, 34, 6, 6, '通过了', '通过', '2024-05-05 22:41:19');
INSERT INTO `b_content_examine` VALUES (23, 2, 35, 5, 4, '通过了', '通过', '2024-05-05 22:42:42');
INSERT INTO `b_content_examine` VALUES (24, 2, 34, 6, 6, '通过了', '通过', '2024-05-05 22:43:32');
INSERT INTO `b_content_examine` VALUES (25, 2, 34, 6, 6, '通过了', '通过', '2024-05-05 22:43:43');
INSERT INTO `b_content_examine` VALUES (26, 2, 35, 5, 4, '通过了', '通过', '2024-05-05 22:44:19');
INSERT INTO `b_content_examine` VALUES (27, 2, 33, 6, 4, '通过了', '通过', '2024-05-05 22:45:04');
INSERT INTO `b_content_examine` VALUES (28, 2, 34, 6, 6, '通过了', '通过', '2024-05-05 22:45:15');
INSERT INTO `b_content_examine` VALUES (29, 2, 35, 5, 4, '通过了', '通过', '2024-05-05 22:45:21');
-- ----------------------------
-- Table structure for b_content_image
-- ----------------------------
DROP TABLE IF EXISTS `b_content_image`;
CREATE TABLE `b_content_image` (
`id` int NOT NULL AUTO_INCREMENT,
`content_id` int NOT NULL,
`img_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`flag` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_content_image
-- ----------------------------
-- ----------------------------
-- Table structure for b_content_review
-- ----------------------------
DROP TABLE IF EXISTS `b_content_review`;
CREATE TABLE `b_content_review` (
`id` int NOT NULL AUTO_INCREMENT,
`review_content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL COMMENT '评论内容',
`user_id` int NOT NULL,
`parent_id` int NULL DEFAULT NULL COMMENT '父级评论',
`create_time` datetime NOT NULL,
`like_count` bigint UNSIGNED NULL DEFAULT NULL,
`content_id` int NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_content_review
-- ----------------------------
INSERT INTO `b_content_review` VALUES (8, '这是假的🤑', 6, NULL, '2024-05-05 21:31:50', 0, 30);
INSERT INTO `b_content_review` VALUES (9, '你觉得呢', 6, 8, '2024-05-05 21:32:12', 0, 30);
INSERT INTO `b_content_review` VALUES (10, 'dsfvwadsv😇', 5, NULL, '2024-05-10 09:50:19', 0, 31);
-- ----------------------------
-- Table structure for b_content_tag
-- ----------------------------
DROP TABLE IF EXISTS `b_content_tag`;
CREATE TABLE `b_content_tag` (
`id` int NOT NULL AUTO_INCREMENT,
`tag_id` int NOT NULL,
`content_id` int NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_content_tag
-- ----------------------------
-- ----------------------------
-- Table structure for b_history_look
-- ----------------------------
DROP TABLE IF EXISTS `b_history_look`;
CREATE TABLE `b_history_look` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`content_id` int NOT NULL,
`create_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_history_look
-- ----------------------------
INSERT INTO `b_history_look` VALUES (31, 6, 30, '2024-05-05 21:33:21');
INSERT INTO `b_history_look` VALUES (32, 5, 31, '2024-05-10 09:49:50');
INSERT INTO `b_history_look` VALUES (33, 5, 30, '2024-05-20 10:01:01');
INSERT INTO `b_history_look` VALUES (34, 5, 32, '2024-05-09 16:51:26');
-- ----------------------------
-- Table structure for b_menu
-- ----------------------------
DROP TABLE IF EXISTS `b_menu`;
CREATE TABLE `b_menu` (
`id` int NOT NULL AUTO_INCREMENT,
`menu_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL,
`menu_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL,
`is_menu` int NOT NULL COMMENT '是否是菜单栏',
`icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL,
`pid` int NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_menu
-- ----------------------------
INSERT INTO `b_menu` VALUES (1, '首页', '/main', 1, 'House', NULL);
INSERT INTO `b_menu` VALUES (2, '用户管理', '', 1, 'UserFilled', NULL);
INSERT INTO `b_menu` VALUES (3, '用户管理', '/usermanager', 1, 'User', 2);
INSERT INTO `b_menu` VALUES (4, '角色管理', '/adminmanager', 1, 'Avatar', 2);
INSERT INTO `b_menu` VALUES (5, '关键字管理', '', 1, 'Promotion', NULL);
INSERT INTO `b_menu` VALUES (6, '分类管理', '/type', 1, 'Document', 5);
INSERT INTO `b_menu` VALUES (7, '标签管理', '/tag', 1, 'Discount', 5);
INSERT INTO `b_menu` VALUES (9, '敏感词管理', '/sensitivity', 1, 'Files', NULL);
INSERT INTO `b_menu` VALUES (10, '系统管理', NULL, 1, 'Setting', NULL);
INSERT INTO `b_menu` VALUES (11, '参数设置', '/argument', 1, 'WalletFilled', 10);
INSERT INTO `b_menu` VALUES (12, '通知设置', '/notice', 1, 'DataLine', 10);
INSERT INTO `b_menu` VALUES (13, '审核管理', NULL, 1, 'Notebook', NULL);
INSERT INTO `b_menu` VALUES (14, '文章审核', '/content', 1, 'Edit', 13);
INSERT INTO `b_menu` VALUES (15, '评论审核', '/review', 1, 'ScaleToOriginal', 13);
INSERT INTO `b_menu` VALUES (16, '权限管理', '/role', 1, 'Filter', NULL);
-- ----------------------------
-- Table structure for b_menu_role
-- ----------------------------
DROP TABLE IF EXISTS `b_menu_role`;
CREATE TABLE `b_menu_role` (
`id` int NOT NULL AUTO_INCREMENT,
`role_id` int NOT NULL,
`menu_id` int NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1107 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_menu_role
-- ----------------------------
-- ----------------------------
-- Table structure for b_new_notice
-- ----------------------------
DROP TABLE IF EXISTS `b_new_notice`;
CREATE TABLE `b_new_notice` (
`id` int NOT NULL AUTO_INCREMENT,
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`create_time` datetime NULL DEFAULT NULL,
`status` int NULL DEFAULT NULL COMMENT '状态:0-启用,1-禁用',
`admin_id` int NULL DEFAULT NULL,
`user_id` int NULL DEFAULT NULL COMMENT '目标用户,可以为空,代表所有人',
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL,
`type` int NULL DEFAULT NULL COMMENT '0-公告,1-通知',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 59 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_new_notice
-- ----------------------------
INSERT INTO `b_new_notice` VALUES (1, '抱歉,由于您的发表内容不符合规定,存在违禁词,所以现在需要重新审核', '2024-04-06 18:01:56', 0, 2, 6, '重审通知', 1);
INSERT INTO `b_new_notice` VALUES (2, '你的社区问答已通过-->\"<div>\\n<div>你好,我是**...', '2024-04-06 18:44:16', 0, 2, 2, '通知', 1);
INSERT INTO `b_new_notice` VALUES (3, '抱歉,由于您的发表内容不符合规定,存在违禁词,所以现在需要重新审核', '2024-04-06 18:44:34', 0, 2, 6, '重审通知', 1);
INSERT INTO `b_new_notice` VALUES (4, '你的社区问答已通过-->\"<div>\\n<div>你好,我是**...', '2024-04-06 18:44:43', 0, 2, 2, '通知', 1);
INSERT INTO `b_new_notice` VALUES (5, '你的文章《 假的》于2024-04-10T11:20:03.342520900通过了审核', '2024-04-10 11:20:03', 0, 2, 6, '通知', 1);
INSERT INTO `b_new_notice` VALUES (6, '你的文章《 AJAX(Asynchronous JavaScript And Xml)》于2024-05-05T21:30:14.241102700通过了审核', '2024-05-05 21:30:14', 0, 2, 6, '通知', 1);
INSERT INTO `b_new_notice` VALUES (7, '你的文章《 关于docker的学习与记录》于2024-05-05T22:21:41.684633400通过了审核', '2024-05-05 22:21:42', 0, 2, 6, '通知', 1);
INSERT INTO `b_new_notice` VALUES (8, '你的文章《 关于RPC框架Dubbo的详解》于2024-05-05T22:22:07.320827通过了审核', '2024-05-05 22:22:07', 0, 2, 6, '通知', 1);
INSERT INTO `b_new_notice` VALUES (9, '你的文章《 代码仓库的操作语言----git》于2024-05-05T22:22:57.685853900通过了审核', '2024-05-05 22:22:58', 0, 2, 6, '通知', 1);
INSERT INTO `b_new_notice` VALUES (10, '你的文章《 Java web---Servlet》于2024-05-05T22:27:37.726660500通过了审核', '2024-05-05 22:27:38', 0, 2, 5, '通知', 1);
INSERT INTO `b_new_notice` VALUES (11, '你的文章《 代码仓库的操作语言----git》于2024-05-05T22:41:19.181913300通过了审核', '2024-05-05 22:41:19', 0, 2, 6, '通知', 1);
INSERT INTO `b_new_notice` VALUES (12, '你的文章《 Java web---Servlet》于2024-05-05T22:42:41.557960400通过了审核', '2024-05-05 22:42:42', 0, 2, 5, '通知', 1);
INSERT INTO `b_new_notice` VALUES (13, '你的文章《 代码仓库的操作语言----git》于2024-05-05T22:43:31.574537800通过了审核', '2024-05-05 22:43:32', 0, 2, 6, '通知', 1);
INSERT INTO `b_new_notice` VALUES (14, '你的文章《 代码仓库的操作语言----git》于2024-05-05T22:43:43.098569800通过了审核', '2024-05-05 22:43:43', 0, 2, 6, '通知', 1);
INSERT INTO `b_new_notice` VALUES (15, '你的文章《 Java web---Servlet》于2024-05-05T22:44:18.792851600通过了审核', '2024-05-05 22:44:19', 0, 2, 5, '通知', 1);
INSERT INTO `b_new_notice` VALUES (16, '你的文章《 基于Lucene的搜索引擎 “Elasticsearch”》于2024-05-05T22:45:04.110812200通过了审核', '2024-05-05 22:45:04', 0, 2, 6, '通知', 1);
INSERT INTO `b_new_notice` VALUES (17, '你的文章《 代码仓库的操作语言----git》于2024-05-05T22:45:15.083328900通过了审核', '2024-05-05 22:45:15', 0, 2, 6, '通知', 1);
INSERT INTO `b_new_notice` VALUES (18, '你的文章《 Java web---Servlet》于2024-05-05T22:45:20.605090100通过了审核', '2024-05-05 22:45:21', 0, 2, 5, '通知', 1);
INSERT INTO `b_new_notice` VALUES (19, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 14:59:35', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (20, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 15:02:52', 0, 2, 2, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (21, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 15:07:27', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (22, '抱歉,当前账户已解禁', '2024-05-06 15:08:14', 0, 2, 2, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (23, '抱歉,当前账户已解禁', '2024-05-06 15:08:16', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (24, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 15:08:43', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (25, '抱歉,当前账户已解禁', '2024-05-06 15:15:04', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (26, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 15:15:12', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (27, '抱歉,当前账户已解禁', '2024-05-06 15:19:12', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (28, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 15:19:31', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (29, '抱歉,当前账户已解禁', '2024-05-06 15:24:11', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (30, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 15:24:16', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (31, '抱歉,当前账户已解禁', '2024-05-06 15:43:59', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (32, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 15:44:04', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (33, '抱歉,当前账户已解禁', '2024-05-06 15:47:13', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (34, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 15:47:58', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (35, '抱歉,当前账户已解禁', '2024-05-06 15:49:18', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (36, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 15:49:25', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (37, '抱歉,当前账户已解禁', '2024-05-06 15:50:10', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (38, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 15:53:37', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (39, '抱歉,当前账户已解禁', '2024-05-06 16:08:07', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (40, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 16:08:11', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (41, '抱歉,当前账户已解禁', '2024-05-06 16:09:25', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (42, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 16:09:32', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (43, '抱歉,当前账户已解禁', '2024-05-06 16:11:07', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (44, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 16:14:09', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (45, '抱歉,当前账户已解禁', '2024-05-06 16:14:43', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (46, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 16:14:52', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (47, '抱歉,当前账户已解禁', '2024-05-06 16:16:18', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (48, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 16:19:43', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (49, '抱歉,当前账户已解禁', '2024-05-06 16:23:01', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (50, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 16:23:09', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (51, '抱歉,当前账户已解禁', '2024-05-06 16:29:27', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (52, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 16:29:38', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (53, '抱歉,当前账户已解禁', '2024-05-06 16:29:55', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (54, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 16:30:18', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (55, '抱歉,当前账户已解禁', '2024-05-06 16:30:51', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (56, '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 16:31:09', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (57, '抱歉,当前账户已解禁', '2024-05-06 16:31:22', 0, 2, 5, '解禁通知', 1);
INSERT INTO `b_new_notice` VALUES (58, '抱歉,当前账户因违反规定,已被封禁', '2024-05-20 10:02:40', 0, 2, 5, '禁用通知', 1);
INSERT INTO `b_new_notice` VALUES (59, '抱歉,当前账户已解禁', '2024-05-20 10:02:58', 0, 2, 5, '解禁通知', 1);
-- ----------------------------
-- Table structure for b_notice
-- ----------------------------
DROP TABLE IF EXISTS `b_notice`;
CREATE TABLE `b_notice` (
`id` int NOT NULL AUTO_INCREMENT,
`admin_id` int NOT NULL,
`userId` int NULL DEFAULT NULL,
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '通知标题',
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL COMMENT '通知内容',
`create_time` datetime NULL DEFAULT NULL,
`status` int NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_notice
-- ----------------------------
INSERT INTO `b_notice` VALUES (19, 2, 2, '通知', '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 14:46:24', 0);
INSERT INTO `b_notice` VALUES (20, 2, 5, '通知', '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 14:48:04', 0);
INSERT INTO `b_notice` VALUES (21, 2, 6, '通知', '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 14:48:23', 0);
INSERT INTO `b_notice` VALUES (22, 2, 2, '通知', '抱歉,当前账户因违反规定,已被封禁', '2024-05-06 14:54:39', 0);
INSERT INTO `b_notice` VALUES (23, 2, NULL, '公告', '系统维护通知:系统将在5月20日进行不停服维护', '2024-05-20 10:04:14', 0);
INSERT INTO `b_notice` VALUES (24, 2, NULL, '公告', '系统维护通知:这是一条维护信息', '2024-05-20 10:06:52', 0);
INSERT INTO `b_notice` VALUES (25, 2, NULL, '公告', '测试信息2:我测试了数据', '2024-05-20 10:08:59', 0);
-- ----------------------------
-- Table structure for b_role
-- ----------------------------
DROP TABLE IF EXISTS `b_role`;
CREATE TABLE `b_role` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`grade` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`role` int NOT NULL COMMENT '权限等级,0(全部权限),1(自定义权限)',
`status` int NOT NULL COMMENT '角色状态',
`create_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_role
-- ----------------------------
INSERT INTO `b_role` VALUES (1, '超级管理员', 'admin', 0, 1, '2024-03-16 13:01:53');
-- ----------------------------
-- Table structure for b_tag
-- ----------------------------
DROP TABLE IF EXISTS `b_tag`;
CREATE TABLE `b_tag` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`create_time` datetime NULL DEFAULT NULL,
`update_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 47 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_tag
-- ----------------------------
INSERT INTO `b_tag` VALUES (1, 'java', '2024-03-01 14:08:06', NULL);
INSERT INTO `b_tag` VALUES (2, '测试', '2024-03-14 14:08:09', NULL);
INSERT INTO `b_tag` VALUES (4, 'html', '2024-03-31 14:08:16', '2024-04-13 11:25:15');
INSERT INTO `b_tag` VALUES (6, 'c语言', '2024-04-07 11:39:12', NULL);
INSERT INTO `b_tag` VALUES (7, 'c ++', '2024-04-13 11:15:22', '2024-04-13 11:25:15');
INSERT INTO `b_tag` VALUES (9, 'Javase', '2024-05-09 18:04:02', NULL);
INSERT INTO `b_tag` VALUES (10, 'spring', '2024-05-09 18:05:20', NULL);
INSERT INTO `b_tag` VALUES (11, 'springMVC', '2024-05-09 18:05:20', NULL);
INSERT INTO `b_tag` VALUES (12, 'springboot', '2024-05-09 18:05:20', NULL);
INSERT INTO `b_tag` VALUES (13, 'redission', '2024-05-09 18:05:20', NULL);
INSERT INTO `b_tag` VALUES (14, '多线程', '2024-05-09 18:05:20', NULL);
INSERT INTO `b_tag` VALUES (15, '并发', '2024-05-09 18:05:20', NULL);
INSERT INTO `b_tag` VALUES (16, '线程安全', '2024-05-09 18:05:20', NULL);
INSERT INTO `b_tag` VALUES (17, 'sql注入', '2024-05-09 18:05:20', NULL);
INSERT INTO `b_tag` VALUES (18, 'mysql', '2024-05-09 18:05:20', NULL);
INSERT INTO `b_tag` VALUES (19, 'redis', '2024-05-09 18:05:20', NULL);
INSERT INTO `b_tag` VALUES (20, 'restful', '2024-05-09 18:06:28', NULL);
INSERT INTO `b_tag` VALUES (21, 'es', '2024-05-09 18:06:28', NULL);
INSERT INTO `b_tag` VALUES (22, 'elasticsearch', '2024-05-09 18:06:28', NULL);
INSERT INTO `b_tag` VALUES (23, 'solr', '2024-05-09 18:06:28', NULL);
INSERT INTO `b_tag` VALUES (24, 'docker', '2024-05-09 18:06:28', NULL);
INSERT INTO `b_tag` VALUES (25, 'js', '2024-05-09 18:06:28', NULL);
INSERT INTO `b_tag` VALUES (26, 'javascript', '2024-05-09 18:06:28', NULL);
INSERT INTO `b_tag` VALUES (27, 'css', '2024-05-09 18:06:28', NULL);
INSERT INTO `b_tag` VALUES (28, 'scss', '2024-05-09 18:06:28', NULL);
INSERT INTO `b_tag` VALUES (29, '后端开发', '2024-05-09 18:10:22', NULL);
INSERT INTO `b_tag` VALUES (30, '前端开发', '2024-05-09 18:10:22', NULL);
INSERT INTO `b_tag` VALUES (31, '运维', '2024-05-09 18:10:22', NULL);
INSERT INTO `b_tag` VALUES (32, '技术', '2024-05-09 18:10:22', NULL);
INSERT INTO `b_tag` VALUES (33, 'mybatis', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (34, 'mybatis-plus', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (35, 'linux', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (36, 'zookeeper', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (37, 'dubbo', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (38, 'websocket', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (39, '', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (40, 'juc', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (41, 'jdbc', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (42, 'mq', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (43, 'rabbitmq', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (44, 'rocketmq', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (45, 'kafka', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (46, 'tomcat', '2024-05-09 18:31:13', NULL);
INSERT INTO `b_tag` VALUES (47, 'netty', '2024-05-09 18:31:13', NULL);
-- ----------------------------
-- Table structure for b_type
-- ----------------------------
DROP TABLE IF EXISTS `b_type`;
CREATE TABLE `b_type` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`create_time` datetime NULL DEFAULT NULL,
`update_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 29 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_type
-- ----------------------------
INSERT INTO `b_type` VALUES (1, '前端', '2024-03-06 14:05:38', '2024-03-19 19:11:06');
INSERT INTO `b_type` VALUES (2, '后端', '2024-03-01 14:05:42', '2024-03-19 19:11:06');
INSERT INTO `b_type` VALUES (4, 'java', '2024-03-08 14:05:52', NULL);
INSERT INTO `b_type` VALUES (6, '数据库', '2024-03-12 14:06:03', NULL);
INSERT INTO `b_type` VALUES (21, 'redis', '2024-05-09 17:47:57', NULL);
INSERT INTO `b_type` VALUES (22, 'elasticsearch', '2024-05-09 17:47:57', NULL);
INSERT INTO `b_type` VALUES (23, 'Kibana', '2024-05-09 17:47:57', NULL);
INSERT INTO `b_type` VALUES (24, 'Lucene', '2024-05-09 17:47:57', NULL);
INSERT INTO `b_type` VALUES (25, 'springboot', '2024-05-09 17:48:58', NULL);
INSERT INTO `b_type` VALUES (26, 'vue2', '2024-05-09 17:48:58', NULL);
INSERT INTO `b_type` VALUES (27, 'vue3', '2024-05-09 17:48:58', NULL);
INSERT INTO `b_type` VALUES (28, 'element-ui', '2024-05-09 17:48:58', NULL);
INSERT INTO `b_type` VALUES (29, 'python', '2024-05-09 17:49:06', NULL);
-- ----------------------------
-- Table structure for b_user
-- ----------------------------
DROP TABLE IF EXISTS `b_user`;
CREATE TABLE `b_user` (
`id` int NOT NULL AUTO_INCREMENT,
`nickname` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '用户名',
`username` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`password` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL,
`is_delete` int UNSIGNED NOT NULL COMMENT '是否禁用',
`follow_count` bigint UNSIGNED NULL DEFAULT NULL COMMENT '关注数量',
`like_count` bigint NULL DEFAULT NULL COMMENT '获得点赞总数',
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL,
`is_login` int UNSIGNED NOT NULL COMMENT '0(未登录),1(已登录)',
`create_time` datetime NULL DEFAULT NULL,
`login_time` datetime NULL DEFAULT NULL COMMENT '上一次登录时间',
`active_time` bigint NULL DEFAULT NULL COMMENT '登录时长',
`emil` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL DEFAULT NULL COMMENT '邮箱账户',
`visit` bigint NULL DEFAULT NULL COMMENT '被访问量',
`description` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NULL COMMENT '个人描述',
PRIMARY KEY (`id`, `is_login`) USING BTREE,
UNIQUE INDEX `username`(`username` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_user
-- ----------------------------
INSERT INTO `b_user` VALUES (2, '张三', 'user', 'admin', 0, 0, 0, NULL, 0, '2024-03-28 18:14:11', NULL, NULL, '1403781604@qq.com', NULL, NULL);
INSERT INTO `b_user` VALUES (5, 'user666', 'user666', 'a951456dfd640864f5ce3c13cd7d2d33', 0, 0, 0, 'http://localhost:8080/image/1715074239526-fantasy_girl-wallpaper-2048x1152.jpg', 1, '2024-03-29 14:53:13', '2024-05-20 10:03:09', NULL, '19923807615@163.com', NULL, NULL);
INSERT INTO `b_user` VALUES (6, '@随时WIFI', 'user777', 'e10adc3949ba59abbe56e057f20f883e', 0, 0, 0, 'http://localhost:8080/image/1712657448478-CL.jpg', 1, '2024-03-29 14:53:41', '2024-05-09 17:12:15', NULL, '19923807615@163.com', NULL, '编程路上的初学者乘凉');
-- ----------------------------
-- Table structure for b_user_follow
-- ----------------------------
DROP TABLE IF EXISTS `b_user_follow`;
CREATE TABLE `b_user_follow` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`follow_user_id` int NOT NULL COMMENT '用户关注的用户id',
`create_time` datetime NOT NULL COMMENT '关注时间',
`status` int NULL DEFAULT NULL COMMENT '状态,1-我关注的,2-关注我的',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_user_follow
-- ----------------------------
INSERT INTO `b_user_follow` VALUES (17, 5, 2, '2024-05-10 09:35:32', NULL);
-- ----------------------------
-- Table structure for b_user_like
-- ----------------------------
DROP TABLE IF EXISTS `b_user_like`;
CREATE TABLE `b_user_like` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '点赞表',
`user_id` int NOT NULL,
`content_id` int NOT NULL,
`create_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 171 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_as_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of b_user_like
-- ----------------------------
INSERT INTO `b_user_like` VALUES (151, 6, 2, '2024-04-03 15:41:47');
INSERT INTO `b_user_like` VALUES (157, 6, 3, '2024-04-04 14:27:48');
INSERT INTO `b_user_like` VALUES (158, 6, 4, '2024-04-04 17:40:36');
INSERT INTO `b_user_like` VALUES (163, 6, 30, '2024-05-05 21:33:13');
INSERT INTO `b_user_like` VALUES (171, 5, 30, '2024-05-06 14:34:35');
SET FOREIGN_KEY_CHECKS = 1;
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。