首页 文章 精选 留言 我的

精选列表

搜索[知识管理],共10010篇文章
优秀的个人博客,低调大师

Python机器学习小知识:lambda

关于Lambda的定义:Lambdas are one line functions. They are also known as anonymous functions in some other languages. You might want to use lambdas when you don’t want to use a function twice in a program. They are just like normal functions and even behave like them. 大意为:lambda函数也叫匿名函数,即没有具体名称的函数,它允许快速定义单行函数,可以用在任何需要函数的地方。这区别于def定义的函数。 lambda调用方式为:lambda [arg1 [, agr2,.....argn]] : expression) lambda与def函数的区别: 1)lambda只是一个表达式,而def则是一个语句。lambda一般用来定义简单的函数,而def可以定义复杂的函数。 2)def创建的方法是有名称的,而lambda没有。 3)lambda会返回一个函数对象,但这个对象不会赋给一个标识符,而def则会把函数对象赋值给一个函数名。 4)lambda表达式的冒号(:)后面,只能有一个表达式,def则可以有多个。 5)像if或for或print等语句不能用于lambda中,def可以。 例如:add = lambda x, y: x + y print(add(3, 5)) Output: 8 List排序a = [(1, 2), (4, 1), (9, 10), (13, -3)]a.sort(key=lambda x: x[1])print(a) Output: [(13, -3), (4, 1), (1, 2), (9, 10)]

优秀的个人博客,低调大师

【Vue_01】基础知识

一、Vue 介绍 1. 作者介绍 2. Vue 简介 ① Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。 ② Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。 ③ Vue 借鉴了 Angular 的模板和双向绑定技术;借鉴了 react 的组件化和虚拟 DOM 技术。 ④ MVVM 模式是 Model-View-ViewModel 的缩写,Model 代表数据模型,定义数据操作的业务逻辑,View 代表视图层,负责将数据模型渲染到页面上,ViewModel 通过双向绑定把 View 和 Model 进行同步交互,不需要手动操作DOM 的一种设计思想 3. MVVM 介绍 M: 即Model,模型,包括数据和一些基本操作 V: 即View,视图,页面渲染结果 VM:即View-Model,模型与视图间的双向操作(无需开发人员干涉) 在MVVM之前,开发人员从后端获取需要的数据模型,然后要通过DOM操作Model渲染到View中。而后当用户操作视图,我们还需要通过DOM获取View中的数据,然后同步到Model中。 而MVVM中的VM要做的事情就是把DOM操作完全封装起来,开发人员不用再关心Model和View之间是如何互相影响的, 只要我们Model发生了改变,View上自然就会表现出来。当用户修改了View,Model中的数据也会跟着改变。把开发人员从繁琐的DOM操作中解放出来,把关注点放在如何操作Model上。 二、快速使用 1. Vue 起步 创建一个 HTML 文件,引入: <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- 生产环境版本,优化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue"></script> 【注意】使用自闭合标签可能会出现问题 2. 声明式渲染 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>第一个程序</title> <script src="vue.js" ></script> </head> <body> <div id="app"> <!-- 插值表达式 --> <h2>姓名:{{name}},年龄:{{age}}</h2> </div> <script> // 创建vue实例 var app = new Vue({ el:"#app", // el即element,该vue实例要渲染的页面元素 data:{ // 渲染页面需要的数据 name : "王大锤", age : 250 } }); </script> </body> </html> 此时我们已经成功创建了一个 Vue 应用,打开 HTML 页面会显示 Hello Vue! 。这就是声明式渲染,Vue 的核心是:采用简洁的模板语法声明式地将数据渲染进 DOM 的系统。这里的核心思想就是没有繁琐的 DOM 操作,例如 jQuery 中,我们需要先找到 div 节点,获取到 DOM 对象,然后进行一系列的节点操作。 三、Vue实例 1. 创建Vue实例 var app = new Vue({ // Vue实例 el:"#app", // Vue可以对id为app的div元素进行渲染,该div之外的无法控制 data:{ // 数据,存放可能会用到的数据 name : "王大锤", age : 250 }, methods: { // 方法,声明可能会用到的方法 test() { console.log("测试方法"); } } }); 2. 双向绑定 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>第一个程序</title> <script src="vue.js" ></script> </head> <body> <div id="app"> 请输入年龄:<input type="text" v-model="age"> <h2>姓名:{{name}},年龄:{{age}}</h2> </div> <script> var app = new Vue({ el:"#app", data:{ name : "王大锤", age : 250 } }); </script> </body> </html> 其中 input 的的变化可以影响到 age,age 的变化也可以影响到 input 3. 生命周期 【生命周期钩子函数】 beforeCreated:在 Vue 实例化之前调用,也可以将他理解为初始化函数。在 Vue1.0 时,这个函数的名字就是 init。 created:在创建实例之后进行调用。 beforeMount:页面加载完成,没有渲染。此时页面还是 {{name}} mounted:他的功能就是:在 dom 文档渲染完毕之后将要执行的函数,该函数在 Vue1.0 版本中名字为 compiled。可以将他理解为原生 js 中的 window.onload=function({.,.}) ,所以也可以理解为 jquery 中的 $(document).ready(function(){….})。此时页面中的 {{name}} 已被渲染 beforeDestroy:该函数将在销毁实例前进行调用 。 destroyed:改函数将在销毁实例后进行调用。 beforeUpdate:组件更新之前。 updated:组件更新之后。 4. this var app = new Vue({ el:"#app", data:{ num : 10 }, methods: { test() { let a = num; // 拿不到值 let b = this.num ++; // 可以拿到值 } } }); 四、模板语法 1. 插值表达式 {{ }} <div id="app"> <h2>姓名:{{name}},年龄:{{age}}</h2> </div> 2. v-model <div id="app"> <!-- 双向绑定 --> <input type="text" v-model="num"> <span>{{num}}</span> </div> <script> var app = new Vue({ el:"#app", data:{ num : 200 } }); </script> 3. v-test / v-html <div id="app"> <!-- 纯文本展示 --> <span v-text="num"></span> <!-- 解析为html --> <span v-html="num"></span> </div> <script> var app = new Vue({ el:"#app", data:{ num : "<h1>Hello Vue!</h1>" } }); </script> 4. v-on 语法 <div id="app"> {{num}} <br/> <!-- 绑定事件 --> <button v-on:click="add">add</button> <!-- 简写形式 --> <button @click="add">add</button> </div> <script> var app = new Vue({ el:"#app", data:{ num : 1 }, methods: { add() { this.num ++; } } }); </script> 事件修饰符 .prevent:阻止默认事件发生 <!-- 阻止默认链接跳转,使用 add 方法处理该事件,也可不处理 --> <a href="" @click.prevent="add">链接</a> 5. v-for 语法 <div id="app"> <lu> <!-- 遍历集合 --> <li v-for="item in list"> {{item}} </li> <hr/> <!-- 遍历集合,拿到索引 --> <li v-for="(item, index) in list"> {{index + 1}}---{{item}} </li> <hr/> <!-- 遍历对象,拿到 key,拿到索引 --> <li v-for="(item, key, index) in user"> {{index + 1}}---{{key}}---{{item}} </li> </lu> </div> <script> var app = new Vue({ el:"#app", data:{ list: ["唐三藏", "熏悟空", "猪八盖", "洒悟净", "小百龙"], user: {name: "王大锤", age: 23} } }); </script> 注意 我们一般会加 key 来提高效率,理想的 key 是每项都有且唯一的 <li v-for="(item, index) in lsit" :key="index"> {{index + 1}}---{{item}} </li> 6. v-if 语法 <div id="app"> <span v-if="flag">10086</span> <span v-if="!flag">10001</span> </div> <script> var app = new Vue({ el:"#app", data:{ flag: true } }); </script> 注意 v-if 和 v-for 在一起使用时,v-for 的优先级高于 v-if <div id="app"> <span v-for="item in list" v-if="item.age > 24">{{item}}</span> </div> <script> var app = new Vue({ el:"#app", data:{ list: [ {name: "001", age: 23}, {name: "002", age: 24}, {name: "003", age: 25} ] } }); </script> v-else-if、v-else 可以与 v-if 连用,但是之间不允许出现任何其他标签 <div id="app"> <!-- 中间间不允许出现任何其他标签 --> <!-- ########## --> <span v-if="num > 80">优秀</span> <span v-else-if="num > 60">及格</span> <span v-else>不合格</span> <!-- ########## --> </div> <script> var app = new Vue({ el:"#app", data:{ num: 61 } }); </script> 7. v-show 与 v-if 类似,但是 v-show 不满足条件时是将标签隐藏 8. v-bind 不使用 v-bind 时,变量会被认为时字符串,而不会解析为值 <!-- 动态绑定 img 地址 --> <div id="app"> <!-- 响应的更新属性 --> <img v-bind:src="url"> <!-- 简写形式 --> <img :src="url"> </div> <script> var app = new Vue({ el:"#app", data:{ url: "http://pic1.win4000.com/pic/0/4a/ff62f14ce3.jpg" } }); </script> --------------------------- <!-- 动态改变字体大小 --> <div id="app"> <!-- 此处要使用驼峰命名法 即:fontSize 而不能使用 font-size --> <span v-bind:style="{fontSize:size}">你好</span> <button @click="add">加大</button> </div> <script> var app = new Vue({ el:"#app", data:{ size: "10px", num: 10 }, methods: { add() { this.num += 5, this.size = this.num + "px" } } }); </script> 五、常用特性 1. 计算属性 computed 计算属性用法与方法相似,但是计算属性有缓存数据,只有在它的相关依赖发生改变时才会重新求值。而且计算属性一定要有返回值。 <div id="app"> {{add}} </div> <script> var app = new Vue({ el:"#app", computed: { add() { return 10 } } }); </script> 2. 监听器 watch <div id="app"> <input type="text" v-model="msg"> </div> <script> var app = new Vue({ el:"#app", data: { msg: "" }, watch: { <!-- 监听 msg --> msg(newValue, oldValue) { console.log("旧数据:" + oldValue + "\t" + "新数据:" + newValue) } } }); </script>

