知识回顾

JAVA 基础

1.集合

image-20210730165910799

1.List Set Map之间的区别

比较 List Set Map
继承接口 Collection Collection
常见实现类 AbstractList(其常用子类有ArrayList,LinkedList,Vector) AbstractSet(其常用子类有HashSet,LinkedHashSet,TreeSet) HashMap,HashTable
常见方法 add()/remove()/clear()/get()/contains()/size() add()/remove()/clear()/contains()/size() put()/get()/remove()/clear()/containsKey()/containsValue()/KeySet()/values()/size()
元素 可重复 不可重复 不可重复
顺序 有序 无序(实际上由HashCode决定的)
线程安全 Vector线程安全 HashTable线程安全

2.ArrayList 和 linkedList 的区别是什么?

最明显的区别是ArrayList底层的数据结构就是数组,支持随机访问,而LinkedList的底层数据结构是双向循环链表,不支持随机访问,.使用下标访问一个元素,ArrayList的时间复杂度是O(1),而LinkedList是O(n);

3.如何实现数组和list之间的转换?

List转换成为数组:调用ArrayList的toArray方法.

数组转换成为List:调用Arrays的asList方法;(但是这个方法是个坑,只能看,不能操作,阿里规范明确提到)

image-20210730183428637

4.ArrayList和Vector的区别是什么?

Vector是同步的,而ArrayList不是,然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWritrArrayList.

ArrayList比Vector块,它因为有同步,不会过载.

ArrayList更加通用,因为我们可以使用Collections工具类轻易的获取同步列表和只读列表;

5.Array和ArrayList有何区别?

Array可以容纳基本类型和对象,而ArrayList只能容纳对象.

Array是指定大小的,而ArrayList大小是固定的.

Array没有提供ArrayList那么多功能,比如addAll,removeAll和iterator等;

6.在Queue中poll() 和 remove( )有什么区别?

poll() 和 remove() 都是从队列中取出一个元素,但是poll() 在获取元素失败的时候会返回空,但是remove() 失败的时候会抛出异常;

7.哪些集合类是线程安全的?

vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在不太建议使用.在web应用中,

特别是前台页面,往往效率(页面响应速度)是优先考虑的.

stack: 堆栈类 先进后出;

hashtable: 就比hashmap多了个线程安全.

enumeration: 枚举 相当于迭代器

2.HashMap

1 HashMap 和 Hashtable 有什么区别

HashMap 去掉了HashTable的contains方法,但是加上了containsValue() 和containsKey()方法;

HashTable同步的,二HashMap是非同步的,效率上逼HashTable要高;

HashMap允许空键值,而hashtable不允许;

2 如何决定使用HashMap还是TreeMap?

对于Map中插入,删除和定位元素这类操作,HashMap是最好的选择.然而,加入你需要对一个有序的key集合进行遍历,treeMap是更好的选择.基于你的collection的大小,也学HashMap中添加元素会更快,将map换位treeMap进行有序Key的遍历;

3.HashMap的实现原理

HashMap概述:hashMap是基于哈希表的Map接口的非同步实现.此实现提供所有的可选的映射操作,并允许使用null值和null键,此类不保证映射的顺序,特别是它不保证该顺序恒久不变.

HashMap的数据接口:在java编程语言中,最基本的接口就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据接口都可以用这两个基本结构来构造,HashMap也不例外.Hashmap实际上是一个"链表散列"的数据结构,即数组和链表的结合体.

当我们网HashMap中put元素时,首先根据key的Hashcode重新计算Hash值,根据hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在联投,最先加入的放入链尾,如果数组汇总该位置没有元素,就直接将该元素放到数组的该位置上

需要注意jdk1.8中对hashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑数来提高查询效率,从原来的O(n)到O(logn)

可以结合看:https://www.cnblogs.com/HHbJ/articles/13364662.html

4.HashSet的实现原理?

HashSet底层由HashMap实现;

HashSet的值存放于HashMap的Key上

HashMap的value统一为Present

3.设计模式及原则(熟悉)

设计模式6大原则,单一职责原则,开放封闭原则,里式替换原则,依赖导致原则,迪米特原则和接口隔离原则。

1、单一职责原则【SINGLE RESPONSIBILITY PRINCIPLE】:一个类负责一项职责.
2、里氏替换原则【LISKOV SUBSTITUTION PRINCIPLE】:继承与派生的规则.
3、依赖倒置原则【DEPENDENCE INVERSION PRINCIPLE】:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。即针对接口编程,不要针对实现编程.
4、接口隔离原则【INTERFACE SEGREGATION PRINCIPLE】:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少.
5、迪米特法则【LOW OF DEMETER】:低耦合,高内聚.
6、开闭原则【OPEN CLOSE PRINCIPLE】:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.
7、组合/聚合复用原则【Composition/Aggregation Reuse Principle(CARP) 】:尽量使用组合和聚合少使用继承的关系来达到复用的原则.

24种设计模式

一、创建型模式

1、抽象工厂模式(Abstract factory pattern): 提供一个接口, 用于创建相关或依赖对象的家族, 而不需要指定具体类.
2、生成器模式(Builder pattern): 使用生成器模式封装一个产品的构造过程, 并允许按步骤构造. 将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示.
3、工厂模式(factory method pattern): 定义了一个创建对象的接口, 但由子类决定要实例化的类是哪一个. 工厂方法让类把实例化推迟到子类.
4、原型模式(prototype pattern): 当创建给定类的实例过程很昂贵或很复杂时, 就使用原形模式.
5、单例了模式(Singleton pattern): 确保一个类只有一个实例, 并提供全局访问点.
6、多例模式(Multition pattern): 在一个解决方案中结合两个或多个模式, 以解决一般或重复发生的问题.

二、结构型模式

1、适配器模式(Adapter pattern): 将一个类的接口, 转换成客户期望的另一个接口. 适配器让原本接口不兼容的类可以合作无间. 对象适配器使用组合, 类适配器使用多重继承.
2、桥接模式(Bridge pattern): 使用桥接模式通过将实现和抽象放在两个不同的类层次中而使它们可以独立改变.
3、组合模式(composite pattern): 允许你将对象组合成树形结构来表现"整体/部分"层次结构. 组合能让客户以一致的方式处理个别对象以及对象组合.
4、装饰者模式(decorator pattern): 动态地将责任附加到对象上, 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案.
5、外观模式(facade pattern): 提供了一个统一的接口, 用来访问子系统中的一群接口. 外观定义了一个高层接口, 让子系统更容易使用.
6、亨元模式(Flyweight Pattern): 如想让某个类的一个实例能用来提供许多"虚拟实例", 就使用蝇量模式.
7、代理模式(Proxy pattern): 为另一个对象提供一个替身或占位符以控制对这个对象的访问.

三、行为型模式

1、责任链模式(Chain of responsibility pattern): 通过责任链模式, 你可以为某个请求创建一个对象链. 每个对象依序检查此请求并对其进行处理或者将它传给链中的下一个对象.
2、命令模式(Command pattern): 将"请求"封闭成对象, 以便使用不同的请求,队列或者日志来参数化其他对象. 命令模式也支持可撤销的操作.
3、解释器模式(Interpreter pattern): 使用解释器模式为语言创建解释器.
4、迭代器模式(iterator pattern): 提供一种方法顺序访问一个聚合对象中的各个元素, 而又不暴露其内部的表示.
5、中介者模式(Mediator pattern) : 使用中介者模式来集中相关对象之间复杂的沟通和控制方式.
6、备忘录模式(Memento pattern): 当你需要让对象返回之前的状态时(例如, 你的用户请求"撤销"), 你使用备忘录模式.
7、观察者模式(observer pattern): 在对象之间定义一对多的依赖, 这样一来, 当一个对象改变状态, 依赖它的对象都会收到通知, 并自动更新.
8、状态模式(State pattern): 允许对象在内部状态改变时改变它的行为, 对象看起来好象改了它的类.
9、策略模式(strategy pattern): 定义了算法族, 分别封闭起来, 让它们之间可以互相替换, 此模式让算法的变化独立于使用算法的客户.
10、模板方法模式(Template pattern): 在一个方法中定义一个算法的骨架, 而将一些步骤延迟到子类中. 模板方法使得子类可以在不改变算法结构的情况下, 重新定义算法中的某些步骤.
11、访问者模式(visitor pattern): 当你想要为一个对象的组合增加新的能力, 且封装并不重要时, 就使用访问者模式.

4.对象拷贝

1.深拷贝 和 浅拷贝

浅拷贝:只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝;

深拷贝:是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝;

2.深拷贝实现方式

1):实现cloneable接口并重写object类中的clone()方法;

2.实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
 
public class MyUtil {
 
    private MyUtil() {
        throw new AssertionError();
    }
 
    /**
    *基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持	*序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对	 *象。让问题在编译的时候暴露出来总是好过把问题留到运行时。
    */
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(obj);
 
        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();

    }
}

5.Stream() 流操作

6.lambda 表达式

JAVA 进阶

1.JUC

2.JVM

Spring

1.简介及目的

什么是spring?

spring是一个轻量级Java开发框架,最早有rod johnson创建,目的是为了解决企业级应用开发的业务逻辑和其他各层的耦合问题,它是一个分层的javaSE/javaEE full-stack(一站式)轻量级开源框架,为开发java应用程序提供全面的基础架构支持.spring负责基础架构,因此java开发者可以专注于应用程序的开发.

spring 最根本的使命

是解决企业级应用开发的复杂性,即简化java开发

spring设计目标:

spring为开发者提供一个一站式轻量级应用开发平台

spring设计理念:

在javaEE开发中,支持POJO和javaBean开发方式,使应用面向接口开发,充分支持OO(面向对象)设计方法;spring通过ioc容器实现对象耦合关系的管理,并实现依赖反转,将对象之间的依赖交给ioc容器,实现解耦;

spring框架的核心:

ioc容器和aop模块.通过ioc容器管理POJO对象以及他们之间的耦合关系:通过AOP以动态非侵入的方式增强服务;ioc 让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件.

Spring的优缺点是什么?

优点:

1.方便解耦,简化开发 spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给spring管理

2.AOP编程的支持 spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能;

3.声明式事务的支持 只需要通过配置就可以完成对事务的管理,而无需手动编程.

4.方便程序的测试 spring对junit4支持,可以通过注解方便的测试sprin程序.

5.方便集成各种优秀框架 spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持(如:struts\hibernate\mybatis)

6.降低JavaEE API的使用难度 Spring对JavaEE开发中非常难用的一些API(JDBC JavaMail 远程调用等),都提供了封装,使这些API应用难度大大降低.

缺点:

1.spring明明一个很轻量级的框架,却给人感觉大而全

2.spring依赖反射,反射影响性能

3.使用门槛升高,入门spring需要较长时间

spring价值

1.spring是非侵入式的框架,目标是使应用程序代码对框架依赖最小化;

2.spring提供一个一致的编程模型,使应用直接使用POJO开发,与运行环境隔离开来;

3.spring推动应用设计风格面向对象和面向接口开发专变,提高代码的重用性和可测试性;

spring由哪些模块组成

spring总共有20个模块,由1300多个文件构成,

spring core 提供了框架的基本组成部分,包括控制反转(inversion of control ioc)依赖注入(dependency injection DI)功能

spring beans 提供beanFactory,是一个工厂模式的一个经典实现,spring将管理对象成为Bean;

spring context 构建于core封装包基础之上的context封装包,提供了一种框架式的对象访问方法

spring jdbc 提供了一个JDBC的抽象层,消除了单锁的JDBC编码和数据库厂商特有的错误代码解析,用于简化JDBC.

spring aop 提供了面向切面的编程实现,让你可以自定义拦截器\切点等

spring web 提供了针对WEB开发的集成特性,例如文件上传,利用servlet listeners进行 ioc 容器初始化和针对WEB的ApplicationContext

spring test 主要为测试提供支持的,支持使用JUnit或TestNg对spring组件进行单元测试和集成测试

spring 框架用到的设计模式

1.工厂模式,BeanFactiry就是简单工厂模式的实现,用来创建对象的实例;

2.单例模式,Bean默认为单例模式

3.代理模式:spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;

4.模板方法:用来解决代码重复的问题,比如RestTemplate,JmsTemplate,JpaTemplate

5.观察者模式:定义对象键一种一堆多的依赖关系,当一个对象的状态发生改变是,所有依赖于它的对象都会得到通知被制动更新,如spring中listener的实现-ApplicationListener;

2.核心 IOC

image-20210730082141075

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

DI—Dependency Injection,即“依赖注入”组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解

控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,也就是ico容器;DI(依赖注入)其实是ioc的另外一种说法

3.核心 AOP

1.介绍

Aop(Aspect--Oriented Programming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对面编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行文的时候,OOP则显得无能为力,也就是说。OOP允许你定义从上到下的关系,但并不适合从左到右的关系。例如日志功能。日志代码旺旺水平的散步在所有对象层次中,而与它所散步到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散步在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用

AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将哪些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即切面,所谓“切面”,简单的说,就是将哪些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表队额是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向切面编程的方法,就仿了。然后它又以佛一把利刃,讲这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“切面”了。然后他又以巧夺天工的妙手讲这些剖开的切面复原,不留痕迹。

使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点和横切关注点。业务处理的主要流程是核心关注点,预支关系不大的部分是横切关注电。横切关注电的一个特点是,他们经常发生在核心关注电的多出,而各处都基本相似。比如权限认证、日志、事务处理‘,AOP’的作用在于分离系统中的各种关注点,将核心关注电和横切关注电分离开来。正如Avanade公司的高级方案架构师所说。AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离”

2.基本概念

连接点(JoinPoint):需要在程序中插入横切关注点的点,连接点可能是在类初始化、方法调用、字段调用或处理异常等等。Spring中只支持方法执行连接点。

切入点(Pointcut):一组相关连接点的集合。

通知(Advice):在连接点上执行的行为,增强提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段。包括前置增强(before advice)、后置增强 (after advice)、环绕增强 (around advice)。

切面(Aspect):通知和切入点的结合。

织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程。

代理(Proxy):通过代理方式来对目标对象应用切面。AOP代理可以用JDK动态代理或CGLIB代理实现。

目标对象(Target):需要被织入关注点的对象。即被代理的对象.

4.代理模式

  • 静态代理 是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前已经确定好。

  • 动态代理 是程序在运行期间动态构建代理对象和动态调用代理方法的一种机制。在程序运行时,通过反射机制动态创建而成。

    ​ 准备:

    ​ 创建一个增删改接口

    public interface BaseMapper {
        
        void add();
        void del();
        void update();
        void select();
    }
    

    创建一个实现

    public class BaseMapperImpl implements BaseMapper {
        @Override
        public void add() {
            System.out.println("执行了新增方法");
        }
    
        @Override
        public void del() {
            System.out.println("执行了删除方法");
        }
    
        @Override
        public void update() {
            System.out.println("执行了更新方法");
        }
    
        @Override
        public void select() {
            System.out.println("执行了查询方法");
        }
    }
    
    • cglib (net.sf.cglib.proxy)实现MethodInterceptor接口

      public class MyMethodInterceptor implements MethodInterceptor {
          /**
           * 
           * @param o  cglib生成的代理对象
           * @param method 被代理对象的方法
           * @param objects  方法入参
           * @param methodProxy 代理方法
           * @return
           * @throws Throwable
           */
          @Override
          public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
              log(method.getName());
              return methodProxy.invokeSuper(o,objects);
          }
      
          void log(String msg){
              System.out.println("log:执行了"+msg+"方法....");
          }
      }
      

      使用代理

      public class Client {
          public static void main(String[] args) {
              /****************************cglib代理s********************************/
              //通过CGLIB动态代理获取代理对象的过程
              Enhancer enhancer = new Enhancer();
              //设置enhancer对象的父类
              enhancer.setSuperclass(BaseMapperImpl.class);
              //设置enhancer的回调对象
              enhancer.setCallback(new MyMethodInterceptor());
              //创建代理对象
              BaseMapperImpl proxy = (BaseMapperImpl) enhancer.create();
              //代理对象调用目标方法
              proxy.add();
      
              /****************************cglib代理e********************************/
      
          }
      }
      
      ---结果
      log:执行了add方法....
      执行了新增方法
      
      Process finished with exit code 0
      
      1. 实现流程

        CGLib 的调用流程就是通过调用拦截器的 intercept 方法来实现对被代理类的调用。而拦截逻辑可以写在 intercept 方法的 invokeSuper(o, objects);的前后实现拦截。

      2. 底层原理

        通过查看 Enhancer 类源码,最终也是生成动态代理类的字节码,动态代理类继承要被代理的类,然后实现其方法。

        和 JDK Proxy 的实现代码比较类似,都是通过实现代理器的接口,再调用某一个方法完成动态代理的,唯一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。

    • jdk 动态代理 (java.lang.reflect.Proxy)实现InvocationHandler接口

      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      /**
       * <b>标题:实现代理类</b>Proxy <br>
       * <b>创建:</b> 2021-07-28 15:50<br>
       *
       * @author majie
       * @version 1.0
       */
      public class ProxyInvocationHandler implements InvocationHandler {
          
          //被代理的类
          private BaseMapperImpl baseMapper;
      
          public void setBaseMapperImpl(BaseMapperImpl baseMapper) {
              this.baseMapper = baseMapper;
          }
      
          //生成得到代理类
          public Object getProxy(){
              return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                      baseMapper.getClass().getInterfaces(),
                      this);
          }
          
          //处理代理实例,并返回结果
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              log(method.getName());
              Object invoke = method.invoke(baseMapper, args);
              return invoke;
          }
          
          void log(String msg){
              System.out.println("log:执行了"+msg+"方法....");
          }
      }
      
      

      使用代理

      
      /**
       * <b>标题:</b>Client <br>
       * <b>创建:</b> 2021-07-28 15:48<br>
       *
       * @author majie
       * @version 1.0
       */
      public class Client {
          public static void main(String[] args) {
      
              /****************************jdk代理s********************************/
              //要代理的对象
              BaseMapperImpl baseMapper = new BaseMapperImpl();
              //使用
              ProxyInvocationHandler proxy = new ProxyInvocationHandler();
              //将要代理的对象放入
              proxy.setBaseMapperImpl(baseMapper);
              //获取代理对象
              BaseMapper proxyProxy = (BaseMapper) proxy.getProxy();
              //执行添加方法
              proxyProxy.add();
              /****************************jdk代理e********************************/
      }
          
      //结果增加了日志功能
      log:执行了add方法....
      执行了新增方法
      
      Process finished with exit code 0
      
      1. 调用流程

        当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的 handler 对象的 invoke 方法来进行调用;即当执行 proxyProxy.add();会自动调用 ProxyInvocationHandler的 invoke 方法。

      2. 底层原理

        关键在于生成代理对象是用 Proxy 类的静态方 newProxyInstance 获得代理对象

        这个代理类继承了 Proxy 类,并实现了之前定义的 Sister 接口。通过带有 InvocationHandler 参数的构造方法来创建实例,并把 InvocationHandler 传递到了 Proxy 类中,它覆写了接口中所有的方法,同时覆写了 Object 类的 equals、hashCode、toString 方法。

    cglib 和 jdk 代理的区别

    • JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现;
    • Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新,Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
    • JDK Proxy 是通过拦截器加反射的方式实现的;
    • JDK Proxy 只能代理实现接口的类;
    • JDK Proxy 实现和调用起来比较简单;
    • CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;
    • CGLib 无需通过接口来实现,它是针对类实现代理,主要是对指定的类生成一个子类,它是通过实现子类的方式来完成调用的。

    拓展

    CGlib 是否一定快

    1. 使用 CGLib 实现动态代理,CGLib 底层采用 ASM 字节码生成框架,使用字节码技术生成代理类,比使用 Java 反射效率要高。
    2. 对 JDK 动态代理与 CGlib 动态代理的代码进行测试,1W 次执行下,JDK 1.8 的动态代理性能比 CGlib 要好 20%左右。

5.创建容器的三种方式

  • 使用ClassPathXmlApplicationContext ClassPath类路径加载:指的就是classes路径(从类路径下的XML配置文件中加载上下文定义,把应用上下文定义文件作为类资源;)

    ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
    
  • 使用FileSystemXmlApplicationContext 文件系统路径获得配置文件【绝对路径】(读取文件系统下的XML配置文件并加载上下文定义;)

    ApplicationContext context = new FileSystemXmlApplicationContext("F:\\IdeaProjects\\spring-demo2\\out\\production\\spring-demo2\\beans.xml");
    
  • 使用BeanFactory,现在这种已经被废弃了(读取Web应用下的XML配置文件并装在上下文定义;)

    String path = "F:\\IdeaProjects\\spring-demo2\\out\\production\\spring-demo2\\beans.xml";
    BeanFactory context = new XmlBeanFactory(new FileSystemResource(path));
    

区别

BeanFactory 采取延迟加载,第一次getBean时才会初始化Bean

ApplicationContext 即时加载

后续还有

使用Groovy 配置

ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");

6.依赖注入的几种方式

  1. set 注入
  2. 构造器注入
  3. c-namespace/p-namespace 注入
  4. 注解注入

7.自动装配 注解 @Autowired @Resource

  • byName,需要保证所有bean的id唯一,并且这个bean需要和自动注入属性的set方法的值一致!
  • byType,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!

区别

@Autowiired 来自spring,默认是根据类型来查找对应的bean,如果有多个的话,可以配合@@qualifier来指定

@Resource 来自java,默认是根据bean名字来查找对应的bean,如果有多个的话,直接使用name属性指定

SpringMVC

1.简介及目的

  • MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计的规范;
  • 是将业务逻辑、数据、显示分离的方法来组织代码;
  • MVC主要的作用是降低了视图与业务逻辑间的双向耦合;
  • MVC不是一种设计模式,MVC是一种架构模式,不同的MVC存在差异

特点:

  1. 轻量级,简单易学
  2. 高效、基于请求响应的MVC框架
  3. 与Spring兼容性好,无缝结合
  4. 约定优于配置
  5. 功能强大:RESTful、数据验证、格式化、本地化、主题等;
  6. 简洁灵活

2.SpringMVC的工作流程

  1. 工作流程图

    image-20210728103510320

  2. 工作流程

    1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;

    2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;

    3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter;(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)

    4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

      HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

      数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

      数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

      数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

    5. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;

    6. 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;

    7. ViewResolver 结合Model和View,来渲染视图;

    8. 将渲染结果返回给客户端。

  3. 释义

    • DispatcherServlet:前端控制器,也称为中央控制器,它是整个请求响应的控制中心,组件的调用由它统一调度。
    • HandlerMapping:处理器映射器,它根据用户访问的 URL 映射到对应的后端处理器 Handler。也就是说它知道处理用户请求的后端处理器,但是它并不执行后端处理器,而是将处理器告诉给中央处理器。
    • HandlerAdapter:处理器适配器,它调用后端处理器中的方法,返回逻辑视图 ModelAndView 对象。
    • ViewResolver:视图解析器,将 ModelAndView 逻辑视图解析为具体的视图(如 JSP)。
    • Handler:后端处理器,对用户具体请求进行处理,也就是我们编写的 Controller 类。

SpringBoot

1.简介及目的

Spring Boot 是用来简化 Spring 的搭建和开发过程的全新框架。Spring Boot 去除了大量的 xml 配置文件,简化了复杂的依赖管理,配合各种 starter 使用,基本上可以做到自动化配置。Spring 可以做的事情,现在用 Spring boot 都可以做。

Spring Boot 相比Spring来说,最直观地感受就是省去了大量的配置

2.特点

为基于Spring的开发提供更快的入门体验

开箱即用,没有代码生成,也无需XML配置。同时也可以修改默认值来满足特定的需求

提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等

SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式

3.优点

1.使用Spring的IOC容器,将对象之间的依赖关系交给Spring,降低组件之间的耦合性,让我们更专注于应用逻辑
2.可以提供众多服务,事务管理,WS等。
3.AOP的很好支持,方便面向切面编程。
4.对主流的框架提供了很好的集成支持,如Hibernate,Struts2,JPA等
5.Spring DI机制降低了业务对象替换的复杂性。
6.Spring属于低侵入,代码污染极低。
7.Spring的高度可开放性,并不强制依赖于Spring,开发者可以自由选择Spring部分或全部

4.策略

SpringBoot框架中还有两个非常重要的策略:== 开箱即用和约定优于配置 ==。

开箱即用,Outofbox,是指在开发过程中,通过在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期。这个特点使得开发人员摆脱了复杂的配置工作以及依赖的管理工作,更加专注于业务逻辑。

约定优于配置,Convention over configuration,是一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。这一特点虽降低了部分灵活性,增加了BUG定位的复杂性,但减少了开发人员需要做出决定的数量,同时减少了大量的XML配置,并且可以将代码编译、测试和打包等工作自动化。

5.springBoot配置位置优先级

1.file:./config/
2.file:./
3.classpath:/config/
4.classpath:/

6.静态资源默认的映射路径有

//WebProperties
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/","classpath:/resources/", "classpath:/static/", "classpath:/public/" };

2.主程序

@SpringBootApplication
public class SecurityApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication.class, args);
    }

}

