程序猿本猿从事Java开发三年
断断续续记录下的知识点笔记
不仅于此,后续慢慢再补。。。

复习提纲

文章目录

  • 程序猿本猿从事Java开发三年 断断续续记录下的知识点笔记 不仅于此,后续慢慢再补。。。
  • 一、JVM
    • 1. JVM内存结构
    • 2. 对象分配规则
    • 3. 解释内存中的栈(stack)、堆(heap)、和方法区(area)的用法
    • 4. 什么是类的加载
    • 5. 类的生命周期
    • 6. 引用的分类
    • 7. OOM排查
  • 二、线程
    • 1. 线程池的实现原理:
    • 2. 创建线程池有几种方法?
    • 3. 创建线程有几种方式?
    • 4. 线程有哪些状态?
    • 5. sleep()和wait()的区别?
    • 6. 怎么保证多线程的运行安全【并发编程三要素】
    • 7. Synchronized
    • 8. 类锁与对象锁的区别
    • 9. 死锁是什么?怎么防止死锁
    • 10. Threalocal
  • 三、JAVA
    • 1. HashMap
    • 2. List,Set,Map的区别
    • 3. ArrayList的扩容机制
    • 4.
  • 四、Spring
    • 1. IoC 反转控制
    • 2. Spring Bean
    • 3. AOP 面向切面
    • 4. Spring 事务的七种传播级别
    • 5. 代理
    • 6. Spring 中使用了哪些设计模式
  • 五、Spring MVC
    • 1. Spring MVC工作原理
  • 六、Spring Boot
    • 1. SpringBoot 自动装配原理
  • 七、Spring Cloud
    • 1. Eureka:服务注册与发现
    • 2. Ribbon:负载均衡客户端
    • 3. Fegin:服务接口调用
    • 4.Hystrix:降级、熔断、限流、监控
    • 5. Zuul:统一网关处理
    • 6. Config Server:配置中心
    • 7. Sleuth+Zipkin:分布式链路跟踪
    • 分布式链路跟踪介绍:
    • Zipkin简介:
    • 为什么要Zipkin?
    • zipkin server 数据持久化问题
    • 使用Elasticsearch 做数据持久化
    • 8. 微服务总结
  • 八、微服务
    • 1. 微服务技术栈
  • 九、Docker
    • 1. Docker架构
    • 2. 常用命令
    • 3. 实例:mysql
    • 4. Docker网络
    • 5. Compose
  • 十、Redis
    • 1. redis是什么?
    • 2. Redis支持的数据类型
    • 3. Redis实现分布式锁
    • 4. Redis实现消息队列
    • 5. Redis事务
      • 1 Redis事务的概念:
      • 2 Redis事务没有隔离级别的概念:
      • 3 Redis不保证原子性:
      • 4 Redis事务的三个阶段:
      • 5 Redis事务相关命令:
      • 6 Redis事务使用案例:
      • 7 总结:
  • 十一、MQ
    • 1. 消息中间件
    • 2. MQ的四大作用
    • 3. RabbitMq
      • ***AMQP**
      • ***Exchange的类型**
      • **direct(路由键模式):**
      • **fanout(广播模式):**
      • **topic(主题模式):(因为\*在这个笔记软件里面是关键字,所以下面就用'星'替换掉了)**
      • **headers:**
    • 4. kafka
      • ***kafka的特性**
      • ***kafka的应用场景**
      • ***kafka架构**
      • *****kafka吞吐量为什么这么高
      • ***kafka简易测试命令**
    • 5. kafka与rabbitMQ选型对比
  • 十二、Linux
    • 1. 防火墙
    • 2. SVN
    • 3. FTP
    • 4. 查询命令
    • 5.安装命令
    • 6.卸载
    • 7.压缩/打包
    • 8.解压/解包
    • 9.字符集
    • 10.用户/分组/权限
    • 11.软连接

一、JVM

https://www.cnblogs.com/JesseP/p/11750847.html

1. JVM内存结构

  • Java堆(Heap),是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
  • 方法区(Method Area),方法区与Java堆一样,是各个线程共享的内存区域,它用于存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  • 程序计数器(Program Counter Register),程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
  • JVM栈(JVM Stacks),与程序计数器一样,Java虚拟机栈也是线程私有的,他的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
  • 本地方法栈(Native Method Stacks),本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

2. 对象分配规则

  • 年轻代(Eden区+Survivor区(from space区 + to space区))、老年代。
  • 对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。
  • 大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在年轻代(Eden区和Survivor区)之间发生大量的内存拷贝。
  • 长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计算器,每次发生Minor GC时,Eden区存活下来的对象将进入Survivor区,并设定年龄为1,Survivor区中存活下来的对象的年龄加1,年龄达到阈值(默认15)将进入老年区。
  • 动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
  • 空间分配担保。每次进行GC是,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一个Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Minor GC,false则进行Full GC。

3. 解释内存中的栈(stack)、堆(heap)、和方法区(area)的用法

String str = new String("hello");

上面的语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而“hello”这个变量放在方法区。

4. 什么是类的加载

类的加载指的是将类的.calss文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后再堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

5. 类的生命周期

类的生命周期包括这几个部分,加载、连接、初始化、使用和卸载,其中前三部是类的加载的过程,如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KGYBQjhm-1603961459301)(/…/…/images/image-20200903101829449-1602661688789.png)]

  • 加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象
  • 连接,连接又包含三块内容:验证、准备、初始化。 1)验证,文件格式、元数据、字节码、符号引用验证; 2)准备,为类的静态变量分配内存,并将其初始化为默认值; 3)解析,把类中的符号引用转换为直接引用
  • 初始化,为类的静态变量赋予正确的初始值
  • 使用,new出对象程序中使用
  • 卸载,执行垃圾回收

6. 引用的分类

  • 强引用:任何时候都不会被回收,即使报了OOM错误。清除方法需要手动将对象置null,如调用List的clear方法等
  • 软引用:soft,内存不够时,为了避免oom问题,会回收软引用对应的对象。
  • 弱引用:weak,无论内存是否足够,只要发生了垃圾回收,就会进行回收。
  • 虚引用:phantom,任意时候都会被回收。

7. OOM排查

  1. 先查看应用进程号pid: ps -ef | grep 应用名
    先查看垃圾回收情况: jstat -gc pid 5000(时间间隔)
  2. dump 查看方法栈信息:jstack -l pid > /home/test/jstack.txt
  3. dump 查看JVM内存分配以及使用情况:jmap -heap pid > /home/test/jmapHeap.txt

二、线程

https://blog.csdn.net/qq_36387471/article/details/105479238

1. 线程池的实现原理:

提交任务到线程池,判断线程池中线程数是否达到核心线程数设定值,否则创建线程并对线程加全局锁,是则判断是否达到最大线程数,如果没达到最大线程数则添加任务到阻塞队列等待可用线程,如果达到最大线程数,则执行饱和策略。

2. 创建线程池有几种方法?

**创建线程池有两种方法,分别是Executors工厂方法创建和new ThreadPoolExecutor()自定义创建

Executors提供四种线程池,分别为可缓存线程池、定长线程池、周期线程池、单线程化的线程池

3. 创建线程有几种方式?

①. 继承Thread类
②. 实现Runnable接口
③. 通过Callable和Future创建线程
④. 通过线程池创建线程;

4. 线程有哪些状态?

①. 创建状态
②. 就绪状态
③. 运行状态
④. 阻塞状态
⑤. 死亡状态

5. sleep()和wait()的区别?

sleep()方法是Thread类的静态方法,是线程用来控制自身流程的。而wait()方法是Object类的方法,用于线程间的通信。

调用wait()的时候方法会释放当前持有的锁,而sleep方法不会释放锁。

① sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
② 调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

6. 怎么保证多线程的运行安全【并发编程三要素】

原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作(atomic,synchronized);
可见性:一个线程对主内存的修改可以及时被其他线程看到(synchronized);
有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序(happens-before原则);

7. Synchronized

https://www.cnblogs.com/aspirant/p/11470858.html

8. 类锁与对象锁的区别

https://zhuanlan.zhihu.com/p/127171227

9. 死锁是什么?怎么防止死锁

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

10. Threalocal

https://blog.csdn.net/h2604396739/article/details/83033302

三、JAVA

https://www.cnblogs.com/zengcongcong/p/11295349.html

1. HashMap

  • 谈谈HashMap
    hashMap是基于哈希表的Map接口的实现。有几个重要的参数,加载因子和初始大小,初始容量为16,扩容方式为2N,加载因子默认0.75。JDK1.7前以数组加链表实现,JDK1.8后加入了红黑树,当数组长度大于等于 64 且链表长度大于 8 时会将链表转换成红黑树。当调用get(key)方法时,会将key hash出一个数组下标,根据在数组中的链表(红黑树)equals遍历是否相等,相等取出对应元素。
  • 1.8之前 在 扩容 resize 的时候会进行 rehash,在并发的情况下会导致链表闭环而导致死锁
  • 尽量给 HashMap 设置初始化容量,否则会发生多次扩容影响性能
  • JDK 1.7前 使用头插法,1.8后使用尾插法,主要为了解决 rehash 出现的死循环问题


2. List,Set,Map的区别

三者都属于集合,用来存储元素。

  • List存储元素有序可重复。
  • Set存储元素无序不可重复,set的实现为HashMap的子集。
  • Map存储键值对形式的数据,键Key可为空,不可重复,值value可重复。