优秀的个人博客,低调大师

Java并发核心知识讲解

并发编程是Java程序员最重要的技能之一,也是最难掌握的一种技能。它要求编程者对计算机最底层的运作原理有深刻的理解,同时要求编程者逻辑清晰、思维缜密,这样才能写出高效、安全、可靠的多线程并发程序。 一、共享性数据共享性是线程安全的主要原因之一。如果所有的数据只是在线程内有效,那就不存在线程安全性问题,这也是我们在编程的时候经常不需要考虑线程安全的主要原因之一。但是,在多线程编程中,数据共享是不可避免的。最典型的场景是数据库中的数据,为了保证数据的一致性,我们通常需要共享同一个数据库中数据,即使是在主从的情况下,访问的也同一份数据,主从只是为了访问的效率和数据安全,而对同一份数据做的副本 二、原子性原子性就是指对数据的操作是一个独立的、不可分割的整体。换句话说,就是一次操作,是一个连续不可中断的过程,数据不会执行的一半的时候被其他线程所修改。保证原子性的最简单方式是操作系统指令,就是说如果一次操作对应一条操作系统指令,这样肯定可以能保证原子性。但是很多操作不能通过一条指令就完成。 三、互斥性资源互斥是指同时只允许一个访问者对其进行访问,具有唯一性和排它性。我们通常允许多个线程同时对数据进行读操作,但同一时间内只允许一个线程对数据进行写操作。所以我们通常将锁分为共享锁和排它锁,也叫做读锁和写锁。如果资源不具有互斥性,即使是共享资源,我们也不需要担心线程安全。 四、有序性 为了提高性能,编译器和处理器可能会对指令做重排序。重排序可以分为三种: (1)编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。 (2)指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。 (3)内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行 五、可见性要理解可见性,需要先对JVM的内存模型有一定的了解,JVM的内存模型与操作系统类似 本文对Java 并发编程中的理论基础进行了讲解,有些东西在后续的分析中还会做更详细的讨论,如可见性、顺序性等。后续的文章都会以本章内容作为理论基础来讨论。如果大家能够很好的理解上述内容,相信无论是去理解其他并发编程的文章还是在平时的并发编程的工作中,都能够对大家有很好的帮助。 参考资料下载:https://pan.baidu.com/s/1qiPjCaoY490QyXBsshP9qA 提取码: ed7p

优秀的个人博客,低调大师

java部分基础知识汇总