1. 注解

@SpringBootConfiguration :springboot的配置
    @Configuration: spring配置类
    	@Component: 说明这也是一个spring的组件

@EnableAutoConfiguration: 自动配置
    @AutoConfigurationPackage :自动配置包
        @Import(AutoConfigurationPackages.Registrar.class) 自动配置 自注册
    @Import(AutoConfigurationImportSelector.class)

springboot所有的自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里,但是不一定生效,判断条件是否成立,只要导入了对应的satart,就有对应的容器,有了启动器,自动装配就会生效,配置成功

2.SpringApplication.run()

主要分为两部分,一部分是springApplication的实例化,而是run方法的执行;

  • SpringApplication
    1. 推断应用 的类型是普通的项目还是web项目
    2. 查找并加载所有可用初始化器,设置到initializers属性中
    3. 找出所有的应用程序监听器,设置到listeners属性中
    4. 推断并设置main方法的定义类,找到运行的主类
//	SpringApplication构造器
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

image-20210730171324198

总结:

springApplication.run()一共做了两件事

1.创建SpringApplication对象;在对象初始化时保存事件监听器,容器初始化类以及判断是否为web应用,保存包含main方法的主配置类。
2.调用run方法;准备spring的上下文,完成容器的初始化,创建,加载等。会在不同的时机触发监听器的不同事件。

3.自动装配

https://www.processon.com/view/link/6103c6e07d9c083494f239e4

SpringCloud

1.简介及目的

1.了解提出微服务的原文与译文

主要是了解为什么要有微服务,了解其思想.

原文:https://martinfowler.com/articles/microservices.html

汉化:https://www.cnblogs.com/HHbJ/articles/14689415.html

一些参考学习路径

spring cloud netflix

https://www.springcloud.cc/spring-cloud-netflix.html

中文:https://www.springcloud.cc/spring-cloud-dalston.html

2.dubbo 和 Springcloud对比

社区活跃度

springcloud 比dubbo活跃

Dubbo Springcloud
服务注册中心 Zookeeper Spring Cloud Netflix Eureka
服务调用方式 RPC REST API
服务监控 Dubbo-monitor Spring Boot Admin
断路器 不完善 Spring Cloud Netflix Hystrix
服务网关 Spring Cloud Netflix Zuul
分布式配置 Spring Cloud Config
服务跟踪 Spring Cloud Sleuth
消息总线 Spring Cloud Bus
数据流 Spring Cloud Stream
批量任务 Spring Cloud Task

最大的区别:SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式

3.CAP定理

CAP原则

RDBMS(Mysql Oracle sqlServer) ====>ACID

ACID是什么?

  • A(Atomicity) 原子性
  • C(Consistency) 一致性
  • I (Isolation) 隔离性
  • D (Durability) 持久性

NoSQL(redis mongdb) ====>CAP

CAP是什么?

  • C (Consistency) 强一致性

    在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)

  • A (Availability) 可用性

    保证每个请求不管成功或者失败都有响应。

  • P (Partition tolerance) 分区容错性

    系统中任意信息的丢失或失败不会影响系统的继续运作

CAP的三进二: CA AP CP

CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。如果在某个分布式系统中数据无副本, 那么系统必然满足强一致性条件, 因为只有独一数据,不会出现数据不一致的情况,此时C和P两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了CP系统,但是CAP不可同时满足。

因此在进行分布式架构设计时,必须做出取舍。当前一般是通过分布式缓存中各节点的最终一致性来提高系统的性能,通过使用多节点之间的数据异步复制技术来实现集群化的数据一致性。通常使用类似 memcached 之类的 NOSQL 作为实现手段。虽然 memcached 也可以是分布式集群环境的,但是对于一份数据来说,它总是存储在某一台 memcached 服务器上。如果发生网络故障或是服务器死机,则存储在这台服务器上的所有数据都将不可访问。由于数据是存储在内存中的,重启服务器,将导致数据全部丢失。当然也可以自己实现一套机制,用来在分布式 memcached 之间进行数据的同步和持久化,但是实现难度是非常大的 。

2.常见的一些问题

1 负载均衡的意义 是什么

在计算中,负载均衡可以改善跨计算机,计算机集群,网络连接,中央处理单元或磁盘驱动器等多种计算资源的工作负载分布.负载均衡旨在优化资源使用,最大吞吐量,最小响应时间并避免任何单一资源的过载,使用多个组件进行负载均衡而不是单个组件可能会通过荣誉来提高可靠性和可用性.负载均衡通常涉及专用软件或硬件,例如多层交换机或域名系统服务进程.

2 SpringCloud如何实现服务的注册?

1 服务发布时,指定对应的服务名,将服务注册到注册中心(eureka zookeeper)

2 注册中心加@EnableEurekaServer,服务用语@EnableDiscoveryClient,然后用ribbon或feign进行服务直接的调用发现

3 什么是服务熔断?什么是服务降级?

为了解决某个微服务的调用响应时间过长或者不可用进而占用越来越多的系统资源引起雪崩效应就需要进行服务熔断和服务降级处理。

所谓的服务熔断指的是某个服务故障或异常一起类似显示世界中的“保险丝"当某个异常条件被触发就直接熔断整个服务,而不是一直等到此服务超时。

服务熔断就是相当于我们电闸的保险丝,一旦发生服务雪崩的,就会熔断整个服务,通过维护一个自己的线程池,当线程达到阈值的时候就启动服务降级,如果其他请求继续访问就直接返回fallback的默认值

3.spring cloud 组件

1.Eureka

工作流程:

1、Eureka Server 启动成功,等待服务端注册。在启动过程中如果配置了集群,集群之间定时通过 Replicate 同步注册表,每个 Eureka Server 都存在独立完整的服务注册表信息

2、Eureka Client 启动时根据配置的 Eureka Server 地址去注册中心注册服务

3、Eureka Client 会每 30s 向 Eureka Server 发送一次心跳请求,证明客户端服务正常

4、当 Eureka Server 90s 内没有收到 Eureka Client 的心跳,注册中心则认为该节点失效,会注销该实例

5、单位时间内 Eureka Server 统计到有大量的 Eureka Client 没有上送心跳,则认为可能为网络异常,进入自我保护机制,不再剔除没有上送心跳的客户端

6、当 Eureka Client 心跳请求恢复正常之后,Eureka Server 自动退出自我保护模式

7、Eureka Client 定时全量或者增量从注册中心获取服务注册表,并且将获取到的信息缓存到本地

8、服务调用时,Eureka Client 会先从本地缓存找寻调取的服务。如果获取不到,先从注册中心刷新注册表,再同步到本地缓存

9、Eureka Client 获取到目标服务器信息,发起服务调用

10、Eureka Client 程序关闭时向 Eureka Server 发送取消请求,Eureka Server 将实例从注册表中删除

Redis

1.简介及目的

Redis(Remote Dictionary Server),即远程数据字典

是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API.

Redis会周期性的把更新的数据写入磁盘或者把修改操作追加的记录文件,并且在此基础上实现了master-slave(主从同步)

免费和开源!是当下最热门的NoSQL技术之一!也被人们称之为结构化数据库!!

Redis 能干嘛?

1.内存存储、持久化,内存中是断点即失、所以说持久化很重要(rdb、aof)

2.效率高,可以用于高速缓存

3.发布订阅系统

4.地图信息分析

5.计时器、计数器(浏览量)

特性

1.多样的数据类型

2.持久化

3.集群

4.事务

2.NoSql 的四大分类

kv键值对

  • 新浪:Redis
  • 美团: Redis+Tair
  • 阿里\百度:Redis + memecache

文档型数据库(bson 和 json一样)

  • MongoDB (一般要掌握)
    • MongoDB 是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档
    • MongoDB 是一个介于关系型数据库和非关系型数据库中中间的产品!MongoDB 是非关系型数据库中功能最丰富,最像关系型数据库的!
  • ConthDB

列存储数据库

  • HBase
  • 分布式文件系统

图关系数据库

  • 他不是存图形,放的是关系,比如:朋友圈社交网络,广告
  • Neo4j,infoGrid
分类 Examples举例 典型应用场景 数据模型 优点 缺点
键值(key-value) Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。 Key 指向 Value 的键值对,通常用hash table来实现 查找速度快 数据无结构化,通常只被当作字符串或者二进制数据[3]
列存储数据库 Cassandra, HBase, Riak 分布式的文件系统 以列簇式存储,将同一列数据存在一起 查找速度快,可扩展性强,更容易进行分布式扩展 功能相对局限
文档型数据库 CouchDB, MongoDb Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) Key-Value对应的键值对,Value为结构化数据 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 查询性能不高,而且缺乏统一的查询语法。
图形(Graph)数据 Neo4J, InfoGrid, Infinite Graph 社交网络,推荐系统等。专注于构建关系图谱 图结构 利用图结构相关算法。比如最短路径寻址,N度关系查找等 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案。

3.基本的数据类型

1、String(字符串)

127.0.0.1:6379> set key1 v1			#设置值
OK
127.0.0.1:6379> get key1
"v1"
127.0.0.1:6379> append key1 "hello"		# 追加值,如果不存在,相当于 set key
(integer) 7
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> strlen key1		# 获取字符串长度
(integer) 7
127.0.0.1:6379> 

自增、自减

127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views		# 自增 1
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> decr views       # 自减 1
(integer) 0
127.0.0.1:6379> decr views
(integer) -1
127.0.0.1:6379> get views
"-1"
127.0.0.1:6379> incrby views 10		# 设置步长、自增 10 
(integer) 9
127.0.0.1:6379> decrby views 5      # 设置步长、自减 5
(integer) 4

字符串范围

127.0.0.1:6379> set key1 "hello,world!"
OK
127.0.0.1:6379> get key1
"hello,world!"
127.0.0.1:6379> getrange key1 0 3		# 截取字符串[0, 3]
"hell"
127.0.0.1:6379> getrange key1 0 -1		# 获取全部的字符串,和 get key一样
"hello,world!"
127.0.0.1:6379> 

替换:

127.0.0.1:6379> set key2 abcdefg
OK
127.0.0.1:6379> get key2
"abcdefg"
127.0.0.1:6379> setrange key2 1 xx
(integer) 7
127.0.0.1:6379> get key2
"axxdefg"
127.0.0.1:6379> 

setex(set with expire):设置过期时间

setnx(set if not exist):不存在再设置(在分布式锁中会经常使用)

127.0.0.1:6379> setex key3 30 "hello"		# 设置 30 秒后过期
OK
127.0.0.1:6379> ttl key3					# 剩余过期时间
(integer) 25
127.0.0.1:6379> setnx mykey "redis"			# mykey 不存在时设置成功
(integer) 1
127.0.0.1:6379> keys *
1) "key2"
2) "key1"
3) "views"
4) "mykey"
127.0.0.1:6379> setnx mykey "mongoDB"		# mykey 存在时设置失败
(integer) 0
127.0.0.1:6379> get mykey					# mykey 值不变
"redis"
127.0.0.1:6379> 
mset` 和 `mget
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3		# 同时设置多个值
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k2"
127.0.0.1:6379> mget k1 k2 k3			# 同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4       # msetnx 是一个原子性的操作,要么一起成功,要么都失败
(integer) 0
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379>

对象

set user:1 {name:zhangsan, age:3}     # 设置一个 user:1 对象 值为 json  字符来保存一个对象

127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "2"
127.0.0.1:6379> 

getset:先 get 再 set

127.0.0.1:6379> getset db redis		# 如果不存在值,则返回 nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb		# 如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379> get db
"mongodb"
127.0.0.1:6379> 

String 的使用场景:value 除了是字符串以外还可以是数字

  • 计数器
  • 统计多单位的数量
  • 粉丝数
  • 对象缓存存储

2、List(列表)

基本的数据类型,列表。

在 Redis 中可以把 list 用作栈、队列、阻塞队列。

list 命令多数以 l开头。

127.0.0.1:6379> lpush list one			# 将一个值或者多个值,插入到列表的头部(左)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three 
(integer) 3
127.0.0.1:6379> lrange list 0 -1			# 查看全部元素
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1				# 通过区间获取值
1) "three"
2) "two"
127.0.0.1:6379> rpush list right			# 将一个值或者多个值,插入到列表的尾部(右)
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> 

弹出 pop

127.0.0.1:6379> lrange list 0 -1
1) "!"
2) "world"
3) "world"
4) "hello"
127.0.0.1:6379> lpop list		# 移除list的第一个元素
"!"
127.0.0.1:6379> lrange list 0 -1
1) "world"
2) "world"
3) "hello"
127.0.0.1:6379> rpop list			# 移除list的第一个元素
"hello"
127.0.0.1:6379> lrange list 0 -1
1) "world"
2) "world"
127.0.0.1:6379> 

索引 Lindex

127.0.0.1:6379> lrange list 0 -1
1) "hjk"
2) "world"
3) "world"
127.0.0.1:6379> lindex list 1		# 通过下标获取list中的某一个值
"world"
127.0.0.1:6379> lindex list 0
"hjk"
127.0.0.1:6379> 

Llen 长度:

127.0.0.1:6379> llen list
(integer) 3
127.0.0.1:6379>

移除指定的值:

127.0.0.1:6379> lrange list 0 -1
1) "hjk"
2) "world"
3) "world"
127.0.0.1:6379> lrem list 1 world		# 移除list集合中指定个数的value,精确匹配
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "hjk"
2) "world"
127.0.0.1:6379> lpush list hjk
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "hjk"
2) "hjk"
3) "world"
127.0.0.1:6379> lrem list 2 hjk
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "world"
127.0.0.1:6379> 

trim 截断

127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
4) "hello4"
127.0.0.1:6379> ltrim mylist 1 2 # 通过下标截取指定长度,这个list已经被破坏了,截断之后只剩下截断后的元素
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello2"
2) "hello3"
127.0.0.1:6379> 

rpoplpush :移除列表的最后一个元素,将他移动到新的列表中。

127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
127.0.0.1:6379> rpoplpush mylist myotherlist		# 移除列表的最后一个元素,将他移动到新的列表中。
"hello3"
127.0.0.1:6379> lrange mylist 0 -1		# 查看原来的列表
1) "hello1"
2) "hello2"
127.0.0.1:6379> lrange myotherlist 0 -1		# 查看目标列表中,确实存在该值
1) "hello3"
127.0.0.1:6379> 

lset:将列表中指定下标的值替换为另一个值,更新操作

127.0.0.1:6379> exists list		# 判断这个列表是否存在
(integer) 0
127.0.0.1:6379> lset list 0 item		# 如果不存在的话,更新会报错
(error) ERR no such key
127.0.0.1:6379> lpush list value1
(integer) 1
127.0.0.1:6379> lrange list 0 0 
1) "value1"
127.0.0.1:6379> lset list 0 item		# 如果存在,更新当前下标的值
OK
127.0.0.1:6379> lset list 1 other		# 如果不存在的话,更新会报错
(error) ERR index out of range
127.0.0.1:6379> 

linsert:将某个具体的value插入到列表中某个元素的前面或者后面

127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
127.0.0.1:6379> linsert mylist before "hello2" hello
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello"
3) "hello2"
127.0.0.1:6379> linsert mylist after "hello2" hello
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello"
3) "hello2"
4) "hello"
127.0.0.1:6379> 

小结

  • list 实际上是一个链表,前后都可以插入
  • 如果key不存在,创建新的链表
  • 如果移除了所有的值,空链表,也代表不存在
  • 在两边插入或者改动值,效率最高。

3、Set (集合)

127.0.0.1:6379> sadd myset "hello"		# set 集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset "world"		
(integer) 1
127.0.0.1:6379> smembers myset		    # 查看指定Set的所有值
1) "world"
2) "hello"
127.0.0.1:6379> sismember myset hello		# 判断某一个值是不是在set中
(integer) 1
127.0.0.1:6379> sismember myset hello1
(integer) 0
127.0.0.1:6379> 
127.0.0.1:6379> scard myset		# 获取集合中的个数
(integer) 2
127.0.0.1:6379> sadd myset "hello2"		
(integer) 1
127.0.0.1:6379> smembers myset   
1) "world"
2) "hello2"
3) "hello"
127.0.0.1:6379> srem myset hello   # 移除元素
(integer) 1
127.0.0.1:6379> smembers myset
1) "world"
2) "hello2"
127.0.0.1:6379> 
127.0.0.1:6379> smembers myset
1) "kkk"
2) "world"
3) "hjk"
4) "hello2"
127.0.0.1:6379> srandmember myset			# 随机抽取一个元素
"hjk"
127.0.0.1:6379> srandmember myset
"hello2"
127.0.0.1:6379> srandmember myset 2			# 随机抽取指定个数的元素
1) "world"
2) "hello2"
127.0.0.1:6379> srandmember myset 2
1) "hello2"
2) "hjk"
127.0.0.1:6379> 
127.0.0.1:6379> smembers myset
1) "kkk"
2) "world"
3) "hjk"
4) "hello2"
127.0.0.1:6379> spop myset		# 随机删除元素
"hjk"
127.0.0.1:6379> smembers myset
1) "kkk"
2) "world"
3) "hello2"
127.0.0.1:6379> spop myset
"hello2"
127.0.0.1:6379> smembers myset
1) "kkk"
2) "world"
127.0.0.1:6379> 
127.0.0.1:6379> smembers myset
1) "kkk"
2) "world"
127.0.0.1:6379> sadd myset2 set2
(integer) 1
127.0.0.1:6379> smove myset myset2 "kkk"			# 将一个特定的值,移动到另一个set集合中
(integer) 1
127.0.0.1:6379> smembers myset
1) "world"
127.0.0.1:6379> smembers myset2
1) "kkk"
2) "set2"
127.0.0.1:6379> 
127.0.0.1:6379> smembers key1
1) "b"
2) "a"
3) "c"
127.0.0.1:6379> smembers key2
1) "e"
2) "d"
3) "c"
127.0.0.1:6379> sdiff key1 key2			# 差集
1) "b"
2) "a"
127.0.0.1:6379> sinter key1 key2         # 交集
1) "c"
127.0.0.1:6379> sunion key1 key2		# 并集
1) "e"
2) "a"
3) "c"
4) "d"
5) "b"

4、Hash(哈希)

也是 key - value 形式的,但是value 是一个map。

127.0.0.1:6379> hset myhash field xxx		# set 一个 key-value
(integer) 1
127.0.0.1:6379> hget myhash field			# 获取一个字段值
"xxx"
127.0.0.1:6379> hmset myhash field1 hello field2 world		# set 多个 key-value
OK
127.0.0.1:6379> hmget myhash field field1 field2			# 获取多个字段值
1) "xxx"
2) "hello"
3) "world"
127.0.0.1:6379> hgetall myhash				# 获取全部的数据
1) "field"
2) "xxx"
3) "field1"
4) "hello"
5) "field2"
6) "world"
127.0.0.1:6379> hdel myhash field1		# 删除指定的key,对应的value也就没有了
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field"
2) "xxx"
3) "field2"
4) "world"
127.0.0.1:6379> 
127.0.0.1:6379> hlen myhash		# 获取长度
(integer) 2
127.0.0.1:6379> hexists myhash field1   # 判断指定key是否存在
(integer) 0
127.0.0.1:6379> hexists myhash field2
(integer) 1
127.0.0.1:6379> hkeys myhash		# 获取所有的key
1) "field"
2) "field2"
127.0.0.1:6379> hvals myhash		# 获取所有的value
1) "xxx"
2) "world"
127.0.0.1:6379> 
127.0.0.1:6379> hset myhash field3 5		
(integer) 1
127.0.0.1:6379> hincrby myhash field3 1		# 指定增量
(integer) 6
127.0.0.1:6379> hincrby myhash field3 -1
(integer) 5
127.0.0.1:6379> hsetnx myhash field4 hello		# 如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 world		# 如果存在则不能设置
(integer) 0
127.0.0.1:6379> 

Hash 适合存储经常变动的对象信息,String 更适合于存储字符串。

5、zset (有序集合)

127.0.0.1:6379> zadd myset 1 one		# 添加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three	# 添加多个值
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> 

实现排序:

127.0.0.1:6379> zadd salary 2500 xiaohong
(integer) 1
127.0.0.1:6379> zadd salary 5000 xiaoming
(integer) 1
127.0.0.1:6379> zadd salary 500 xaiozhang
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "xaiozhang"
2) "xiaohong"
3) "xiaoming"
127.0.0.1:6379> zrangebyscore salary -inf +inf		# 从小到大显示全部的用户
1) "xaiozhang"
2) "xiaohong"
3) "xiaoming"
127.0.0.1:6379> zrevrange salary 0 -1		# 从大到小进行排序
1) "xiaoming"
2) "xiaohong"
3) "xaiozhang"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores   # 附带成绩的显示所有用户
1) "xaiozhang"
2) "500"
3) "xiaohong"
4) "2500"
5) "xiaoming"
6) "5000"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores			# 显示工资小于 2500 的用户
1) "xaiozhang"
2) "500"
3) "xiaohong"
4) "2500"
127.0.0.1:6379> zrange salary 0 -1
1) "xaiozhang"
2) "xiaohong"
3) "xiaoming"
127.0.0.1:6379> zrem salary xiaohong  # 移除特定元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "xaiozhang"
2) "xiaoming"
127.0.0.1:6379> zcard salary		# 获取有序集合的个数
(integer) 2
127.0.0.1:6379> 
127.0.0.1:6379> zadd myset 1 hello
(integer) 1
127.0.0.1:6379> zadd myset 2 world 3 !
(integer) 2
127.0.0.1:6379> zcount myset 1 3		# 获取指定区间的人员数量
(integer) 3
127.0.0.1:6379> zcount myset 1 2
(integer) 2

4、Redis 三种特殊数据类型

1、geospatial

Redis 在 3.2 推出 Geo 类型,该功能可以推算出地理位置信息,两地之间的距离。

文档: https://www.redis.net.cn/order/3687.html

借助网站模拟一些数据: http://www.jsons.cn/lngcode/

geoadd 添加地理位置

规则:两极无法直接添加,一般会下载城市数据,直接通过 Java 程序一次性导入。

有效的经度从 -180 度到 180 度。有效的纬度从 -85.05112878 度到 85.05112878 度。当坐标位置超出指定范围时,该命令将会返回一个错误。

(error) ERR invalid longitude latitude pair xxx yyy

添加一些模拟数据:

127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shengzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
127.0.0.1:6379> 

geopos 获得当前定位坐标值

127.0.0.1:6379> geopos china:city beijing		# 获得指定城市的经纬度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city shanghai
1) 1) "121.47000163793563843"
   2) "31.22999903975783553"
127.0.0.1:6379> 

geodist 获取两个位置之间的距离

单位:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。

127.0.0.1:6379> geodist china:city beijing shanghai km	# 查看北京和上海直接的直线距离
"1067.3788"
127.0.0.1:6379> geodist china:city beijing chongqing km
"1464.0708"
127.0.0.1:6379> 

georedius 以给定的经纬度为中心,找出某一半径内的元素

127.0.0.1:6379> georadius china:city 110 30 1000 km # 以110, 30 这个点为中心,寻找方圆 1000km 的城市
1) "chongqing"
2) "xian"
3) "shengzhen"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 500 km 
1) "chongqing"
2) "xian"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord	#  显示他人的定位信息
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "xian"
   2) 1) "108.96000176668167114"
      2) "34.25999964418929977"
127.0.0.1:6379> 
127.0.0.1:6379> georadius china:city 110 30 500 km withdist #  显示到中心点的距离
1) 1) "chongqing"
   2) "341.9374"
2) 1) "xian"
   2) "483.8340"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 1  # 指定数量
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 2
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "xian"
   2) "483.8340"
   3) 1) "108.96000176668167114"
      2) "34.25999964418929977"
127.0.0.1:6379> 

GEORADIUSBYMEMBER 找出位于指定元素周围的其他元素

127.0.0.1:6379> georadiusbymember china:city shanghai 1000 km
1) "hangzhou"
2) "shanghai"
127.0.0.1:6379> 

geo 底层实现原理其实就是 zset ,可以使用 zset 命令操作 geo

127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> zrem china:city beijing		# 删除一个元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
127.0.0.1:6379> 

2、hyperloglog

基数:数学上集合的元素个数,是不能重复的。

UV(Unique visitor):是指通过互联网访问、浏览这个网页的自然人。访问的一个电脑客户端为一个访客,一天内同一个访客仅被计算一次。

Redis 2.8.9 版本更新了 hyperloglog 数据结构,是基于基数统计的算法。

hyperloglog 的优点是占用内存小,并且是固定的。存储 2^64 个不同元素的基数,只需要 12 KB 的空间。但是也可能有 0.81% 的错误率。

这个数据结构常用于统计网站的 UV。传统的方式是使用 set 保存用户的ID,然后统计 set 中元素的数量作为判断标准。但是这种方式保存了大量的用户 ID,ID 一般比较长,占空间,还很麻烦。我们的目的是计数,不是保存数据,所以这样做有弊端。但是如果使用 hyperloglog 就比较合适了。

