《码农翻身》总结

1 计算机的世界

  • TCP连接是虚拟的,连接的状态信息并不会在中间保存,相反,连接的状态信息是在两端维持的。
  • TCP通过三次握手确认连接没问题,例如a连接b,第一次:a发送给b,b能知道a发消息,b收消息没问题。第二次:b发送给a,a能知道b发消息没问题,a收消息没问题,a发消息没问题,这时a能知道自己收发消息没问题,b收发消息没问题,但是b不知道自己发消息有没有问题,所以第三次:a再发给b,b接收到能证明自己收发消息也没问题。
  • CPU的工作效率是按纳秒为单位的,比内存快百倍,比硬盘快百万倍。
  • CPU主要负责运行指令,但指令是存在内存中的。
  • 程序的局部性原理:1.一个内存位置被访问后,过不了多久还会被再次访问。2.一个内存位置被访问了,附近的位置很快就会被访问到。
  • 冯诺依曼将计算机从逻辑上划分为五大部件:运算器、控制器、存储器、输入设备、输出设备。
  • 一个正在运行的程序叫做进程,进程除了需要保存寄存器的值和操作的东西之外,还要保存使用的时间和等待时间等信息,这些信息统称为进程控制块PCB(Processing Control Block)。
  • 文件的存放方式可以是连续分配的,这种方式在随机访问文件时效率极好,因为只要知道了开头和长度,就想数组一样随便访问。但是这会造成中间硬盘空间的空洞而浪费空间;存放方式也可以是链式分配的,这个文件从第一块磁盘开始,形成一条链1-9-18-8-3,每块空闲的磁盘都会得到充分利用,效率很高,但是随机访问的效果太差了,每次都要从第一块磁盘开始,沿着链条往后面找;存放方式还可以是索引式的,专门找个磁盘快,存储一个文件所使用的磁盘块号列表。这个磁盘块叫做索引块,通过它可以轻松的找到这个文件所用的所有磁盘快,无论是顺序还是随机访问,速度都很快,唯一的缺点是索引快本身也占空间。
  • 操作系统中还有inode结构,记录了磁盘块、文件权限、所有者、时间标记等信息。inode中可以存另一个inode信息,即间接块,可以使一次、二次、三次等多次间接块,极大地增加了文件所能使用的磁盘块数。和文件一样,每个目录也是一个inode,其中有目录属性,还有存放这个目录内容的磁盘块号,在磁盘块中才真正的放着目录下的内容。
  • 内存就是一个个小格子,每个小格子都有一个编号,这个编号被成为内存的地址,格子中的数据可以被cpu所读/写。CPU内部的构造超级复杂,主要是运算器和寄存器。
  • 运算器可以进行各种运算,但是有一个限制,即这个运算器不能直接操作内存进行运算,它在运算时使用的是内部的数据格子,把他们叫做R1,R2,R3,R4,同城Rx。CPU必须把数据装在到寄存器中才能进行运算。
  • CPU的主要操作:1.从内存的某个格子中读取数据,放入自己内部的寄存器Rx。2.吧Rx中的数据写入内存的某个格子中(会覆盖原数据)。3.进行数学运算和逻辑运算。4.根据条件进行跳转。
  • CPU在运行时需要从内存中获取运行指令,并进行分析译码,看看这条指令是干什么的,再进行运算。内存中的小格子存放的不仅仅是数据,还有至关重要的程序指令。这些指令是以二进制的形式表示的。而内存中的数据需要在运行时由硬盘加载到内存。
  • 汇编语言的优点就是贴近机器,运行效率极高;缺点是太贴近机器,直接操作内存和CPU寄存器,难以结构化编程。被称为低级语言。
  • 编译的过程包括:源程序–>词法分析–>语法分析–>语义分析–>中间代码生成–>代码优化–>代码生成–>目标程序。词法分析会将语句分隔,形成token,并生成符号表。语法分析会将符号表形成语法树,其中的表达式是递归定义的。再其次是语义分析,会检查标识符的类型,作用域是否正确,运算是否合法,取值范围有没有问题等。
  • 一个开源工具ANTLR,能帮助开发者完成词法分析和语法分析等工作。
  • 在计算机内部,是使用补码来表示二进制数的,如果是一个正数,补码就是他本身;如果是一个负数,则需要吧除了符号位之外的二进制数执行取反加1的操作。
  • 每个栈帧就代表了被调用中的一个函数,这些函数栈帧以先进后出的方式排列起来,就形成了一个栈。但是如果递归层次太深就会出问题。所以可以改进递归方式为尾递归,可以复用同一个栈帧。