1、受检异常和非受检异常的区别? 所有的异常都是继承至Throwable,包括Error和Exception两个大类 Error: 不用捕获,通常是一些底层和硬件的错误,与程序本身无关Exception: 非受检异常:程序本身的异常,如果不主动捕获的,会由jvm去进行处理 受检异常:IOException/SQLException ,必须要去捕获的异常 当发生异常时,可以通过try catch去捕获异常,或通过throws抛出去。 eg:在用户修改密码时,对于开放的临时用户的密码时不允许修改的,修改的时候可以抛出RejectException这样去做,之前调用的代码就会发生变化,因为异常是主逻辑的补充逻辑,修改一个补充逻辑就改了主逻辑,这样是不行的实现类的变更会影响到调用者,这样破坏了封装性。因为一行代码多个捕获条件,使用降低了代码的可读性。这是在使用受检异常时可能会引发的问题。 什么时候使用受检异常? 例如 IOException/SQLException ,必须要去捕获的异常 。 受检异常可以转化为非受检异常,当受检异常威胁到系统的安全性、稳定性的时候必须要去处理。2、软引用、弱引用、虚引用、强引用的对比? 强引用:new Object(); 不会被回收,宁愿抛出OOM异常也不去回收。 public class ReferenceDemo { static Object strongRef = new Object(); public static void main(String[] args) { Object obj = strongRef; strongRef=null; System.gc(); System.out.println("gc之后:"+obj); } } 之前当前这个类会卸载的时候才会被回收,当obj=null的时候可以被回收。回收的机制有两个:脱离作用域、这个对象被设置为null的时候。 软引用:SoftReference,用来描述一些有用但并不是必需的对象,对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。弱引用:WeakReference,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。 WeakReference<String> sr = new WeakReference<String>(new String("hello")); System.out.println(sr.get()); System.gc(); //通知JVM的gc进行垃圾回收 System.out.println(sr.get()); 虚引用:(PhantomReference)并不影响对象的生命周期,在任何时候都可能被垃圾回收器回收。。可以用来跟踪对象看时候被释放,释放之后可以做一些处理。 3、Integer的原理? 对于Integer,如果想要交换a和b的值,那么如果用下面的swap方法是无法进行交换的。因为java中是按值传递的。如果传递的是基本类型,那么函数接收的是函数原始值的副本,因此如果改变这个函数的值,只是改变了原始值的副本,而原始值不变.如果传的是引用类型,那么函数接收的是原始引用类型的内存地址而不是值的副本,因此如果改变这个函数的值,会改变原始值. public class Swap { public static void main(String[] args) { Integer a=1,b=2; System.out.println("before:a="+a+",b="+b); swap(a,b); System.out.println("after:a="+a+",b="+b); } //交换两个数的值 public static void swap(Integer i1,Integer i2){ Integer tmp = i1; i1=i2; i2=tmp; } } 结果:before:a=1,b=2after:a=1,b=2 Integer源码中:定义了value是final的。 private final int value; 解决方案:使用反射来处理 //交换两个数的值 public static void swap(Integer i1,Integer i2) throws Exception { Field field=Integer.class.getDeclaredField("value"); field.setAccessible(true); int tmp=i1.intValue(); field.set(i1,i2.intValue()); field.set(i2,tmp); } 结果:before:a=1,b=2after:a=2,b=2 但是反射也会出现一个问题,就是只换成功了一个,另一个却还是2,交换后应该是1了。 我们可以看到Integer a=1;其实有一个装箱操作,相当于Integer a=Integer.valueOf(1);找到到Integer中的这个valueOf里面的操作: public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } 这里可以看到有一个IntegerCache,如果i在这个范围内,就直接从缓存中取值,缓存中的范围是-128到127 static final int low = -128; int h = 127; 引申:在-128在127之间,是true,其他的都是false Integer i1=-129; Integer i2=-129; System.out.println(i1==i2); false Integer i1=1; Integer i2=1; System.out.println(i1==i2); true 最终解决方案:避免进行自动装箱操作 //交换两个数的值 public static void swap(Integer i1,Integer i2) throws Exception { Field field=Integer.class.getDeclaredField("value"); field.setAccessible(true); Integer tmp=new Integer(i1.intValue()); field.set(i1,i2.intValue()); field.set(i2,tmp); } 或者: field.setInt(i1,i2.intValue()); field.setInt(i2,tmp); 4、动态代理的原理? jdk动态代理必须要实现接口,cglib并不需要。 jdk动态代理 public class DynamicProxy implements InvocationHandler { private Object target; public Object bind(Object target){ this.target = target; return Proxy.newProxyInstance( target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("记录日志开始"); Object result = method.invoke(target,args); System.out.println("记录日志结束"); return result; } } CGLIB public class CglibDynamicProxy implements MethodInterceptor { private Object target; public Object getInstance(Object target){ this.target=target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("记录日志开始"); Object result = methodProxy.invoke(o,objects); System.out.println("记录日志结束"); return result; } } 5、设计模式的落地 6大原则:单一职责(一个类只做一个事情)、李氏替换(子类可以扩展父类的功能,但不能改变父类原有的功能)、依赖倒置、接口隔离、开闭原则(对扩展开放)、迪米特法则(类之间互相知道的越少越好,将逻辑封装到类的内部) 策略模式:应用场景:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。 特点 :行为型模式 最终执行结果是固定的。执行过程和执行逻辑不一样。 6、List和Set的区别? List: 有序并且允许重复Set: 不允许重复且无序ArrayList: 可变长度动态数组,可以动态扩容 ArrayList的内部结构: transient Object[] elementData; 绕过transient进行序列化,因为通过重写了readObject(java.io.ObjectInputStream s)和writeObject来对elementData进行序列化,数组长度大于0的时候才进行序列化操作。如果这个数组没有长度,则默认是空的,初始化是10。动态扩容每次会递增1.5倍,当删除一个元素的时候让右边的元素每次左移一位,同时将元素size减1 。List允许重复:每次是以数据下标的方式去递增,不会覆盖掉之前的数据。 Set的数据结构:常用的是HashSet和TreeSet 7、ClassLoader加载机制 程序在启动时不是一次性加载所有的类到内存中,而是根据需要的时候再去加载。类装载器经过验证、准备、解析,最后初始化。类装载器分为启动类、扩展类、系统类装载器、自定义装载器。启动顺序是bootstrap->extension->application->user classload分别装载核心类库、/lib/ext下目录的类、classpath。装载类的时候会有双亲委派装载。 8、乐观锁和悲观锁 锁的目的是保证在多线程并行执行的时候对共享资源访问的安全性。乐观锁:它认为每次去获取共享资源的时候是不会有冲突的,不会主动去加锁,是通过CAS或者加versoin来实现。 CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。 version机制: update from table set name=?,version=version+1 where version=0${version} and id=? 悲观锁:每次都加锁,会阻塞线程。 9、AIO、BIO、NIO BIO:Blocking IO 同步阻塞IO(JDK1.4之前都是这种) 同步IO:是否要亲自去监听操作,不断进行关注是否已经完成对应的操作改进方案:在IO操作的时候进行创建线程处理。 NIO:Non-Blocking IO 同步非阻塞IO。Channel、Buffer、Selector先通过Channel登记,等需要进行IO操作的时候再去创建一个Thread进行读写操作。 阻塞的理解:在单线程的环境中,如果IO操作没有完成,那么当前线程就一定会等待在这边,不能做其他操作。 非阻塞的理解: 无论当前IO是否完成,都会去返回一个结果。通过一个缓存来保证。Buffer: 通过数组的方式来实现 ,记录整个数据的移动过程。可以临时去保存这些数据。 总之:同步,要自己不间断的询问是否已经完成非阻塞:IO操作不需要再阻塞在那边,而是直接返回。 AIO: aysnc IO 异步非阻塞IO用户如果触发了IO操作之后,程序这个时候就委托给操作系统来完成,当操作系统完成这个IO操作之后,再告诉自己。 10、spring中对象注入的几种方式和区别? 注解注入属性、构造方法注入、set方法注入 11、mysql的事务隔离级别? ACID:原子性,一致性,隔离性,持久性脏读、不可重复读、幻读 12、 HashMap源码分析? 数组加链表的存储方式 transient Node[] table; hash算法的作用:为了node节点落点的一个前戏计算 经过map组装后的,会优先进入数组中,HashMap的默认数组大小是16,位置是落在0-15之间,通过hash算法,得到数组的下标的整型数,key.hashCode,得到了int32的数,通过高16位和低16位进行一个异或运算。 static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);} put的过程(key,value这边,put时候的流程是什么样的) if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length; 先通过hash算法得到一个result,然后去判断一下当前node节点的数组是否为空,如果为空就先去初始化这个数组的大小,将数组的默认大小16赋值给newCap。 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); 然后根据key,value值组装成node节点,通过hash计算出来的结果与上01111(15),算出来的结果是在0000-01111之间的,也就是0-15之间。这里为什么不用取模, hash%16 和hash&15的性能对比:对于计算机来说,用&的效率会更加高。 然后不为null,如果key相同,只需要把value值进行覆盖就行了;红黑树:在jdk1.8开始使用,因为当链表不断加长之后,当下次put元素之后,要不断对比的值,这样路程会很长,会影响效率,所以不要让链表的长度过长;当过长之后,当超过8之后,就把这个形式转换为红黑树,类似于二叉树,就是会把节点颜色变成红色或者黑色, 最后还需要判断数组的大小,如果数组过大时,还需要进行扩容,扩容因子是0.75,如果使用的容量大于等于12的时候,就进行扩容,2倍扩容,原因是因为需要位置尽可能均匀的分散在每个数组上,要进行异或运算,必须是n的2次幂,让数组的大小为2*n,可以使使用效率增加。 对这个新的数据进行使用,需要将原来数组中的元素移动到新的数组上,1) 数组下面有元素,下面为空的,就直接计算hash值,然后放到新数组上面去,2) 如果数组位置有元素,是红黑树的话, 就将这个红黑树进行拆分((TreeNode)e).split(this, newTab, j, oldCap);3)如果数组下面的是链表,就对它循环遍历,e.hash & oldCap的方式是用于计算在数组中下标的位置,移动的标准是原来的hash倒数第5位为0时,就等于多了一个16的大小,直接省略掉了计算的过程,这样新的链表的节点的位置只有可能在两个地方:1 是在原来的位置 2 原来的位置加上oldCap. Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } 全部源码如下: final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) //如果是红黑树了结构就直接使用红黑树进行处理 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st //如果长度超过8,就讲这个链表转为红黑树处理 treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; //扩容 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; } 红黑树的方式是从jdk1.8中开始引用,在jdk7中:当我们给put()方法传递键和值时,HashMap会由key来调用hash()方法,返回键的hash值,计算Index后用于找到bucket(哈希桶)的位置来储存Entry对象。 当get时要按顺序遍历链表的每个 Entry,直到找到想获取的 Entry 为止——如果恰好要搜索的 Entry 位于该 Entry 链的最末端(该 Entry 是最早放入该 bucket 中),那HashMap必须循环到最后才能找到该元素。 这和红黑树的方式使用相比性能开销很大。 13、红黑树算法 (1)每个节点或者是黑色,或者是红色。(2)根节点是黑色。(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!](4)如果一个节点是红色的,则它的子节点必须是黑色的。(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。 它的时间复杂度是O(lgn), 14、如何停止一个线程 1、interrupt方式 private static int i; public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ while(!Thread.currentThread().isInterrupted()){ i++; } System.out.println("num-"+i); }); thread.start(); TimeUnit.SECONDS.sleep(2); thread.interrupt(); } 2、设置flag的方式 private static int i; private volatile static boolean stop = false; public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ while(!stop){ i++; } System.out.println("num-"+i); }); thread.start(); TimeUnit.SECONDS.sleep(2); stop=true; } 15、Thread.join的实现原理 join:等待线程执行完成,获得一个线程执行结果。原理: public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } 16、ThreadLocal的分析 ThreadLocal的实例代表了一个线程局部的变量,每条线程都只能看到自己的值,并不会意识到其它的线程中也存在该变量。每个线程都是一个一个引用副本。它采用采用空间来换取时间的方式,解决多线程中相同变量的访问冲突问题。 原理: 每个Thread的对象都有一个ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。在该类中,我觉得最重要的方法就是两个:set()和get()方法。当调用ThreadLocal的get()方法的时候,会先找到当前线程的ThreadLocalMap,然后再找到对应的值。set()方法也是一样。 public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } 17、volatile 用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile具有可见性、有序性,不具备原子性。可见性:当多个线程访问同一个变量x时,线程1修改了变量x的值,线程1、线程2…线程n能够立即读取到线程1修改后的值。有序性:即程序执行时按照代码书写的先后顺序执行。在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。volatile会禁止指令重排。volatile不会让线程阻塞,响应速度比synchronized高,这是它的优点。原理是通过内存屏障去实现是,是基于jvm代码中,多了ACC_VOLATILT。18、缓存穿透的原理 当缓存体系建立起来之后,如果每次查询的都不在缓存中,那么数据库就会收到大量的并发请求,例如当在大流量流入时,可能因为频繁访问存储层导致DB直接宕机,这样会形成被人利用不存在的key频繁攻击应用的漏洞。 解决方案: 最为常简的是采用布隆过滤器,将所有可能存在的数据哈希到一个足够发的 bigmap 中,一个一定不存在的数据会被该 bigmap 拦截掉,从而避免对底层存储系统造成查询压力。另一种更为简单的方法,如果一个查询返回的数据为空(无论数据为空,或是系统故障),将空结果进行缓存,设置一个最长不超过五分钟的过期时间。缓存雪崩 设置缓存时采用了相同的过期时间,导致缓存在某时刻同时失效,请求全部转向DB,DB瞬时压力过重雪崩。Redis宕机,导致客户端的请求之间流向DB,拖垮DB。解决方案: 简单方案就是将缓存失效时间分散开,我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。保持缓存层服务器的高可用。 监控、集群、哨兵。当一个集群里面有一台服务器有问题,让哨兵踢出去。依赖隔离组件为后端限流并降级。19、幂等 一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。 业务开发中,经常会遇到重复提交的情况,无论是由于网络问题无法收到请求结果而重新发起请求,或是前端的操作抖动而造成重复提交情况。 在交易系统,支付系统这种重复提交造成的问题有尤其明显,比如: 用户在APP上连续点击了多次提交订单,后台应该只产生一个订单; 向支付宝发起支付请求,由于网络问题或系统BUG重发,支付宝应该只扣一次钱。 很显然,声明幂等的服务认为,外部调用者会存在多次调用的情况,为了防止外部多次调用对系统数据状态的发生多次改变,将服务设计成幂等。 20、数据库和缓存双写如何保证数据一致性 最经典的做法: 1、读的时候,先读缓存,缓存没有的话,那么就读数据库,然后取出数据后放入缓存,同时返回响应;2、更新的时候,先删除缓存,然后再更新数据库。