127.0.0.1:6379> pfadd mykey a b c d e f g h i j	# 创建第一组元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey					# 统计 mykey 基数
(integer) 10
127.0.0.1:6379> PFADD mykey2 i j z x c v b n m  # 创建第二组元素
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2					# 统计 mykey2 基数
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2		# 合并两组 mykey mykey2 => mykey3
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 15
127.0.0.1:6379> 

3、bitmap 位图

bitmap就是通过最小的单位bit来进行0或者1的设置,表示某个元素对应的值或者状态。一个bit的值,或者是0,或者是1;也就是说一个bit能存储的最多信息是2。

bitmap 常用于统计用户信息比如活跃粉丝和不活跃粉丝、登录和未登录、是否打卡等。

这里使用一周打卡的案例说明其用法:

127.0.0.1:6379> setbit sign 0 1		# 周一打卡了
(integer) 0
127.0.0.1:6379> setbit sign 1 0		# 周二未打卡
(integer) 0
127.0.0.1:6379> setbit sign 2 0		# 周三未打卡
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
127.0.0.1:6379> 

查看某一天是否打卡:

127.0.0.1:6379> GETBIT sign 3
(integer) 1
127.0.0.1:6379> GETBIT sign 6
(integer) 0
127.0.0.1:6379> 

统计:统计打卡的天数

127.0.0.1:6379> BITCOUNT sign
(integer) 4
127.0.0.1:6379> 

5.常用的命令

http://www.redis.cn/commands.html

127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set name xxx
OK
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> set age 1
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> exists name		# 判断key 是否存在
(integer) 1
127.0.0.1:6379> exists name1
(integer) 0
127.0.0.1:6379> move name 1
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> set name yyy
OK
127.0.0.1:6379> expire name 10  # 设置key的过期时间,单位是秒
(integer) 1
127.0.0.1:6379> ttl name		# 查看当前key的剩余过期时间
(integer) 7
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> type age		# 查看当前key的类型
string
127.0.0.1:6379> 

6.配置文件和持久化

https://www.cnblogs.com/itzhouq/p/redis4.html

1、Redis.conf 详解

找到启动时指定的配置文件:

[root@itzhouc ~]# cd /usr/local/bin
[root@itzhouc bin]# ls
dump.rdb         jemalloc.sh  kconfig           luajit        mcrypt    redis-benchmark  redis-check-rdb  redis-sentinel
jemalloc-config  jeprof       libmcrypt-config  luajit-2.0.4  mdecrypt  redis-check-aof  redis-cli        redis-server
[root@itzhouc bin]# cd kconfig/
[root@itzhouc kconfig]# vim redis.conf 

1、单位

# Redis configuration file example.
#
# Note that in order to read the configuration file, Redis must be
# started with the file path as first argument:
#
# ./redis-server /path/to/redis.conf

# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.

配置文件中 unit 单位对大小写不敏感。

2、包含

################################## INCLUDES ###################################

# Include one or more other config files here.  This is useful if you
# have a standard template that goes to all Redis servers but also need
# to customize a few per-server settings.  Include files can include
# other files, so use this wisely.
#
# Notice option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or Redis Sentinel. Since Redis always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
#
# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
#
# include /path/to/local.conf
# include /path/to/other.conf

配置文件可以将多个配置文件合起来使用。

3、NETWORK 网络

bind 127.0.0.1		# 绑定的 IP
protected-mode no   # 保护模式
port 6379			# 端口设置

4、GENERAL 通用

daemonize yes		# 以守护进程的方式运行,默认是 no ,我们需要自己开启为 yes
pidfile /var/run/redis_6379.pid		#  如果是后台启动,我们需要指定一个pid 文件

# 日志级别
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
logfile ""		# 日志文件的位置
databases 16	# 数据库的数量,默认是 16
always-show-logo yes  # 是否总是显示 LOGO

5、快照 SNAPSHOTTING

持久化,在规定的时间内,执行了多少次操作则会持久化到文件

Redis 是内存数据库,如果没有持久化,那么数据断电即失。

################################ SNAPSHOTTING  ################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving completely by commenting out all "save" lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save ""
# 如果 900s 内,至少有 1 个 key 进行了修改,进行持久化操作
save 900 1

# 如果 300s 内,至少有 10 个 key 进行了修改,进行持久化操作
save 300 10
save 60 10000

stop-writes-on-bgsave-error yes  # 如果持久化出错,是否还要继续工作
rdbcompression yes    # 是否压缩 rdb 文件,需要消耗一些 cpu 资源
rdbchecksum yes # 保存 rdb 文件的时候,进行错误的检查校验
dir ./  # rdb 文件保存的目录

6、SECURITY 安全

可以设置 Redis 的密码,默认是没有密码的。

[root@itzhouc bin]# redis-cli -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> config get requirepass		# 获取 redis 密码
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass "123456"  # 设置 redis 密码
OK
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.		# 发现所有的命令都没有权限了
127.0.0.1:6379> auth 123456			# 使用密码登录
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"
127.0.0.1:6379> 

7、CLIENTS 限制

################################### CLIENTS ####################################

# Set the max number of connected clients at the same time. By default
# this limit is set to 10000 clients, however if the Redis server is not
# able to configure the process file limit to allow for the specified limit
# the max number of allowed clients is set to the current file limit
# minus 32 (as Redis reserves a few file descriptors for internal uses).
#
# Once the limit is reached Redis will close all the new connections sending
# an error 'max number of clients reached'.
#
# maxclients 10000		# 设置能链接上 redis 的最大客户端数量
# maxmemory <bytes>		# redis 设置最大的内存容量
 maxmemory-policy noeviction  # 内存达到上限之后的处理策略
     - noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
    - allkeys-lru:在所有键中采用lru算法删除键,直到腾出足够内存为止。
    - volatile-lru:在设置了过期时间的键中采用lru算法删除键,直到腾出足够内存为止。
    - allkeys-random:在所有键中采用随机删除键,直到腾出足够内存为止。
    - volatile-random:在设置了过期时间的键中随机删除键,直到腾出足够内存为止。
    - volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。

8、APPEND ONLY 模式 AOF 配置

appendonly no  # 默认是不开启 AOF 模式的,默认使用 rdb 方式持久化,大部分情况下,rdb 完全够用

appendfilename "appendonly.aof"		# 持久化的文件的名字
# appendfsync always  # 每次修改都会 sync 消耗性能
appendfsync everysec  # 每秒执行一次 sync 可能会丢失这 1s 的数据。
# appendfsync no      # 不执行 sync 这个时候操作系统自己同步数据,速度最快。

2、持久化

Redis RDB 持久化详解

面试和工作,持久化都是重点。

Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态就会消失,所以 Redis 提供了持久化功能。

1、RDB (Redis DataBase)

什么是 RDB

image-20210809180218807

在指定的时间间隔内,将内存中的数据集快照写入磁盘,也就是 Snapshot 快照,它恢复时是将快照文件直接读取到内存里的。

Redis 会单独创建(fork)一个子进程进行持久化,会先将数据写入一个临时文件中,待持久化过程结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程不进行任何 IO 操作,这就确保的极高的性能。如果需要大规模的数据的恢复,且对数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加高效。RDB 唯一的缺点是最后一次持久化的数据可能会丢失。

生产环境下,需要对这个文件记性

默认持久化方式是 RDB,一般不需要修改。

rdb 保存的文件是 dump.rdb :

# The filename where to dump the DB
dbfilename dump.rdb

测试1:

首先修改配置文件保存快照的策略

################################ SNAPSHOTTING  ################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   save ""

# save 900 1
# save 300 10
# save 60 10000
save 60 5  # 只要 60s 内修改了 5 次 key 就会触发 rdb 操作。

保存配置文件:

127.0.0.1:6379> save
OK
127.0.0.1:6379> 

删除原始的 dump.rdb 文件:

[root@itzhouc bin]# ls
dump.rdb         jemalloc.sh  kconfig           luajit        mcrypt    redis-benchmark  redis-check-rdb  redis-sentinel
jemalloc-config  jeprof       libmcrypt-config  luajit-2.0.4  mdecrypt  redis-check-aof  redis-cli        redis-server
[root@itzhouc bin]# rm -rf dump.rdb 
[root@itzhouc bin]# ls
jemalloc-config  jeprof   libmcrypt-config  luajit-2.0.4  mdecrypt         redis-check-aof  redis-cli       redis-server
jemalloc.sh      kconfig  luajit            mcrypt        redis-benchmark  redis-check-rdb  redis-sentinel
[root@itzhouc bin]# 

60s 内修改 5 次 key :

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> set k4 v4
OK
127.0.0.1:6379> set k5 v5
OK

dump.rdb 文件再次出现。

[root@itzhouc bin]# ls
dump.rdb         jemalloc.sh  kconfig           luajit        mcrypt    redis-benchmark  redis-check-rdb  redis-sentinel
jemalloc-config  jeprof       libmcrypt-config  luajit-2.0.4  mdecrypt  redis-check-aof  redis-cli        redis-server
[root@itzhouc bin]# 

恢复数据:

关闭 Redis 服务和客户端,再次进入时数据被自动恢复:

127.0.0.1:6379> shutdown		# 关闭 Redis 服务
not connected> exit
[root@itzhouc bin]# ps -ef|grep redis		# redis 已经关闭了
root     25989 23576  0 14:27 pts/1    00:00:00 grep --color=auto redis
[root@itzhouc bin]# redis-server kconfig/redis.conf 		# 再次开启服务
25994:C 02 May 2020 14:28:01.003 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
25994:C 02 May 2020 14:28:01.003 # Redis version=5.0.8, bits=64, commit=00000000, modified=0, pid=25994, just started
25994:C 02 May 2020 14:28:01.003 # Configuration loaded
[root@itzhouc bin]# redis-cli -p 6379		# 客户端连接
127.0.0.1:6379> get k2						# 可以直接获取数据,说明k2 被持久化了。
"v2"
127.0.0.1:6379> 

测试2:

删除 dump.rdb 文件

root@itzhouc bin]# rm -rf dump.rdb 
[root@itzhouc bin]# ls
jemalloc-config  jeprof   libmcrypt-config  luajit-2.0.4  mdecrypt         redis-check-aof  redis-cli       redis-server
jemalloc.sh      kconfig  luajit 

在客户端清除所有数据:

127.0.0.1:6379> flushall
OK

再次检验 dump.rdb 文件:

[root@itzhouc bin]# ls
dump.rdb         jemalloc.sh  kconfig           luajit        mcrypt    redis-benchmark  redis-check-rdb  redis-sentinel
jemalloc-config  jeprof       libmcrypt-config  luajit-2.0.4  mdecrypt  redis-check-aof  redis-cli        redis-server
[root@itzhouc bin]# 

dump.rdb 文件再次出现。

触发机制

1、save 的规则满足的情况下,会自动触发 rdb 规则

2、执行 flushall 命令,也会触发 rdb 规则

3、退出 redis 也会产生 rdb 文件

备份就自动生成一个 dump.rdb 文件。

如何恢复 rdb 文件

1、只需要将 rdb 文件放在 Redis 启动目录就可以,Redis 启动的时候会自动检查 dump.rdb ,恢复其中的数据;

2、查看存放 rdb 文件的位置,在客户端中使用如下命令。

127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin"  # 如果在这个目录下存在 dump.rdb 文件,启动就会自动恢复其中的数据
127.0.0.1:6379> 

RDB 的优缺点

优点:

1、适合大规模的数据恢复

2、对数据的完整性要求不高

缺点:

1、需要一定的时间间隔进行操作,如果 Redis 意外宕机,最后一次修改的数据就没有了

2、fork 进程的时候,会占用一定的空间。

2、AOF (Append Only File)

AOF( append only file )持久化以独立日志的方式记录每次写命令,并在 Redis 重启时在重新执行 AOF 文件中的命令以达到恢复数据的目的。AOF 的主要作用是解决数据持久化的实时性。

Redis AOF持久化详解

以日志形式来记录每个操作,将 Redis 执行的过程的所有指令记录下来(读操作不记录),只追加文件但不可以改写文件,redis 启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一遍以完成数据的恢复工作。

AOF 保存的是 appendonly.aof 文件。

开启 AOF 模式

将配置文件中默认为 no 的 appendonly 修改为 yes ,重启服务。

appendonly yes
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"

重启后可以看到 AOF 文件:

[root@itzhouc bin]# ls
appendonly.aof  ipsort           jeprof            logs          mcrypt    redis-benchmark  redis-cli       tntrecht
backup.db       jemalloc-config  kconfig           luajit        mdecrypt  redis-check-aof  redis-sentinel
dump.rdb        jemalloc.sh      libmcrypt-config  luajit-2.0.4  pnscan    redis-check-rdb  redis-server

但是文件是空的。使用客户端添加一些数据再次查看:

[root@itzhouc bin]# redis-cli -p 6379
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> 
[root@itzhouc bin]# cat appendonly.aof 
*2
$6
SELECT
$1
0
*3
$3
set
$2
k1
$2
v1
*3
$3
set
$2
k2
$2
v2
*3
$3
set
$2
k3
$2
v3

自动修复 AOF文件

如果手动修改AOF 文件,可能导致 Redis 服务不能启动。比如这里我手动在 AOF 文件的最后一行随便添加一些命令:

set
$2
k3
$2
v3
gjjjjjjjjj

删除 dump.rdb 文件,重启服务:

[root@itzhouc bin]# rm -rf dump.rdb 
[root@itzhouc bin]# ls
appendonly.aof   jemalloc.sh  libmcrypt-config  luajit-2.0.4  pnscan           redis-check-rdb  redis-server
ipsort           jeprof       logs              mcrypt        redis-benchmark  redis-cli        tntrecht
jemalloc-config  kconfig      luajit            mdecrypt      redis-check-aof  redis-sentinel
[root@itzhouc bin]# redis-server kconfig/redis.conf 
13746:C 02 May 2020 16:22:43.345 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
13746:C 02 May 2020 16:22:43.346 # Redis version=5.0.8, bits=64, commit=00000000, modified=0, pid=13746, just started
13746:C 02 May 2020 16:22:43.346 # Configuration loaded
[root@itzhouc bin]# redis-cli -p 6379
Could not connect to Redis at 127.0.0.1:6379: Connection refused		# 连接失败
not connected>

如果这个 AOF 文件有错位,客户端就不能链接了,需要修复 AOF 文件。Redis 提供了工具 redis-check-aof --fix

[root@itzhouc bin]# redis-check-aof --fix appendonly.aof 
0x              6e: Expected prefix '*', got: 'g'
AOF analyzed: size=122, ok_up_to=110, diff=12
This will shrink the AOF from 122 bytes, with 12 bytes, to 110 bytes
Continue? [y/N]: y
Successfully truncated AOF
[root@itzhouc bin]# 

重启服务,再次尝试链接成功。

AOF 的优缺点

appendonly yes		# 默认是 no
appendfilename "appendonly.aof"  # 持久化的文件的名字
# appendfsync always   # 每次修改都会 sync ,消耗性能
appendfsync everysec   # 每秒执行一次 sync ,可能会丢失这 1s 的数据
# appendfsync no       # 不执行 sync,这个时候操作系统自己同步数据,速度最快

优点:

1、每一次修改都同步,文件的完整性更加好

2、每秒同步一次,可能会丢失一秒的数据

3、从不同步,效率最高的

缺点:

1、相对于数据文件来说, AOF 远远大于 RDB ,修复的速度也比 RDB 慢

2、AOF 的运行效率也比 RDB 慢,所以 Redis 默认的配置就是 RDB 持久化。

3、扩展

1、RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储

2、AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF 命令以Redis 协议追加保存每次写的操作到文件尾部,Redis 还能对 AOF 文件记性后台重写,使得AOF 文件的体积不至于过大

3、只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化

4、同时开启两种持久化方式

  • 在这种情况下,当 Redis 重启的时候会优先加载AOF 文件来恢复原始的数据,因为在通常情况下,AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整。
  • RDB 的数据不实时,同步使用两者时服务器重启也只会找 AOF 文件。那要不要只使用 AOF 呢?作者建议不要,因为 RDB 更适合用于备份数据库(AOF 在不断变化不好备份),快速重启,而且不会有 AOF 可能潜在的 BUG,留着作为一个万一的手段。

5、性能建议

  • 因为 RDB 文件只用作后备用途,建议只在 Slave 上持久化 RDB 文件,而且只要 15 分钟备份一次就够了,只保留save 900 1 这条规则。
  • 如果 Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒的数据,启动脚本较简单只 load 自己的 AOF 文件就可以了,代价是一是带来了持续的IO,而是 AOF rewrite 的最后将rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少 AOF rewrite 的频率,AOF 重写的基础大小默认值是 64M 太小了,可以设置到 5G 以上,默认值超过原大小 100% 大小重写可以改到适当的数值。
  • 如果不 Enable AOF ,仅靠 Master-Slave Repllcation 实现高可用也可以,能省掉一大笔 IO ,也减少了 rewrite 时带来的系统波动。代价是如果 Master/Slave 同时宕掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB 文件,载入较新的那个,微博就是这种架构。

7.发布订阅模式、主从复制和哨兵模式

1、Redis 订阅发布

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者发(pub)送消息,订阅者(sub)接收消息。

Redis 客户端可以订阅任意数量的频道。

订阅 / 发布消息图:

image-20210811104153530

下图展示了频道 channel1,已经订阅这个频道的三个客户端。

image-20210811104202791

当有新消息通过 publish 命令发送给频道 channel1 时,这个消息就会被发送给订阅它的三个客户端。

命令

这些命令被广泛应用于构建即时通讯应用、比如网络聊天室和实时广播、实时提醒等。

序号 命令及描述
1 [PSUBSCRIBE pattern pattern ...] 订阅一个或多个符合给定模式的频道。
2 [PUBSUB subcommand argument [argument ...]] 查看订阅与发布系统状态。
3 PUBLISH channel message 将信息发送到指定的频道。
4 [PUNSUBSCRIBE pattern [pattern ...]] 退订所有给定模式的频道。
5 [SUBSCRIBE channel channel ...] 订阅给定的一个或多个频道的信息。
6 [UNSUBSCRIBE channel [channel ...]] 指退订给定的频道。

测试

以下实例演示了发布订阅是如何工作的。在我们实例中我们创建了订阅频道名为 redisChat:

redis 127.0.0.1:6379> SUBSCRIBE redisChat

Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1

现在,我们先重新开启个 redis 客户端,然后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息。

redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"

(integer) 1

redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"

(integer) 1

# 订阅者的客户端会显示如下消息
1) "message"
2) "redisChat"
3) "Redis is a great caching technique"
1) "message"
2) "redisChat"
3) "Learn redis by runoob.com"

原理

Redis 是使用 C 实现的,通过分析 Redis 源码里的 public.c 文件,了解发布和订阅机制的底层实现,借此加深对 Redis 的理解。Redis 通过 public 、subscribe 和 psubscribe 等命令实现发布和订阅功能。

微信:

通过 subscribe 命令订阅某频道后,redis=server 里面维护了一个字典,字典的键就是一个个频道!而字典的值则是一个链表,链表保存了所有订阅这个 channel 的客户端。subscribe 命令的关键,就是讲客户端添加到给定 channel 的订阅链中。

通过 publish 命令向订阅者发送消息,redis-server 会使用给定的频道作为键,在它所维护的channel 字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有的订阅者。

使用场景:

1、实时消息系统

2、实时聊天

3、订阅、关注系统都可以

稍微复杂的场景更多的使用消息中间件 MQ。

2、主从复制

1、概念

主从复制,是指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。前者称之为主节点(master/leader),后者称之为从节点(slave/flower);数据的复制都是单向的,只能从主节点到从节点。Master 以写为主,Slave 以读为主。

默认情况下,每台 Redis 服务器都是主节点。且一个主节点可以有多个从节点或者没有从节点,但是一个从节点只能有一个主节点。

2、主从复制的作用

1、数据冗余:主从复制实现了数据的热备份,是持久化的之外的一种数据冗余方式。

2、故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复。实际也是一种服务的冗余。

3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写 Redis 数据时应用连接主节点,读 Redis 的时候应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个节点分担读负载,可以大大提高 Redis 服务器的并发量。

4、高可用(集群)的基石:除了上述作用以外,主从复制还是哨兵模式和集群能够实施的基础,因此说主从复制是 Redis 高可用的基础。

一般来说,要将Redis 运用于工程项目中,只使用一台 Redis 是万万不能的(可能会宕机),原因如下:

1、从结构上,单个 Redis 服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力很大;

2、从容量上,单个 Redis 服务器内存容量有限,就算一台 Redis 服务器内存容量为 265G, 也不能将所有的内存用作 Redis 存储内存,一般来说,单台 Redis最大使用内存不应该超过 20G

电商网站上的商品,一般都是一次上传,无数次浏览的,说专业点就是“多读少写”。

对于这种场景,我们可以使用如下这种架构:

image-20210811142450508

主从复制,读写分离!80% 的情况下,都是在进行读操作。这种架构可以减少服务器压力,经常使用实际生产环境中,最少是“一主二从”的配置。真实环境中不可能使用单机 Redis。

3、环境配置

只配置从库,不用配置主库。

[root@itzhouc bin]# redis-cli -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> info replication			# 查看当前库的信息
# Replication
role:master									# 角色
connected_slaves:0							# 当前没有从库
master_replid:2467dd9bd1c252ce80df280c925187b3417055ad
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379> 

复制 3 个配置文件,然后修改对应的信息

1、端口

2、pid 名称

3、log 文件名称

4、dump.rdb 名称

port 6381
pidfile /var/run/redis_6381.pid
logfile "6381.log"
dbfilename dump6381.rdb

修改完毕后,启动我们的 3 个 redis 服务器,可以通过进程信息查询。

[root@itzhouc ~]# ps -ef|grep redis
root       426     1  0 16:53 ?        00:00:00 redis-server *:6379
root       446     1  0 16:54 ?        00:00:00 redis-server *:6380
root       457     1  0 16:54 ?        00:00:00 redis-server *:6381
root       464   304  0 16:54 pts/3    00:00:00 grep --color=auto redis

4、一主二从

默认情况下,每台 Redis 服务器都是主节点,我们一般情况下,只用配置从机就好了。

主机:6379, 从机:6380 和 6381

配置的方式有两种:一种是直接使用命令配置,这种方式当 Redis 重启后配置会失效。另一种方式是使用配置文件。这里使用命令演示一下。

下面将80 和 81 两个配置为在从机。

127.0.0.1:6380> SLAVEOF 127.0.0.1 6379		# SLAVEOF host  port
OK
127.0.0.1:6380> info replication
# Replication
role:slave			# 角色已经是从机了
master_host:127.0.0.1	# 主节点地址
master_port:6379			# 主节点端口
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:0
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:907bcdf00c69d361ede43f4f6181004e2148efb7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:0
127.0.0.1:6380> 

配置好了之后,看主机:

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2		# 主节点下有两个从节点
slave0:ip=127.0.0.1,port=6380,state=online,offset=420,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=420,lag=1
master_replid:907bcdf00c69d361ede43f4f6181004e2148efb7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:420
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:420
127.0.0.1:6379> 

真实的主从配置应该是在配置文件中配置,这样才是永久的。这里使用命令是暂时的。

配置文件 redis.conf

################################# REPLICATION #################################

# Master-Replica replication. Use replicaof to make a Redis instance a copy of
# another Redis server. A few things to understand ASAP about Redis replication.
#
#   +------------------+      +---------------+
#   |      Master      | ---> |    Replica    |
#   | (receive writes) |      |  (exact copy) |
#   +------------------+      +---------------+
#
# 1) Redis replication is asynchronous, but you can configure a master to
#    stop accepting writes if it appears to be not connected with at least
#    a given number of replicas.
# 2) Redis replicas are able to perform a partial resynchronization with the
#    master if the replication link is lost for a relatively small amount of
#    time. You may want to configure the replication backlog size (see the next
#    sections of this file) with a sensible value depending on your needs.
# 3) Replication is automatic and does not need user intervention. After a
#    network partition replicas automatically try to reconnect to masters
#    and resynchronize with them.
#
# replicaof <masterip> <masterport>			# 这里配置

# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the replica to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the replica request.
#
# masterauth <master-password>

配置方式也是一样的。

5、几个问题

1、主机可以写,从机不能写只能读。主机中的所有信息和数据都会保存在从机中。如果从机尝试进行写操作就会报错。

127.0.0.1:6381> get k1			# k1的值是在主机中写入的,从机中可以读取到。
"v1"
127.0.0.1:6381> set k2 v2			# 从机尝试写操作,报错了
(error) READONLY You can't write against a read only replica.
127.0.0.1:6381> 

2、如果主机断开了,从机依然链接到主机,可以进行读操作,但是还是没有写操作。这个时候,主机如果恢复了,从机依然可以直接从主机同步信息。

3、使用命令行配置的主从机,如果从机重启了,就会变回主机。如果再通过命令变回从机的话,立马就可以从主机中获取值。这是复制原理决定的。