//普通递归,层级太深容易出问题
int factorial(int n) {
        if(n == 1){
            return 1;
        }else {
            return n * factorial(n-1);
        }
    }
//尾递归
int factorial(int n, int result) {
        if(n == 1){
            return result;
        }else {
            return factorial(n - 1, n * result);
        }
    }
  • 尾递归:当递归调用是函数体中最后执行的语句,并且它的返回值不属于表达式的一部分时,这个递归就是尾递归。这时编译器就会生成优化的代码,复用栈帧。第一种算法中因为有n * factorial(n-1),虽然也是递归,但是递归的结果处于一个表达式中,还要进行计算,所以就没法复用栈帧了,只能一层一层的调用下去。

2 Java帝国

  • C语言贴近硬件,运行极快,效率极高,指针功能无比强大,能直接操作内存。但也是因为指针和内存管理,让程序员背负了两个沉重的枷锁。
  • JVM的LcassLoader分三层,最顶层的是Bootstrap ClassLoader,下一层级叫Extension ClassLoader,最底层的是App ClassLoader。
  • final关键字可以用来修饰类、方法和变量。用final修饰的类不能被继承;用final修饰的方法不能被子类覆写;用final修饰的变量初始化后不能更改(修饰基本类型时,表示该值在初始化后不能变化,当修饰引用类型时,则对其初始化之后的便不能只想其他对象,单该引用所指向的对象内容还是可以变化的)。
  • JDK的动态代理主要是在Proxy.newProxyInstance这里,动态地生成了一个类,当调用方法时其实是调用了代理类的方法,由代理类的方法中添加了增强的逻辑,再去调原方法。但是JDK的动态代理的缺点是必须实现接口。
  • 元数据:描述数据的数据。元注解:描述注解的注解。
  • Java的泛型是擦除法。简单说就是一个参数化的类型经过擦除后会去除参数,如ArrayList<T>会被擦除为ArrayList。然后在编译的时候会加一个自动转型的操作,如Integer i = (Integer) list.get(0)。
  • 如果Apple是Fruit的子类,但是ArrayList<Apple>却不是Array<Fruit>的子类,他们之间是没有关系的。因为如果可以这么做,那么不但可以向这个List中加入Apple,还可以加入其他如Orange,这样泛型就被破坏了。
  • 上面的情况可以使用通配符来解决,但是在函数中只能对list进行遍历,不能执行添加操作
public void print(ArrayList<? extends Fruit> list) {}
  • 设计模式–模板方法:在父类中已经吧那些乱七八糟的非功能性代码写好了,只留了一个口子(抽象方法)让子类去实现。但是这种方式的巨大缺陷是父类会定义一切,要执行哪些非功能性代码,以什么顺序执行等等,子类只能无条件接受。
  • JAVA是一门静态的强类型语言,代码一旦写好,编译成Java类以后,就可以在运行时通过反射来查看类的信息,但是要想对编译好的类进行修改是不可能的。
  • 为了实现这个限制,基本上现在有这样两个技术:
    1. 修改现有类:在编译的时候做手脚,根据AOP的配置信息,把切面代码和业务类逻辑编译在一起。这种方式使业务类被改变,切面代码和业务逻辑掺和在一起,不太好。
    2. 瞒天过海:在运行期间做手脚,在业务类加载以后,为该业务类动态生成一个代理类,让代理类去调用执行这些切面代码,增强现有业务类,业务类不用进行任何改变。实际上客户直接使用的是代理类对象,而不是原生的业务类对象了。
  • 动态生成代理类的方法有两种:第一种是使用JAVA动态代理技术,这种技术要求业务类必须有接口才能工作;第二种就是使用CGLib,只要业务类没有被标记为final就可以因为它会生成一个业务类的子类来作为代理类。