3. ArrayList的扩容机制

  • 使用无参构造方法构建时,初始化赋值为一个空数组,只有当add进去一个元素后,此时容量才扩大为10.
  • JDK1.6的扩容机制为1.5倍+1,JDK1.6后为1.5倍

4.

四、Spring

1. IoC 反转控制

IoC是一种设计思想,在java中将设计好的对象交给IoC容器控制,IoC容器会在你需要的时候给你相应的对象。

  • Spring 容器使用依赖注入来管理组成应用程序的 Bean 对象。
  • 容器通过读取提供的配置元数据 Bean Definition 来接收对象进行实例化,配置和组装的指令。
  • 该配置元数据 Bean Definition 可以通过 XML,Java 注解或 Java Config 代码提供

依赖注入有哪几种方式?

  • 接口注入
  • 构造函数注入
  • setter注入

Spring中有多少种IoC容器?

  • BeanFactory:就像一个包含Bean集合的工厂类。它会在客户端要求时实例化Bean对象。
  • ApplicationContext:扩展了BeanFactory接口,它在BeanFactory基础上提供了一些额外的功能。

IoC的好处:

  • 可以资源集中管理,实现资源的可配置和易管理。
  • 降低耦合度。
  • 减少对象的创建和内存消耗
  • 使得整个程序的维护性、扩展性、灵活性变高。

2. Spring Bean

Bean的生命周期:

  • 根据配置元数据实例化Bean对象==》Spring使用依赖注入填充所有属性==》Aware相关的属性注入到Bean对象==》调用相应的方法,进一步初始化Bean对象==》调用destroy方法销毁容器

Spring Bean 怎么解决循环依赖的问题?

  • 首先造成循环依赖主要是由于模块设计时没有充分考虑各模块职责划分导致的,在模块设计的时候就应该避免这个问题。清除各个模块功能职责,建立有层次,自下而上的依赖关系。

  • 出现循环依赖的原因:Bean a 依赖 Bean b ,a 未完成实例化, 开始查找 b。b没有被找到,容器则会去创建,在创建 b 过程中,又发现 b 依赖 a。

  • spring使用三级缓存来解决依赖循环问题,三级缓存其实就是利用了三个map加一个创建标识。

    实例化A的时候,会去Bean容器中创建Bean A,去一级缓存中找是否存在A的实例对象,不存在则判断A是否在创建中(第一次进来是不会在创建中的),如果A再创建中,则去二级、三级缓存中找。没找到则创建单例对象A,并把早期对象A暴露到三级缓存中,标记A创建中。为A注入依赖,发现A依赖B,则创建B,同上。为B注入依赖时也发现B依赖A,则去创建A,此时,在一级缓存中依然拿不到A的实例对象,因为A还不是一个完整的对象(没完成依赖注入及后续处理),因为此时A被标识为创建中,可以去二级缓存中拿,拿不到再去三级缓存中拿,上面已经把A存放到了三级缓存中,拿到A的早其对象后注入到B,B完成实例化后放到一级缓存,并从二级、三级缓存中清空,实例化后完整的B注入到A中,同上,A放到一级缓存清空二三级缓存。
    

    除了靠Spring三级缓存解决以外,我们还可以加@lazy注解来延迟依赖的加载,或者使用setter注入。

3. AOP 面向切面

  • 什么是AOP?

    AOP是Spring中的一种面向切面编程思想,通过代理将代码切入到类的指定方法、指定位置上的一种技术。

  • AOP的相关概念
    Aspect:切面,切入系统的一个切面。比如事务管理是一个切面,权限管理也是一个切面。
    Join point:连接点,也就是可以进行横向切入的位置。
    Advice:通知,切面在某个连接点执行的操作(分为: Before advice , After returning advice , After throwing advice , --------------After (finally) advice , Around advice );
    pointcut:切点,符合切点表达式的连接点,也就是真正被切入的地方;

4. Spring 事务的七种传播级别

  1. PROPAGATION_REQUIRED 默认的 spring 事务传播级别,使用该级别的特点是,如果上下文存在事务,则加入到事务中执 行。如果不存在,则新建事务。这个级别通常能满足处理大多数的业务场景
  2. PROPAGATION_SUPPORTS 如果上下文存在事务,则支持事务加入。如果不存在,则使用非事务的方式执行。通常用来处理并 非原子性的非核心业务逻辑操作。
  3. PROPAGATION_MANDATORY 要求上下文必须存在事务,否则就抛出异常。严格!
  4. PROPAGATION_NOT_SUPPORTED 如果上下文存在事务,则挂起事务执行当前逻辑。结束后恢复事务的执行
  5. PROPAGATION_REQUIRE_NEW 每次都新建一个事务,并且同时将上下文的事务挂起,执行完新建的事务后再恢复执行被挂起的事 务 应用场景:发送100个红包,在发送前要进行系统初始化、验证等操作才发送红包,然后记录发送 日志,发送日志要求100%正确,如果不准确整个事务回滚
  6. PROPAGATION_NEVER 不允许事务的存在。如果上下文存在事务,则抛出异常,强制停止执行
  7. PROPAGATION_NESTED 如果上下文存在事务则嵌套执行事务。不存在则新建事务。子事务嵌套在父事务执行,子事务是父 事务的一部分。在进入子事务之前,父事务建立一个回滚点叫 save point,然后执行子事务。子 事务执行结束,继续执行父事务

5. 代理

  • 什么是动态代理?
    动态代理是在程序运行时通过反射机制操作字节码,从而动态的创建 字节码文件,进而创建代理对象。
  • jdk动态代理和cglib动态代理的区别:
    jdk:先有实现接口的类的对象,然后对对象进行代理
    cglib:只要有类(无论是否实现接口),对类做代理,简单来说就是对指定的类生成一个子类,覆盖其中的方法(继承)
  • jdk动态代理和cglib动态代理在spring中
    当Bean实现接口时,Spring默认使用jdk动态代理
    当Bean没有实现接口时,Spring使用cglib动态代理
    如果Bean实现接口,也可以强制使用cglib动态代理(在spring配置中加入)

6. Spring 中使用了哪些设计模式

  • 工厂模式:Spring 中的 BeanFactory,根据传入的唯一标识来获取 Bean 对象
  • 单例模式:提供了全局的访问点 BeanFactory
  • 代理模式:AOP; JDK动态代理、CGLib 字节码生成代理
  • 装饰器模式:依赖注入使用 BeanWrapper
  • 观察者模式:Spring 的 Listener 的实现,如 ApplicationListener
  • 策略模式:Bean 实例化的时候决定采用何种方式实例化,反射或者 CGLIB 动态字节码生成

五、Spring MVC

1. Spring MVC工作原理

  • 用户发送请求到前端控制器 DispatcherServlet
  • DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器
  • HandlerMapping 找到具体的处理器返回给 DispatcherServlet
  • DispatcherServlet 调用 HandlerAdapter处理器适配器
  • HandlerAdapter 经过适配调用具体的控制器(Controller)
  • Controller 返回 ModelAndView 给 DispatcherServlet
  • DispatcherServlet 调用 ViewReslover 视图解析器
  • ViewReslover 返回 View 给 DispatcherServlet
  • DispatcherServlet 对 View 进行数据渲染后返回给用户

六、Spring Boot

1. SpringBoot 自动装配原理

  • 启动类上的 @SpringBootApplication 注解上的 @EnableAutoConfiguration 上的 @Import({AutoConfigurationImportSelector.class})
  • AutoConfigurationImportSelector 通过 SpringFactoriesLoader.loadFactoryNames 扫描所有具有 META-INF/spring.factories 的 jar 包

七、Spring Cloud

https://www.cnblogs.com/aishangJava/p/11927311.html

1. Eureka:服务注册与发现

Eureka心跳机制

在应用启动后,节点们将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

eureka自我保护机制是什么?

如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:

  1. Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
  2. Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
  3. 当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中

2. Ribbon:负载均衡客户端

Ribbon提供了七种负载均衡策略:http://www.wityx.com/post/758_1_1.html

***负载均衡原理:***https://blog.csdn.net/wudiyong22/article/details/80829808

LoadBalancerClient(RibbonLoadBalancerClient是实现类)在初始化的时候(execute方法),会通过ILoadBalance(BaseLoadBalancer是实现类)向Eureka注册中心获取服务注册列表,并且每10s一次向EurekaClient发送“ping”,来判断服务的可用性,如果服务的可用性发生了改变或者服务数量和之前的不一致,则从注册中心更新或者重新拉取。LoadBalancerClient有了这些服务注册列表,就可以根据具体的IRule来进行负载均衡

3. Fegin:服务接口调用

什么是Fegin?

Feign是一个声明式的服务客户端,使得编写web服务客户端变得非常容易,Feign采用的是基于接口的注解。

Feign整合了Ribbon与Hystrix,具有负载均衡、降级、熔断的功能。

4.Hystrix:降级、熔断、限流、监控