6、复制原理

Slave 启动成功连接到 Master 后会发送一个 sync 同步命令。

Master 接收到命令后,启动后台的存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,master 将传送整个数据文件到 slave ,并完成一次完全同步。

全量复制:Slave 服务在接收到数据库文件后,将其存盘并加载到内存中。

增量复制: Master 继续将新的所有收集到的修改命令一次传给 slave,完成同步。

但是只要重新连接 master ,一次完全同步(全量复制)将被自动执行。我们的数据一定可以在从机中看到。

这种模式的原理图:

image-20210811133811865

第二种模式

image-20210811133945767

这种模式的话,将 6381 的主节点配置为 6380 。主节点 6379 只有一个从机。

如果现在 6379 节点宕机了, 6380 和 6381 节点都是从节点,只能进行读操作,都不会自动变为主节点。需要手动将其中一个变为主节点,使用如下命令:

SLAVEOF no one

3、哨兵模式

1、概述

主从切换技术的方式是:当主机服务器宕机之后,需要手动将一台服务器切换为主服务器,这需要人工干预,费时费力,还会造成一段时间内的服务不可用。这不是一种推荐的方式,更多的时候我们优先考虑的的是哨兵模式。Redis 从 2.8 开始正式提供了 Sentinel(哨兵)架构来解决这个问题。

哨兵模式能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

哨兵模式是一种特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它独立运行。其原理是哨兵通过发送命令,等待 Redis 服务器响应,从而监控运行的多个 Redis 实例

image-20210811133956876

这里的哨兵有两个作用

  • 通过发送命令,让 Redis 服务器返回监控其运行状态,包括主服务器和从服务器
  • 当哨兵检测到 master 宕机,会自动将 slave 切换为 master,然后通过发布订阅模式通知其他的从放服务器,修改配置文件,让他们切换主机。

然而一个哨兵进程对 Redis 服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

image-20210811134009233

假设主服务器宕机了,哨兵1先检测到这个结果,系统并不会马上进行 failover 过程,仅仅是哨兵 1 主观认为主服务器不可用,这个现象称之为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行 failover 【故障转移】。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称之为客观下线

2、配置一个一主二从的哨兵模式

哨兵模式的优点

1、哨兵集群,基于主从复制模式,所有的主从配置优点,它全有

2、主从可以切换,故障可以转移,系统的可用性就会更好

3、哨兵模式就是主从模式的升级,手动到自动,更加健壮。

哨兵模式的缺点

1、Redis 不方便在线扩容,集群达到一定的上限,在线扩容就会十分麻烦;

2、实现哨兵模式的配置其实也很麻烦,里面有甚多的配置项。

windows 哨兵模式部署 https://blog.csdn.net/u010648555/article/details/79430105

名词概念:https://www.cnblogs.com/kevingrace/p/9004460.html

8.缓存穿透、缓存击穿和缓存雪崩

概念参考:

https://www.cnblogs.com/HHbJ/articles/13371075.html

https://www.cnblogs.com/itzhouq/p/redis6.html

Mysql

1.简介及目的

MySQL 是一款安全、跨平台、高效的,并与 PHP、Java 等主流编程语言紧密结合的数据库系统。该数据库系统是由瑞典的 MySQL AB 公司开发、发布并支持,由 MySQL 的初始开发人员 David Axmark 和 Michael Monty Widenius 于 1995 年建立的。

MySQL 的象征符号是一只名为 Sakila 的海豚,代表着 MySQL 数据库的速度、能力、精确和优秀本质。

image-20210803183336709
目前 MySQL 被广泛地应用在 Internet 上的中小型网站中。由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,使得很多公司都采用 MySQL 数据库以降低成本。

MySQL 数据库可以称得上是目前运行速度最快的 SQL 语言数据库之一。除了具有许多其他数据库所不具备的功能外,MySQL 数据库还是一种完全免费的产品,用户可以直接通过网络下载 MySQL 数据库,而不必支付任何费用。

2.log文件有几种,大致都是什么功能

日志相关来源:https://www.cnblogs.com/myseries/p/10728533.html

  1. 重做日志(redo log)

  2. 回滚日志(undo log)

  3. 二进制日志(bin log)

  4. 错误日志(error log)

  5. 慢查询日志(slow query log)

  6. 一般查询日志(general log)

  7. 中继日志(relay log)

    其中重做日志和和回滚日志与事务操作息息相关,二进制日志也与事务操作有一定的关系,这三种日志,对理解MySQL中的事务操作有着重要的意义

一、重做日志(redo log)

作用:

  确保事务的持久性。redo日志记录事务执行后的状态,用来恢复未写入data file的已成功事务更新的数据。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。

内容:

  物理格式的日志,记录的是物理数据页面的修改的信息,其redo log是顺序写入redo log file的物理文件中去的。

什么时候产生:

  事务开始之后就产生redo log,redo log的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo log文件中。

什么时候释放:

  当对应事务的脏页写入到磁盘之后,redo log的使命也就完成了,重做日志占用的空间就可以重用(被覆盖)。

对应的物理文件:

  默认情况下,对应的物理文件位于数据库的data目录下的ib_logfile1&ib_logfile2

  innodb_log_group_home_dir 指定日志文件组所在的路径,默认./ ,表示在数据库的数据目录下。

  innodb_log_files_in_group 指定重做日志文件组中文件的数量,默认2

关于文件的大小和数量,由以下两个参数配置:

  innodb_log_file_size 重做日志文件的大小。

  innodb_mirrored_log_groups 指定了日志镜像文件组的数量,默认1

其他:

  很重要一点,redo log是什么时候写盘的?前面说了是在事物开始之后逐步写盘的。

  之所以说重做日志是在事务开始之后逐步写入重做日志文件,而不一定是事务提交才写入重做日志缓存,原因就是,重做日志有一个缓存区Innodb_log_buffer,Innodb_log_buffer的默认大小为8M(这里设置的16M),Innodb存储引擎先将重做日志写入innodb_log_buffer中。

image-20210804083313727

  然后会通过以下三种方式将innodb日志缓冲区的日志刷新到磁盘

  Master Thread 每秒一次执行刷新Innodb_log_buffer到重做日志文件。

  每个事务提交时会将重做日志刷新到重做日志文件。

  当重做日志缓存可用空间 少于一半时,重做日志缓存被刷新到重做日志文件

  由此可以看出,重做日志通过不止一种方式写入到磁盘,尤其是对于第一种方式,Innodb_log_buffer到重做日志文件是Master Thread线程的定时任务。

  因此重做日志的写盘,并不一定是随着事务的提交才写入重做日志文件的,而是随着事务的开始,逐步开始的。

另外引用《MySQL技术内幕 Innodb 存储引擎》(page37)上的原话:

  即使某个事务还没有提交,Innodb存储引擎仍然每秒会将重做日志缓存刷新到重做日志文件。

  这一点是必须要知道的,因为这可以很好地解释再大的事务的提交(commit)的时间也是很短暂的。

二、回滚日志(undo log)

作用:

  保证数据的原子性,保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读

内容:

  逻辑格式的日志,在执行undo的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现的,这一点是不同于redo log的。

什么时候产生:

  事务开始之前,将当前是的版本生成undo log,undo 也会产生 redo 来保证undo log的可靠性

什么时候释放:

  当事务提交之后,undo log并不能立马被删除,而是放入待清理的链表,由purge线程判断是否由其他事务在使用undo段中表的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间。

对应的物理文件:

  MySQL5.6之前,undo表空间位于共享表空间的回滚段中,共享表空间的默认的名称是ibdata,位于数据文件目录中。

  MySQL5.6之后,undo表空间可以配置成独立的文件,但是提前需要在配置文件中配置,完成数据库初始化后生效且不可改变undo log文件的个数

  如果初始化数据库之前没有进行相关配置,那么就无法配置成独立的表空间了。

关于MySQL5.7之后的独立undo 表空间配置参数如下:

  innodb_undo_directory = /data/undospace/ –undo独立表空间的存放目录 innodb_undo_logs = 128 –回滚段为128KB innodb_undo_tablespaces = 4 –指定有4个undo log文件

  如果undo使用的共享表空间,这个共享表空间中又不仅仅是存储了undo的信息,共享表空间的默认为与MySQL的数据目录下面,其属性由参数innodb_data_file_path配置。

image-20210804082735442

其他:

  undo是在事务开始之前保存的被修改数据的一个版本,产生undo日志的时候,同样会伴随类似于保护事务持久化机制的redolog的产生。

  默认情况下undo文件是保持在共享表空间的,也即ibdatafile文件中,当数据库中发生一些大的事务性操作的时候,要生成大量的undo信息,全部保存在共享表空间中的。

  因此共享表空间可能会变的很大,默认情况下,也就是undo 日志使用共享表空间的时候,被“撑大”的共享表空间是不会也不能自动收缩的。

  因此,mysql5.7之后的“独立undo 表空间”的配置就显得很有必要了。

三、二进制日志(binlog):

作用:

  用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步。

  用于数据库的基于时间点的还原。

内容:

  逻辑格式的日志,可以简单认为就是执行过的事务中的sql语句。

  但又不完全是sql语句这么简单,而是包括了执行的sql语句(增删改)反向的信息,也就意味着delete对应着delete本身和其反向的insert;update对应着update执行前后的版本的信息;insert对应着delete和insert本身的信息。

  在使用mysqlbinlog解析binlog之后一些都会真相大白。

  因此可以基于binlog做到类似于oracle的闪回功能,其实都是依赖于binlog中的日志记录。

什么时候产生:

  事务提交的时候,一次性将事务中的sql语句(一个事物可能对应多个sql语句)按照一定的格式记录到binlog中。

  这里与redo log很明显的差异就是redo log并不一定是在事务提交的时候刷新到磁盘,redo log是在事务开始之后就开始逐步写入磁盘。

  因此对于事务的提交,即便是较大的事务,提交(commit)都是很快的,但是在开启了bin_log的情况下,对于较大事务的提交,可能会变得比较慢一些。

  这是因为binlog是在事务提交的时候一次性写入的造成的,这些可以通过测试验证。

什么时候释放:

  binlog的默认是保持时间由参数expire_logs_days配置,也就是说对于非活动的日志文件,在生成时间超过expire_logs_days配置的天数之后,会被自动删除。

image-20210804083134071

对应的物理文件:

  配置文件的路径为log_bin_basename,binlog日志文件按照指定大小,当日志文件达到指定的最大的大小之后,进行滚动更新,生成新的日志文件。

  对于每个binlog日志文件,通过一个统一的index文件来组织。

image-20210804083121385

其他:

  二进制日志的作用之一是还原数据库的,这与redo log很类似,很多人混淆过,但是两者有本质的不同

  作用不同:redo log是保证事务的持久性的,是事务层面的,binlog作为还原的功能,是数据库层面的(当然也可以精确到事务层面的),虽然都有还原的意思,但是其保护数据的层次是不一样的。

  内容不同:redo log是物理日志,是数据页面的修改之后的物理记录,binlog是逻辑日志,可以简单认为记录的就是sql语句

  另外,两者日志产生的时间,可以释放的时间,在可释放的情况下清理机制,都是完全不同的。

  恢复数据时候的效率,基于物理日志的redo log恢复数据的效率要高于语句逻辑日志的binlog

  关于事务提交时,redo log和binlog的写入顺序,为了保证主从复制时候的主从一致(当然也包括使用binlog进行基于时间点还原的情况),是要严格一致的,MySQL通过两阶段提交过程来完成事务的一致性的,也即redo log和binlog的一致性的,理论上是先写redo log,再写binlog,两个日志都提交成功(刷入磁盘),事务才算真正的完成。

四、错误日志

  错误日志记录着mysqld启动和停止,以及服务器在运行过程中发生的错误的相关信息。在默认情况下,系统记录错误日志的功能是关闭的,错误信息被输出到标准错误输出。
  指定日志路径两种方法:
    编辑my.cnf 写入 log-error=[path]
    通过命令参数错误日志 mysqld_safe –user=mysql –log-error=[path] &

显示错误日志的命令(如下图所示)

image-20210804083051361

五、普通查询日志 general query log

  记录了服务器接收到的每一个查询或是命令,无论这些查询或是命令是否正确甚至是否包含语法错误,general log 都会将其记录下来 ,记录的格式为 {Time ,Id ,Command,Argument }。也正因为mysql服务器需要不断地记录日志,开启General log会产生不小的系统开销。 因此,Mysql默认是把General log关闭的。

查看日志的存放方式:show variables like ‘log_output’;
image-20210804083038559

  如果设置mysql> set global log_output=’table’ 的话,则日志结果会记录到名为gengera_log的表中,这表的默认引擎都是CSV
  如果设置表数据到文件set global log_output=file;
  设置general log的日志文件路径:
    set global general_log_file=’/tmp/general.log’;
    开启general log: set global general_log=on;
    关闭general log: set global general_log=off;

image-20210804082846499

  然后在用:show global variables like ‘general_log’

image-20210804082901511

六、慢查询日志

  慢日志记录执行时间过长和没有使用索引的查询语句,报错select、update、delete以及insert语句,慢日志只会记录执行成功的语句。

image-20210804082827579

>https://www.cnblogs.com/myseries/p/10728533.html

基于binlog的主从复制

image-20210803190237111

redo log的策略

image-20210803190304561

3.sql语句的执行流程(select、insert/update)

图不清晰的话,可以查看 https://www.processon.com/view/link/60e7a8201efad449c608480

对于名词不熟悉的话,

mysql底层机制:https://blog.csdn.net/OrangeRawNorthland/article/details/85316247

mysql日志模块:https://blog.csdn.net/OrangeRawNorthland/article/details/85321170

1.select 执行流程

image-20210803184023064

连接器

想要连接数据库那么必须是要通过连接器,连接器负责与客户端建立连接、获取权限、维持和管理连接。
当你输入了用户名和密码后,连接器会在权限表中查询你拥有的权限,之后本次连接中你对表能否操作查询删除修改等,都依赖于此时读取到的权限。
假设此时修改了你的权限,那么也是不会影响你本次的连接的,只有在下一次创建连接,查询权限的时候才会生效。

查询缓存

在经过连接器之后,会执行第二步查询缓存,在MySql拿到查询后,会先到查询缓存查看是否执行过这条语句,之前执行的结果会以key-value对的形式存在,如果不在查询缓存中,会继续执行后面的极端,如果命中缓存则直接返回结果。在MySql8.0之后去掉了查询缓存的功能。

分析器

分析器会先做“词法分析”,识别出sql里的字符串分别是什么,读出“select”关键字对出这是一条查询语句,把字符串T识别成“表T”
“语法分析”,根据词法分析的结果,判断该条sql是否满足MySql的语法

优化器

优化器的作用在于选择最优的逻辑执行sql,例如在一个语句进行多表关联的时候,决定各个表的连接顺序

执行器

在开始执行前,先判断你对表T是否有执行查询的权限,没有就返回没有权限的错误,有权限则继续执行
执行器根据表的引擎定义,调用该引擎提供的接口。

2.update执行流程

image-20210803183709478

image-20210806164950324

4.mysql 优化

可以查看借鉴:

https://mp.weixin.qq.com/s/gAoosK9vAGxeCB7rnik1GA

https://www.cnblogs.com/HHbJ/articles/14649186.html

1.、Mysql中,可以使用SHOW STATUS语句查询一些Mysql数据库的性能参数。SHOW STATUS语句语法如下所示:

SHOW STATUS LIKE 'value';
其中,value是要查询的参数值,一些常用的性能参数如下:
a、Connections,连接mysql服务器的次数。
b、Uptime,mysql服务器的上线时间。
c、Slow_queries,慢查询的次数。
d、Com_select,查询操作的次数。
e、Com_insert,插入操作的次数。
f、Com_update,更新操作的次数。
g、Com_delete,删除操作的次数。

1 -- 查询mysql服务器的连接次数
2 SHOW STATUS LIKE 'Connections';
3 
4 -- 查询mysql服务器的慢查询次数。慢查询次数参数可以结合慢查询日志,找出慢查询语句,然后针对慢查询语句进行表结构优化或者查询语句优化。
5 SHOW STATUS LIKE 'Slow_queries';

2.、EXPLAIN语句的基本语法如下:(索引分析),一般情况达到ref即可

通过对查询语句的分析,可以了解查询语句执行的情况,找出查询语句执行的瓶颈,从而优化查询语句。mysql中提供了EXPLAIN语句和DESCRIBE语句,用来分析查询语句。
EXPLAIN语句的基本语法如下:
EXPLAIN [EXTENDED] SELECT select_options;
使用EXTENED关键字,EXPLAIN语句将产生附加信息。select_options是select语句的查询选项,包括from where子句等等。
执行该语句,可以分析EXPLAIN后面的select语句的执行情况,并且能够分析出所查询的表的一些特征。
例如:EXPLAIN SELECT * FROM user;

查询结果进行解释说明:
a、id:select识别符,这是select的查询序列号。
b、select_type:标识select语句的类型。
    它可以是以下几种取值:
        b1、SIMPLE(simple)表示简单查询,其中不包括连接查询和子查询。
        b2、PRIMARY(primary)表示主查询,或者是最外层的查询语句。
        b3、UNION(union)表示连接查询的第2个或者后面的查询语句。
        b4、DEPENDENT UNION(dependent union)连接查询中的第2个或者后面的select语句。取决于外面的查询。
        b5、UNION RESULT(union result)连接查询的结果。
        b6、SUBQUERY(subquery)子查询的第1个select语句。
        b7、DEPENDENT SUBQUERY(dependent subquery)子查询的第1个select,取决于外面的查询。
        b8、DERIVED(derived)导出表的SELECT(FROM子句的子查询)。
c、table:表示查询的表。
d、type:表示表的连接类型。
    下面按照从最佳类型到最差类型的顺序给出各种连接类型。
    d1、system,该表是仅有一行的系统表。这是const连接类型的一个特例。
    d2、const,数据表最多只有一个匹配行,它将在查询开始时被读取,并在余下的查询优化中作为常量对待。const表查询速度很快,因为它们只读一次。const用于使用常数值比较primary key或者unique索引的所有部分的场合。
        例如:EXPLAIN SELECT * FROM user WHERE id=1;
    d3、eq_ref,对于每个来自前面的表的行组合,从该表中读取一行。当一个索引的所有部分都在查询中使用并且索引是UNIQUE或者PRIMARY KEY时候,即可使用这种类型。eq_ref可以用于使用“=”操作符比较带索引的列。比较值可以为常量或者一个在该表前面所读取的表的列的表达式。
        例如:EXPLAIN SELECT * FROM user,db_company WHERE user.company_id = db_company.id;
    d4、ref对于来自前面的表的任意行组合,将从该表中读取所有匹配的行。这种类型用于所以既不是UNION也不是primaey key的情况,或者查询中使用了索引列的左子集,即索引中左边的部分组合。ref可以用于使用=或者<=>操作符的带索引的列。
    d5、ref_or_null,该连接类型如果ref,但是如果添加了mysql可以专门搜索包含null值的行,在解决子查询中经常使用该连接类型的优化。
    d6、index_merge,该连接类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素。
    d7、unique_subquery,该类型替换了下面形式的in子查询的ref。是一个索引查询函数,可以完全替代子查询,效率更高。
    d8、index_subquery,该连接类型类似于unique_subquery,可以替换in子查询,但是只适合下列形式的子查询中非唯一索引。
    d9、range,只检索给定范围的行,使用一个索引来选择行。key列显示使用了那个索引。key_len包含所使用索引的最长关键元素。当使用=,<>,>,>=,<,<=,is null,<=>,between或者in操作符,用常量比较关键字列时,类型为range。
    d10、index,该连接类型与all相同,除了只扫描索引树。着通常比all快,引文索引问价通常比数据文件小。
    d11、all,对于前面的表的任意行组合,进行完整的表扫描。如果表是第一个没有标记const的表,这样不好,并且在其他情况下很差。通常可以增加更多的索引来避免使用all连接。
e、possible_keys:possible_keys列指出mysql能使用那个索引在该表中找到行。如果该列是null,则没有相关的索引。在这种情况下,可以通过检查where子句看它是否引起某些列或者适合索引的列来提高查询性能。如果是这样,可以创建适合的索引来提高查询的性能。
f、key:表示查询实际使用到的索引,如果没有选择索引,该列的值是null,要想强制mysql使用或者忽视possible_key列中的索引,在查询中使用force index、use index或者ignore index。
g、key_len:表示mysql选择索引字段按照字节计算的长度,如果健是null,则长度为null。注意通过key_len值可以确定mysql将实际使用一个多列索引中的几个字段。
h、ref:表示使用那个列或者常数或者索引一起来查询记录。
i、rows:显示mysql在表中进行查询必须检查的行数。
j、Extra:该列mysql在处理查询时的详细信息。

3.在写查询语句应避免什么

  • 避免在字段上进行函数运算(内部函数也包括(+,-,*,/,! 等) ),会导致索引失效
  • 注意类型保持一致避免让字段进行隐式转换,可能会导致索引失效
  • 不需要的字段尽量避免,不返回
  • 如果只查询一条的话,使用limit 1来提前返回
  • 索引固然可以提高相应的 SELECT 的效率,但同时也降低了 INSERT 及 UPDATE 的效。因为 INSERT 或 UPDATE 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过 6 个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
  • 应尽量避免在 WHERE 子句中使用 != 或 <> 操作符。MySQL 只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的 LIKE(like value%)。
  • 应尽量避免在 WHERE 子句中使用 OR 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,可以使用 UNION 合并查询
  • 应尽量避免全表扫描,首先应该考虑where及order by涉及的列上建立索引.

4. 数据库中事务是什么?特性?

1.事务是访问并可能更新数据库中各种数据项的一个程序单元.

2事务通常由sql语言或变成语言发起并控制

3 特性:

​ 1.事务是回复和并发控制的基本单位

​ 2.事务具有四个属性:原子性(Atomicity) 一致性(Consistency) 隔离性(Isolation) 持久性(Durability). 这四个属性通常称为ACID

5. sql语句关键词的执行顺序?

1from子句,组装来自不同数据源的数据

2where子句.基于指定的条件对记录进行筛选.

3.group by子句,将数据划分为多个分组

4.使用聚合函数进行计算

5使用having子句筛选分组

6.计算所有的表达式

7.使用order by对结果集进行排序

8.即from → where →group by → having → 计算所有的表达式 →order by →select输出

6.数据库有哪几种约束类型?

1 主键约束 PRIMARY KEY

2 外键约束 FOREIGN KEY

3 唯一约束 UNIQUE

4 检查约束 CHECK

5 非空约束 NOT NULL

7 清除数据库表

truncate table 表名

delete 和 truncate 的区别

  • 相同点:都能删除数据,都不会删除表结构
  • 不同:
    • truncate 重新设置 自增列计数器会归零
    • truncate 不会影响事务

了解: delete删除的问题,重启数据库现象

  • innoDB :自增列会重1开始(存在内存当中,断点即失)
  • myisam: 继续从上一个自增量开始(存在文件中,不会丢失)

5.数据库的引擎有哪些,各有什么不同

mysql存储引擎MyIsaminnoDb区别

常用的存储引擎有以下:

InnoDB:提供了对数据库acid事务的支持,并且还提供了行级锁和外键的约束,他的事迹的目标就是处理大数据容量的数据库系统.

MyIASM:不提供事务的支持,也不支持行级锁和外键

MeMory:所有的数据都在内存中,数据的处理速度块,但是安全性不高

对于引擎的选择:

如果没有特别的需求,使用默认的Innodb即可.

myisam:以读写插入为主的应用程序,比如博客系统

innodb:更新(删除)操作频率也高,或者要保证数据的完整性;并发量搞,支持事务和外键,比如OA自动化办公系统

InnoDB的四大特性

插入缓冲 二次写 自适应哈希索引 预读

MyISAM Innodb
结构 每张表被存放在三个文件:frm-表格定义、MYD(MYData)-数据文件、MYI(MYIndex)-索引文件 所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB
存储空间 MyISAM可被压缩,存储空间较小 InnoDB的表需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引
可移植性、备份及恢复 由于MyISAM的数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作 免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了
文件格式 数据和索引是分别存储的,数据.MYD,索引.MYI 数据和索引是集中存储的,.ibd
记录存储顺序 按记录插入顺序保存 按主键大小有序插入
外键 不支持 支持
事务 不支持 支持
锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的) 表级锁定 行级锁定、表级锁定,锁定力度小并发能力高
SELECT MyISAM更优
INSERT、UPDATE、DELETE InnoDB更优
select count(*) myisam更快,因为myisam内部维护了一个计数器,可以直接调取。
索引的实现方式 B+树索引,myisam 是堆表 B+树索引,Innodb 是索引组织表
哈希索引 不支持 支持
全文索引 支持 不支持

image-20210806153616597

6.主从复制

主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库;主数据库一般是准实时的业务数据库。

**主从复制的作用(好处,或者说为什么要做主从)重点

1、做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。

2、架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。