3 浪潮之巅的Web

  • 对称加密:信息传输前使用加密算法加密,生成密钥,接收者根据秘钥使用解密算法解密。加密解密使用同一个密钥,而加密解密算法是对外公开的。那如果密钥被别人获取了,消息就可能被别人窃取。
    在这里插入图片描述
  • RSA非对称加密:发送信息双方各自生成一对钥匙,一个是保密的称为私钥,一个是公开的称为公钥。用私钥加密的数据,只有对应的公钥才能解密;用公钥加密的数据,只有对应的私钥才能解密。当张大胖给Bill发送消息时,就先可以用Bill的公钥去加密,当Bill收到消息后,再用自己的私钥解密(只有Bill才能解开,因为私钥是保密的)。
    在这里插入图片描述
  • 非对称加密+对称加密:RSA算法的加解密是有点慢的,可以使用组合的方式,先是张大胖使用对称加密,生成密钥,然后使用RSA非对称加密,使用Bill的公钥对对称加密的密钥进行加密,传输给Bill,Bill收到后用自己的私钥解密得到对称加密的密钥,以后两个人传输就可以使用对称加密了,因为Bill已经安全的获取了张大胖的对称加密的密钥了。
  • 中间人劫持:当Bill给张大胖发送公钥的时候,有一个中间人截取了Bill的公钥,而把自己的公钥给了张大胖,而张大胖发送Bill公钥的时候,被中间人截取,而把自己的公钥给了Bill,这样,中间人就成功的模仿了对方而获取双方的信息。
    在这里插入图片描述
  • 认证中心CA:Bill可把他的公钥和个人信息用Hash算法生成一个消息摘要,这种Hash算法有一个极好的特性,只要输入数据有一点变化,那么生成的摘要就会巨变,这样就可以防止别人修改原始内容。生成摘要后再有公信力的认证中心用它的私钥对信息摘要加密,形成签名(公正中心的公钥是公开的),再把原始信息和数字签名合并,形成数字证书。当Bill将数字证书发送给张大胖时,张大胖就可以对原始信息进行Hash获取摘要,然后可以使用CA的公钥对数字签名解密获取摘要,将两个摘要比对就可以看到信息是否被人修改了。
    在这里插入图片描述
    在这里插入图片描述
  • 实际上CA本身也有证书来证明自己的身份,但是CA的证书怎么验证没被篡改呢,只好由这个CA的上一级来验证,然后由再上一级来验证,于是CA们形成了一根分级的链条,在链条的根部就是操作系统/浏览器预置的顶层CA证书,相当于你自动信任了他们。
  • 一个简化版的HTTPS原理图:
    在这里插入图片描述
  • 普通登录:浏览器请求服务器,验证成功,生成session,把sessionid通过cookie发送到浏览器,下次再访问服务器会自动带过来cookie,验证sessionid就知道了登录了。
  • 跨系统登录:由于cookie不能跨域,所以可以使用token技术,在一个系统登录后生成token,再访问别的系统时对token校验。JWT可以生成token(用户信息hash后加密生成签名,再和用户信息放一起生成token),别的系统通过密钥解密签名和用户信息的hash值比对就可以知道是否是登录的人。但是问题是密钥的分发是一个问题,其次用户信息不同系统是不一致的,拿到用户信息没什么用。
    在这里插入图片描述
    在这里插入图片描述
  • 单点登录:用户在一个系统登录就可以随意登录其他关联系统了。具体步骤稍微复杂一些:
    1. 用户通过浏览器访问你的系统的www.a.com/pageA,pageA是一个需要登录才能访问的页面,你发现用户没有登录,这时候需要重定向到认证中心,www.sso.com/login?redirect=www.a.com/pageA,用户在sso登录成功后需要在sso系统建立一个session,创建一个ticket(可以认为是一个随机的字符串),再重定向到之前的页面,url中带着ticket:www.a.com/pageA?ticket=123。同时cookie也会被发送给浏览器,比如:set cookie: ssoid=1234;domain=sso.com
    2. 重定向回到a系统后,带着ticket,a系统会拿着ticket去sso进行验证,验证通过,则创建session,将自己系统的sessionid通过cookie返回过去。

在这里插入图片描述