https://www.cnblogs.com/yawen/p/6655352.html

  • 降级:降级是当某个微服务响应时间过长,或不可用了,当发生这种问题的时候我们直接对应的策略(一个方法,写在调用端)来快速返回这个请求,不让他卡在那 ,这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。

  • 熔断:在服务客户端根据yml文件配置中的值判断某个请求在设定时间内请求失败次数达到设定阈值,则会触发熔断,不再发送该请求,而是调用降级方法。

    • 熔断状态:

      1. Closed:熔断器关闭状态,调用失败次数积累,到了阈值(或一定比例)则启动熔断机制;
      2. Open:熔断器打开状态,此时内部直接返回降级方法,不走网络。但设计了一个时钟选项,默认的时钟达到了一定时间(默认5秒),到了这个时间,进入半开状态;
      3. Half-Open:半开状态,允许定;量的服务请求,如果调用都成功(或一定比例)则认为恢复了,关闭熔断器,否则认为还没好,又回到熔断器打开状态;
  • 限流:限制某个微服务的可用线程
    hystrix通过线程池的方式来管理你的微服务调用(全局线程池默认大小:10),对每个command创建一个自己的线程池,执行调用。通过线程池隔离来保证不同调用不会相互干扰和每一个调用的并发限制。并发超过核心线程数(加等待线程数),会直接返回降级方法。在@HystrixCommand注解中可以使用参数新建线程池。更多功能参考@HystrixCommand参数详解

  • 监控:https://blog.csdn.net/chengqiuming/article/details/80783680

5. Zuul:统一网关处理

https://www.pianshen.com/article/8342843266/

  • Zuul包含了对请求的路由和过滤两个最主要的功能:

    • 其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础。

    • 而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。

    • Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得.

  • Zuul的过滤器

    Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。

    • PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

    • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。

    • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

    • ERROR:在其他阶段发生错误时执行该过滤器。
      除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

  • Zuul的作用

    Zuul可以通过加载动态过滤机制,从而实现以下各项功能:

    • 验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。

    • 审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。

    • 动态路由: 以动态方式根据需要将请求路由至不同后端集群处。

    • 压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。

    • 负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。

    • 静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。

    • 多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。

  • Zuul和Nginx的区别

    • 相同点:Zuul和Nginx都可以实现负载均衡、反向代理(隐藏真实IP地址),过滤请求,实现网关的效果。

    • 不同点:Nginx–C语言开发 Zuul–Java语言开发

      Zuul负载均衡实现:采用Ribbon+Eureka实现本地负载均衡

      Nginx负载均衡实现:采用服务器来实现负载均衡

      Nginx相比Zuul功能更加强大,因为Nginx整合一些脚本语言(Nginx+lua)

      Nginx适合于服务端负载均衡

    ​ Zuul适合微服务中实现网关

  • Zuul限流

    • 流算法

      • 漏桶: leakey bucket,原理:桶的下方的小孔会以一个相对恒定的速率漏水,而不管入桶的水流量,这样就达到了控制出水口的流量
      • 令牌桶: token bucket,原理:以相对恒定的速率向桶中加入令牌,请求来时于桶中取令牌,取到了就放行,没能取到令牌的请求则丢弃
    • 限流粒度
      粗粒度

      • 网关限流
      • 单个服务
    • 细粒度
      • user: 认证用户或者匿名,针对某个用户粒度进行限流
      • origin: 客户机的IP,针对请求客户机的IP进行限流
      • url: 特定url,针对请求的url粒度进行限流
      • serviceId: 特定服务,针对某个服务的id粒度进行限流

6. Config Server:配置中心

  • config server是什么?

    config server是一个分布式配置中心,服务消费端可以通过bootstrap.yml文件从config server读取github中的配置信息。服务消费端可以同时存在application.yml与bootstrap.yml文件。同属性配置内容,后者比前者优先级高。

  • config访问配置文件,是需要一个具体的访问规则的, 那么这个访问规则到底是什么呢? 我们可以在官网找到:

    /{application}/{profile}[/{label}]
    /{application}-{profile}.yml
    /{label}/{application}-{profile}.yml
    /{application}-{profile}.properties
    /{label}/{application}-{profile}.properties

    application就是配置文件的名字, profile就是对应的环境 label就是不同的分支 , 对于yml 和properties类型config可以完美转换

7. Sleuth+Zipkin:分布式链路跟踪

8. 微服务总结

这里我画了张图简单总结了以下我们的spring cloud 的学习:

  • 由上图可以发现, spring cloud 把各个组件相互配合起来, 整合成一套成熟的微服务架构体系
  • 其中, 由eureka做服务注册与发现,很好的把各个服务链接起来
  • ribbon+fegin提供了微服务的调用和负载均衡解决方案
  • hystrix 负责监控微服务之间的调用情况,以及降级和熔断保护
  • Hystrix dashboard监控Hystrix的熔断情况以及监控信息以图形化界面展示
  • spring cloud config 提供了统一的配置中心服务
  • 所有外来的请求由zuul统一进行路由和转发,起到了API网关的作用
  • Sleuth+Zipkin把我们微服务的追踪数据记录下来并展示方便我们进行后续分析

八、微服务

https://www.cnblogs.com/aishangJava/p/11927311.html

1. 微服务技术栈

  • 维度(springcloud)
  • 服务开发:springboot spring springmvc
  • 服务配置与管理:Netfix公司的Archaiusm ,阿里的Diamond
  • 服务注册与发现:Eureka,Zookeeper
  • 服务调用:Rest RPC gRpc
  • 服务熔断器:Hystrix
  • 服务负载均衡:Ribbon Nginx
  • 服务接口调用:Fegin
  • 消息队列:Kafka Rabbitmq activemq
  • 服务配置中心管理:SpringCloudConfig
  • 服务路由(API网关)zuul、gateway
  • 事件消息总线:SpringCloud Bus

九、Docker

1. Docker架构

Docker使用C/S架构,Client通过接口与Server进程通信实现容器的构建,运行和发布,如图:

  • Host(Docker 宿主机):

    安装了Docker程序,并运行了Docker daemon的主机。

  • Docker daemon(Docker 守护线程):

    运行在宿主机上,Docker守护进程,用户通过Docker client(Docker命令)与Docker daemon交互。

  • Images(镜像):

    将软件环境打包好的模板,用来创建容器的,一个镜像可以创建多个容器。

  • Containers(容器):

    Docker的运行组件,启动一个镜像就是一个容器,容器与容器之间相互隔离,并且互不影响。

  • Docker Client(Docker 客户端):

    Docker命令行工具,用户是用Docker Client与Docker daemon进行通信并返回结果给用户。也可以使用其他工具通过Docker Api 与Docker daemon通信。

  • Registry(仓库服务注册):

    经常会和仓库(Repository)混为一谈,实际上Registry上可以有多个仓库,每个仓库可以看成是一个用户,一个用户的仓库放了多个镜像。仓库分为了公开仓库(Public Repository)和私有仓库(Private Repository),最大的公开仓库是官方的Docker Hub,国内也有如阿里云、时速云等,可以给国内用户提供稳定快速的服务。用户也可以在本地网络内创建一个私有仓库。当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来就可以了

2. 常用命令

镜像常用操作:

  • 查找镜像:docker search 关键词

  • 下载镜像:docker pull 镜像名:TAG

    # Tag表示版本,有些镜像的版本显示latest,为最新版本

  • 查看镜像:docker images

    # 查看本地所有镜像

  • 删除镜像:docker rmi -f 镜像 ID 或者镜像名:TAG

    # 删除指定本地镜像

    # -f 表示强制删除

  • 获取元信息:docker inspect 镜像ID或者镜像名:TAG

    # 获取镜像的元信息,详细信息

容器常用操作:

  • 运行:

    docker run --name 容器名 -i -t -p 主机端口:容器端口 -d -v 主机目录:容器目录:ro 镜像ID或镜像名:TAG

    # --name 指定容器名,可自定义,不指定自动命名

    # -i 以交互模式运行容器

    # -t 分配一个伪终端,即命令行,通常-it组合来使用

    # -p 指定映射端口,讲主机端口映射到容器内的端口

    # -d 后台运行容器

    # -v 指定挂载主机目录到容器目录,默认为rw读写模式,ro表示只读

  • 容器列表:

    docker ps -a -q

    # docker ps查看正在运行的容器

    # -a 查看所有容器(运行中、未运行)

    # -q 只查看容器的ID

  • 启动容器:

    docker start 容器ID或容器名

  • 停止容器:

    docker stop 容器ID或容器名

  • 删除容器:

    docker rm -f 容器ID或容器名

    # -f 表示强制删除

  • 查看日志:docker logs 容器ID或容器名

  • 进入正在运行容器:

    docker exec -it 容器ID或者容器名 /bin/bash

    # 进入正在运行的容器并且开启交互模式终端

    # /bin/bash是固有写法,作用是因为docker后台必须运行一个进程,否则容器就会退出,在这里表示启动容器后启动bash

    # 也可以用docker exec在运行中的容器执行命令

  • 拷贝文件:

    docker cp 主机文件路径容器ID或容器名:容器路径

    #主机中文件拷贝到容器中

    docker cp 容器ID或容器名:容器路径主机文件路径

    #容器中文件拷贝到主机中

  • 获取容器元信息:

    docker inspect 容器ID或容器名

  • 使用Dockerfile构建SpringBoot应用镜像

    1. 把你的项目打包成可执行jar包

    2. 把jar包上传到Linux服务器

    3. 在jar包路径下创建Dockerfile文件vi Dockerfile

      # 指定基础镜像,本地没有会从dockerHub pull下来
      FROM java:8
      #作者
      MAINTAINER huaan
      # 把可执行jar包复制到基础镜像的根目录下
      ADD luban.jar /luban.jar
      # 镜像要暴露的端口,如要使用端口,在执行docker run命令时使用-p生效
      EXPOSE 80
      # 在镜像运行为容器后执行的命令
      ENTRYPOINT ["java","-jar","/luban.jar"]
    4. 使用docker build命令构建镜像,基本语法

      docker build -t huaan/mypro:v1 .
      # -f指定Dockerfile文件的路径# -t指定镜像名字和TAG# .指当前目录,这里实际上需要一个上下文路径
      
    5. 运行刚才构建的镜像

      docker run --name pro -p80:80 -d镜像名:TAG
      