优秀的个人博客,低调大师

Kubernetes集群部署关键知识总结

Kubernetes集群部署需要安装的组件东西很多,过程复杂,对服务器环境要求很苛刻,最好是能连外网的环境下安装,有些组件还需要连google服务器下载,这一点一般很难满足,因此最好是能提前下载好准备的就尽量下载好。 Kubernetes集群部署要求 服务器必须是Centos 7.2及以上 Kubernetes采用1.12版本 Docker-ce v17.03.2 Etcd 3.2.9 Flanneld v0.7.0-amd64 TLS 认证通信(所有组件,如etcd、kubernetes master 和node) RBAC 授权 kubedns、dashboard、heapster等插件 harbor,使用nfs后端存储 …… 部署方式 Minikube快速搭建单节点Kubenetes集群的工具,只能用作学习实践。 kubeadm是Kubernetes官方提供的用于快速安装Kubernetes集群的工具 使用Rancher部署K8S集群,布署在Docker环境中,方便快捷。 Ansible脚本安装K8S集群 如果对Rancher吃不透的话还是推荐使用Ansible脚本安装K8S集群,Ansible脚本将安装的流程都封装到了脚本里,只需更改安装主机服务器地址和环境就能实现一键布署。 推荐使用github上的kubeasz,能简化很多流程。 kubeasz致力于提供快速部署高可用k8s集群的工具, 并且也努力成为k8s实践、使用的参考书;基于二进制方式部署和利用ansible-playbook实现自动化:即提供一键安装脚本, 也可以分步执行安装各个组件, 同时讲解每一步主要参数配置和注意事项。 集群特性:TLS双向认证、RBAC授权、多Master高可用、支持Network Policy、备份恢复 布署关键点 为了初次安装顺利,关闭防火墙。 确保各节点时区设置一致、时间同步。 无法访问公网情况下,请下载离线docker镜像完成集群安装。 从国内下载docker官方仓库镜像非常缓慢,所以对于k8s集群来说配置镜像加速非常重要,配置/etc/docker/daemon.json,若访问不了外网就要配置局域网的镜像仓库地址。 docker要开启docker远程API修改docker配置文件docker.service 在ExecStart这一行后面加上 -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock docker 从 1.13 版本开始,将iptables的filter表的FORWARD链的默认策略设置为DROP,从而导致 ping 其它 Node 上的 Pod IP 失败,因此必须在filter表的FORWARD链增加一条默认允许规则iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT 后续calico网络、kube-proxy等将大量使用 iptables规则,要维护好服务器本身的iptables规则。 网络组件 Kubernetes中网络是最复杂的,虽然从架构图上看是很清楚的,但实际操作起来还是到处报错,涉及到防火墙,iptables规则,服务器间的网络,网络组件的配置、容器与容器间的访问,容器与服务器的互相访问等等。 推荐flannel ,这里参考其它博文着重介绍下: Flannel的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。 Flannel实质上是一种“覆盖网络(overlay network)”,也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得“同属一个内网”且”不重复的”IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。 默认的节点间数据通信方式是UDP转发,在Flannel的GitHub页面有如下的一张原理图: 1. 数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端。 Flannel也是通过修改Node的路由表实现这个效果的。 2. 源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡,然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一样由docker0路由到达目标容器。 3. 使每个结点上的容器分配的地址不冲突。Flannel通过Etcd分配了每个节点可用的IP地址段后,再修改Docker的启动参数。“--bip=X.X.X.X/X”这个参数,它限制了所在节点容器获得的IP范围。 flannel 使用 vxlan 技术为各节点创建一个可以互通的 Pod 网络,使用的端口为 UDP 8472,需要开放该端口。 kube-apiserver的高可用 keepalived+haproxy配置多个后端真实kube-apiserver的endpoints,并启用存活监测后端kube-apiserver,保证kube-apiserver的高可用。 dashboard dashboard 1.7 以后默认开启了自带的登陆验证机制,1.7 开始,dashboard 只允许通过 https 访问,如果使用 kube proxy 则必须监听 localhost 或 127.0.0.1,对于 NodePort 没有这个限制,但是仅建议在开发环境中使用。 对于不满足这些条件的登录访问,在登录成功后浏览器不跳转,始终停在登录界面。 参考:https://github.com/kubernetes/dashboard/wiki/Accessing-Dashboard---1.7.X-and-abovehttps://github.com/kubernetes/dashboard/issues/2540 kubernetes-dashboard 服务暴露了 NodePort,可以使用https://NodeIP:NodePort地址访问 dashboard; 通过 kube-apiserver 访问 dashboard; 通过 kubectl proxy 访问 dashboard: Ingress Ingress其实就是从kuberenets集群外部访问集群的一个入口,将外部的请求转发到集群内不同的Service 上,其实就相当于nginx、apache 等负载均衡代理服务器,再加上一个规则定义,路由信息的刷新需要靠Ingress controller来提供。 Ingress controller可以理解为一个监听器,通过不断地与kube-apiserver打交道,实时的感知后端service、pod 等的变化,当得到这些变化信息后,Ingress controller再结合Ingress的配置,更新反向代理负载均衡器,达到服务发现的作用。其实这点和服务发现工具consul的consul-template非常类似。 未配置ingress: 集群外部 -> NodePort -> K8S Service配置了ingress: 集群外部 -> Ingress -> K8S Service 注意:ingress 本身也需要部署Ingress controller时暴露NodePort让外部访问;如果你集群支持,可以方便地使用LoadBalancer地址暴露ingress服务。 Traefik 提供了一个简单好用 Ingress controller,是一款开源的反向代理与负载均衡工具。它最大的优点是能够与常见的微服务系统直接整合,可以实现自动化动态配置。 如果采用Rancher部署会有从k8s.gcr.io拉取镜像失败问题 新版本的Kubernetes在安装部署中,需要从k8s.grc.io仓库中拉取所需镜像文件,但由于国内网络防火墙问题导致无法正常拉取。 解决方案 docker.io仓库对google的容器做了镜像,可以通过下列命令下拉取相关镜像: docker pull mirrorgooglecontainers/kube-apiserver:v1.12.0 docker pull mirrorgooglecontainers/kube-controller-manager:v1.12.0 docker pull mirrorgooglecontainers/kube-scheduler:v1.12.0 docker pull mirrorgooglecontainers/kube-proxy:v1.12.0 docker pull mirrorgooglecontainers/pause:3.1 docker pull mirrorgooglecontainers/etcd:3.2.24 docker pull coredns/coredns:1.2.2 版本信息需要根据实际情况进行相应的修改。通过docker tag命令来修改镜像的标签: docker tag docker.io/mirrorgooglecontainers/kube-proxy:v1.12.0 k8s.gcr.io/kube-proxy:v1.12.0 docker tag docker.io/mirrorgooglecontainers/kube-scheduler:v1.12.0 k8s.gcr.io/kube-scheduler:v1.12.0 docker tag docker.io/mirrorgooglecontainers/kube-apiserver:v1.12.0 k8s.gcr.io/kube-apiserver:v1.12.0 docker tag docker.io/mirrorgooglecontainers/kube-controller-manager:v1.12.0 k8s.gcr.io/kube-controller-manager:v1.12.0 docker tag docker.io/mirrorgooglecontainers/etcd:3.2.24 k8s.gcr.io/etcd:3.2.24 docker tag docker.io/mirrorgooglecontainers/pause:3.1 k8s.gcr.io/pause:3.1 docker tag docker.io/coredns/coredns:1.2.2 k8s.gcr.io/coredns:1.2.2 使用docker rmi删除不用的镜像 dashboard无法显示监控图 dashboard 和heapster influxdb都部署完成后 dashboard依旧无法显示监控图 通过排查 heapster log有超时错误 $ kubectl logs -f pods/heapster-2882613285-58d9r -n kube-system E0630 17:23:47.339987 1 reflector.go:203] k8s.io/heapster/metrics/sources/kubelet/kubelet.go:342: Failed to list *api.Node: Get http://kubernetes.default/api/v1/nodes?resourceVersion=0: dial tcp: i/o timeout E0630 17:23:47.340274 1 reflector.go:203] k8s.io/heapster/metrics/heapster.go:319: Failed to list *api.Pod: Get http://kubernetes.default/api/v1/pods?resourceVersion=0: dial tcp: i/o timeout E0630 17:23:47.340498 1 reflector.go:203] k8s.io/heapster/metrics/processors/namespace_based_enricher.go:84: Failed to list *api.Namespace: Get http://kubernetes.default/api/v1/namespaces?resourceVersion=0: dial tcp: lookup kubernetes.default on 10.254.0.2:53: dial udp 10.254.0.2:53: i/o timeout E0630 17:23:47.340563 1 reflector.go:203] k8s.io/heapster/metrics/heapster.go:327: Failed to list *api.Node: Get http://kubernetes.default/api/v1/nodes?resourceVersion=0: dial tcp: lookup kubernetes.default on 10.254.0.2:53: dial udp 10.254.0.2:53: i/o timeout E0630 17:23:47.340623 1 reflector.go:203] k8s.io/heapster/metrics/processors/node_autoscaling_enricher.go Failed to list *api.Node: Get http://kubernetes.default/api/v1/nodes?resourceVersion=0: dial tcp: lookup kubernetes.default on 10.254.0.2:53: dial udp 10.254.0.2:53: i/o timeout E0630 17:23:55.014414 1 influxdb.go:150] Failed to create infuxdb: failed to ping InfluxDB server at "monitoring-influxdb:8086" - Get http://monitoring-influxdb:8086/ping: dial tcp: lookup monitoring-influxdb on 10.254.0.2:53: read udp 172.30.45.4:48955->10.254.0.2:53: i/o timeout` 我是docker的systemd Unit文件忘记添加 ExecStart=/root/local/bin/dockerd --log-level=error $DOCKER_NETWORK_OPTIONS 后边的$DOCKER_NETWORK_OPTIONS,导致docker0的网段跟flannel.1不一致。 kube-proxy报错kube-proxy[2241]: E0502 15:55:13.889842 2241 conntrack.go:42] conntrack returned error: error looking for path of conntrack: exec: “conntrack”: executable file not found in $PATH 导致现象:kubedns启动成功,运行正常,但是service 之间无法解析,kubernetes中的DNS解析异常 解决方法:CentOS中安装conntrack-tools包后重启kubernetes 集群即可。 Unable to access kubernetes services: no route to host 导致现象: 在POD 内访问集群的某个服务的时候出现no route to host $ curl my-nginx.nx.svc.cluster.local curl: (7) Failed connect to my-nginx.nx.svc.cluster.local:80; No route to host 解决方法:清除所有的防火墙规则,然后重启docker 服务 $ iptables --flush && iptables -tnat --flush $ systemctl restart docker 使用NodePort 类型的服务,只能在POD 所在节点进行访问 导致现象: 使用NodePort 类型的服务,只能在POD 所在节点进行访问,其他节点通过NodePort 不能正常访问 解决方法: kube-proxy 默认使用的是proxy_model就是iptables,正常情况下是所有节点都可以通过NodePort 进行访问的,我这里将阿里云的安全组限制全部去掉即可,然后根据需要进行添加安全限制。