3、读写分离,使数据库能支撑更大的并发。在报表中尤其重要。由于部分报表sql语句非常的慢,导致锁表,影响前台服务。如果前台使用master,报表使用slave,那么报表sql将不会造成前台锁,保证了前台速度。

前提一定是要先打开binlog日志

查看mysql 是否开启了二进制日志:

log_bin 为ON表示开启,为OFF表示未开启

log_bin_basename 表示二进制的文件保存路径

mysql> show variables like 'log_bin%';
+---------------------------------+------------------------------------------------------------------+
| Variable_name                   | Value                                                            |
+---------------------------------+------------------------------------------------------------------+
| log_bin                         | ON                                                               |
| log_bin_basename                | C:\Program Files\MYSQL\mysql-5.7.32-winx64\data\master-bin       |
| log_bin_index                   | C:\Program Files\MYSQL\mysql-5.7.32-winx64\data\master-bin.index |
| log_bin_trust_function_creators | OFF                                                              |
| log_bin_use_v1_row_events       | OFF                                                              |
+---------------------------------+------------------------------------------------------------------+

查看当前二进制日志文件状态:

mysql> show master status;
+-------------------+----------+--------------+------------------------------------------------------------------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB                                                             | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------------------------------------------------------------------+-------------------+
| master-bin.000002 |     2432 | master       | mysql,information_schema,performance_schema,ds0,ds1,slave,sszt_base,sys,test |                   |
+-------------------+----------+--------------+------------------------------------------------------------------------------+-------------------+

主库配置

修改主库配置

在my.ini 文件中找到[mysqld](#注释自行删除)

添加如下配置(需要同步的数据库有多少都可以写进去,主从同步会根据库名称找到对应的丛库去同步数据)

server-id=1#主库和从库需要不一致
log-bin=master-bin
log_bin_index=master-bin.index
binlog-do-db=master#同步的数据库(需要同步的数据库名字,可以多个)
binlog-do-db=master2#同步的数据库(需要同步的数据库名字,可以多个)
binlog-ignore-db=mysql#不需要同步的数据库
binlog-ignore-db=mysql2#不需要同步的数据库

修改完成之后需要重启mysql服务

然后确认二进制日志文件开启 ON

为从库创建用户

将用户名与密码记住

create user slave;
grant replication slave on *.* to slave@'从库ip' identified by '密码';
flush privileges;

查看主库状态

mysql> show master status;
+-------------------+----------+--------------+------------------------------------------------------------------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB                                                             | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------------------------------------------------------------------+-------------------+
| master-bin.000002 |     2432 | master       | mysql,information_schema,performance_schema,ds0,ds1,slave,sszt_base,sys,test |                   |
+-------------------+----------+--------------+------------------------------------------------------------------------------+-------------------+

**File名称 与 Position要记住从库需要用到 **

到此主库配置完毕

从库配置

修改从库配置

一样先修改my.ini文件

server-id=2#主库和从库需要不一致
relay-log=slave-relay-bin
relay-log-index=slave-relay-bin.index
replicate-do-db=master#需要同步的库
replicate-do-db=master2#需要同步的库
replicate-ignore-db=mysql#不需要同步的库
replicate-ignore-db=mysql2#不需要同步的库

然后重新启动

设置运行代码从库配置

stop slave;
change master to master_host='主库ip',master_port=端口,master_user='slave',master_password='密码',master_log_file='master-bin.000008',master_log_pos=120;
start slave;

**File名称 与 Position从主库查询 **

测试是否打通

mysql> show slave status;
+----------------------------------+---------------+-------------+-------------+---------------+-------------------+---------------------+------------------------+---------------+-----------------------+------------------+-------------------+-----------------+--------------------------------------------------------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-----------------------------------------------------------------+-----------+---------------------+--------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+
| Slave_IO_State                   | Master_Host   | Master_User | Master_Port | Connect_Retry | Master_Log_File   | Read_Master_Log_Pos | Relay_Log_File         | Relay_Log_Pos | Relay_Master_Log_File | Slave_IO_Running | Slave_SQL_Running | Replicate_Do_DB | Replicate_Ignore_DB                                                | Replicate_Do_Table | Replicate_Ignore_Table | Replicate_Wild_Do_Table | Replicate_Wild_Ignore_Table | Last_Errno | Last_Error | Skip_Counter | Exec_Master_Log_Pos | Relay_Log_Space | Until_Condition | Until_Log_File | Until_Log_Pos | Master_SSL_Allowed | Master_SSL_CA_File | Master_SSL_CA_Path | Master_SSL_Cert | Master_SSL_Cipher | Master_SSL_Key | Seconds_Behind_Master | Master_SSL_Verify_Server_Cert | Last_IO_Errno | Last_IO_Error | Last_SQL_Errno | Last_SQL_Error | Replicate_Ignore_Server_Ids | Master_Server_Id | Master_UUID                          | Master_Info_File                                                | SQL_Delay | SQL_Remaining_Delay | Slave_SQL_Running_State                                | Master_Retry_Count | Master_Bind | Last_IO_Error_Timestamp | Last_SQL_Error_Timestamp | Master_SSL_Crl | Master_SSL_Crlpath | Retrieved_Gtid_Set | Executed_Gtid_Set | Auto_Position | Replicate_Rewrite_DB | Channel_Name | Master_TLS_Version |
+----------------------------------+---------------+-------------+-------------+---------------+-------------------+---------------------+------------------------+---------------+-----------------------+------------------+-------------------+-----------------+--------------------------------------------------------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-----------------------------------------------------------------+-----------+---------------------+--------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+
| Waiting for master to send event | 192.168.10.77 | slave       |        3306 |            60 | master-bin.000002 |                2432 | slave-relay-bin.000002 |           321 | master-bin.000002     | Yes              | Yes               | master          | mysql,information_schema,performance_schema,local,ll,sszt_base,sys |                    |                        |                         |                             |          0 |            |            0 |                2432 |             528 | None            |                |             0 | No                 |                    |                    |                 |                   |                |                     0 | No                            |             0 |               |              0 |                |                             |                1 | 541f334e-2fa7-11eb-9536-244bfe03354a | C:\Program Files\dev\mysql\mysql-5.7.32-winx64\data\master.info |         0 | NULL                | Slave has read all relay log; waiting for more updates |              86400 |             |                         |                          |                |                    |                    |                   |             0 |                      |              |                    |
+----------------------------------+---------------+-------------+-------------+---------------+-------------------+---------------------+------------------------+---------------+-----------------------+------------------+-------------------+-----------------+--------------------------------------------------------------------+--------------------+------------------------+-------------------------+-----------------------------+------------+------------+--------------+---------------------+-----------------+-----------------+----------------+---------------+--------------------+--------------------+--------------------+-----------------+-------------------+----------------+-----------------------+-------------------------------+---------------+---------------+----------------+----------------+-----------------------------+------------------+--------------------------------------+-----------------------------------------------------------------+-----------+---------------------+--------------------------------------------------------+--------------------+-------------+-------------------------+--------------------------+----------------+--------------------+--------------------+-------------------+---------------+----------------------+--------------+--------------------+

找到

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

这两个必须是yes,由于太长可能不好看,所以可以复制在文本里查看;

至此配置完毕.

测验的话 在主库添加从库会同步添加.

解除主从同步

mysql> stop slave;
mysql> reset slave all;

7.集群搭建

8.分库分表

Mybatis Mybatis-Plus

Mybatis

1.简介 和目的

官网:https://mybatis.org/mybatis-3/s

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
  • 提供映射标签,支持对象与数据库的orm字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供xml标签,支持编写动态sql。

2.mybatis 的优点

1基于sql语句编程,相当灵活,不会对应用程序或数据库的现有设计造成任何影响,sql写在xml里,解除sql与程序代码的耦合,便于统一管理;提供xml标签,支持编写动态sql语句,并可重用

2与jdbc相比,减少了50%以上的代码,消除了jdbc大量冗余的代码,不需要手动开关连接;

3很好的与各种数据库兼容(因为mybatis使用jdbc来连接数据库,所以只要jdbc支持的数据库mybatis都支持)

4能够与spring很好的继承

5提供映射标签,支持对象与数据库的orm字段关系映射;提供对象关系映射标签,支持对象关系组件维护

3.mybatis 缺点

1sql语句的编写工作量大,尤其字段多\关联表多时,对开发人员编写sql语句的功底有一定的要求

2 sql语句依赖数据库,导致数据库移植性差,不能随意更换数据库

4.#和$的区别

{}是预编译 处理,${}是字符串替换;

mybatis在处理#{}时,会将sql中的#{}替换为?,调用PreparedStatemment的set方法来赋值;

mybatis在处理${}时,就是把${}替换成变量的值.

使用#{}可以有效的防止sql注入,提高系统安全性

Mynatis-plus

1.简介 和目的

官网 :https://mp.baomidou.com/

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

框架结构:

image-20210809100646639

2.特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

3.核心功能

1.代码生成器

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

如果要生成自己有个性的化的模板生成器,可以参考

https://mp.baomidou.com/config/generator-config.html#%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE

2.CRUD 接口

3.条件构造器

4.分页插件

1.添加分页拦截

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    //分页
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
}

使用page对象

@Test
void testPage(){
    Page<User> page = new Page<>(1,20);
    Page<User> userPage = userMapper.selectPage(page, null);
    page.getRecords().stream().forEach(System.out::println);
}

5.主键(默认使用的是雪花算法)

public enum IdType {
    /**
     * 数据库ID自增
     * <p>该类型请确保数据库设置了 ID自增 否则无效</p>
     */
    AUTO(0),
    /**
     * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
     */
    NONE(1),
    /**
     * 用户输入ID
     * <p>该类型可以通过自己注册自动填充插件进行填充</p>
     */
    INPUT(2),

    /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
    /**
     * 分配ID (主键类型为number或string),
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
     *
     * @since 3.3.0
     */
    ASSIGN_ID(3),
    /**
     * 分配UUID (主键类型为 string)
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
     */
    ASSIGN_UUID(4),
    /**
     * @deprecated 3.3.0 please use {@link #ASSIGN_ID}
     */
    @Deprecated
    ID_WORKER(3),
    /**
     * @deprecated 3.3.0 please use {@link #ASSIGN_ID}
     */
    @Deprecated
    ID_WORKER_STR(3),
    /**
     * @deprecated 3.3.0 please use {@link #ASSIGN_UUID}
     */
    @Deprecated
    UUID(4);

    private final int key;

    IdType(int key) {
        this.key = key;
    }
}

4.扩展

1.逻辑删除

mybatis-plus:
  global-config:
    logic-delete-field: deleted  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
    logic-delete-value: 1 # 逻辑已删除值(默认为 1)
    logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

在字段加上这个

@TableLogic
private Integer deleted;

2.自动填充

1.添加填充策略

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("insert start...........");
        this.fillStrategy(metaObject, "gmtCreate", new Date()); 
        this.fillStrategy(metaObject, "gmtModify", new Date()); 
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("update start...........");
        this.fillStrategy(metaObject, "gmtModify", new Date());
    }
}

2.在字段上进行指定

public class User {

    // 注意!这里需要标记为填充字段
    @TableField(.. fill = FieldFill.INSERT)
    private String fillField;

    ....
}
public enum FieldFill {
    /**
     * 默认不处理
     */
    DEFAULT,
    /**
     * 插入填充字段
     */
    INSERT,
    /**
     * 更新填充字段
     */
    UPDATE,
    /**
     * 插入和更新填充字段
     */
    INSERT_UPDATE
}

3.乐观锁

添加拦截器

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    //乐观锁
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

在字段加上@version

@Version
private Integer version;

RabbitMQ

1.简介及目的

image-20210702105404560

MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。

RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、 安全。AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。

使用场景

1.解耦(为面向服务的架构(SOA)提供基本的最终一致性实现)

2. 异步提升效率 传统的做法有两种 1.串行的方式;2.并行方式

3. 流量削峰 流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛

2.AMQP核心概念

  • Server:又称Broker,接受客户端的连接,实现AMQP实体服务

  • Connection:连接,应用程序与Broker的网络连接

  • Channel:网络信道,几乎所有的操作都是在Channel中进行,Channel是进行消息读写的通道.客户端可建立多个Channel,每个Channel代表一个会话任务.

  • Messages:消息,服务器和应用程序之间传送的数据,由Properties和Body组成.Properties可以对消息进行修饰,比如消息的优先级、延迟等高级特性,Body则就是消息体内容.

  • Virtual host:虚拟地址,用于进行逻辑隔离,最上层的消息路由.一个Virtual Host里面可以有若干个Exchange和Queue,同一个Virtual Host里面不能有相同名称的Exchange或Queue

    • Exchange:交换机,接受消息,根据路由键转发消息到绑定的队列
  • Bingding:Exachange 和 Queue之间的虚拟连接,bingding中可以包含routing key

    • Routing key: 一个路由规则,虚拟机可用它来确定如何路由一个特定消息 (* 有且只有一个 #一个或者多个)
  • Queue:也称为Messgaes Queue,消息队列,保存消息并将它们转发给消费者

3.消息分发策略机制和对比

ActiveMQ RabbitMQ kafka RocketMQ
发布订阅 支持 支持 支持 支持
分发 支持 支持 支持 /
公平分发 / 支持 支持 /
重发 支持 支持 / 支持
消息拉取 / 支持 支持 支持

4.RabbitMQ 角色分类

  • none
    • 不能访问management plugin
  • management: 查看自己相关节点信息
    • 列出自己可以通过AMQP登入的虚拟机
    • 查看自己的虚拟机节点virtual hosts的queues,exchanges和bingdings信息
    • 查看和关闭自己的channel和connections
    • 查看有关自己的虚拟机节点virtual hosts的统计信息,包括其他用户在这个节点virtual hosts中的活动信息
  • policymaker
    • 包含management所有权限
    • 查看和创建和删除自己virtual hosts所属的policies和parameters信息
  • moitoring
    • 包含management所有权限
    • 罗列出所有的virtual hosts,包括不能登录的virtual hosts
    • 查看其他用户的connections和channels信息
    • 查看节点级别的数据如clustering和memory使用情况
    • 查看所有的virtual hosts的全局统计信息
  • administrator
    • 最高权限
    • 可以创建和删除virtual hosts
    • 可以查看 创建 删除用户users
    • 查看创建permisssions
    • 关闭所有用户的connections

5.消息模式

https://www.rabbitmq.com/getstarted.html

  • 1.简单模式 Simple
  • 2.工作模式 work
  • 发布订阅模式
    • 类型 fanout
  • 路由模式
    • 类型 direct
  • 主体topic模式
    • 类型 topic
  • 参数模式
    • 类型 headers

6.RabbitMQ -- 过期时间TTL

过期时间TTL表示可以对详细设置预期的时间,在这个时间内都可以被消费者接受获取;过了之后消息将自动被删除.RabbitMQ可以对消息和队列 设置TTL.目前有两种方法可以设置

  • 通过队列属性设置,队列中所有消息都有相同的过期时间
  • 对消息进行单重设置,每条消息的TTL可以不同

如果上述两种方法同时使用,则消息的过期时间以两者之间TTL较小的那个数值为准.消息在队列的生存时间一旦超过设置的ttl值,就成为dead message被投递到死信队列,消费者将无法再收到该消息.

7.RabbitMQ-- 死信队列

DLX,全称为Dead-Letter-Exchange,可以称之为私信交换机,也有人称之为死信邮箱.当消息在一个队列中变成死信(dead message)之后,他可能被重新发送到另一个交换机中,这个交换机就是DLX,绑定DLX的队列就称之为私信队列.消息编程私信,可能是由于以下的原因:

  • 消息被拒绝
  • 消息过期
  • 队列达到最大长度

DLX也是一个正常的交换机,和一般的交换机没有区别,他能在任何的队列上被指定,实际上就是设置某个队列的属性,当这个队列中存在死信时,Rabbitmq就会自动的将这个消息重新发布到设置的DLX上去,进而被路由到另一个队列,即死信队列.

要想使用死信队列,只需要在定义队列的时候设置队列参数x-dead-letter-exchange指定交换机即可.

8.RabbitMQ 虽然能提供一些方便,但是亦需要考虑的更多

例如:可靠投递、重复消费等等;

image-20210803090728847

Maven

1.Maven 解决了最根本的问题

Maven 是一个项目管理工具,它包含了一个 项目对象模型 (POM:Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。

从繁琐工作中解放出来,能帮你构建工程,管理 jar包,编译代码,还能帮你自动运行单元测试,打包,生成报表,甚至能帮你部署项目.

2.Maven 阿里镜像

	<mirror>
        <id>alimaven</id>
        <mirrorOf>central</mirrorOf>
        <name>aliyun maven</name>
        <url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
    </mirror>

3.maven 的核心概念

  • POM : 一个文件 名称是pom.xml,pom翻译过来叫做项目对象模型.

    ​ maven把一个项目当做一个模型使用.控制maven构建项目的过程,管理jar依赖

  • 约定的目录结构: maven 项目的目录和文件的位置都是规定的

  • 坐标: 是一个唯一的字符串,用来表示资源的

  • 依赖管理: 管理你的项目可以使用jar文件

  • 仓库管理(了解): 你的资源存放的位置

  • 生命周期(了解): maven工具构建项目的过程,就是生命周期

  • 插件和目标(了解): 执行maven构建的时候使用的工具是插件

  • 继承

  • 聚合

4.Maven 工程约定目录结构

maven中约定的目录结构;

Hello

​ |---src

​ |---|---main #主程序代码和配置文件

​ |---|---java #程序包和包中的java文件

​ |---|---resources # java程序要使用的配置文件

​ |---|---test # 测试程序代码和文件(可以没有)

​ |---|---java # 测试程序包和包中的java文件

​ |---|---resources # 测试java程序中要使用的配置文件

​ |---pom.xml #maven的核心文件(maven项目必须要有的)

5.pom文件初识(来自菜鸟教程)

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/maven-v4_0_0.xsd">
    <!--父项目的坐标。如果项目中没有规定某个元素的值,那么父项目中的对应值即为项目的默认值。 坐标包括group ID,artifact ID和 
        version。 -->
    <parent>
        <!--被继承的父项目的构件标识符 -->
        <artifactId />
        <!--被继承的父项目的全球唯一标识符 -->
        <groupId />
        <!--被继承的父项目的版本 -->
        <version />
        <!-- 父项目的pom.xml文件的相对路径。相对路径允许你选择一个不同的路径。默认值是../pom.xml。Maven首先在构建当前项目的地方寻找父项 
            目的pom,其次在文件系统的这个位置(relativePath位置),然后在本地仓库,最后在远程仓库寻找父项目的pom。 -->
        <relativePath />
    </parent>
    <!--声明项目描述符遵循哪一个POM模型版本。模型本身的版本很少改变,虽然如此,但它仍然是必不可少的,这是为了当Maven引入了新的特性或者其他模型变更的时候,确保稳定性。 -->
    <modelVersion>4.0.0</modelVersion>
    <!--项目的全球唯一标识符,通常使用全限定的包名区分该项目和其他项目。并且构建时生成的路径也是由此生成, 如com.mycompany.app生成的相对路径为:/com/mycompany/app -->
    <groupId>asia.banseon</groupId>
    <!-- 构件的标识符,它和group ID一起唯一标识一个构件。换句话说,你不能有两个不同的项目拥有同样的artifact ID和groupID;在某个 
        特定的group ID下,artifact ID也必须是唯一的。构件是项目产生的或使用的一个东西,Maven为项目产生的构件包括:JARs,源 码,二进制发布和WARs等。 -->
    <artifactId>banseon-maven2</artifactId>
    <!--项目产生的构件类型,例如jar、war、ear、pom。插件可以创建他们自己的构件类型,所以前面列的不是全部构件类型 -->
    <packaging>jar</packaging>
    <!--项目当前版本,格式为:主版本.次版本.增量版本-限定版本号 -->
    <version>1.0-SNAPSHOT</version>
    <!--项目的名称, Maven产生的文档用 -->
    <name>banseon-maven</name>
    <!--项目主页的URL, Maven产生的文档用 -->
    <url>http://www.baidu.com/banseon</url>
    <!-- 项目的详细描述, Maven 产生的文档用。 当这个元素能够用HTML格式描述时(例如,CDATA中的文本会被解析器忽略,就可以包含HTML标 
        签), 不鼓励使用纯文本描述。如果你需要修改产生的web站点的索引页面,你应该修改你自己的索引页文件,而不是调整这里的文档。 -->
    <description>A maven project to study maven.</description>
    <!--描述了这个项目构建环境中的前提条件。 -->
    <prerequisites>
        <!--构建该项目或使用该插件所需要的Maven的最低版本 -->
        <maven />
    </prerequisites>
    <!--项目的问题管理系统(Bugzilla, Jira, Scarab,或任何你喜欢的问题管理系统)的名称和URL,本例为 jira -->
    <issueManagement>
        <!--问题管理系统(例如jira)的名字, -->
        <system>jira</system>
        <!--该项目使用的问题管理系统的URL -->
        <url>http://jira.baidu.com/banseon</url>
    </issueManagement>
    <!--项目持续集成信息 -->
    <ciManagement>
        <!--持续集成系统的名字,例如continuum -->
        <system />
        <!--该项目使用的持续集成系统的URL(如果持续集成系统有web接口的话)。 -->
        <url />
        <!--构建完成时,需要通知的开发者/用户的配置项。包括被通知者信息和通知条件(错误,失败,成功,警告) -->
        <notifiers>
            <!--配置一种方式,当构建中断时,以该方式通知用户/开发者 -->
            <notifier>
                <!--传送通知的途径 -->
                <type />
                <!--发生错误时是否通知 -->
                <sendOnError />
                <!--构建失败时是否通知 -->
                <sendOnFailure />
                <!--构建成功时是否通知 -->
                <sendOnSuccess />
                <!--发生警告时是否通知 -->
                <sendOnWarning />
                <!--不赞成使用。通知发送到哪里 -->
                <address />
                <!--扩展配置项 -->
                <configuration />
            </notifier>
        </notifiers>
    </ciManagement>
    <!--项目创建年份,4位数字。当产生版权信息时需要使用这个值。 -->
    <inceptionYear />
    <!--项目相关邮件列表信息 -->
    <mailingLists>
        <!--该元素描述了项目相关的所有邮件列表。自动产生的网站引用这些信息。 -->
        <mailingList>
            <!--邮件的名称 -->
            <name>Demo</name>
            <!--发送邮件的地址或链接,如果是邮件地址,创建文档时,mailto: 链接会被自动创建 -->
            <post>banseon@126.com</post>
            <!--订阅邮件的地址或链接,如果是邮件地址,创建文档时,mailto: 链接会被自动创建 -->
            <subscribe>banseon@126.com</subscribe>
            <!--取消订阅邮件的地址或链接,如果是邮件地址,创建文档时,mailto: 链接会被自动创建 -->
            <unsubscribe>banseon@126.com</unsubscribe>
            <!--你可以浏览邮件信息的URL -->
            <archive>http:/hi.baidu.com/banseon/demo/dev/</archive>
        </mailingList>
    </mailingLists>
    <!--项目开发者列表 -->
    <developers>
        <!--某个项目开发者的信息 -->
        <developer>
            <!--SCM里项目开发者的唯一标识符 -->
            <id>HELLO WORLD</id>
            <!--项目开发者的全名 -->
            <name>banseon</name>
            <!--项目开发者的email -->
            <email>banseon@126.com</email>
            <!--项目开发者的主页的URL -->
            <url />
            <!--项目开发者在项目中扮演的角色,角色元素描述了各种角色 -->
            <roles>
                <role>Project Manager</role>
                <role>Architect</role>
            </roles>
            <!--项目开发者所属组织 -->
            <organization>demo</organization>
            <!--项目开发者所属组织的URL -->
            <organizationUrl>http://hi.baidu.com/banseon</organizationUrl>
            <!--项目开发者属性,如即时消息如何处理等 -->
            <properties>
                <dept>No</dept>
            </properties>
            <!--项目开发者所在时区, -11到12范围内的整数。 -->
            <timezone>-5</timezone>
        </developer>
    </developers>
    <!--项目的其他贡献者列表 -->
    <contributors>
        <!--项目的其他贡献者。参见developers/developer元素 -->
        <contributor>
            <name />
            <email />
            <url />
            <organization />
            <organizationUrl />
            <roles />
            <timezone />
            <properties />
        </contributor>
    </contributors>
    <!--该元素描述了项目所有License列表。 应该只列出该项目的license列表,不要列出依赖项目的 license列表。如果列出多个license,用户可以选择它们中的一个而不是接受所有license。 -->
    <licenses>
        <!--描述了项目的license,用于生成项目的web站点的license页面,其他一些报表和validation也会用到该元素。 -->
        <license>
            <!--license用于法律上的名称 -->
            <name>Apache 2</name>
            <!--官方的license正文页面的URL -->
            <url>http://www.baidu.com/banseon/LICENSE-2.0.txt</url>
            <!--项目分发的主要方式: repo,可以从Maven库下载 manual, 用户必须手动下载和安装依赖 -->
            <distribution>repo</distribution>
            <!--关于license的补充信息 -->
            <comments>A business-friendly OSS license</comments>
        </license>
    </licenses>
    <!--SCM(Source Control Management)标签允许你配置你的代码库,供Maven web站点和其它插件使用。 -->
    <scm>
        <!--SCM的URL,该URL描述了版本库和如何连接到版本库。欲知详情,请看SCMs提供的URL格式和列表。该连接只读。 -->
        <connection>
            scm:svn:http://svn.baidu.com/banseon/maven/banseon/banseon-maven2-trunk(dao-trunk)
        </connection>
        <!--给开发者使用的,类似connection元素。即该连接不仅仅只读 -->
        <developerConnection>
            scm:svn:http://svn.baidu.com/banseon/maven/banseon/dao-trunk
        </developerConnection>
        <!--当前代码的标签,在开发阶段默认为HEAD -->
        <tag />
        <!--指向项目的可浏览SCM库(例如ViewVC或者Fisheye)的URL。 -->
        <url>http://svn.baidu.com/banseon</url>
    </scm>
    <!--描述项目所属组织的各种属性。Maven产生的文档用 -->
    <organization>
        <!--组织的全名 -->
        <name>demo</name>
        <!--组织主页的URL -->
        <url>http://www.baidu.com/banseon</url>
    </organization>
    <!--构建项目需要的信息 -->
    <build>
        <!--该元素设置了项目源码目录,当构建项目的时候,构建系统会编译目录里的源码。该路径是相对于pom.xml的相对路径。 -->
        <sourceDirectory />
        <!--该元素设置了项目脚本源码目录,该目录和源码目录不同:绝大多数情况下,该目录下的内容 会被拷贝到输出目录(因为脚本是被解释的,而不是被编译的)。 -->
        <scriptSourceDirectory />
        <!--该元素设置了项目单元测试使用的源码目录,当测试项目的时候,构建系统会编译目录里的源码。该路径是相对于pom.xml的相对路径。 -->
        <testSourceDirectory />
        <!--被编译过的应用程序class文件存放的目录。 -->
        <outputDirectory />
        <!--被编译过的测试class文件存放的目录。 -->
        <testOutputDirectory />
        <!--使用来自该项目的一系列构建扩展 -->
        <extensions>
            <!--描述使用到的构建扩展。 -->
            <extension>
                <!--构建扩展的groupId -->
                <groupId />
                <!--构建扩展的artifactId -->
                <artifactId />
                <!--构建扩展的版本 -->
                <version />
            </extension>
        </extensions>
        <!--当项目没有规定目标(Maven2 叫做阶段)时的默认值 -->
        <defaultGoal />
        <!--这个元素描述了项目相关的所有资源路径列表,例如和项目相关的属性文件,这些资源被包含在最终的打包文件里。 -->
        <resources>
            <!--这个元素描述了项目相关或测试相关的所有资源路径 -->
            <resource>
                <!-- 描述了资源的目标路径。该路径相对target/classes目录(例如${project.build.outputDirectory})。举个例 
                    子,如果你想资源在特定的包里(org.apache.maven.messages),你就必须该元素设置为org/apache/maven /messages。然而,如果你只是想把资源放到源码目录结构里,就不需要该配置。 -->
                <targetPath />
                <!--是否使用参数值代替参数名。参数值取自properties元素或者文件里配置的属性,文件在filters元素里列出。 -->
                <filtering />
                <!--描述存放资源的目录,该路径相对POM路径 -->
                <directory />
                <!--包含的模式列表,例如**/*.xml. -->
                <includes />
                <!--排除的模式列表,例如**/*.xml -->
                <excludes />
            </resource>
        </resources>
        <!--这个元素描述了单元测试相关的所有资源路径,例如和单元测试相关的属性文件。 -->
        <testResources>
            <!--这个元素描述了测试相关的所有资源路径,参见build/resources/resource元素的说明 -->
            <testResource>
                <targetPath />
                <filtering />
                <directory />
                <includes />
                <excludes />
            </testResource>
        </testResources>
        <!--构建产生的所有文件存放的目录 -->
        <directory />
        <!--产生的构件的文件名,默认值是${artifactId}-${version}。 -->
        <finalName />
        <!--当filtering开关打开时,使用到的过滤器属性文件列表 -->
        <filters />
        <!--子项目可以引用的默认插件信息。该插件配置项直到被引用时才会被解析或绑定到生命周期。给定插件的任何本地配置都会覆盖这里的配置 -->
        <pluginManagement>
            <!--使用的插件列表 。 -->
            <plugins>
                <!--plugin元素包含描述插件所需要的信息。 -->
                <plugin>
                    <!--插件在仓库里的group ID -->
                    <groupId />
                    <!--插件在仓库里的artifact ID -->
                    <artifactId />
                    <!--被使用的插件的版本(或版本范围) -->
                    <version />
                    <!--是否从该插件下载Maven扩展(例如打包和类型处理器),由于性能原因,只有在真需要下载时,该元素才被设置成enabled。 -->
                    <extensions />
                    <!--在构建生命周期中执行一组目标的配置。每个目标可能有不同的配置。 -->
                    <executions>
                        <!--execution元素包含了插件执行需要的信息 -->
                        <execution>
                            <!--执行目标的标识符,用于标识构建过程中的目标,或者匹配继承过程中需要合并的执行目标 -->
                            <id />
                            <!--绑定了目标的构建生命周期阶段,如果省略,目标会被绑定到源数据里配置的默认阶段 -->
                            <phase />
                            <!--配置的执行目标 -->
                            <goals />
                            <!--配置是否被传播到子POM -->
                            <inherited />
                            <!--作为DOM对象的配置 -->
                            <configuration />
                        </execution>
                    </executions>
                    <!--项目引入插件所需要的额外依赖 -->
                    <dependencies>
                        <!--参见dependencies/dependency元素 -->
                        <dependency>
                            ......
                        </dependency>
                    </dependencies>
                    <!--任何配置是否被传播到子项目 -->
                    <inherited />
                    <!--作为DOM对象的配置 -->
                    <configuration />
                </plugin>
            </plugins>
        </pluginManagement>
        <!--使用的插件列表 -->
        <plugins>
            <!--参见build/pluginManagement/plugins/plugin元素 -->
            <plugin>
                <groupId />
                <artifactId />
                <version />
                <extensions />
                <executions>
                    <execution>
                        <id />
                        <phase />
                        <goals />
                        <inherited />
                        <configuration />
                    </execution>
                </executions>
                <dependencies>
                    <!--参见dependencies/dependency元素 -->
                    <dependency>
                        ......
                    </dependency>
                </dependencies>
                <goals />
                <inherited />
                <configuration />
            </plugin>
        </plugins>
    </build>
    <!--在列的项目构建profile,如果被激活,会修改构建处理 -->
    <profiles>
        <!--根据环境参数或命令行参数激活某个构建处理 -->
        <profile>
            <!--构建配置的唯一标识符。即用于命令行激活,也用于在继承时合并具有相同标识符的profile。 -->
            <id />
            <!--自动触发profile的条件逻辑。Activation是profile的开启钥匙。profile的力量来自于它 能够在某些特定的环境中自动使用某些特定的值;这些环境通过activation元素指定。activation元素并不是激活profile的唯一方式。 -->
            <activation>
                <!--profile默认是否激活的标志 -->
                <activeByDefault />
                <!--当匹配的jdk被检测到,profile被激活。例如,1.4激活JDK1.4,1.4.0_2,而!1.4激活所有版本不是以1.4开头的JDK。 -->
                <jdk />
                <!--当匹配的操作系统属性被检测到,profile被激活。os元素可以定义一些操作系统相关的属性。 -->
                <os>
                    <!--激活profile的操作系统的名字 -->
                    <name>Windows XP</name>
                    <!--激活profile的操作系统所属家族(如 'windows') -->
                    <family>Windows</family>
                    <!--激活profile的操作系统体系结构 -->
                    <arch>x86</arch>
                    <!--激活profile的操作系统版本 -->
                    <version>5.1.2600</version>
                </os>
                <!--如果Maven检测到某一个属性(其值可以在POM中通过${名称}引用),其拥有对应的名称和值,Profile就会被激活。如果值 字段是空的,那么存在属性名称字段就会激活profile,否则按区分大小写方式匹配属性值字段 -->
                <property>
                    <!--激活profile的属性的名称 -->
                    <name>mavenVersion</name>
                    <!--激活profile的属性的值 -->
                    <value>2.0.3</value>
                </property>
                <!--提供一个文件名,通过检测该文件的存在或不存在来激活profile。missing检查文件是否存在,如果不存在则激活 profile。另一方面,exists则会检查文件是否存在,如果存在则激活profile。 -->
                <file>
                    <!--如果指定的文件存在,则激活profile。 -->
                    <exists>/usr/local/hudson/hudson-home/jobs/maven-guide-zh-to-production/workspace/
                    </exists>
                    <!--如果指定的文件不存在,则激活profile。 -->
                    <missing>/usr/local/hudson/hudson-home/jobs/maven-guide-zh-to-production/workspace/
                    </missing>
                </file>
            </activation>
            <!--构建项目所需要的信息。参见build元素 -->
            <build>
                <defaultGoal />
                <resources>
                    <resource>
                        <targetPath />
                        <filtering />
                        <directory />
                        <includes />
                        <excludes />
                    </resource>
                </resources>
                <testResources>
                    <testResource>
                        <targetPath />
                        <filtering />
                        <directory />
                        <includes />
                        <excludes />
                    </testResource>
                </testResources>
                <directory />
                <finalName />
                <filters />
                <pluginManagement>
                    <plugins>
                        <!--参见build/pluginManagement/plugins/plugin元素 -->
                        <plugin>
                            <groupId />
                            <artifactId />
                            <version />
                            <extensions />
                            <executions>
                                <execution>
                                    <id />
                                    <phase />
                                    <goals />
                                    <inherited />
                                    <configuration />
                                </execution>
                            </executions>
                            <dependencies>
                                <!--参见dependencies/dependency元素 -->
                                <dependency>
                                    ......
                                </dependency>
                            </dependencies>
                            <goals />
                            <inherited />
                            <configuration />
                        </plugin>
                    </plugins>
                </pluginManagement>
                <plugins>
                    <!--参见build/pluginManagement/plugins/plugin元素 -->
                    <plugin>
                        <groupId />
                        <artifactId />
                        <version />
                        <extensions />
                        <executions>
                            <execution>
                                <id />
                                <phase />
                                <goals />
                                <inherited />
                                <configuration />
                            </execution>
                        </executions>
                        <dependencies>
                            <!--参见dependencies/dependency元素 -->
                            <dependency>
                                ......
                            </dependency>
                        </dependencies>
                        <goals />
                        <inherited />
                        <configuration />
                    </plugin>
                </plugins>
            </build>
            <!--模块(有时称作子项目) 被构建成项目的一部分。列出的每个模块元素是指向该模块的目录的相对路径 -->
            <modules />
            <!--发现依赖和扩展的远程仓库列表。 -->
            <repositories>
                <!--参见repositories/repository元素 -->
                <repository>
                    <releases>
                        <enabled />
                        <updatePolicy />
                        <checksumPolicy />
                    </releases>
                    <snapshots>
                        <enabled />
                        <updatePolicy />
                        <checksumPolicy />
                    </snapshots>
                    <id />
                    <name />
                    <url />
                    <layout />
                </repository>
            </repositories>
            <!--发现插件的远程仓库列表,这些插件用于构建和报表 -->
            <pluginRepositories>
                <!--包含需要连接到远程插件仓库的信息.参见repositories/repository元素 -->
                <pluginRepository>
                    <releases>
                        <enabled />
                        <updatePolicy />
                        <checksumPolicy />
                    </releases>
                    <snapshots>
                        <enabled />
                        <updatePolicy />
                        <checksumPolicy />
                    </snapshots>
                    <id />
                    <name />
                    <url />
                    <layout />
                </pluginRepository>
            </pluginRepositories>
            <!--该元素描述了项目相关的所有依赖。 这些依赖组成了项目构建过程中的一个个环节。它们自动从项目定义的仓库中下载。要获取更多信息,请看项目依赖机制。 -->
            <dependencies>
                <!--参见dependencies/dependency元素 -->
                <dependency>
                    ......
                </dependency>
            </dependencies>
            <!--不赞成使用. 现在Maven忽略该元素. -->
            <reports />
            <!--该元素包括使用报表插件产生报表的规范。当用户执行"mvn site",这些报表就会运行。 在页面导航栏能看到所有报表的链接。参见reporting元素 -->
            <reporting>
                ......
            </reporting>
            <!--参见dependencyManagement元素 -->
            <dependencyManagement>
                <dependencies>
                    <!--参见dependencies/dependency元素 -->
                    <dependency>
                        ......
                    </dependency>
                </dependencies>
            </dependencyManagement>
            <!--参见distributionManagement元素 -->
            <distributionManagement>
                ......
            </distributionManagement>
            <!--参见properties元素 -->
            <properties />
        </profile>
    </profiles>
    <!--模块(有时称作子项目) 被构建成项目的一部分。列出的每个模块元素是指向该模块的目录的相对路径 -->
    <modules />
    <!--发现依赖和扩展的远程仓库列表。 -->
    <repositories>
        <!--包含需要连接到远程仓库的信息 -->
        <repository>
            <!--如何处理远程仓库里发布版本的下载 -->
            <releases>
                <!--true或者false表示该仓库是否为下载某种类型构件(发布版,快照版)开启。 -->
                <enabled />
                <!--该元素指定更新发生的频率。Maven会比较本地POM和远程POM的时间戳。这里的选项是:always(一直),daily(默认,每日),interval:X(这里X是以分钟为单位的时间间隔),或者never(从不)。 -->
                <updatePolicy />
                <!--当Maven验证构件校验文件失败时该怎么做:ignore(忽略),fail(失败),或者warn(警告)。 -->
                <checksumPolicy />
            </releases>
            <!-- 如何处理远程仓库里快照版本的下载。有了releases和snapshots这两组配置,POM就可以在每个单独的仓库中,为每种类型的构件采取不同的 
                策略。例如,可能有人会决定只为开发目的开启对快照版本下载的支持。参见repositories/repository/releases元素 -->
            <snapshots>
                <enabled />
                <updatePolicy />
                <checksumPolicy />
            </snapshots>
            <!--远程仓库唯一标识符。可以用来匹配在settings.xml文件里配置的远程仓库 -->
            <id>banseon-repository-proxy</id>
            <!--远程仓库名称 -->
            <name>banseon-repository-proxy</name>
            <!--远程仓库URL,按protocol://hostname/path形式 -->
            <url>http://192.168.1.169:9999/repository/</url>
            <!-- 用于定位和排序构件的仓库布局类型-可以是default(默认)或者legacy(遗留)。Maven 2为其仓库提供了一个默认的布局;然 
                而,Maven 1.x有一种不同的布局。我们可以使用该元素指定布局是default(默认)还是legacy(遗留)。 -->
            <layout>default</layout>
        </repository>
    </repositories>
    <!--发现插件的远程仓库列表,这些插件用于构建和报表 -->
    <pluginRepositories>
        <!--包含需要连接到远程插件仓库的信息.参见repositories/repository元素 -->
        <pluginRepository>
            ......
        </pluginRepository>
    </pluginRepositories>
 
 
    <!--该元素描述了项目相关的所有依赖。 这些依赖组成了项目构建过程中的一个个环节。它们自动从项目定义的仓库中下载。要获取更多信息,请看项目依赖机制。 -->
    <dependencies>
        <dependency>
            <!--依赖的group ID -->
            <groupId>org.apache.maven</groupId>
            <!--依赖的artifact ID -->
            <artifactId>maven-artifact</artifactId>
            <!--依赖的版本号。 在Maven 2里, 也可以配置成版本号的范围。 -->
            <version>3.8.1</version>
            <!-- 依赖类型,默认类型是jar。它通常表示依赖的文件的扩展名,但也有例外。一个类型可以被映射成另外一个扩展名或分类器。类型经常和使用的打包方式对应, 
                尽管这也有例外。一些类型的例子:jar,war,ejb-client和test-jar。如果设置extensions为 true,就可以在 plugin里定义新的类型。所以前面的类型的例子不完整。 -->
            <type>jar</type>
            <!-- 依赖的分类器。分类器可以区分属于同一个POM,但不同构建方式的构件。分类器名被附加到文件名的版本号后面。例如,如果你想要构建两个单独的构件成 
                JAR,一个使用Java 1.4编译器,另一个使用Java 6编译器,你就可以使用分类器来生成两个单独的JAR构件。 -->
            <classifier></classifier>
            <!--依赖范围。在项目发布过程中,帮助决定哪些构件被包括进来。欲知详情请参考依赖机制。 - compile :默认范围,用于编译 - provided:类似于编译,但支持你期待jdk或者容器提供,类似于classpath 
                - runtime: 在执行时需要使用 - test: 用于test任务时使用 - system: 需要外在提供相应的元素。通过systemPath来取得 
                - systemPath: 仅用于范围为system。提供相应的路径 - optional: 当项目自身被依赖时,标注依赖是否传递。用于连续依赖时使用 -->
            <scope>test</scope>
            <!--仅供system范围使用。注意,不鼓励使用这个元素,并且在新的版本中该元素可能被覆盖掉。该元素为依赖规定了文件系统上的路径。需要绝对路径而不是相对路径。推荐使用属性匹配绝对路径,例如${java.home}。 -->
            <systemPath></systemPath>
            <!--当计算传递依赖时, 从依赖构件列表里,列出被排除的依赖构件集。即告诉maven你只依赖指定的项目,不依赖项目的依赖。此元素主要用于解决版本冲突问题 -->
            <exclusions>
                <exclusion>
                    <artifactId>spring-core</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
            </exclusions>
            <!--可选依赖,如果你在项目B中把C依赖声明为可选,你就需要在依赖于B的项目(例如项目A)中显式的引用对C的依赖。可选依赖阻断依赖的传递性。 -->
            <optional>true</optional>
        </dependency>
    </dependencies>
    <!--不赞成使用. 现在Maven忽略该元素. -->
    <reports></reports>
    <!--该元素描述使用报表插件产生报表的规范。当用户执行"mvn site",这些报表就会运行。 在页面导航栏能看到所有报表的链接。 -->
    <reporting>
        <!--true,则,网站不包括默认的报表。这包括"项目信息"菜单中的报表。 -->
        <excludeDefaults />
        <!--所有产生的报表存放到哪里。默认值是${project.build.directory}/site。 -->
        <outputDirectory />
        <!--使用的报表插件和他们的配置。 -->
        <plugins>
            <!--plugin元素包含描述报表插件需要的信息 -->
            <plugin>
                <!--报表插件在仓库里的group ID -->
                <groupId />
                <!--报表插件在仓库里的artifact ID -->
                <artifactId />
                <!--被使用的报表插件的版本(或版本范围) -->
                <version />
                <!--任何配置是否被传播到子项目 -->
                <inherited />
                <!--报表插件的配置 -->
                <configuration />
                <!--一组报表的多重规范,每个规范可能有不同的配置。一个规范(报表集)对应一个执行目标 。例如,有1,2,3,4,5,6,7,8,9个报表。1,2,5构成A报表集,对应一个执行目标。2,5,8构成B报表集,对应另一个执行目标 -->
                <reportSets>
                    <!--表示报表的一个集合,以及产生该集合的配置 -->
                    <reportSet>
                        <!--报表集合的唯一标识符,POM继承时用到 -->
                        <id />
                        <!--产生报表集合时,被使用的报表的配置 -->
                        <configuration />
                        <!--配置是否被继承到子POMs -->
                        <inherited />
                        <!--这个集合里使用到哪些报表 -->
                        <reports />
                    </reportSet>
                </reportSets>
            </plugin>
        </plugins>
    </reporting>
    <!-- 继承自该项目的所有子项目的默认依赖信息。这部分的依赖信息不会被立即解析,而是当子项目声明一个依赖(必须描述group ID和 artifact 
        ID信息),如果group ID和artifact ID以外的一些信息没有描述,则通过group ID和artifact ID 匹配到这里的依赖,并使用这里的依赖信息。 -->
    <dependencyManagement>
        <dependencies>
            <!--参见dependencies/dependency元素 -->
            <dependency>
                ......
            </dependency>
        </dependencies>
    </dependencyManagement>
    <!--项目分发信息,在执行mvn deploy后表示要发布的位置。有了这些信息就可以把网站部署到远程服务器或者把构件部署到远程仓库。 -->
    <distributionManagement>
        <!--部署项目产生的构件到远程仓库需要的信息 -->
        <repository>
            <!--是分配给快照一个唯一的版本号(由时间戳和构建流水号)?还是每次都使用相同的版本号?参见repositories/repository元素 -->
            <uniqueVersion />
            <id>banseon-maven2</id>
            <name>banseon maven2</name>
            <url>file://${basedir}/target/deploy</url>
            <layout />
        </repository>
        <!--构件的快照部署到哪里?如果没有配置该元素,默认部署到repository元素配置的仓库,参见distributionManagement/repository元素 -->
        <snapshotRepository>
            <uniqueVersion />
            <id>banseon-maven2</id>
            <name>Banseon-maven2 Snapshot Repository</name>
            <url>scp://svn.baidu.com/banseon:/usr/local/maven-snapshot</url>
            <layout />
        </snapshotRepository>
        <!--部署项目的网站需要的信息 -->
        <site>
            <!--部署位置的唯一标识符,用来匹配站点和settings.xml文件里的配置 -->
            <id>banseon-site</id>
            <!--部署位置的名称 -->
            <name>business api website</name>
            <!--部署位置的URL,按protocol://hostname/path形式 -->
            <url>
                scp://svn.baidu.com/banseon:/var/www/localhost/banseon-web
            </url>
        </site>
        <!--项目下载页面的URL。如果没有该元素,用户应该参考主页。使用该元素的原因是:帮助定位那些不在仓库里的构件(由于license限制)。 -->
        <downloadUrl />
        <!--如果构件有了新的group ID和artifact ID(构件移到了新的位置),这里列出构件的重定位信息。 -->
        <relocation>
            <!--构件新的group ID -->
            <groupId />
            <!--构件新的artifact ID -->
            <artifactId />
            <!--构件新的版本号 -->
            <version />
            <!--显示给用户的,关于移动的额外信息,例如原因。 -->
            <message />
        </relocation>
        <!-- 给出该构件在远程仓库的状态。不得在本地项目中设置该元素,因为这是工具自动更新的。有效的值有:none(默认),converted(仓库管理员从 
            Maven 1 POM转换过来),partner(直接从伙伴Maven 2仓库同步过来),deployed(从Maven 2实例部 署),verified(被核实时正确的和最终的)。 -->
        <status />
    </distributionManagement>
    <!--以值替代名称,Properties可以在整个POM中使用,也可以作为触发条件(见settings.xml配置文件里activation元素的说明)。格式是<name>value</name>。 -->
    <properties />
</project>

6.Maven 的常用命令

Maven对所有的功能都提供相对应的命令,maven三大功能:管理依赖、构建项目、管理依赖。只需要声明就可以自动到仓库下载:管理项目信息其实就是生成一个站点文档,一个命令就可以解决,最后再说:maven功能的主体其实就是项目构建

Maven提供一个项目构建的模型,把编译、测试、打包、部署等都对应成一个个的生命周期阶段,并对每个阶段提供相对应的命令,程序员只需要掌握一点命令,就可以完成项目的构建过程

mvn clean 清理(会删除原来编译和测试的目录,即target目录,但是已经install到仓库的包不会删除)

mvn compile 编译主程序(会在当前目录下生成一个target,里边存放编译主程序之后生成的字节码文件 target/classes)

mvn test-compile 编译测试程序(会在当前目录下生成一个target,里边存放编译测试程序之后生成的字节码文件,target/test-classes)

mvn test 测试(会生成一个目录surefire-reports,保存测试结果)

mvn package 打包主程序(会编译、编译测试、测试,并且按照pom.xml配置把主程序打包生成jar包或者war包)

mvn install 安装主程序(会把本工程打包,并且按照本工程的坐标保存到本地仓库中)

mvn deploy 部署主程序(会把本工程打包,按照本工程的坐标保存到本地库中,并且还会保存到私服仓库中,还会自动把项目部署到web容器中)

注意:执行上命令必须在命令行进入pom.xml所在目录

7.依赖范围

使用表示

scope的值有compile,test,provided,默认是compile

scope:表示依赖使用的范围,也就是在maven构建项目的那些阶段有用,

compile test provided
对主程序是否有效
对测试程序是否有效
是否参与打包
是否参与部署

8.Maven 常用设置

1.maven的属性设置

设置maven的常用属性

2.maven的全局变量

​ 自定义的属性: 1.在通过自定义标签声明变量(标签名就是变量名)

​ 2.在pom.xml文件中的其他位置,使用${标签名}使用变量的值

自定义全局变量一般是定义 依赖的版本号,当你的项目中要使用多个相同的版本号,先使用全局变量定义,在使用${变量名}

3.资源插件

<build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <!-- 包括目录下的.properties.xml 文件都会被扫描到 -->
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                </includes>
                <!-- filtering选项false不器用过滤器,*.properties已经起到了过滤的作用了 -->
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

1.默认没有使用resources的时候,maven执行编译代码时,会把src/main/resources目录中的文件拷贝到target/classes目录中.src/main/java目录下的非java文件不处理,不拷贝到target/classes目录中

2.我们的程序有需要把一些文件存放在src/main/java目录中,当我在执行程序时,需要用到src/main/java目录中的文件.需要告诉maven在mvn src/main/java目录下的程序时,需要把文件一同拷贝到target/classes目录中.此时就需要在中加入

Nginx

1.什么是Nginx?

Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。2011年6月1日,nginx 1.0.4发布。

其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。在全球活跃的网站中有12.18%的使用比率,大约为2220万个网站。

Nginx 是一个安装非常的简单、配置文件非常简洁(还能够支持perl语法)、Bug非常少的服务。Nginx 启动特别容易,并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动。你还能够不间断服务的情况下进行软件版本的升级。

Nginx代码完全用C语言从头写成。官方数据测试表明能够支持高达 50,000个并发连接数的响应。

Nginx由内核和模块组成。

  Nginx本身做的工作实际很少,当它接到一个HTTP请求时,它仅仅是通过查找配置文件将此次请求映射到一个location block,而此location中所配置的各个指令则会启动不同的模块去完成工作,因此模块可以看做Nginx真正的劳动工作者。通常一个location中的指令会涉及一个handler模块和多个filter模块(当然,多个location可以复用同一个模块)。handler模块负责处理请求,完成响应内容的生成,而filter模块对响应内容进行处理。

用户根据自己的需要开发的模块都属于第三方模块。正是有了这么多模块的支撑,Nginx的功能才会如此强大。

Nginx的模块从结构上分为核心模块、基础模块和第三方模块:

  • 核心模块:HTTP模块、EVENT模块和MAIL模块
  • 基础模块:HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块,
  • 第三方模块:HTTP Upstream Request Hash模块、Notice模块和HTTP Access Key模块。

Nginx的模块从功能上分为如下三类:

  • Handlers(处理器模块)。此类模块直接处理请求,并进行输出内容和修改headers信息等操作。Handlers处理器模块一般只能有一个。
  • Filters (过滤器模块)。此类模块主要对其他处理器模块输出的内容进行修改操作,最后由Nginx输出。
  • Proxies (代理类模块)。此类模块是Nginx的HTTP Upstream之类的模块,这些模块主要与后端一些服务比如FastCGI等进行交互,实现服务代理和负载均衡等功能。

image-20210803082839349

2.Nginx的作用

Http代理,反向代理: 作为web服务器最常用的功能之一,尤其是反向代理.

正向代理

代理客户端的 正向代理

反向代理

代理服务端的 反向代理

Nginx提供的负载均衡策略有2种:内置策略和扩展策略。内置策略为轮询,加权轮询,Ip hash。扩展策略,就天马行空,只有你想不到的没有他做不到的。

轮询

加权轮询(给服务器加权重以控制访问量或者说比例 weight)

iphash对客户端请求的ip进行hash操作,然后根据hash结果将同一个客户端ip的请求分发给同一台服务器进行处理,可以解决session不共享的问题。

动静分离,在我们的权健开发过程中,有些请求是需要后台处理的,有些请求是不需要经过后台处理的(如:css、html、jpg、js等等文件),这些不需要经过后台处理的文件称为静态文件。让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作。提高资源响应的速度。

3.配置介绍

# 1.全局配置start
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

# 全局配置end


# 2.最大监听数配置 start
events {
    worker_connections  1024;
}
# 最大监听数配置 end

# http配置 start
http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
    
	# 负载均衡配置  weight=1 配置权重
	upstream tomcats {
	 #默认轮询方式
            #fail_timeout	与max_fails结合使用
                #Nginx基于连接探测,如果发现后端异常,在单位周期为fail_timeout设置的时间,中达到max_fails次数,这个周期次数内,如果后端同一个节点不可用,
                #那么接将把节点标记为不可用,并等待下一个周期(同样时常为fail_timeout)再一次去请求,判断是否连接是否成功。如果成功,将恢复之前的轮询方式,
                #如果不可用将在下一个周期(fail_timeout)再试一次。
            #max_fails	设置在fail_timeout参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被认为是停机了
            #fail_time	服务器会被认为停机的时间长度,默认为10s
            #backup	标记该服务器为备用服务器。当主服务器停止时,请求会被发送到它这里
            #down	标记服务器永久停机了
		ip_hash;#同一个ip定位到同一个服务器
		least_conn;#把请求转发给连接数较少的后端服务器
		#第三方
		hash $request_uri;#同一个url定位到同一个服务器
		fair;#转发给连接最少的服务器
	
		server ip:port;
		server ip:port;
		server ip:port;
		server ip:port;
	}

    server {
        listen       80;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            proxy_pass http://tomcats;
        }
        
        # 配置端口下 根路径admin下的请求
        # location /admin {
        #     proxy_pass http://tomcats;
        # }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}
# http配置 end
# 演示
upstream lb{
    server 127.0.0.1:8080 weight=1;
    server 127.0.0.1:8081 weight=1;
}
location / {
    proxy_pass http://lb;
}

Docker

1.介绍和目的

Docker时Docker.Lnc公司开源的一个基于LXC技术之上搭建的Container容器引擎,源代码托管在Github上,基于Go语言并遵从Apache2.0协议开源。
Docker属于Linux容器的一种封装,提供简单易用的容器使用接口。
Docker将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了Docker,就不用担心环境问题。
总体来说,Docker的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。

Docker的主要用途,目前又三大类:

提供了一次性的环境:比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。
提供弹性的云服务:因为Docker容器可以随开随关,很适合动态扩容和所容。
组建微服务架构:通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。

文档地址: https://docs.docker.com/

https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi.loli.net%2F2020%2F08%2F01%2F9VSxDTPkYJ5n6eZ.png&refer=http%3A%2F%2Fi.loli.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1625276689&t=df674edbbf4cf7e3f84b69081175e8c9

2.阿里云镜像加速

1.登录阿里云--容器镜像服务-镜像工具-镜像加速

#  找到对应的系统(这里是centOs) 四步操作
sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["自己的地址"]
}
EOF

sudo systemctl daemon-reload

sudo systemctl restart docker

3.底层原理

Docker是怎么工作的?

Docker 是一个C/S结构,Docker的守护进程运行在主机上.通过Socker从客户端访问!

DockerServer接受到Docker-Client的指令,就会执行这个命令!

4.Docker的常用命令

1.帮助命令

docker version      # 显示docker的版本信息
docker info			# 显示docker的系统信息,包括镜像和容器的数量
docker 命令 --help       # 帮助命令

帮助文档:https://docs.docker.com/reference/

2.镜像命令

  • docker images
docker images # 查看所有的本地镜像

[root@VM_0_9_centos ~]# docker images
REPOSITORY     TAG       IMAGE ID       CREATED        SIZE
hello-world    latest    d1165f221234   2 months ago   13.3kB

REPOSITORY 镜像的仓库源
TAG		   镜像的标签
IMAGE ID   镜像的id
CREATED    镜像的创建时间
SIZE       镜像的大小

#可选项
Options:
  -a, --all             Show all images (default hides intermediate images) # 显示所有的信息
      --digests         Show digests
  -f, --filter filter   Filter output based on conditions provided
      --format string   Pretty-print images using a Go template
      --no-trunc        Don't truncate output
  -q, --quiet           Only show image IDs   								# 只显示id
  • docker search xx
# 搜索
[root@VM_0_9_centos ~]# docker search mysql
NAME                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql                             MySQL is a widely used, open-source relation…   10940     [OK]       
mariadb                           MariaDB Server is a high performing open sou…   4137      [OK]       
mysql/mysql-server                Optimized MySQL Server Docker images. Create…   812   


## 可选  --filter过滤
[root@VM_0_9_centos ~]# docker search mysql --filter=STARS=300
NAME                 DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql                MySQL is a widely used, open-source relation…   10940     [OK]       
mariadb              MariaDB Server is a high performing open sou…   4137      [OK]       
mysql/mysql-server   Optimized MySQL Server Docker images. Create…   812                  [OK]
  • docker pull xx
# 拉取命令 下载镜像
docker pull 镜像名:[tag]
[root@VM_0_9_centos ~]# docker pull mysql
Using default tag: latest  # 不写tag,默认就是latest
latest: Pulling from library/mysql
69692152171a: Pull complete  #分层下载  docker image的核心  联合文件系统
1651b0be3df3: Pull complete 
951da7386bc8: Pull complete 
0f86c95aa242: Pull complete 
37ba2d8bd4fe: Pull complete 
6d278bb05e94: Pull complete 
497efbd93a3e: Pull complete 
f7fddf10c2c2: Pull complete 
16415d159dfb: Pull complete 
0e530ffc6b73: Pull complete 
b0a4a1a77178: Pull complete 
cd90f92aa9ef: Pull complete 
Digest: sha256:d50098d7fcb25b1fcb24e2d3247cae3fc55815d64fec640dc395840f8fa80969
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest  #真实地址

# 等价于它
docker pull mysql
docker pull docker.io/library/mysql:latest

# 指定版本下载
[root@VM_0_9_centos ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
69692152171a: Already exists 
1651b0be3df3: Already exists 
951da7386bc8: Already exists 
0f86c95aa242: Already exists 
37ba2d8bd4fe: Already exists 
6d278bb05e94: Already exists 
497efbd93a3e: Already exists 
a023ae82eef5: Pull complete 
e76c35f20ee7: Pull complete 
e887524d2ef9: Pull complete 
ccb65627e1c3: Pull complete 
Digest: sha256:a682e3c78fc5bd941e9db080b4796c75f69a28a8cad65677c23f7a9f18ba21fa
Status: Downloaded newer image for mysql:5.7
  • docker rmi -f
# 根据镜像id删除镜像
[root@VM_0_9_centos ~]# docker rmi -f 镜像id
# 删除多个镜像
[root@VM_0_9_centos ~]# docker rmi -f 镜像id 镜像id 镜像id 镜像id 镜像id
# 删除所有的镜像
[root@VM_0_9_centos ~]# docker rmi -f $(docker images -aq)

3.容器命令

说明:我们有了镜像才可以创建容器,linux,下载一个centos镜像来测试学习

docker pull centos

新建容器并启动

docker run [可选参数] image

# 参数说明
--name="name"  容器名字 例如:tomcat1 tomcat2 来区分容器
-d    		   后台方式运行
-it 		   使用交互方式运行,进入容器查看内容
-p 			   指定容器的端口号 -p 8080:8080
	-p ip:主机端口:容器端口
	-p 主机端口:容器端口(常用)
	-p 容器端口
	容器端口
-p   随机端口


# 测试  启动并进入容器
[root@VM_0_9_centos ~]# docker run -it centos /bin/bash
[root@d180385f5cec /]# ls  查看容器里面的东西
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@d180385f5cec /]# exit  # 退出容器

列出所有的启动的容器

# docker ps 
	#列出正在运行的容器
-a  #列出当前正在运行的容器+历史运行过的容器
-n=?#显示最近创建的容器
-q  #只显示容器的编号



[root@VM_0_9_centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
[root@VM_0_9_centos ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS                          PORTS     NAMES
d180385f5cec   centos         "/bin/bash"   5 minutes ago   Exited (0) About a minute ago             distracted_jang
c87b560d3b5f   d1165f221234   "/hello"      6 hours ago     Exited (0) 6 hours ago                    sad_curie
923e661230f6   aa0a17bb9333   "./red5.sh"   2 weeks ago     Exited (143) 7 hours ago                  red5

退出容器

#直接容器停止并退出
[root@d180385f5cec /]# exit  # 退出容器
# 容器不停止 退出
Ctrl + P + Q

删除容器

docker rm 容器id  # 删除指定的容器,不能删除正在运行的容器,如果强制删除 rm -f
docker rm -f $(docker ps -aq)  #删除所有的容器
docker ps -a -q|xargs docker rm #删除所有的容器

启动和停止的操作

docker start 容器id  #启动容器
docker restart 容器id #重启容器id
docker stop 容器id   #停止当前正在运行的容器
docker kill 容器id   #强制停止当前容器

4.常用的其他命令

后台启动

# 命令 docker run -d 镜像名
[root@VM_0_9_centos ~]# docker run -d centos

#常见的坑  docker容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止
# nginx 容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了

查看日志

docker logs -f -t --tail 10 容器id
-tf #显示日志
--tail number # 显示日志的条数
[root@VM_0_9_centos ~]# docker logs -f -t --tail 10 0ee023f92396

查看容器中进程信息

# 命令 dokcer top 容器id
[root@VM_0_9_centos ~]# docker top 0ee023f92396
UID                 PID                 PPID                C                   STIME               TTY                 TIME    
root                4047                4026                0                   Jun02               pts/0               00:00:00 

查看容器的元数据

# 命令 docker inspect 容器id

[root@VM_0_9_centos ~]# docker inspect 0ee023f92396
[
    {
        "Id": "0ee023f923961f20acaefdd932397f560615e1d40d7fa08c297b7b7e52331ff2",
        "Created": "2021-06-02T09:59:58.672333184Z",
        "Path": "/bin/bash",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 4047,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2021-06-02T09:59:59.005481979Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:300e315adb2f96afe5f0b2780b87f28ae95231fe3bdd1e16b9ba606307728f55",
        "ResolvConfPath": "/var/lib/docker/containers/0ee023f923961f20acaefdd932397f560615e1d40d7fa08c297b7b7e52331ff2/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/0ee023f923961f20acaefdd932397f560615e1d40d7fa08c297b7b7e52331ff2/hostname",
        "HostsPath": "/var/lib/docker/containers/0ee023f923961f20acaefdd932397f560615e1d40d7fa08c297b7b7e52331ff2/hosts",
        "LogPath": "/var/lib/docker/containers/0ee023f923961f20acaefdd932397f560615e1d40d7fa08c297b7b7e52331ff2/0ee023f923961f20acaefdd932397f560615e1d40d7fa08c297b7b7e52331ff2-json.log",
        "Name": "/peaceful_blackburn",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "CgroupnsMode": "host",
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/8e5ba7bcd3b6a6d32905ebd9d23ec870954bc46b27c39391b1c4cb0605663c58-init/diff:/var/lib/docker/overlay2/4c0603d4c2e6360f9d5ce2181d269833b93a3f2603cec58185a77aacfa384070/diff",
                "MergedDir": "/var/lib/docker/overlay2/8e5ba7bcd3b6a6d32905ebd9d23ec870954bc46b27c39391b1c4cb0605663c58/merged",
                "UpperDir": "/var/lib/docker/overlay2/8e5ba7bcd3b6a6d32905ebd9d23ec870954bc46b27c39391b1c4cb0605663c58/diff",
                "WorkDir": "/var/lib/docker/overlay2/8e5ba7bcd3b6a6d32905ebd9d23ec870954bc46b27c39391b1c4cb0605663c58/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "0ee023f92396",
            "Domainname": "",
            "User": "",
            "AttachStdin": true,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": true,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/bash"
            ],
            "Image": "centos",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "org.label-schema.build-date": "20201204",
                "org.label-schema.license": "GPLv2",
                "org.label-schema.name": "CentOS Base Image",
                "org.label-schema.schema-version": "1.0",
                "org.label-schema.vendor": "CentOS"
            }
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "a4def5208a143efd3dd3b3f3d0ab36fe91465fd1f70924dc13b9a960ca7c20c4",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/a4def5208a14",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "d3058b9f623c2b55a2135f257cd377eb5c87f41753f78a39d0d911db1c6d337a",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "72e66206b5244f61efa654e11e13609d828e7f7247e6fb6c31b217db4e137087",
                    "EndpointID": "d3058b9f623c2b55a2135f257cd377eb5c87f41753f78a39d0d911db1c6d337a",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]

进入当前正在运行的容器

# 我们通常容器都是后台方式运行,需要进入容器,修改一些配置

# 命令方式一
docker exec -it 镜像id bashshell

[root@VM_0_9_centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED        STATUS        PORTS     NAMES
0ee023f92396   centos    "/bin/bash"   15 hours ago   Up 15 hours             peaceful_blackburn
[root@VM_0_9_centos ~]# docker exec -it 0ee023f92396 /bin/bash
[root@0ee023f92396 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@0ee023f92396 /]# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Jun02 pts/0    00:00:00 /bin/bash
root        15     0  0 01:12 pts/1    00:00:00 /bin/bash
root        30    15  0 01:12 pts/1    00:00:00 ps -ef

# 命令二
docker attach 容器id
[root@VM_0_9_centos ~]# docker attach 0ee023f92396

# 区别 
#docker exec 进入容器后开启一个新的中断,可以在里面操作(常用)
#docker attach  进入容器正在执行的终端,不会启动新的进程

从容器内拷贝到主机上

docker cp 容器id:容器卷内路径 目的主机路径

# 启动容器
[root@VM_0_9_centos ~]# docker run -d centos
de91a07f3dcac9c8c71944734f15bbe66d269f641e82dbf9c5c58bae30bfc33f
[root@VM_0_9_centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
[root@VM_0_9_centos ~]# docker run -it centos
[root@fb2ca96f41ef /]# [root@VM_0_9_centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS         PORTS     NAMES
fb2ca96f41ef   centos    "/bin/bash"   6 seconds ago   Up 5 seconds             magical_franklin
# 进入容器
[root@VM_0_9_centos ~]# docker attach fb2ca96f41ef
[root@fb2ca96f41ef /]# cd /home
[root@fb2ca96f41ef home]# touch test.java
[root@fb2ca96f41ef home]# exit
exit
[root@VM_0_9_centos ~]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
[root@VM_0_9_centos ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED              STATUS                      PORTS     NAMES
fb2ca96f41ef   centos    "/bin/bash"   About a minute ago   Exited (0) 27 seconds ago             magical_franklin
de91a07f3dca   centos    "/bin/bash"   2 minutes ago        Exited (0) 2 minutes ago              great_hofstadter
6c203ed5a0b1   centos    "/bin/bash"   49 minutes ago       Exited (0) 2 minutes ago              epic_sanderson
0ee023f92396   centos    "/bin/bash"   16 hours ago         Exited (0) 17 minutes ago             peaceful_blackburn
# 拷贝文件到主机上
[root@VM_0_9_centos ~]# docker cp fb2ca96f41ef:/home/test.java /home
[root@VM_0_9_centos ~]# cd home
-bash: cd: home: No such file or directory
[root@VM_0_9_centos ~]# cd /home
[root@VM_0_9_centos home]# ls
mysql  test.java

5.Docker镜像讲解

1.镜像是什么

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。

所有的应用,直接打包docker镜像,就可以直接跑起来

2.Docker镜像加载原理

UnionFS(联合文件系统)

UnionFS(联合文件系统):UnionFS(联合文件系统)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层叠加,同时可以将不同日志挂载到同一个虚拟文件系统下(unite seceral directories into a single virtual filesystem). Union文件系统是Docker镜像的基础.镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像

特性: 一次同时加载多个文件系统,但从外面看来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包括所有底层的文件和目录.

Docker镜像加载原理

docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS

bootfs(boot file system) 主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载到bootfs文件系统,在Docker镜像的最底层是bootfs,这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核.当boot加载完成之后整个内核都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs.

rootfs(root file system),在bootfs之上,包含的就是典型Linux系统中的/dev/,/proc,/etc等标准目录和文件,rootfs就是各种不同的操作系统发型版,比如Ubuntu,CentOS等等.

对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的Kermel,自己只需要提供rootfs就可以了.由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs.

3.分层理解

分层镜像

查看镜像分层的方式可以通过 docker image inspect命令!

[root@VM_0_9_centos ~]# docker image inspect redis:latest
[
    //......
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:02c055ef67f5904019f43a41ea5f099996d8e7633749b6e606c400526b2c4b33",
                "sha256:ec5652c3523d96657d66169c0eb71b572ff065711c705a15ec02f60a21c212c3",
                "sha256:76d3e24d63f60e6a73af70be15959eb4021dd7a5a09da6925037d3b4a1673fca",
                "sha256:f06719b0aa43029f32c821c8f14f9f5941a8be6d3b61dcd9f3f884b39e9a4f23",
                "sha256:b896f490f2edc62cc9d190465bbeab871619590d1e9beeffb92e4ca9cc08116d",
                "sha256:e3f4077f577bf07c0940d6345ddd17014ff824d3f4f7f3effc9a8c4dae3e527b"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

特点

Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!

这一层就是我们通常说容器层,容器之下的都叫镜像层!

4.commit镜像

docker commit 提交容器成为一个新的副本

# 命令和git原理类似
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]

实战测试

# 启动一个默认的tomcat
[root@VM_0_9_centos ~]# docker ps -a
CONTAINER ID   IMAGE                 COMMAND                  CREATED        STATUS                      PORTS     NAMES
3763ffb4d694   portainer/portainer   "/portainer"             18 hours ago   Exited (2) 17 hours ago               exciting_williamson
8ef606329603   c43a65faae57          "catalina.sh run"        23 hours ago   Exited (143) 23 hours ago             tomcat1
[root@VM_0_9_centos ~]# docker start 8ef606329603
8ef606329603
# 发现这个默认的tomcat是没有webapps应用,镜像的原因,官方的镜像默认webapps下面是没有文件的
# 拷贝基本文件
cp -r webapps.dist/* webapps

# 生成新的镜像
[root@VM_0_9_centos ~]# docker commit -a="zhonglao" -m="add webapps app" 8ef606329603 tomcatwebapps:1.0.0
[root@VM_0_9_centos ~]# docker images
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
tomcatwebapps         1.0.0     6dd55ed9bbe1   7 seconds ago   672MB
tomcat                latest    c43a65faae57   3 weeks ago     667MB

6.容器数据卷

1.什么是容器数据卷?

容器的持久化和同步操作!容器间也是可以数据共享的!

2.使用数据卷

方式一:直接使用命令挂载 -v

docker run -it -v 主机目录:容器内目录

# 测试   双向绑定的  意味着主机目录中文件变动容器内会变,反之容器内变,主机也会跟着变.
[root@VM_0_9_centos home]# docker run -it -v /home/ceshi:/home centos /bin/bash
# 查看信息
[root@VM_0_9_centos home]# docker inspect eb0277415bd7
[
		//......
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/home/ceshi",  # 主机的地址
                "Destination": "/home",   # 容器内的地址
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
		......
]

好处:在主机修改 ,容器内的配置会跟着同步,不用每次进入容器中修改.

3.实战 安装mysql

# 获取镜像
docker pull mysql:5.7

#运行容器,需要做数据挂载 # 安装启动mysql,需要配置密码,这是要注意的
#官方测试:docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-sercet-pw -d mysql:tag

# 启动我们的
[root@VM_0_9_centos /]# docker run -d -p 3310:3306 -v /home/mysql:/etc/mysql/conf.d -v/home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSEORD=123456 --name mysql1 mysql:5.7

就算删除了容器,挂载在本地的数据卷依旧没有丢失,这就实现了数据的持久化!

具名和匿名挂载

# 匿名挂载
-v 容器内路径
[root@VM_0_9_centos ~]# docker run -d -p 8200:80  --name nginx10 -v /etc/nginx nginx

# 查看卷信息
[root@VM_0_9_centos ~]# docker volume ls
DRIVER    VOLUME NAME
local     5f7fb7e22e1a3f106993a1b9fce407acdbd9058ac31c9363b9e2da685ecbdcbd
# 我们在-v 只写了容器内的路径,没有写对应的主机路径

# 具名挂载
[root@VM_0_9_centos ~]# docker run -d -p 8201:80  --name nginx11 -v nginx-config:/etc/nginx nginx
[root@VM_0_9_centos ~]# docker volume ls
DRIVER    VOLUME NAME
local     nginx-config
# 通过 -v 卷名:容器内路径
# 查看这个卷在哪个位置
[root@VM_0_9_centos ~]# docker volume inspect nginx-config
[
    {
        "CreatedAt": "2021-06-04T11:51:13+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/nginx-config/_data",
        "Name": "nginx-config",
        "Options": null,
        "Scope": "local"
    }
]

没有指定挂载的主机位置,默认的都是在/var/lib/docker/volumes/xxx/_data目录之下

通过具名挂载可以方便的找到我们的卷,大多数情况在使用的具名挂载

# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载
-v 容器内路径  # 匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:容器内路径  #指定路径挂载

拓展

# 通过 -v 容器内路径:ro rw改变读写权限
ro readonly #只读
rw readwrdite #可读可写

# 一旦这个设置了容器 容器对我们挂载出来的内容就有限定了
docker run -d -p --name nginx2 -v juming-ngin:ro nginx
docker run -d -p --name nginx2 -v juming-ngin:rw nginx

# ro 只要看到ro就说明这个路径 真能通过宿主机来操作,容器内就无法操作了

4.初始DockerFile

通过这个脚本可以生成镜像

# 创建一个dockerfile文件
# 内容 指令和参数
FROM centos

VOLUME ["volume01","volume02"]

CMD echo "--------end-------------"

CMD /bin/bash

构建镜像

# -f dockerfile的路径
[root@VM_0_9_centos ~]# docker build -f /home/docker-test-volume/dockerfile1 -t zhonglao/centos .


# 查看挂载信息
docker image 镜像id

这种方式未来使用的,也是十分多,之后我们也会经常创建.

如果 在构建镜像的时候没有挂载卷,要手动进行挂载 -v 卷名:容器内路径!

5.数据卷容器

# 多个mysql或redis 实现数据共享
[root@VM_0_9_centos ~]# docker run -it --name docker-centos --volumes-from eb0277415bd7 centos

# 启动的第一个centos容器 eb0277415bd7
[root@VM_0_9_centos ~]# docker inspect eb0277415bd7
[
    {
        "Id": "eb0277415bd7483f4d69159461923b092dd8e3a45bc23e35f17d6710702e7b85",
		......
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/home/ceshi",
                "Destination": "/home",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
        .......
    }
]
# 第二个centos 挂载在第一上面 会发现集成了上面的挂载文件
[root@VM_0_9_centos ~]# docker inspect c4c7107755a6
[
    {
        "Id": "c4c7107755a6ae7246abf062d8d3c5e58b663e930c0d64e80eef541eb8e58a8a",
        ......
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/home/ceshi",
                "Destination": "/home",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
        ......
        }
    }
]

多个mysql 实现数据共享

docker run -d -p 3310:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7

docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql:5.7

# 这个时候.可以实现两个容器数据同步.

结论:

容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用.

但是一旦你持续话到了本地,这个时候,本地的数据是不会删除的!

7.DockerFile

1.DockerFile介绍

docker 是用来构建docker镜像的文件!命令参数脚本!

构建步骤:

1.编写一个dockerfile 文件

2.docker build构建成为一个镜像

3.docker run 运行镜像

4.docker push 发布镜像(DockerHub 阿里云镜像仓库)

2.DockerFile构建过程

基础知识

  • 1.每个保留关键字(指令)都是必须是大写字母;
  • 2.执行从上到下顺序执行
  • 3.# 表示注释
  • 4.每个指令都会创建提交一个新的镜像层,并提交!

DockerFile: 构建文件,定义了一切的步骤,源代码

DockerImages:通过DockerFile构建生成的镜像,最终发布和运行的产品

Docker容器:容器就是镜像运行起来提供服务

3.DockerFile 指令

FROM             # 基础镜像,一切从这里开始构建
MAINTAINER       # 镜像是谁写的,姓名+邮箱
RUN              # 镜像构建的时候需要运行的命令
ADD              # 步骤: tomcat镜像,这个tomcat压缩包!添加内容
WORKDIR			 # 镜像的工作目录
VOLUME           # 挂载的目录
EXPOSE      	 # 暴露端口配置
CMD              # 指定这个容器启动的时候要运行的命令,之后最后一个生效
ENTRYPOINT       # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD		     # 当构建一个被集成 Dockerfile 这个命令就会运行ONBUILD的指令,触发指令
COPY             # 类似ADD,将我们文件拷贝到镜像中
ENV				 # 构建的时候设置环境变量

4.测试创建属于自己的centos

# 1.创建dockerfile文件
[root@VM_0_9_centos dockerfile]# cat mydockerfile 
FROM centos
MAINTAINER zhonglao<zhonglaoxy@qq.com>

ENV MYPATH /usr/local
WORKDIR  $MYPATH

RUN yum -y install vim
RUN yum -y install net-tools

EXPOSE 80

CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash


# 2.编译构建
[root@VM_0_9_centos dockerfile]# docker build -f mydockerfile -t mycentos:1.0 .
# 查看生成的构建
[root@VM_0_9_centos dockerfile]# docker images
REPOSITORY            TAG       IMAGE ID       CREATED              SIZE
mycentos              1.0       a3b7aba4217d   About a minute ago   295MB

# 测试启动  这些命令也可以使用了
[root@7e9a5378c20f local]# pwd
/usr/local
[root@7e9a5378c20f local]# ifconfig   
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.4  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:04  txqueuelen 0  (Ethernet)
        RX packets 8  bytes 656 (656.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
# 查看镜像的历史变动
[root@VM_0_9_centos dockerfile]# docker history a3b7aba4217d
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
a3b7aba4217d   8 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/bin…   0B        
6ae76aad672c   8 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B        
923c095d5f23   8 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B        
9de7f3bb5e7b   8 minutes ago   /bin/sh -c #(nop)  EXPOSE 80                    0B        
b25669a7ae01   8 minutes ago   /bin/sh -c yum -y install net-tools             24.8MB    
0336d0f55f52   8 minutes ago   /bin/sh -c yum -y install vim                   60.4MB    
264c34fe4974   8 minutes ago   /bin/sh -c #(nop) WORKDIR /usr/local            0B        
0f34337f3767   8 minutes ago   /bin/sh -c #(nop)  ENV MYPATH=/usr/local        0B        
e434f46c176b   8 minutes ago   /bin/sh -c #(nop)  MAINTAINER zhonglao<zhong…   0B        
300e315adb2f   6 months ago    /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B        
<missing>      6 months ago    /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B        
<missing>      6 months ago    /bin/sh -c #(nop) ADD file:bd7a2aed6ede423b7…   209MB  

CMD 和 ENTRYPOINT 的区别

测试CMD

# 编写dockerfile文件
[root@VM_0_9_centos dockerfile]# vim dockerfile-test 
FROM centos
CMD ["ls","-a"]
# 构建镜像
[root@VM_0_9_centos dockerfile]# docker build -f dockerfile-test -t centos-cmd .
Sending build context to Docker daemon  3.072kB
Step 1/2 : FROM centos
 ---> 300e315adb2f
Step 2/2 : CMD ["ls","-a"]
 ---> Running in 55c593c219b3
Removing intermediate container 55c593c219b3
 ---> e4c84ec0f31f
Successfully built e4c84ec0f31f
Successfully tagged centos-cmd:latest

# run运行
[root@VM_0_9_centos dockerfile]# docker run e4c84ec0f31f
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

# 追加一个命令 -l ls -al
[root@VM_0_9_centos dockerfile]# docker run e4c84ec0f31f -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "-l": executable file not found in $PATH: unknown.
# cmd的情况下 -l 替换可CMD["ls","-a"]命令,-l 不是命令所以报错

测试ENTRYPOINT

# 编写dockerfile文件
[root@VM_0_9_centos dockerfile]# vim dockerfile-entrypoint-test
FROM centos
ENTRYPOINT ["ls","-a"]
# 构建镜像
[root@VM_0_9_centos dockerfile]# docker build -f dockerfile-entrypoint-test -t entorypoint-test .
Sending build context to Docker daemon  4.096kB
Step 1/2 : FROM centos
 ---> 300e315adb2f
Step 2/2 : ENTRYPOINT ["ls","-a"]
 ---> Running in b5b250f4bc1b
Removing intermediate container b5b250f4bc1b
 ---> 62f4a65c85d6
Successfully built 62f4a65c85d6
Successfully tagged entorypoint-test:latest
[root@VM_0_9_centos dockerfile]# docker images
REPOSITORY            TAG       IMAGE ID       CREATED             SIZE
entorypoint-test      latest    62f4a65c85d6   4 seconds ago       209MB
centos-cmd            latest    e4c84ec0f31f   19 minutes ago      209MB
mycentos              1.0       a3b7aba4217d   About an hour ago   295MB
tomcatwebapps         1.0.0     6dd55ed9bbe1   3 days ago          672MB
redis                 latest    fad0ee7e917a   4 days ago          105MB
nginx                 latest    d1a364dc548d   12 days ago         133MB
tomcat                latest    c43a65faae57   3 weeks ago         667MB
mysql                 5.7       2c9028880e58   3 weeks ago         447MB
portainer/portainer   latest    580c0e4e98b0   2 months ago        79.1MB
centos                latest    300e315adb2f   6 months ago        209MB
301517/button         latest    619c89a3488d   12 months ago       647MB
elasticsearch         7.6.2     f29a1ee41030   14 months ago       791MB
mondain/red5          latest    aa0a17bb9333   5 years ago         647MB
[root@VM_0_9_centos dockerfile]# docker run 62f4a65c85d6
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
# 测试在后面 直接追加命令 
[root@VM_0_9_centos dockerfile]# docker run 62f4a65c85d6 -l
total 56
drwxr-xr-x  1 root root 4096 Jun  7 02:53 .
drwxr-xr-x  1 root root 4096 Jun  7 02:53 ..
-rwxr-xr-x  1 root root    0 Jun  7 02:53 .dockerenv
lrwxrwxrwx  1 root root    7 Nov  3  2020 bin -> usr/bin
drwxr-xr-x  5 root root  340 Jun  7 02:53 dev
drwxr-xr-x  1 root root 4096 Jun  7 02:53 etc
drwxr-xr-x  2 root root 4096 Nov  3  2020 home
lrwxrwxrwx  1 root root    7 Nov  3  2020 lib -> usr/lib
lrwxrwxrwx  1 root root    9 Nov  3  2020 lib64 -> usr/lib64
drwx------  2 root root 4096 Dec  4  2020 lost+found
drwxr-xr-x  2 root root 4096 Nov  3  2020 media
drwxr-xr-x  2 root root 4096 Nov  3  2020 mnt
drwxr-xr-x  2 root root 4096 Nov  3  2020 opt
dr-xr-xr-x 95 root root    0 Jun  7 02:53 proc
dr-xr-x---  2 root root 4096 Dec  4  2020 root
drwxr-xr-x 11 root root 4096 Dec  4  2020 run
lrwxrwxrwx  1 root root    8 Nov  3  2020 sbin -> usr/sbin
drwxr-xr-x  2 root root 4096 Nov  3  2020 srv
dr-xr-xr-x 13 root root    0 Jun  7 02:45 sys
drwxrwxrwt  7 root root 4096 Dec  4  2020 tmp
drwxr-xr-x 12 root root 4096 Dec  4  2020 usr
drwxr-xr-x 20 root root 4096 Dec  4  2020 var

5.实战: tomcat镜像

1.准备jdk 和tomcat

2.构建镜像(注意路径 别写错了)

[root@VM_0_9_centos centos]# vim Dockerfile 
FROM centos
MAINTAINER zhonglao<zhonglaoxy@qq.com>

COPY readme.txt /usr/local/readme.txt

ADD jdk-8u291-linux-x64.tar.gz /usr/local
ADD apache-tomcat-9.0.46.tar.gz /usr/local

RUN yum -y install vim

ENV MYPATH /usr/local
WORKDIR $MYPATH

ENV JAVA_HOME /usr/local/jdk1.8.0_291
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.46
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.46
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin

EXPOSE 8080

CMD /usr/local/apache-tomcat-9.0.46/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.46/bin/log/catalina.out

3.构建镜像

# 因为使用的`Dockerfile`  是官方推荐使用的 所以不用-f指定
[root@VM_0_9_centos centos]# docker build -t diytomcat .
# 查看镜像
[root@VM_0_9_centos centos]# docker images
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
diytomcat             latest    dfbdd168f5fa   3 minutes ago   645MB

4.启动镜像

[root@VM_0_9_centos centos]# docker run -d -p 9090:8080 --name zhonglaotomcat -v /home/dockerfile/centos/test/:/usr/local/apache-tomcat-9.0.46/webapps/test -v /home/dockerfile/centos/tomcatlogs/:/usr/local/apache-tomcat-9.0.46/logs diytomcat

5.访问测试

http://ip:9090

6.发布项目(由于做了卷挂载,直接在本地编写项目就可以发布了)

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>db</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>

index.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>首页</title>
<style>
    *{
    padding:0;
    margin:0;
    font-family:"微软雅黑";
}
.header{
    height:72px;
    background:#458fce ;
}
.header .logo{
    color:#fff ;
    line-height:70px;
    font-size:30px;
    margin-left:20px;
    display:inline-block;
    text-align:center;

}
a {
    color: #fff ;
    text-decoration: none ;
}
.header .login{
    float:right;
    color:#fff ;
    line-height:72px;
    margin-right:2px;
    display:inline-block;
}
.banner{
    height:380px;
    overflow:hidden;
    background: #ddd;
}
</style>
</head>
<body>
    <div class="header">
        <div class="logo">web实践</div>
        <div class ="login">
            <a href ="javascript:void(0)">登录</a>
            <span>|</span> 
            <a href ="javascript:void(0)">故事</a>
        </div>
    </div>
</body>
</html>

请求: http://ip:9090/test

6.发布自己的镜像

DockerHub

1.地址https://hub.docker.com/ 注册账号或者找回

2.登录

[root@VM_0_9_centos test]# docker login --help


Usage:  docker login [OPTIONS] [SERVER]

Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.

Options:
  -p, --password string   Password
      --password-stdin    Take the password from stdin
  -u, --username string   Username
  
[root@VM_0_9_centos test]# docker login -u (你的username)
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

3.提交镜像,docker push

[root@VM_0_9_centos ~]# docker tag dfbdd168f5fa 301517/tomcat:1.0.0
[root@VM_0_9_centos ~]# docker push 301517/tomcat:1.0.0
71ac0ccf56f9: Layer already exists 
6229daf869c1: Layer already exists 
57d7fbfe56fa: Layer already exists 
b1d8d818f4e7: Layer already exists 
2653d992f4ef: Pushed 
1.0.0: digest: sha256:2a7b7cd3ad0fea1743a6266904185de8e7cd5cedbafac946467d5cf34b3d5b47 size: 1373

阿里云镜像服务上

1.登录阿里云

2.找到容器镜像服务

3.创建命名空间

4.根据操作指南一步步操作(1.命令上的那些前缀不能省;)

8.Docker网络

1.理解Docker0

网络地址

[root@VM_0_9_centos ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo   # 本机回环地址
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 10
00
    link/ether 52:54:00:de:11:64 brd ff:ff:ff:ff:ff:ff
    inet 10.206.0.9/20 brd 10.206.15.255 scope global eth0  # 服务器内网地址
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fede:1164/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:aa:1a:88:87 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0  # docker0 地址
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aaff:fe1a:8887/64 scope link 
       valid_lft forever preferred_lft forever

查看容器的网络地址

[root@VM_0_9_centos /]# docker run -d -p 9090:8080 --name tomcat1 tomcat

# 查看容器的内部网络地址  ip addr  发现容器会得到一个eth0@if137IP地址,docker分配的
[root@VM_0_9_centos ~]# docker exec -it tomcat1 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
136: eth0@if137: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
       
# linux 可以ping通容器内部       
[root@VM_0_9_centos ~]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.076 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.051 ms

# 启动第二个容器
[root@VM_0_9_centos ~]# docker run -d -p 9091:8080 --name tomcat2 tomcat
6488f5e8935ba46fed23ae69cef677b1bed071b1b6fd13bec484060da498cb2c
[root@VM_0_9_centos ~]# docker exec -it tomcat2 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
138: eth0@if139: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@VM_0_9_centos ~]#

# tomcat2 ping tomcat1 容器互通
[root@VM_0_9_centos ~]# docker exec -it tomcat2 ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.079 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.050 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.054 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.044 ms
# 我们发现多这个容器带来的网卡都是一对一对的;
# veth-pair,就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
# 正因为有这个特性,veth-pair 充当一个桥梁,连接各种虚拟网络设备的
# Openstac Docker容器之间的连接,OVS的连接,都是使用veth-pair 技术

结论:tomcat1 和 tomcat2是公用的一个路由器,docker0

所有容器不指定网络的情况下,都是docker0 路由的,docker会给我们的容器分配一个默认的可用ip

容器删除之后,虚拟网卡也会消失

在我们使用容器之间的时候,一直使用ip就会显的不方便,所以就有了--link

[root@VM_0_9_centos ~]# docker run -d -p 9092:8080 --name tomcat3 --link tomcat2 tomcat

# 这样的话 我们可以通过名字ping的通
[root@VM_0_9_centos ~]# docker exec -it tomcat3 ping tomcat2
PING tomcat2 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat2 (172.17.0.3): icmp_seq=1 ttl=64 time=0.058 ms
64 bytes from tomcat2 (172.17.0.3): icmp_seq=2 ttl=64 time=0.058 ms

原理:实质上就是在容器的hosts文件中进行了映射配置

[root@VM_0_9_centos ~]# docker exec -it tomcat3 cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3      tomcat2 6488f5e8935b
172.17.0.4      9cb325b4408a

现在,已经不建议使用--link了,

3.自定义网络

查看所有的docker网络

[root@VM_0_9_centos ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
72e66206b524   bridge    bridge    local
1d42f91fec40   host      host      local
6399c8e0576f   none      null      local

网络模式

bridge :桥接 docker(默认的)

none :不配置网络

host : 和宿主机共享网络

container: 容器网络互通!(用的少!局限性很大)

测试

# 我们直接启动的命令 --net bridge, 这个就是我们的dicker0
docker run -d -P --name tomcat1 tomcat
docker run -d -P --name tomcat1 --net bridge tomcat

# docker0特点: 默认 域名不能访问 --link可以打通连接


# --driver bridge
# --subnet 192.168.0.0/16
# --gateway 192
.168.0.1 mynet
[root@VM_0_9_centos ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
07a35d7276a4f75535b2cfa56ed98817fb973f8e72b7fa387afaaf5efc07c390
[root@VM_0_9_centos ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
72e66206b524   bridge    bridge    local
1d42f91fec40   host      host      local
07a35d7276a4   mynet     bridge    local
6399c8e0576f   none      null      local

# 自定义的网络就创建好了
[root@VM_0_9_centos ~]# docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "07a35d7276a4f75535b2cfa56ed98817fb973f8e72b7fa387afaaf5efc07c390",
        "Created": "2021-06-09T17:28:10.796919317+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

# 测试 启动两个容器
[root@VM_0_9_centos ~]# docker run -d -P --name tomcat1 --net mynet tomcat
2ae62051bfda580c2452047f86834c13e0113917d13ad8c2da4563469c547bc2
[root@VM_0_9_centos ~]# docker run -d -P --name tomcat2 --net mynet tomcat
0ee11e5281537e8c5364f8a161b41db280543cc750fede0ffb238586f6c659da
[root@VM_0_9_centos ~]# docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "07a35d7276a4f75535b2cfa56ed98817fb973f8e72b7fa387afaaf5efc07c390",
        "Created": "2021-06-09T17:28:10.796919317+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "2ae62051bfda580c2452047f86834c13e0113917d13ad8c2da4563469c547bc2": {
                "Name": "tomcat1",
                "EndpointID": "cc0cca7928affc5f07266884fb7f131eb55310aeac6fbdbeaaf1714af1db33e9",
                "MacAddress": "02:42:c0:a8:00:02",
                "IPv4Address": "192.168.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
[root@VM_0_9_centos ~]# docker run -d -P --name tomcat2 --net mynet tomcat
0ee11e5281537e8c5364f8a161b41db280543cc750fede0ffb238586f6c659da
# Containers 中有启动的容器的信息
[root@VM_0_9_centos ~]# docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "07a35d7276a4f75535b2cfa56ed98817fb973f8e72b7fa387afaaf5efc07c390",
        "Created": "2021-06-09T17:28:10.796919317+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "0ee11e5281537e8c5364f8a161b41db280543cc750fede0ffb238586f6c659da": {
                "Name": "tomcat2",
                "EndpointID": "b9ac315456de2474b14a963b0552b16f5d5ceccab51ebd7dfe75975fad2a59f3",
                "MacAddress": "02:42:c0:a8:00:03",
                "IPv4Address": "192.168.0.3/16",
                "IPv6Address": ""
            },
            "2ae62051bfda580c2452047f86834c13e0113917d13ad8c2da4563469c547bc2": {
                "Name": "tomcat1",
                "EndpointID": "cc0cca7928affc5f07266884fb7f131eb55310aeac6fbdbeaaf1714af1db33e9",
                "MacAddress": "02:42:c0:a8:00:02",
                "IPv4Address": "192.168.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
# 发现不通过 --link 一样可以使用容器名ping通
[root@VM_0_9_centos ~]# docker exec -it tomcat1 ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.086 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 192.168.0.3: icmp_seq=3 ttl=64 time=0.051 ms
[root@VM_0_9_centos ~]# docker exec -it tomcat1 ping tomcat2
PING tomcat2 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat2.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.027 ms
64 bytes from tomcat2.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.085 ms

推荐:使用

好处:

redis-不同的集群使用不同的网络,保证集群是安全和健康的

mysql-不同的集群使用不同的网络,保证集群是安全和健康的

4.网络连通

不同网络如何互通?

还是之前的tomca1和tomca2(mynet),我们再加两个tomcat01和tomcat02,但是这两个容器使用默认的bridge

[root@VM_0_9_centos ~]# docker run -d -P --name tomcat01 tomcat
de18cf67a3587329ba8d2e061d4c1fde801636b8ad62036f5ae100112babedf9
[root@VM_0_9_centos ~]# docker run -d -P --name tomcat02 tomcat
7c9b7142b19cf782315e7e6aed35e466e11e7cef1756fba1f9f6be668d8c973d

# 显然是不可以ping通的
[root@VM_0_9_centos ~]# docker exec -it tomcat1 ping tomcat01
ping: tomcat01: Name or service not known
# 使用 docker network connect 来打通网络
[root@VM_0_9_centos ~]# docker network connect mynet tomcat01
# 实质上就是将 tomcat01放入了mynet网络中
# 一个容器 两个ip
[root@VM_0_9_centos ~]# docker network inspect mynet 
[
    {
        "Name": "mynet",
        "Id": "07a35d7276a4f75535b2cfa56ed98817fb973f8e72b7fa387afaaf5efc07c390",
        "Created": "2021-06-09T17:28:10.796919317+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "0ee11e5281537e8c5364f8a161b41db280543cc750fede0ffb238586f6c659da": {
                "Name": "tomcat2",
                "EndpointID": "b9ac315456de2474b14a963b0552b16f5d5ceccab51ebd7dfe75975fad2a59f3",
                "MacAddress": "02:42:c0:a8:00:03",
                "IPv4Address": "192.168.0.3/16",
                "IPv6Address": ""
            },
            "2ae62051bfda580c2452047f86834c13e0113917d13ad8c2da4563469c547bc2": {
                "Name": "tomcat1",
                "EndpointID": "cc0cca7928affc5f07266884fb7f131eb55310aeac6fbdbeaaf1714af1db33e9",
                "MacAddress": "02:42:c0:a8:00:02",
                "IPv4Address": "192.168.0.2/16",
                "IPv6Address": ""
            },
            "de18cf67a3587329ba8d2e061d4c1fde801636b8ad62036f5ae100112babedf9": {
                "Name": "tomcat01",
                "EndpointID": "25ecd8bdad5f3d7a8c7c7ead036ce32af0556d4aeb8bc28b6d16824a56661976",
                "MacAddress": "02:42:c0:a8:00:04",
                "IPv4Address": "192.168.0.4/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
# 测试可以连通了
[root@VM_0_9_centos ~]# docker exec -it tomcat1 ping tomcat01
PING tomcat01 (192.168.0.4) 56(84) bytes of data.
64 bytes from tomcat01.mynet (192.168.0.4): icmp_seq=1 ttl=64 time=0.058 ms
64 bytes from tomcat01.mynet (192.168.0.4): icmp_seq=2 ttl=64 time=0.058 ms
64 bytes from tomcat01.mynet (192.168.0.4): icmp_seq=3 ttl=64 time=0.053 ms

Git

1.简介和目的

官网 https://git-scm.com/

Git是目前世界上最先进的分布式版本控制系统。svn也是版本控制系统。

SVN与Git的最主要的区别:

SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本。

然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,就不好使了。

Git是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。

既然每个人的电脑都有一个完整的版本库,那多个人如何协作呢?比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。

2.常用操作命令

这里提供一个练习网站:https://learngitbranching.js.org/

下载

git clone https;

全局的git用户

git config --global user.name "user" 

全局的git邮箱

git config --global user.email "...@qq.com" 

更新当前git版本

git update

初始化

git init

连接至远程仓库

git remote add

配置当前项目的

git config user.name "user" 当前的git用户
git config user.email "...@qq.com" 当前的git邮箱

查看git所有的配置

git config --list

项目路径下就是查看当前项目的配置

git config --list

查询git 中有哪些修改文件

git status

添加修改文件至缓冲区

git add .
git add 1.txt

提交至仓库区

git commit -m "message"

推至远程仓库上

git push 

推至指定分支

git push -u origin (master)[分支名]

创建分支

git branch (分支名称)

分支列表

git branch --list

切换分支

git checkout (分支名)
posted @ 2021-12-21 12:26  终老*  阅读(22)  评论(0编辑  收藏  举报
Live2D 看板娘 v1.4 / Demo 3