Dockerfile常用指令:

  • FROM

    FROM指令是最重要的一个并且必须为Dockerfile文件开篇的第一个非注释行,用于为镜像文件构建过程指定基础镜像,后续的指令运行于此基础镜像提供的运行环境。

    这个基础镜像可以是任何可用镜像,默认情况下docker build会从本地仓库找指定的镜像文件,如果不存在就会从Docker Hub上拉取

    语法:

    FROM <image>
    FROM <image>:<tag>
    FROM <image>@<digest>
    
  • MAINTAINER(depreacted)

    Dockerfile的制作者提供的本人详细信息

    Dockerfile不限制MAINTAINER出现的位置,但是推荐放到FROM指令之后

    语法:name可以是任何文本信息,一般用作者名称或者邮箱

    MAINTAINER <name>
    
  • LABEL

    给镜像指定各种元数据

    一个Dockerfile可以写多个LABEL,但是不推荐这么做,Dockerfile每一条指令都会生成一层镜像,如果LABEL太长可以使用\符号换行。构建的镜像会继承基础镜像的LABEL,并且会去掉重复的,但如果值不同,则后面的值会覆盖前面的值。

    语法:

    LABEL <key>=<value> <key>=<value> <key>=<value>...
    
  • COPY

    用于从宿主机复制文件到创建的新镜像文件

    注意:如果你的路径中有空白字符,通常会使用第二种格式

    规则:

    • 必须是build上下文中的路径,不能是其父目录中的文件

    • 如果是目录,则其内部文件或子目录会被递归复制,但目录自身不会被复制

    • 如果指定了多个,或在中使用了通配符,则必须是一个目录,则必须以/符号结尾

    • 如果不存在,将会被自动创建,包括其父目录路径

    语法:

    COPY <src>...<dest>
    COPY ["<src>",..."<dest>"]
    # <src>:要复制的源文件或者目录,可以使用通配符
    # <dest>:目标路径,即正在创建的image的文件系统路径;建议<dest>使用绝对路径,否则COPY指令则以WORKDIR为其起始路径
    
  • ADD

    基本用法和COPY指令一样,ADD支持使用TAR文件和URL路径

    规则:

    • 和COPY规则相同如果为URL并且没有以/结尾,则

    • 指定的文件将被下载到

    • 如果是一个本地系统上压缩格式的tar文件,它会展开成一个目录;但是通过URL获取的tar文件不会自动展开

    • 如果有多个,直接或间接使用了通配符指定多个资源,则必须是目录并且以/结尾

    语法:

    ADD <src>...<dest>
    ADD ["<src>",..."<dest>"]
    
  • EXPOSE

    用于给容器打开指定要监听的端口以实现和外部通信。

    用于指定传输层协议,可以是TCP或者UDP,默认是TCP协议

    EXPOSE可以一次性指定多个端口,例如:EXPOSE 80/tcp 80/udp

    语法:

    EXPOSE <port>[/<protocol>] [<port>[/<protocol>]...]
    
  • RUN

    用来指定docker build过程中运行指定的命令

    语法:

    RUN <command>
    RUN ["<executable>","<param1>","<param2>"]
    

    第一种格式里面的参数一般是一个shell命令,以 /bin/sh -c 来运行它

    第二种格式中的参数是一个JSON格式的数组,当中是要运行的命令,后面是传递给命令的选项或者参数;但是这种格式不会用/bin/sh -c来发起,所以常见的shell

    操作像变量替换和通配符替换不会进行;如果你运行的命令依赖shell特性,可以替换成类型以下的格式

    RUN ["/bin/bash","-c","<executable>","<param1>"]
    
  • CMD

    容器启动时运行的命令

    语法:

    CMD <command>
    CMD ["<executable>","<param1>","<param2>"]
    CMD ["<param1>","<param2>"]
    

    前两种语法和RUN相同,第三种语法用于为ENTRYPOINT指令提供默认参数

    RUN和CMD区别:

    • RUN指令运行于镜像文件构建过程中,CMD则运行于基于Dockerfile构建出的新镜像文件启动为一个容器的时候。

    • CMD指令的主要目的在于给启动的容器指定默认要运行的程序,且在运行结束后,容器也将终止;不过,CMD命令可以被docker run的命令行选项给覆盖。

    • Dockerfile中可以存在多CMD指令,但是只有最后一个会生效。

  • ENTRYPOINT

    类似于CMD指令功能,用于给容器指定默认运行程序

    语法:

    ENTRYPOINT<command>
    ENTRYPOINT["<executable>","<param1>","<param2>"]
    

    ENTRYPOINT和CMD区别:

    • 和CMD不同的是ENTRYPOINT启动的程序不会被docker run命令指定的参数所覆盖,而且,这些命令行参数会被当做参数传递给ENTRYPOINT指定的程序

      (但是,docker run命令的–entrypoint参数可以覆盖ENTRYPOINT)。

    • docker run命令传入的参数会覆盖CMD指令的内容并且附加到ENTRYPOINT命令最后作为其参数使用。

    • 同样,Dockerfile中可以存在多个ENTRYPOINT指令,但是只有最后一个会生效

    • Dockerfile中如果既有CMD又有ENTRYPOINT,并且CMD是一个完整可执行命令,那么谁在最后谁生效。

3. 实例:mysql

docker pull mysql:5.7\#创建三个要挂载的目录↓mkdir -p /my/mysql/confmkdir -p /my/mysql/datamkdir -p /my/mysql/logs\#复制文件并修改字符(安装默认版本的mysql不能在表中输入中文,需要修改配置文件)↓docker cp mysql:/etc/mysql/mysql.conf.d/mysqld.cnf /my/mysql/conf/vi /my/mysql/conf/mysqld.confcharacter-set-server=utf8\#最终启动命令↓docker run --name mysql -p 3306:3306 -v /my/mysql/conf:/etc/mysql/mysql.conf.d/ \-v /my/mysql/data:/var/lib/mysql \-v /my/mysql/logs:/logs \-e MYSQL_ROOT_PASSWORD=root \-d mysql:5.7

4. Docker网络

Docker允许通过外部访问容器或容器互联的方式来提供网络服务。安装Docker时,会自动安装一块Docker网卡称为docker0,用于Docker各容器及宿主机的网络通信,网段为172.0.0.1。

Docker网络中有三个核心概念:沙盒(Sandbox)、网络(Network)、端点(Endpoint)

  • 沙盒:提供了容器的虚拟网络栈,也即端口套接字、IP路由表、防火墙等内容。隔离容器网络与宿主机网络,形成了完全独立的容器网络环境。
  • 网络:可以理解为Docker内部的虚拟子网,网络内的参与者相互可见并能够进行通讯。Docker的虚拟网络和宿主机网络是存在隔离关系的,其目的主要是形成容器间的安全通讯环境。
  • 端点:位于容器或网络隔离墙之上的洞,主要目的是形成一个可以控制的突破封闭的网络环境的出入口。当容器的端点与网络的端点形成配对后,就如同在这两者之间搭建了桥梁,便能够进行数据传输了。

Docker的四种网络模式:

  • Bridge(默认):桥接模式,主要用来对外通信的,docker容器默认的网络使用的就是bridge。

    使用bridge模式配置容器自定的网络配置:

    # 配置容器的主机名

    docker run --name t1 --network bridge -h [自定义主机名] -it --rm busybox

    # 自定义DNS

    docker run --name t1 --network bridge --dns114.114 -it–rm busybox

    # 给host文件添加一条

    docker run --name t1 --network bridge --add-host [hostname]:[ip] -it --rm busybox

  • Host:host类型的网络就是主机网络的意思,绑定到这种网络上面的容器,内部使用的端口直接绑定在主机上对应的端口,而如果容器服务没有使用端口,则无影响。

  • None:从某种意义上来说,none应该算不上网络了,因为它不使用任何网络,会形成一个封闭网络的容器。

  • Container:共享另外一个容器的network namespace,和host模式差不多,只是这里不是使用宿主机网络,而是使用的容器网络。

开放端口:

  • -p 选项的使用:

    -p

    ​ #将指定的容器端口映射到主机所有地址的一个动态端口

    -p :

    ​ #将容器端口映射到指定的主机端口

    -p ::

    ​ #将指定的容器端口映射到主机指定的动态端口

    -p ::

    ​ #将指定的容器端口映射至主机指定的端口

    ​ #动态端口指随机端口,可以使用docker port命令查看具体映射结果

  • -P(大写) 暴露所有端口(所有端口指构建镜像时EXPOSE的端口)

  • 自定义docker0桥的网络属性信息:/etc/docker/daemon.json文件:

    {

    ​ “bip”: “192.168.1.5/24”

    }

    核心选项为bip,即bridge ip之意,用于指定docker0桥自身的IP地址;其它选项可通过此地址计算得出远程连接创建自定义的桥

    docker network create -d bridge --subnet"172.26.0.0/16"–gateway"172.26.0.1" mybr0