优秀的个人博客,低调大师

js的基础知识笔记

目录 一、基本数据类型 二、函数 三、面向对象 一、基本数据类型 1、使用var声明变量、使用;结尾、使用{}表示代码块、使用驼峰式命名 2、变量是弱类型的,即并不严格要求声明变量的类型,一个变量可以在运行期间动态的指向不同类型的数据 3、变量声明后可以不定义(或叫赋值、初始化),此时它的值是undefined 4、符号$可以作为命名开头的特殊字母,jQuery使用它作为jquery函数的别名 5、数据分为两大类:原始数据和引用数据,所以一个变量中保存的值自然也有两类 原始数据:直接存放在栈中, 变量中保存着此数据的直接值 引用数据:存放在堆中,并把内存地址存放在栈中,即变量中存放着此对象的指针 6、原始类型:undefined、boolean、number、string、null 7、引用类型:即对象object,其他array、function等对象都是在object基础上进一步封装的对象 二、函数 1、函数也是一个对象,构造函数在功能上还类似于一个类 2、函数如果没有显式说明返回值,则默认return undefined 3、函数对输入参数的要求并不严格(数量、类型),可以使用arguments来获得一个函数的输入参数数组 4、函数也是一个对象意味着,函数可以被当做输入参数、也可以被return,这就意味着可以实现闭包 5、函数的toString()方法可以得到函数的源代码 6、闭包,保存了函数定义时上下文的执行环境,当函数执行时,可以获取到这些数据 闭包的支持意味着可以完成装饰器模式 7、函数声明和函数表达式在某些环境下有非常大的区别 函数声明: function show(){} 函数表达式: var show = function(){} 函数声明有很高的优先级,会被解释器提前解析并释放到执行环境中() console.log(show) // 可以正常的打印出show函数对象 function show(){} 以上两句语句可以正常执行而不报错,全局代码被扫描的时候通过词法分析会在当前函数的AO对象中设定show变量的值为对应函数 而函数表达式等价于一个赋值语句,只有在运行此句代码时才执行,一下语句会报错 console.log(show) var show = function(){} 8、词法分析,一个函数在被真正执行内部代码前,解释器会先扫描函数源代码并执行词法分析,目的是提前完成函数作用域的生成, 函数作用域通过一个AO对象来保存,后续在逐行执行函数代码时,所有的变量查询都通过AO对象,词法分析步骤: a、分析函数输入参数,若函数源代码出现输入参数,则发生此步骤。解释器在AO对象中添加此属性,值为undefined,若有接收实参,则覆盖原有的值 b、分析变量,若函数源代码中出现变量定义,则发生此步骤。解释器寻找AO对象中是否有此属性,若无则增加,值为undefined,若有,什么也不做 c、分析函数声明,若函数源代码中出现函数声明(函数表达式会被定义为步骤b),则发生此步骤。解释器寻找AO对象是否有此属性,若无则增加,若有,则覆盖 换句话说,如果在一个函数func1内部有另一个函数声明func2,此func1函数在真正被执行内部代码前就已经生成了AO对象并且func2函数已经在func1函数的作用域中 三、面向对象 1、js对象类似于Python中的字典,里面存放着无序的键值对 2、对象中的属性(数据属性、函数属性)权限均为公开的(和Python一样),使用_xxx_的方式约定俗成的表示成私有数据(Python中使用__xxx) 3、this指代当前对象,尤其在构造函数中使用时,指代当前被初始化的对象(类似Python的__init__中的self) 4、如何定义一个类(实际是构造函数) a、构造函数定义数据+原型定义函数(我个人比较常用此种方式,代码清晰) function Stu(stuName, stuAge){ this.stuName = stuName; this.stuAge = stuAge; } Stu.prototype.goToSchool = function(schoolName){ console.log(this.stuName + ' is going to school: ' + schoolName); } b、构造函数定义数据+函数(这种模型也可以用于处理单例模式) function Stu(stuName, stuAge){ this.stuName = stuName; this.stuAge = stuAge; if(!Stu.__definedFlag){ //实际上此函数被当做单例模式 Stu.prototype.goToSchool = function(schoolName){ console.log(this.stuName + ' is going to school: ' + schoolName); } var Stu.__definedFlag = true; } }