在这里插入图片描述

  • 当访问另一个系统b时,未登录重定向到sso,sso验证已经登陆了,直接返回ticket和cookie,重定向回b,b拿到ticket再去sso验证,验证通过,在b系统创建session,返回b系统的sessionid。
    在这里插入图片描述
    在这里插入图片描述
  • 当其他系统在sso系统验证登录成功后,需要注册当前系统,因为单点退出的时候需要sso把自己的session和cookie销毁,同事告知其他系统,才算真正退出。这个方法是耶鲁大学提出来的,叫做CAS(Central Authentication Service),是一个著名的sso解决方案。
  • TOKEN认证:在app上操作,需要用到另一个机构的信息,需要另一个机构的授权,直接在自己系统输入第三方机构的用户名密码不友好,所以开放链接去第三方机构登录,带回来token信息,再拿着token去第三方机构验证比较好。
    在这里插入图片描述
  • 授权码+TOKEN:由于直接拿到token不太安全,token可能被别人窃取,所以引入中间的授权码,第三方机构先发送授权码,再通过授权码和关联的app_id、app_secret请求第三方机构验证,验证通过返回token。
    在这里插入图片描述
  • Redis的Hash槽(Hash Slot):Redis集群有16384(早期版本,后面版本是65535)个槽,每台服务器分管其中一部分,当有数据保存时,对key通过CRC16算法产生一个整数值,再对16384求余数,看看余数落在哪个槽里,数据就放对应的服务器中。当增加服务器后,可以对key进行迁移,将其中一些槽和其中的数据分出一些交给新服务器管理。
    在这里插入图片描述
    在这里插入图片描述
  • 客户端可以向任意一个节点发出请求,计算出余数后再转发至对应服务器取数。
  • KeepAlived可以把Nginx集群组成一种master-slave的结构,同一时刻只有一个工作,如果主节点挂了,由KeepAlived将待命的切换为主节点,对外提供一个IP地址。
  • 所有的调用都发生在本机内的一个进程中,这种方式称为本地过程调用。因为数据和函数都在一个进程中,想要调用了,直接去函数所在的地址执行代码即可。
  • 需要调用的方法在另一台服务器,中间需要socket通信来调用,这种方式称为远程过程调用RPC。有些RPC直接使用HTTP方式。
  • HTTP1.0 单进程:当有连接以后,创建socket通信无限循环接收数据,但是后面的请求会被阻塞。
  • HTTP2.0 多进程:当接收到连接以后,对于这个新的socket,不在主进程里处理,而是新创建子进程来接管,这样主进程就不会阻塞在receive上,可以子继续接收新的连接了。
  • HTTP3.0 Select模型:一个Socket就是一个所谓的文件描述符(File Descriptor, 简称fd,是一个整数),这个fd背后是一个简单的数据结构,但是用了一个重量级的东西’进程’来表示对它的读写操作。操作系统会在后台检查这些编号的socket可以读写,系统会把socket做一个标记,把cpu唤醒去处理这些socket的数据。cpu处理完了再把socket fd告诉操作系统,cpu再次进入阻塞。这种模式抛弃了一个socket请求对应一个进程的模式,只用一个进程就可以处理所有的socket了。
    在这里插入图片描述
  • HTTP4.0 EPoll模型:epoll和select类似,不同的地方在于操作系统只会告诉cpu哪些是可以读写的socket,cpu只需要处理这些准备就绪的socket就可以了。
    在这里插入图片描述

4 代码管理那些事儿

  • 分布式代码管理:
    1. 官方先设置一个代码仓库。
    2. 每个人都可以fork官方代码仓库,clone到本地,做出修改。
    3. 修改完以后,不直接推送到官方代码仓库,而是推送到自己的代码仓库。
    4. 通知项目维护者,请求他去拉取自己的修改。
    5. 项目维护者在自己的私有代码仓库中获得贡献者的修改,然后决定是否接收一个修改。
    6. 项目维护者将最新的改动推送到官方代码仓库。
    7. 这样一来项目维护者对代码有了审批的权利,只把那些优秀的代码纳入官方代码仓库。
  • 优秀的单元测试:
    1. 单元测试是白盒测试,应该覆盖各个分支流程、异常条件。
    2. 单元测试面向的是一个单元,是由Java的一个类或者几个类组成的单元。
    3. 单元测试的运行速度一定要快。
    4. 单元测试一定是可重复执行的。
    5. 单元测试之间不能有相互依赖,应该是独立的。
    6. 单元测试代码和业务代码同等重要,要一并维护。

5 我的编程语言简史

  • 浏览器从服务器那里获取到HTML网页后,会展示成页面,但在他的内部其实会把html组织成一棵树,这棵树可以被称为DOM
  • Node.js的一个特点:只用一个线程来处理所有请求,由事件驱动编程。
  • 高级程序员考试(现在应该叫做软件设计师),备考的时候把严蔚敏的《数据结构》一书中的习题做了一遍,收获很大,能极大的锻炼逻辑思维能力。
  • 系统级编程博大精深,并且非常稳定,值得深入钻研,成为专家。

6 老司机的精进

  • 吴思先生在《潜规则》(中国历史中的真实游戏)一书中讲述了很多生动有趣的官场故事,透过历史表象,揭示出隐藏在正式规则之下,实际上支配着社会运行的不成文的规矩。
  • 《深入理解计算机系统》一书中提到:指令集是对CPU的抽象,文件是对输入输出设备的抽象,虚拟存储器是对程序存储的抽象,进程是对一个正在运行的程序的抽象,而虚拟机是对整个计算机(包括操作系统、处理器和程序)的抽象。
  • 大脑只是对这个技术点建立了一个整体的概念,在一些细节处做了想当然的假设,等到你用语言再来表达的时候就会发现,原来这个假设是不完成成立的,是有问题的。如果你能把一门技术通俗易懂地给别人讲明白,那就说明你已经掌握了。这种转教别人的办法属于主动学习,效率是最高的。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值