5. Compose

  • 简介:

    Compose的作用是“定义和运行多个Docker容器的应用”。使用Compose,你可以在一个配置文件(yml格式)中配置你应用的服务,然后使用一个命令,即可创建并启动配置中引用的所有服务。

  • Compose中两个重要概念:

    • 服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。

    • 项目 (project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml文件中定义。

  • 使用Docker Compose编排SpringCloud微服务:

    使用docker-compose一次性来编排三个微服务:eureka服务(eureka-server-2.0.2.RELEASE.jar)、user服务(user-2.0.2.RELEASE.jar)、power服务(power-2.0.2.RELEASE.jar)

    1. 创建一个工作目录和docker-compose模板文件。

    2. 工作目录下创建三个文件夹eureka、user、power,并分别构建好三个服务的镜像文件以eureka的Dockerfile为例:

    # 基础镜像
    FROM java:8
    # 作者
    MAINTAINER huaan
    # 把可执行jar包复制到基础镜像的根目录下
    ADD eureka-server-2.0.2.RELEASE.jar /eureka-server-2.0.2.RELEASE.jar
    # 镜像要暴露的端口,如要使用端口,在执行docker run命令时使用-p生效
    EXPOSE 3000
    # 在镜像运行为容器后执行的命令
    ENTRYPOINT ["java","-jar","/eureka-server-2.0.2.RELEASE.jar"]
    

    目录文件结构:

    composedocker-compose.ymleurekaDockerfileeureka-server-2.0.2.RELEASE.jaruserDockerfileuser-2.0.2.RELEASE.jarpowerDockerfilepower-2.0.2.RELEASE.jar
    
    1. 编写docker-compose模板文件:
    version: '1'
    services:  eureka:    image: eureka:v1    ports: - 8080:8080  user: image: user:v1    ports: - 8081:8081  power:    image: power:v1    ports: - 8082:8082
    
    1. 启动微服务,可以加上参数-d后台启动 -f指定启动文件
    docker-compose up -d -f /compose_lnmp/kafka/docker-compose.yml
    
    1. 查看服务日志
    docker-compose logs -f /compose_lnmp/kafka/docker-compose.yml
    
    1. 查看编排服务状态
    docker-compose ps -f /compose_lnmp/kafka/docker-compose.yml
    
    1. 资源回收
    docker-compose down -f /compose_lnmp/kafka/docker-compose.yml
    
    1. 停止编排服务
    docker-compose stop -f /compose_lnmp/kafka/docker-compose.yml
    

十、Redis

https://www.yuque.com/haolonglong/qyws71/mz7i9f

1. redis是什么?

​ redis可以理解就是一个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向。另外,redis 也经常用来做分布式锁。redis 提供了多种数据类型来支持不同的业务场景。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。

2. Redis支持的数据类型

https://blog.csdn.net/javaxiaibai0414/article/details/88666453

  • String字符串 :

    格式:set key value

    string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。

    string类型是Redis最基本的数据类型,一个键最大能存储512MB。

  • Hash(哈希):

    格式:hmset name key1 value1 key2 value2

    Redis hash 是一个键值对集合(name==>(key=>value))

    Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

  • List(列表) :

    • Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

      格式:lpush name value

    • 在 key 对应 list 的头部添加字符串元素

      格式: rpush name value

    • 在 key 对应 list 的尾部添加字符串元素

      格式: lrem name index

    • key 对应 list 中删除 count 个和 value 相同的元素

      格式: llen name

      返回 key 对应 list 的长度

  • Set(集合) :

    格式:sadd name value

    Redis的Set类型是String类型的无序集合

    集合是通过哈希表实现的,所以添加、删除、查找的空间复杂度都是O(1)。

  • zset(sorted set:有序集合):

    格式:zadd name value score value

    Redis zset 和 set一样也是string类型的集合,且不允许值重复。

    不同的是zset每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的元素从小到大排序。

    zset的值(value)是唯一的,但分数(score)可以是重复的。

3. Redis实现分布式锁

https://www.cnblogs.com/gxyandwmm/p/9588383.html

  • Redis分布式锁实现的三个核心要素:

    • 加锁

    • 解锁

    • 锁超时

  • 上面的功能,存在着三个致命问题:

    • setnx和expire的非原子性

      if(setnx(key,1) == 1){  //此处挂掉了.....expire(key,30)try {do something ......}catch(){}finally {del(key)}}
      

      设想一个极端场景,当某线程执行setnx,成功得到了锁:

      setnx刚执行成功,还未来得及执行expire指令,节点1 Duang的一声挂掉了。

      这样一来,这把锁就没有设置过期时间,变得“长生不老”,别的线程再也无法获得锁了。

      怎么解决呢?setnx指令本身是不支持传入超时时间的,Redis 2.6.12以上版本为set指令增加了可选参数,伪代码如下:set(key,1,30,NX),这样就可以取代setnx指令。

    • 超时后使用del 导致误删其他线程的锁

      加锁:
      String threadId = Thread.currentThread().getId()
      set(key,threadId ,30,NX)
      doSomething.....解锁:
      if(threadId .equals(redisClient.get(key))){del(key)
      }
      

      又是一个极端场景,假如某线程成功得到了锁,并且设置的超时时间是30秒。

      如果某些原因导致线程A执行的很慢很慢,过了30秒都没执行完,这时候锁过期自动释放,线程B得到了锁。

      随后,线程A执行完了任务,线程A接着执行del指令来释放锁。但这时候线程B还没执行完,线程A实际上删除的是线程B加的锁

      怎么避免这种情况呢?可以在del释放锁之前做一个判断,验证当前的锁是不是自己加的锁。

      至于具体的实现,可以在加锁的时候把当前的线程ID当做value,并在删除之前验证key对应的value是不是自己线程的ID。

      但是,这样做又隐含了一个新的问题,if判断和释放锁是两个独立操作,不是原子性

      我们都是追求极致的程序员,所以这一块要用Lua脚本来实现:

      String luaScript = ‘if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end’;

      redisClient.eval(luaScript , Collections.singletonList(key), Collections.singletonList(threadId));

      这样一来,验证和删除过程就是原子操作了。

          /*** 解锁* @param key* @param value* @return*/public Boolean unlock(String key, String value) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";RedisScript<Boolean> redisScript = new DefaultRedisScript(script,Boolean.class);Boolean result = redisTemplate.execute(redisScript, new StringRedisSerializer(), new FastJsonRedisSerializer(Boolean.class), Collections.singletonList(key),value);if(SUCCESS.equals(result)) {return true;}return false;}
      
    • 出现并发的可能性

      还是刚才第二点所描述的场景,虽然我们避免了线程A误删掉key的情况,但是同一时间有A,B两个线程在访问代码块,仍然是不完美的。

      怎么办呢?我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续航”。

      当过去了29秒,线程A还没执行完,这时候守护线程会执行expire指令,为这把锁“续命”20秒。守护线程从第29秒开始执行,每20秒执行一次。

      当线程A执行完任务,会显式关掉守护线程。

      另一种情况,如果节点1 忽然断电,由于线程A和守护线程在同一个进程,守护线程也会停下。这把锁到了超时的时候,没人给它续命,也就自动释放了。

4. Redis实现消息队列

  • 基于Redis实现消息队列典型方案

    • 基于List的 LPUSH+BRPOP 的实现
      PUB/SUB,订阅/发布模式

    • 基于SortedSet有序集合的实现
      基于 Stream 类型的实现

5. Redis事务

https://www.yuque.com/haolonglong/qyws71/ug0dq6

1 Redis事务的概念:

Redis 提供的不是严格的事务,Redis 只保证串行执行命令,并且能保证全部执行,但是执行命令失败时并不会回滚,而是会继续执行下去。

2 Redis事务没有隔离级别的概念:

批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。

3 Redis不保证原子性:

Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。

4 Redis事务的三个阶段:

  • 开始事务
  • 命令入队
  • 执行事务

5 Redis事务相关命令:

watch key1 key2 … : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )

multi : 标记一个事务块的开始( queued )

exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )

discard : 取消事务,放弃事务块中的所有命令

unwatch : 取消watch对所有key的监控

6 Redis事务使用案例:

(1)正常执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s6hcVYmO-1635003222775)(/…/…/images/1598509994922-c320949c-6844-4077-9abf-09842250404d.png)]

(2)放弃事务

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IFDhVRmi-1635003222815)(/…/…/images/1598509994842-571dd565-671e-4dc4-839c-58c948f5ddca.png)]

(3)若在事务队列中存在命令性错误(类似于java编译性错误),则执行EXEC命令时,所有命令都不会执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tW9ehT7d-1635003222821)(/…/…/images/1598509994902-39f2ab1a-bb54-4ebe-893b-5552f5b55f71.png)]

(4)若在事务队列中存在语法性错误(类似于java的1/0的运行时异常),则执行EXEC命令时,其他正确命令会被执行,错误命令抛出异常。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sh5HmhrQ-1635003222826)(/…/…/images/1598509994961-bece336d-d3c7-49d5-b129-4b969e2b6d7b.png)]