优秀的个人博客,低调大师

python学习-基础知识-1

1、计算机历史 计算机使用高低电压的两种状态来描述信息。计算机可以理解的只有二进制数据即010100011....,1个比特位可以表示的状态只有2种,n个比特位可以表示的状态有2的n次方种。 所以如果想要描述天气状态:天晴、下雨、刮风、下雪、霜冻,则需要使用3个比特位。 2、编程语言历史 计算机只能理解二进制数据,二进制数据中蕴含了很多指令、操作、数据,计算机通过cpu直接运行二进制代码执行不同的功能,二进制代码被称为机器语言。 但是机器语言(二进制代码)对于人类而言可读性太差、编写太困难,所以人类需要使用适合自身的语言来编程以操作计算机。 一开始,人类将机器语言中的代码进行分类、标记,把蕴含操作行为、蕴含数据的二进制代码使用英语单词标记,形成了汇编语言。汇编语言的模式是:操作行为+被操作数。 操作行为和被操作数都使用英语单词代替了一串010101的二进制代码,这样人类只需要编写汇编语言,再将汇编语言通过之前的映射关系转换成二进制即可。 通过汇编语言,人类即可以方便的编写代码,又可以通过转化得到对应的二进制代码让计算机运行。 但是,汇编语言依然可读性较差、编写也不是非常的方便,要实现一个简单的功能需要非常多的汇编代码,代码冗余性依然很高。 于是,人类模仿汇编和二进制的对应关系,将高级语言和汇编又对应起来,这时候人类就只需要编写高级语言,再通过编译器将高级语言转换成汇编语言或者直接转换成二进制代码。 高级语言的代码非常接近人类的英语,所以使用起来很方便。 因语法规则、语义的不同,高级语言分为很多种,如:python,java、c、c++、JavaScript,ruby等等。 3、编译型高级语言/解释性高级语言 高级语言接近人类英语,编写的代码可读性也很强,但是计算机是无法直接理解和执行源代码的,因为计算机只认识二进制代码,所以不论是哪一种高级语言,都需要将源代码转换成二进制代码后才能被计算机执行。 在转换过程中,有2种不同的情况: 1、编译型 编译型高级语言,在程序执行之前,需要提前将源代码通过编译器转换成目标文件(二进制代码),然后计算机直接执行目标文件。 优点:计算机执行效率高、速度快 缺点: 如果程序需要修改,必须修改源代码并再次编译,修改不方便 一旦编译完成,目标文件就只能在当前操作系统和cpu架构上运行,无法在其他平台使用,除非重新编译 举例:c、c++、c# 2、解释型 解释型高级语言,由解释器实时读取源代码并编译成二级制代码交由计算机执行。 优点: 程序的修改直接操作源文件即可,修改方便 只要计算机安装了对应平台的解释器,源代码就可以被执行,即一份源代码可以在不同平台运行,平台兼容性好 缺点: 因为需要实时编译,所以运行效率低、速度较慢 需要一个匹配版本和平台的解释器与源代码共同工作 举例:python、java、ruby 4、python版本 python有2个相互不兼容的大版本,版本2最高是2.7,版本3截止20180603最高是3.6.5。版本2官方宣布将会维护到2020年,建议现在选择版本3编码。 5、python解释器 解释器也是一种程序,功能是将python源代码翻译成机器语言。解释器可以使用多种语言编写,有如下几种: cpython,官方发布的解释器,使用c语言编写 ipython,在cpython基础上套了一个壳,提高用户交互性 pypy,据说使用jit技术动态实时编译使得代码运行速度更快,但是目前暂不成熟 jpython和icronpython,使用java和.net编写,一般不使用 6、python文件执行 两种方式执行python文件,一种是启动解释器并将python源文件作为参数,解释器读取文件中代码。一种是启动解释器,在交互环境编写代码执行。 7、变量 1、变量存在的原因 用于保存阶段性计算的结果,同时变量用于描述程序中的一些数据,良好的变量命名和使用会增加代码的可读性。 2、变量命名规范 变量名可以是数字、字母、下划线的任意组合,注意:不可使用除了下划线之外的特殊字符 变量名开头不能是数字 变量名不能和python预定义的关键字重名 注意:python竟然可以使用中文作为变量名! 3、变量定义语法 age = 26 定义一个变量,它的名字是age,此变量指向内存中的一个对象,此对象值是26 8、常量 常量的命名规范和变量一样,不过常量的命名约定俗成的是全大写。 常量用于保存基本不变的数据,python没有提供类似c语言的const关键字来强制规定常量的不可变性,所以python中的常量是约定俗成的不可变。 9、python安装 www.python.org官网下载最新版本3.6.5,根据电脑配置选择32或者64位,安装的时候选择add to path并自定义安装位置和其他高级选项,安装完成可以直接shell进入python解释器。 10、输入/输出 python中通过input函数提供shell输入,通过print函数提供shell打印。 input函数会阻塞程序执行直到获取shell输入值 11、注释 不论哪一种高级语言,我们在编码的时候都是将脑海中的思路、信息、步骤简化成实际的代码。将大脑中的信息转换成实际代码的时候,会有很大的信息量丢失,即代码所表达的信息量是很少的。 所以单独的代码无法还原编码时的全部信息量,此时我们就需要使用注释来弥补代码缺失的信息量。优秀的注释+代码可以提供很高的可读性,也方便后续代码使用和维护。 注释有几个需要遵守的规范: 1、注释的内容应该保持与代码的强一致性,注释应该精简、准确的表达代码的含义、编码背景等 2、可以使用中文或者英文 3、注意注释的使用量,在适当且必要的地方使用注释,如:重要流程节点、复杂代码块解释等 12、数据类型 计算机只认识二进制,对于计算机而言,数据没有类别,全部都是010101010... 但是对于人类而言,人类世界中的数据、信息是五花八门的,我们编码的过程其实就是在计算机世界中对现实世界进行建模的过程,所以我们需要对人类世界的数据划分类别。 基础的数据类型有:数字(整数+小数)、字符串、布尔值 我们使用int来表示整数类型、float表示小数类型 我们使用str表示字符串类型 我们使用bool表示布尔值类型 数字类型用于计算、字符串类型用于表示信息、布尔类型用于判断 13、字符串格式化输出 所谓格式化输出,其实就是在编码过程中定义展示模板,在模板中通过占位符代表后续有对应数据填充在此。 占位符: %d 整数类型 %f 小数类型 %s 字符串类型(%s是万能匹配,即任意数据类型都可以填充到%s占位符中) %r 原格式(将数据的原始格式存放到%r占位符中,如字符串的原格式就包含引号) 14、运算符 算数运算符 + - * / (注意,/ 除法运算符得到的商可以是小数,即5/2 = 2.5) // % (地板除得到的结果是商的整数部分,如5//2 = 2。取模得到的结果是未整除的结果,如5%2 = 1,对2取模可以判断奇偶。) ** (幂运算,5**2 = 25) 比较运算符 == > < >= <= != (==表示的是两边对象的值是否相等,python3中使用!=表示不等于,不再使用<>) 赋值运算符 = (赋值) += -= *= /= //= %= **= (在原有值的基础上做运算,并赋值给原有变量,如a = 5, a %= 2,此时a = 1) 逻辑运算符 and or no (逻辑判断会使用短路判断,所以:使用and的时候,概率小的放前面。使用or的时候,概率大的放前面。) 15、流程控制-分支 程序默认是自上而下没有分支的执行代码。很多时候我们需要通过判断程序运行中间的某一种状态、情况、值,来决策程序下一步该执行哪些代码。 通过分支提供多条执行路线,分支有双分支、多分支。 之所以可以划分多种分支是因为可能出现多种不同条件,而这些条件的概率和应该为1。 分支需要特别注意条件判断的逻辑性,多种条件只会执行其中一个条件,条件之间是相互对立的。 通过if elif elif else来提供多分支,通过条件的布尔值结果判断条件是否成立。 注意:条件判断应该把概率大的放在前面,概率小的放后面,对于无法明确具体判断条件的可以放在else统一匹配。另外,判断input的返回值的时候注意,input返回值一定是字符串。 16、流程控制-循环-while while关键字后面的语义是:判断条件是否成立?成立的话执行一次循环体然后再次判断条件是否成立。不成立的话跳过此循环体执行后续代码。 因为循环代码有导致死循环的风险,所以任何一个循环定义,都需要预先设计循环跳出条件。循环可以在循环体中通过break跳出,或者通过while条件判断不成立跳过。(当然,也可以在循环体中exit) 通过continue可以提前结束当前循环,直接开始下一次的循环while判断,注意:continue应用于跳过后续本应该执行的代码。 因为continue可以跳过后续本应该执行的代码,而后续执行代码中可能包含循环跳出的设置如:i += 1。所以continue的使用会有死循环风险,需要单独处理循环跳出问题。 17、while...else... 据说使用while后面的else,如果while正常执行完毕,则执行else。如果while没有正常执行完毕如被break打断,则不会执行else。可以使用else是否执行来判断循环是否正常执行完毕,我觉得这并不合理,考虑下述代码: 1 i = 11 2 while i < 10: 3 print('i is:', i) 4 i += 1 5 if i == 3: 6 break 7 else: 8 print('循环正常执行完毕') 9 10 # 循环没有正常执行完毕,实际上循环根本就没执行,else也被执行了。所以else被用于判断 11 # while是否正常执行我觉得并不合理。 18、while的使用 思考如下练习: # 使用while,完成以下图形的输出 # # * # * * # * * * # * * * * # * * * * * # * * * * # * * * # * * # * 1 star = 1 2 most_star = 5 3 while star <= most_star: 4 print(' *' * star) 5 if star == most_star: 6 while most_star > 0: 7 most_star -= 1 8 print(' *' * most_star) 9 break 10 star += 1 11 12 row = 1 13 most_row = 5 14 while row <= most_row: 15 print(' *' * row) 16 row += 1 17 row = most_row - 1 18 while row > 0: 19 print(' *' * row) 20 row -= 1 while循环只能打印一个方向,如果是两个相反的方向,需要两个while。两个while可以写成第二个while放在第一个while的末尾。