(5)使用watch

案例一:使用watch检测balance,事务期间balance数据未变动,事务执行成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-irH2qm2I-1635003222830)(/…/…/images/1598509994817-31fe4f16-4709-4977-a924-920b6494975c.png)]

案例二:使用watch检测balance,在开启事务后(标注1处),在新窗口执行标注2中的操作,更改balance的值,模拟其他客户端在事务执行期间更改watch监控的数据,然后再执行标注1后命令,执行EXEC后,事务未成功执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-war5D6fl-1635003222833)(/…/…/images/1598509994924-f9cb634d-f508-42e4-8202-5daf502d3a35.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nzxGmJnT-1635003222835)(/…/…/images/1598509995008-d6f8f254-e198-4a77-8d86-57cd2952a3c6.png)]

一但执行 EXEC 开启事务的执行后,无论事务使用执行成功, WARCH 对变量的监控都将被取消。

故当事务执行失败后,需重新执行WATCH命令对变量进行监控,并开启新的事务进行操作。

7 总结:

watch指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。

十一、MQ

1. 消息中间件

消息中间件介绍&为什么要使用消息中间件&什么时候使用消息中间件:

打个比方,我们客户端发送一个下单请求给订单系统(order),订单系统发送了一个请求给我们的库存系统告诉他需要更改库存。我已经下单了,这里,我们可以把每一个请求看作一条消息,但是我们的客户端需要订单系统告诉我们这条消息的处理结果(我们到底有没有下单成功),但是,订单系统不需要知道库存系统对这条消息的处理情况,因为无论你库存系统是否修改成功,我订单还是下了,因为是先下完订单(下成功了)才去修改库存,哪怕库存系统修改出现问题,那也只是库存系统的问题,这个问题不会影响订单系统。****如果这里你能理解的话, 那么我们就能发现 我们用户发送的这条消息(下订单), 是需要同步的(我需要知道结果), 订单发送给库存的消息,是可以异步的(我不想知道你库存到底改了没, 我只是通知你我这边成功下了一个订单)****

那么如果我们还按原来的方式去实现这个需求的话, 那么结果会是这样:

那可能有同学说了, 我们订单系统开辟线程去访问库存系统不就好了吗?

使用线程池解决 确实可以, 但是也有他的缺点, 那么 到底怎么来完美解决这个问题呢?

如果这张图能理解的话, 那么 这个消息系统, 就是我们的消息中间件。

2. MQ的四大作用

异步通信:异步通信,减少线程等待,特别是处理批量等大事务、耗时操作。

系统解耦:系统不直接调用,降低依赖,特别是不在线也能保持通信最终完成。

削峰平谷:压力大的时候,缓冲部分请求消息,类似于背压处理。

可靠通信:提供多种消息模式、服务质量、顺序保障等。

3. RabbitMq

导语:我们刚刚介绍了什么是消息中间件, 那么RabbitMq就是对于消息中间件的一种实现,市面上还有很多很多实现, 比如RabbitMq、ActiveMq、ZeroMq、kafka,以及阿里开源的RocketMQ等等 这里我们主要讲RabbitMq

*AMQP

这里引用百度的一句话 再加以我的理解: AMQP 其实和Http一样 都是一种协议, 只不过 Http是针对网络传输的, 而AMQP是基于消息队列的.

AMQP 协议中的基本概念:

Broker: 接收和分发消息的应用,我们在介绍消息中间件的时候所说的消息系统就是Message Broker。

Virtual host: 出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQ server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange/queue等。

Connection: publisher/consumer和broker之间的TCP连接。断开连接的操作只会在client端进行,Broker不会断开连接,除非出现网络故障或broker服务出现问题。

Channel: 如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销将是巨大的,效率也较低。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。Channel作为轻量级的Connection极大减少了操作系统建立TCP connection的开销。

Exchange: message到达broker的第一站,根据分发规则,匹配查询表中的routing key,分发消息到queue中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)。

Queue: 消息最终被送到这里等待consumer取走。一个message可以被同时拷贝到多个queue中。

Binding: exchange和queue之间的虚拟连接,binding中可以包含routing key。Binding信息被保存到exchange中的查询表中,用于message的分发依据。

*Exchange的类型

direct(路由键模式):

这种类型的交换机的路由规则是根据一个routingKey的标识,交换机通过一个routingKey与队列绑定 ,在生产者生产消息的时候 指定一个routingKey 当绑定的队列的routingKey 与生产者发送的一样 那么交换机会吧这个消息发送给对应的队列。

fanout(广播模式):

这种类型的交换机路由规则很简单,只要与他绑定了的队列, 他就会吧消息发送给对应队列(与routingKey没关系)

topic(主题模式):(因为*在这个笔记软件里面是关键字,所以下面就用’星’替换掉了)

这种类型的交换机路由规则也是和routingKey有关 只不过 topic他可以根据:星,#( 星号代表过滤一单词,#代表过滤后面所有单词, 用.隔开)来识别routingKey 我打个比方 假设 我绑定的routingKey 有队列A和 B A的routingKey是:星.user B的routingKey是: #.user

那么我生产一条消息routingKey 为: error.user 那么此时 2个队列都能接受到, 如果改为 topic.error.user 那么这时候 只有B能接受到了

headers:

这个类型的交换机很少用到,他的路由规则 与routingKey无关 而是通过判断header参数来识别的, 基本上没有应用场景,因为上面的三种类型已经能应付了。

4. kafka

https://mp.weixin.qq.com/s?__biz=MzU1NDA0MDQ3MA==&mid=2247483958&idx=1&sn=dffaad318b50f875eea615bc3bdcc80c&chksm=fbe8efcfcc9f66d9ff096fbae1c2a3671f60ca4dc3e7412ebb511252e7193a46dcd4eb11aadc&scene=21#wechat_redirect

kafka简介

Kafka最初由Linkedin公司开发,是一个分布式的、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常用于web/nginx日志、访问日志、消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。

*kafka的特性

  • 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒;
  • 可扩展性:kafka集群支持热扩展;
  • 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止丢失;
  • 容错性:允许集群中的节点失败(若分区副本数量为n,则允许n-1个节点失败);
  • 高并发:单机可支持数千个客户端同时读写;

*kafka的应用场景

  • 日志收集:一个公司可以用Kafka收集各种服务的log,通过kafka以统一接口开放给各种消费端,例如hadoop、Hbase、Solr等。
  • 消息系统:解耦生产者和消费者、缓存消息等。
  • 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索记录、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
  • 运营指标:Kafka也经常用来记录运营监控数据。
  • 流式处理

*kafka架构

https://www.jianshu.com/p/abbc09ed6703

整体来看,kafka架构中包含四大组件:生产者、消费者、kafka集群、zookeeper集群。对照上面的结构图,我们先来搞清楚几个很重要的术语,(看图!对照图理解~)

1、broker

kafka 集群包含一个或多个服务器,每个服务器节点称为一个broker。

2、topic

每条发布到kafka集群的消息都有一个类别,这个类别称为topic,其实就是将消息按照topic来分类,topic就是逻辑上的分类,同一个topic的数据既可以在同一个broker上也可以在不同的broker结点上。

3、partition

分区,每个topic被物理划分为一个或多个分区,每个分区在物理上对应一个文件夹,该文件夹里面存储了这个分区的所有消息和索引文件。在创建topic时可指定parition数量,生产者将消息发送到topic时,消息会根据 分区策略 追加到分区文件的末尾,属于顺序写磁盘,因此效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证)。

上面提到了分区策略,所谓分区策略就是决定生产者将消息发送到哪个分区的算法。Kafka 为我们提供了默认的分区策略,同时它也支持自定义分区策略。kafka允许为每条消息设置一个key,一旦消息被定义了 Key,那么就可以保证同一个 Key 的所有消息都进入到相同的分区,这种策略属于自定义策略的一种,被称作"按消息key保存策略",或Key-ordering 策略。

同一主题的多个分区可以部署在多个机器上,以此来实现 kafka 的伸缩性。同一partition中的数据是有序的,但topic下的多个partition之间在消费数据时不能保证有序性,在需要严格保证消息顺序消费的场景下,可以将partition数设为1,但这种做法的缺点是降低了吞吐,一般来说,只需要保证每个分区的有序性,再对消息设置key来保证相同key的消息落入同一分区,就可以满足绝大多数的应用。

4、offset

partition中的每条消息都被标记了一个序号,这个序号表示消息在partition中的偏移量,称为offset,每一条消息在partition都有唯一的offset,消息者通过指定offset来指定要消费的消息。

正常情况下,消费者在消费完一条消息后会递增offset,准备去消费下一条消息,但也可以将offset设成一个较小的值,重新消费一些消费过的消息,可见offset是由consumer控制的,consumer想消费哪一条消息就消费哪一条消息,所以kafka broker是无状态的,它不需要标记哪些消息被消费过。

5、producer

生产者,生产者发送消息到指定的topic下,消息再根据分配规则append到某个partition的末尾。

6、consumer

消费者,消费者从topic中消费数据。

7、consumer group

消费者组,每个consumer属于一个特定的consumer group,可为每个consumer指定consumer group,若不指定则属于默认的group。

同一topic的一条消息只能被同一个consumer group内的一个consumer消费,但多个consumer group可同时消费这一消息。这也是kafka用来实现一个topic消息的广播和单播的手段,如果需要实现广播,一个consumer group内只放一个消费者即可,要实现单播,将所有的消费者放到同一个consumer group即可。

用consumer group还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic。

8、leader

每个partition有多个副本,其中有且仅有一个作为leader,leader会负责所有的客户端读写操作。

9、follower

follower不对外提供服务,只与leader保持数据同步,如果leader失效,则选举一个follower来充当新的leader。当follower与leader挂掉、卡住或者同步太慢,leader会把这个follower从ISR列表中删除,重新创建一个follower。

10、rebalance

同一个consumer group下的多个消费者互相协调消费工作,我们这样想,一个topic分为多个分区,一个consumer group里面的所有消费者合作,一起去消费所订阅的某个topic下的所有分区(每个消费者消费部分分区),kafka会将该topic下的所有分区均匀的分配给consumer group下的每个消费者,如下图,

rebalance表示"重平衡",consumer group内某个消费者挂掉后,其他消费者自动重新分配订阅主题分区的过程,是 Kafka 消费者端实现高可用的重要手段。如下图Consumer Group A中的C2挂掉,C1会接收P1和P2,以达到重新平衡。同样的,当有新消费者加入consumer group,也会触发重平衡操作。

*****kafka吞吐量为什么这么高

1、顺序读写磁盘

Kafka是将消息持久化到本地磁盘中的,一般人会认为磁盘读写性能差,可能会对Kafka性能提出质疑。实际上不管是内存还是磁盘,快或慢的关键在于寻址方式,磁盘分为顺序读写与随机读写,内存一样也分为顺序读写与随机读写。基于磁盘的随机读写确实很慢,但基于磁盘的顺序读写性能却很高,一般而言要高出磁盘的随机读写三个数量级,一些情况下磁盘顺序读写性能甚至要高于内存随机读写。

2、page cache

为了优化读写性能,Kafka利用了操作系统本身的Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。这样做是因为,

> JVM中一切皆对象,对象的存储会带来额外的内存消耗;

> 使用JVM会受到GC的影响,随着数据的增多,垃圾回收也会变得复杂与缓慢,降低吞吐量;

另外操作系统本身对page cache做了大量优化,通过操作系统的Page Cache,Kafka的读写操作基本上是基于系统内存的,读写性能也得到了极大的提升。

3、零拷贝

零拷贝是指Kafka利用 linux 操作系统的 “zero-copy” 机制在消费端做的优化。首先来看一下消费端在消费数据时,数据从broker磁盘通过网络传输到消费端的整个过程:

> 操作系统从磁盘读取数据到内核空间(kernel space)的page cache;

> 应用程序读取page cache的数据到用户空间(user space)的缓冲区;

> 应用程序将用户空间缓冲区的数据写回内核空间的socket缓冲区(socket buffer);

> 操作系统将数据从socket缓冲区复制到硬件(如网卡)缓冲区;

整个过程如上图所示,这个过程包含4次copy操作和2次系统上下文切换,而上下文切换是CPU密集型的工作,数据拷贝是I/O密集型的工作,性能其实非常低效。

零拷贝就是使用了一个名为sendfile()的系统调用方法,将数据从page cache直接发送到Socket缓冲区,避免了系统上下文的切换,消除了从内核空间到用户空间的来回复制。从上图可以看出,"零拷贝"并不是说整个过程完全不发生拷贝,而是站在内核的角度来说的,避免了内核空间到用户空间的来回拷贝。

4、分区分段

Kafka的message是按topic分类存储的,topic中的数据又是按照一个一个的partition即分区存储到不同broker节点。每个partition对应了操作系统上的一个文件夹,partition实际上又是按照segment分段存储的。这也非常符合分布式系统分区分桶的设计思想。

通过这种分区分段的设计,Kafka的message消息实际上是分布式存储在一个一个小的segment中的,每次文件操作也是直接操作的segment。为了进一步的查询优化,Kafka又默认为分段后的数据文件建立了索引文件,就是文件系统上的.index文件。这种分区分段+索引的设计,不仅提升了数据读取的效率,同时也提高了数据操作的并行度。

总之,Kafka采用顺序读写、Page Cache、零拷贝以及分区分段等这些设计,再加上在索引方面做的优化,另外Kafka数据读写也是批量的而不是单条的,使得Kafka具有了高性能、高吞吐、低延时的特点。

*kafka简易测试命令

  • 进入kafka容器:docker exec -it ${CONTAINER ID} /bin/bash
  • 进入kafka安装目录:cd /opt/kafka/
  • 查看topic列表:bin/kafka-topics.sh --zookeeper localhost:2181 --list
  • 创建一个名为 testk 4分区1副本的topic:bin/kafka-topics.sh --zookeeper localhost:2181 --create --topic testk --partitions 4 --replication-factor 1
  • 查看topic信息:bin/kafka-topics.sh --zookeeper localhost:2181 --describe --topic testk
  • 运行一个从最新数据开始的消费者:bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic testk
  • 运行一个从头开始的消费者:bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --from-beginning --topic testk
  • 运行一个生产者:bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic testk
  • 查看消费者组:bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
  • 查看offset消费情况:bin/kafka-consumer-groups.sh --describe --bootstrap-server localhost:9092 --group console-consumer-81040

5. kafka与rabbitMQ选型对比

RabbitMQ Kafka
开发语言 erlang scala,Java
架构模型 ① 遵循AMQP;② 生产者、消费者、broker。③ broker由exchange、binding、queue组成;④ consumer消费位置由broker通过确认机制保存; ① 不遵循AMQP;② 生产者、消费者、kafka集群、zookeeper集群;③ kafka集群由多个broker节点组成,消息按照topic分类,每个topic又划分为多个partition;④ broker无状态,offset由消费者指定;
可靠性 RabbitMQ可靠性更好,支持事务,支持消息确认机制
高可用 采用镜像队列,即主从模式,数据是异步同步的,当消息过来,主从全部写完后,回ack,这样保障了数据的一致性。 每个分区都有一个或多个副本,这些副本保存在不同的broker上,其中有且仅有一个分区副本作为leader,其余的作为follower,当leader不可用时,会选举follower作为新leader继续提供服务。只有leader提供读写服务,follower从leader同步拉取数据然后备份。
吞吐量 kafka更高
是否支持事务 支持 不支持
负载均衡 需要外部支持才能实现(如:loadbalancer) kafka利用zk和分区机制实现负载均衡
是否支持消费者Push 不支持 支持
是否支持消费者Pull 支持 支持
适用场景 kafka的优势主要体现在吞吐量上,它主要用在高吞吐量的场景。比如日志采集。具有较高的严谨性,数据丢失的可能性更小,同时具备较高的实时性,用在对实时性、可靠性要求较高的消息传递上。

十二、Linux

1. 防火墙

https://blog.csdn.net/SERE0211/article/details/106999838

  • 列出所有端口:netstat -ntlp

  • 开放指定端口:firewall-cmd --zone=public --add-port=8080/tcp --permanent

  • 开放区间端口:firewall-cmd --zone=public --add-port=8080-9000/tcp --permanent

  • 删除端口:firewall-cmd --zone=public --remove-port=8080/tcp --permanent

  • 重新加载配置:firewall-cmd --reload

  • 查看所有打开的端口:firewall-cmd --zone=public --list-ports

  • 查看端口:firewall-cmd --zone=public --query-port=8080/tcp

  • 启动服务:systemctl start firewalld

  • 关闭服务:systemctl stop firewalld

  • 重启服务:systemctl restart firewalld

  • 查看状态:systemctl status firewalld

  • 开启启动:systemctl enable firewalld

  • 开机禁用:systemctl disable firewalld

  • 查看已启动的服务列表:systemctl list-unit-files | grep enabled

    firewall-cmd防火墙管理工具常用参数:

    –permanent #表示设置为永久
    –state #查看服务状态
    –get-default-zone #查询默认的区域名称
    –set-default-zone= #设置默认的区域,不需要reload或者重启服务,是即改即生效,并且是永久的
    –get-zones #显示可用的区域名称
    –get-services #显示预先定义的服务
    –get-active-zones #显示当前正在使用的区域与网卡名称
    –add-source=/ #将来源于此IP或子网的流量导向指定的区域,例如:–add-source=192.168.2.110/24
    –remove-source=/ #不再将此IP或子网的流量导向某个指定区域,例如:–remove-source=192.168.2.110/24
    –add-interface= #将来自该接口的所有流量到指定区域,例如:–add-interface=eth0
    –change-interface= #将接口已有绑定区域而与其他区域关联,例如:–change-interface=eth0
    –list-all #列出默认区域的所有配置(接口、源、服务和端口)
    –list-all-zones #列出所有区域的所有配置(接口、源、服务和端口)
    –add-service= #设置默认区域允许该服务,例如:–add-service=ssh
    –remove-service= #设置默认区域不再允许该服务,例如:–remove-service=ssh
    –add-port=/ #添加允许该端口,例如:–add-port=8080/tcp
    –remove-port=/ #删除允许该端口,例如:–remove-port=8080/tcp
    –reload #让永久生效的配置规则立即生效
    –panic-on #开启应急状况模式(阻断所有的网络连接)
    –panic-off #关闭应急状况模式(阻断所有的网络连接)