优秀的个人博客,低调大师

代码审计补充知识

参考文献:https://hacksec.xyz/2018/03/23/php-trick/?nsukey=cWDNgiIxMsJayPNWtqM8Rc9amSOIgC0SI9Ko3jxxW2uV9mcoLNLdl4Q1U%2BrV9UhKIs3das0Cizzc8atumDKxG5nHSDPPthMpgeq5Xqa02MDie21AnddLn9BUUyOdXVEOsbvHRrlq1S61OS4%2BRpRffcBRIuwbJUdeL%2FEcZRNmvBtBzhuGQhuRU9fs%2BM9pyrRyVvKRlq5SmzZRSXFYcAjYcQ%3D%3D https://www.jianshu.com/p/9c8c9272e12e ①is_numeric函数 在判断是否是数字时会忽略字符串开头的’ ‘、’\t’、’\n’、’\r’、’\v’、’\f’。 ​ 而’.’可以出现在任意位置,E、e能出现在参数中间,仍可以被判断为数字。也就是说is_numeric(“\r\n\t 0.1e2”) >> TRUE intval()函数会忽略’’ ‘\n’、’\r’、’\t’、’\v’、’\0’ ,也就是说intval(“\r\n\t 12”) >> 12 ②PHP变量名不能带有点[.] 和空格,否则在会被转化为下划线[_] parse_str("na.me=admin&pass wd=123",$test); var_dump($test); 结果: ["na_me"]=> string(5) "admin" ["pass_wd"]=> string(3) "123" } ③htmlspecialchars()函数默认只转义双引号不转义单引号,如果都转义的话需要添加上参数ENT_QUOTES ④in_arrary()函数默认进行松散比较(进行类型转换) in_arrary(“1asd”,arrart(1,2,3,4)) => true in_arrary(“1asd”,arrart(1,2,3,4),TRUE) => false \\(需要设置strict参数为true才会进行严格比较,进行类型检测) ⑤sprintf()格式化漏洞(可以吃掉转义后的单引号) 当我们输入的特殊字符被放到引号中进行转义时,但是又使用了sprintf函数进行拼接时,例如%1$’%s’ 中的 ‘%被当成使用%进行padding,导致后一个’逃逸了 还有一种情况就是’被转义成了\’,例如输入%’ and 1=1#进入,存在SQL过滤,’被转成了\’,于是sql语句变成了 select * from user where username = ‘%\’ and 1=1#’;如果这个语句被使用sprintf函数进行了拼接,%后的\被吃掉了,导致了’逃逸 $sql = "select * from user where username = '%\' and 1=1#';"; $args = "admin"; echo sprintf( $sql, $args ) ; //result: select * from user where username = '' and 1=1#' ?> 不过这样容易遇到 PHP Warning: sprintf(): Too few arguments的报错,这个时候我们可以使用%1$来吃掉转义添加的\ <?php $sql = "select * from user where username = '%1$\' and 1=1#' and password='%s';"; $args = "admin"; echo sprintf( $sql, $args) ; //result: select * from user where username = '' and 1=1#' and password='admin'; ?> ⑥php中 = 赋值运算的优先级高于and $c = is_numeric($a) and is_numeric($b)程序本意是要a、b都为数字才会继续,但是当$a为数字时,会先赋值给$c,所以可能导致$b绕过检测 ⑦url标准的灵活性导致绕过filter_var与parse_url进行ssrf parse_url() 是专门用来解析 URL 而不是 URI 的。不过为遵从 PHP 向后兼容的需要有个例外,对 file:// 协议允许三个斜线(file:///...)。其它任何协议都不能这样. filter_var()函数对于http://evil.com;google.com 会返回false也就是认为url格式错误,但是对于0://evil.com:80;google.com:80/、0://evil.com:80,google.com:80/、0://evil.com:80\google.com:80/却返回true。 ⑧通过file_get_contents获取网页内容并返回到客户端有可能造成xss if(filter_var($argv[1], FILTER_VALIDATE_URL)) { // parse URL $r = parse_url($argv[1]); print_r($r); // check if host ends with google.com if(preg_match('/baidu\.com$/', $r['host'])) { // get page from URL $a = file_get_contents($argv[1]); echo($a); } else { echo "Error: Host not allowed"; } } else { echo "Error: Invalid URL"; } 虽然通过filter_var函数对url的格式进行检查,并且使用正则对url的host进行限定, 但是可以通过data://baidu.com/plain;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pgo== 页面会将<script>alert(1)</script>返回给客户端,就有可能造成xss ⑨parse_url与libcurl对与url的解析差异可能导致ssrf 当url中有多个@符号时,parse_url中获取的host是最后一个@符号后面的host,而libcurl则是获取的第一个@符号之后的。因此当代码对http://user@eval.com:80@baidu.com 进行解析时,PHP获取的host是baidu.com是允许访问的域名,而最后调用libcurl进行请求时则是请求的eval.com域名,可以造成ssrf绕过。此外对于https://evil@baidu.com这样的域名进行解析时,php获取的host是evil@baidu.com,但是libcurl获取的host却是evil.com ⑩md5碰撞 $Param1="\x4d\xc9\x68\xff\x0e\xe3\x5c\x20\x95\x72\xd4\x77\x7b\x72\x15\x87\xd3\x6f\xa7\xb2\x1b\xdc\x56\xb7\x4a\x3d\xc0\x78\x3e\x7b\x95\x18\xaf\xbf\xa2\x00\xa8\x28\x4b\xf3\x6e\x8e\x4b\x55\xb3\x5f\x42\x75\x93\xd8\x49\x67\x6d\xa0\xd1\x55\x5d\x83\x60\xfb\x5f\x07\xfe\xa2"; $Param2="\x4d\xc9\x68\xff\x0e\xe3\x5c\x20\x95\x72\xd4\x77\x7b\x72\x15\x87\xd3\x6f\xa7\xb2\x1b\xdc\x56\xb7\x4a\x3d\xc0\x78\x3e\x7b\x95\x18\xaf\xbf\xa2\x02\xa8\x28\x4b\xf3\x6e\x8e\x4b\x55\xb3\x5f\x42\x75\x93\xd8\x49\x67\x6d\xa0\xd1\xd5\x5d\x83\x60\xfb\x5f\x07\xfe\xa2"; #008ee33a9d58b51cfeb425b0959121c9 $data1="\xd1\x31\xdd\x02\xc5\xe6\xee\xc4\x69\x3d\x9a\x06\x98\xaf\xf9\x5c\x2f\xca\xb5\x07\x12\x46\x7e\xab\x40\x04\x58\x3e\xb8\xfb\x7f\x89\x55\xad\x34\x06\x09\xf4\xb3\x02\x83\xe4\x88\x83\x25\xf1\x41\x5a\x08\x51\x25\xe8\xf7\xcd\xc9\x9f\xd9\x1d\xbd\x72\x80\x37\x3c\x5b\xd8\x82\x3e\x31\x56\x34\x8f\x5b\xae\x6d\xac\xd4\x36\xc9\x19\xc6\xdd\x53\xe2\x34\x87\xda\x03\xfd\x02\x39\x63\x06\xd2\x48\xcd\xa0\xe9\x9f\x33\x42\x0f\x57\x7e\xe8\xce\x54\xb6\x70\x80\x28\x0d\x1e\xc6\x98\x21\xbc\xb6\xa8\x83\x93\x96\xf9\x65\xab\x6f\xf7\x2a\x70"; $data2="\xd1\x31\xdd\x02\xc5\xe6\xee\xc4\x69\x3d\x9a\x06\x98\xaf\xf9\x5c\x2f\xca\xb5\x87\x12\x46\x7e\xab\x40\x04\x58\x3e\xb8\xfb\x7f\x89\x55\xad\x34\x06\x09\xf4\xb3\x02\x83\xe4\x88\x83\x25\x71\x41\x5a\x08\x51\x25\xe8\xf7\xcd\xc9\x9f\xd9\x1d\xbd\xf2\x80\x37\x3c\x5b\xd8\x82\x3e\x31\x56\x34\x8f\x5b\xae\x6d\xac\xd4\x36\xc9\x19\xc6\xdd\x53\xe2\xb4\x87\xda\x03\xfd\x02\x39\x63\x06\xd2\x48\xcd\xa0\xe9\x9f\x33\x42\x0f\x57\x7e\xe8\xce\x54\xb6\x70\x80\xa8\x0d\x1e\xc6\x98\x21\xbc\xb6\xa8\x83\x93\x96\xf9\x65\x2b\x6f\xf7\x2a\x70"; #79054025255fb1a26e4bc422aef54eb4

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册