2. SVN

https://www.cnblogs.com/zhongyehai/p/10620058.html

3. FTP

https://cshihong.github.io/2018/10/25/FTP%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%90%AD%E5%BB%BA%E5%92%8C%E9%85%8D%E7%BD%AE/

安装FTP,要求服务端口为3333,自动分配端口为7000-8000区间端口。

安装完毕后,需要root用户修改/etc/vsftpd/路径下vsftpd.conf配置:

禁止匿名登录:anonymous_enable=NO

启用本地系统用户:local_enable=YES

启动写入权限:write_enable=YES

本地用户上传文件的权限掩码:local_umask=002

显示目录说明文件:dirmessage_enable=YES

记录ftp传输过程:xferlog_enable=YES

日志路径:xferlog_file=/var/log/xferlog

使用标准的ftp xferlog:xferlog_std_format=YES

以独立运行的方式监听服务:listen=YES

默认服务端口:listen_port=3333

自动分配端口:pasv_enable=YES

最小被动端口:pasv_min_port=7000

最大被动端口:pasv_max_port=8000

#将列表中用户锁定在自己的home目录上:chroot_list_enable=YES

#锁定用户列表:chroot_list_file=/etc/vsftpd/chroot_list

listen_ipv6=NO

listen_port=3333

pam_service_name=vsftpd

userlist_enable=YES

tcp_wrappers=YES

因为前面安装了python3,建立了软连接指向python,而防火墙的firewall-cmd和firewall文件只支持python2。所以需要将/usr/bin/firewall-cmd与/usr/sbin/firewalld中的第一行

#!/usr/bin/python 改为 #!/usr/bin/python2

防火墙开放3333端口:

firewall-cmd --zone=public -add-port=3333/tcp --permanent

防火墙开放7000-8000区间端口:

firewall-cmd --zone=public -add-port=7000-8000/tcp --permanent

重启防火墙配置:

Firewall-cmd --reload

启动ftp服务

systemctl start vsftpd

chkconfig vsftpd on

4. 查询命令

查看CPU:ps aux --sort=-pcpu |head -10

查询字符集:locale

查询CPU使用情况:top

查看磁盘使用情况:df -h

查看指定目录磁盘使用情况:df -h /home/rdmapp/

查看指定目录下每个文件夹磁盘使用情况:du --max-depth=1 -h /home/rdmapp/

查询zlib版本:rpm -qa|grep zlib-devel

查询python版本:python --version

查询setuptools版本:pip show setuptools

5.安装命令

rpm包安装:rpm -ivhU zlib*.rpm --nodeps --force

whl包安装:pip install *.whl --force-reinstall

压缩包安装:python setup.py install

6.卸载

rpm包卸载:rpm -e zlib*.rpm

mysql卸载:yum remove mysql

pip卸载:python -m pip uninstall pip

使用pip卸载:pip uninstall pandas

7.压缩/打包

zip test.zip test1 test2

tar zxvf backports.lzma-0.0.14.tar.gz

tar -cvf test.tar test

tar -zcvf test.tar.gz test

8.解压/解包

unzip test.zip

unzip -d /tmp/ test.zip

tar -xvf test.tar

tar -zxvf test.tar.gz test

9.字符集

locale

export LANG=zh_CN.UTF-8

export LC_ALL=zh_CN.UTF-8

10.用户/分组/权限

新增用户:useradd xiaoming

设置用户密码:passwd xiaoming(回车后输入新密码)

删除用户:userdel xiaoming

删除用户及用户home目录:userdel -r xiaoming

添加“开发”分组:groupadd dev

删除“开发”分组:groupdel dev

修改分组列表:vi /etc/group

将“xiaoming”添加到“开发”和“测试”分组:usermod -G dev,test xiaoming

将“xiaoming”主组改为master组:usermod -g master xiaoming

修改权限:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WIXmMVxU-1635003222853)(images/wps87A1.tmp.jpg)]

Linux的文件调用权限分为三级:文件所有者、用户组、其他用户

- :文件d:目录 r:读 4 w:写 2 x:执行 1

rwx rwx rwx -->777

rwx rwx r-x -->775

rwx r-x r-x -->755

Owner Group OtherUsers

将文件设置为拥有者及同组的用户可写入,其他用户不允许写入:

chmod ug+w,o-w file.txt file2.txt

or

chmod 775 file.txt file2.txt

将文件设置为所有人可执行:

chmod +x _bz2.cpython-36m-x86_64-linux-gnu.so

设置指定目录及子目录下权限为775:

chmod -R 775 /home/rdm-ftp/

修改文件属主为daraexch用户:

chown -R dataexch:dataexch /home/rdmapp/datafiles/input

11.软连接

建立软连接:

ln -s /usr/local/python3/bin/pip3 /usr/bin/pip

java开发 三年经验相关推荐

  1. 浅谈三层架构 通过这个,+Java开发模式经验。终于相通了,动软到底是为什么这么做...

    浅谈三层架构 收藏 自己理解的原理 http://www.cnblogs.com/mahaisong/archive/2011/05/12/2044665.html 浅谈三层架构  通过这个,+Jav ...

  2. 2年java开发工作经验

    最近换了个公司,从三月底开始面,面到四月底,面了有快二十家公司.我是一个喜欢总结经验的人,每经过一场面试,我在回来的路上都会仔细回想今天哪些问题可以答的更好,或者哪些问题是自己之前没遇到过的,或者是哪 ...

  3. 2年Java开发工作经验面试总结

    最近换了个公司,从三月底开始面,面到四月底,面了有快二十家公司.我是一个喜欢总结经验的人,每经过一场面试,我在回来的路上都会仔细回想今天哪些问题可以答的更好,或者哪些问题是自己之前没遇到过的,或者是哪 ...

  4. 两年Java开发工作经验面试总结

    微信搜索[程序员囧辉],关注这个坚持分享技术干货的程序员. 我的最新文章:BAT 老兵的经验之谈,成长路上这个道理越早知道越好 动机: 最近换了个公司,从三月底开始面,面到四月底,面了有快二十家公司. ...

  5. 谈谈我在阿里做Java开发三年半的感受和心得

    正好在离职交接空档期,就抽空简单分享自己的一些个人经历给大家,希望对刚毕业不久或者工作三五年的同学能有一些帮助(别问我为什么年终奖不要了,问就是急着回家过年). 个人经历 正文前先来一些简单的自我介绍 ...

  6. java开发项目经验_Java项目经验——程序员成长的钥匙

    今天给大家分享一篇Java项目经验,希望可以给正在成长中的新手Java程序员一个学习的方向! Java是用来做项目的!Java的主要应用领域就是企业级的项目开发!要想从事企业级的项目开发,你必须掌握如 ...

  7. Java 开发实习经验

    半年的实习结束了,总结了一下自己实习经验,希望对即将入职场的同学有些启发. 1.勤记录 进入一个项目组,有一系列需要熟悉的东西,比如文件名,函数,环境配置,或者是分配的任务. 这些基础的东西反复问别人 ...

  8. Java开发面试经验

    Java开发面试两年经验 一.反射是什么? 二.Java如何遍历Map集合? 一.反射是什么? 很多人会说我直接new一个对象不就完了么,干嘛还用反射来获取对象.因为new属于静态编译,而反射属于动态 ...

  9. 2019年初Java开发面试经验(南京)

    背景:16年6月份本科毕业,两份工作经验,第一份半年,第二份两年. 个人技术栈:熟悉常用js和jquery编程,熟悉Maven,svn等工具的使用,熟练掌握web前后端交互,掌握javaSE编程,掌握 ...

最新文章

  1. 编写程序将字符串中最长的单词输出
  2. oracle 增长型分区,oracle 11g 分区表创建(自动按年分区)
  3. 马思伟:视频领域是个海洋,可以游泳、冲浪、潜水和远航
  4. Spring Cloud 一:注册中心
  5. 使用Tensorflow进行脸部识别
  6. java inner class_Java Inner Class 内部类
  7. IDEA中Ctrl+Shift+f快捷键无效的解决方式
  8. VC中调用 Excel 的总结
  9. Qt之QSS使用与基本语法
  10. ZYF loves binary(dp)
  11. Java实现对已有的PDF添加页码
  12. 数据分析学习笔记(二)数据分析三思维七技巧
  13. 2023Java 并发编程面试题
  14. 作为一个精神病人是一种怎样的体验?
  15. 模板--二分图最大匹配
  16. Redis以及Jedis的GEO地图功能
  17. 基于物联网流量指纹的安全威胁轻量级检测方法
  18. Unity——子弹跟踪
  19. 怎么发表计算机论文,潮州发表计算机论文写作方法,怎么发表论文
  20. Cisco VTP配置

热门文章

  1. 行业分析报告|线上教育技术市场现状及未来发展趋势
  2. Java忽略返回字段
  3. python网络数据采集 Tesseract
  4. java压缩字符串_Java压缩字符串的方法收集
  5. 397_压缩图片到一定大小(质量)
  6. 大数据东风下,Clickhouse这坨屎是怎么上天的
  7. 树莓派连接笔记本当外接显示屏使用
  8. 人与人的区别在于八小时之外如何运用
  9. 一代互联网人,知了天命后
  10. 买了云服务器不会用?写了网站不会部署?超详细springboot+vue前后端项目部署教程来啦