| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> 五万字15张导图Java自学路线,小白零基础入门,程序员进阶,收藏这篇就够了 -> 正文阅读 |
|
[Java知识库]五万字15张导图Java自学路线,小白零基础入门,程序员进阶,收藏这篇就够了 |
作者:token annotation punctuation |
哈喽,大家好,我是一条~ 最近粉丝问我有没有Java自学路线,有了方向才能按图索骥,事半功倍。 我一想确实如此,自己去年总结了一份,但有些地方需要修改,索性利用国庆假期,重新整理一份。 没错,就是这篇文章,本文分为学习路线和配套资料两部分。 文章目录自学路线0.路线导学
按计划行事凡事预则立,不预则废。一个好的计划是成功的一半,而这一半,一条已经帮你整理好了,你只需要收藏即可。 该路线图左侧为主路线,需循序渐进,步步为营;右侧为辅助路线,需贯穿始终,熟练掌握。 建议做好时间规划,不断的提高自己的学习效率,学习过程中尽量把手机调至静音给自己一个安静的学习环境和氛围。 抱团生长独脚难行,孤掌难鸣,一个人的力量终究是有限的,一个人的旅途也注定是孤独的。当你定好计划,怀着满腔热血准备出发的时候,一定要找个伙伴,和唐僧西天取经一样,师徒四人团结一心才能通过九九八十一难。 在学习过程中看下自己身边有没有Java这方面的大神,尽量多问,多交流,如果没有的话,来找我,我一定知无不言言无不尽,还可以给你找一群志同道合的人。水涨船高,柴多火旺,就是这个道理,闭门造车注定会半途而废。 贵在坚持驽马十驾,功在不舍。自学Java非一日之功,你知道的越多,不知道的也越多。所以,为自己找一个动力,为了改变命运,或是为了心爱的人,或是为了让别人高看一眼。男儿何不带吴钩,收取关山十五州。岁月无情,余生有涯,请将生活扛在肩上,只顾风雨兼程。 1.Java基础
基本数据类型Java 语言提供了 8 种基本类型,大致分为 4 类(8位=1字节)
引用数据类型简单来说,所有的非基本数据类型都是引用数据类型,除了基本数据类型对应的引用类型外,类、 接口类型、 数组类型、 枚举类型、 注解类型、 字符串型都属于引用类型。 主要有以下区别: 1、存储位置
2、传递方式
访问修饰符访问修饰符就是限制变量的访问权限的。 比如你有个“赚钱”的方法,谁都不想给用,那就把方法设成 后来你有了老婆孩子,你想让他们也会赚钱,就得设置成 后来你又有了第二个孩子,但你发现他不会赚钱的方法,为啥呢?因为你被绿了(default不支持不同包的子类); 可为了大局,你还是选择接受这个孩子,悄悄把方法设置成了 后来你老了,明白了开源才是共赢,就设置成了 不知道你听懂了吗,估计看到被那啥了就不想看了吧,没关系,看图(也是绿的) static关键字主要意义: 我日常调用方法都是对象.方法, 另一个比较关键的作用就是 用来形成静态代码块以优化程序性能。 通俗理解:
final关键字
面向对象三大特性封装 1.什么是封装 封装又叫隐藏实现。就是只公开代码单元的对外接口,而隐藏其具体实现。 其实生活中处处都是封装,手机,电脑,电视这些都是封装。你只需要知道如何去操作他们,并不需要知道他们里面是怎么构造的,怎么实现这个功能的。 2.如何实现封装 在程序设计里,封装往往是通过访问控制实现的。也就是刚才提到的访问修饰符。 3.封装的意义 封装提高了代码的安全性,使代码的修改变的更加容易,代码以一个个独立的单元存在,高内聚,低耦合。 好比只要你手机的充电接口不变,无论以后手机怎么更新,你依然可以用同样的数据线充电或者与其他设备连接。 封装的设计使使整个软件开发复杂度大大降低。我只需要使用别人的类,而不必关心其内部逻辑是如何实现的。我能很容易学会使用别人写好的代码,这就让软件协同开发的难度大大降低。 封装还避免了命名冲突的问题。 好比你家里有各种各样的遥控器,但比还是直到哪个是电视的,哪个是空调的。因为一个属于电视类一个属于空调类。不同的类中可以有相同名称的方法和属性,但不会混淆。 继承 继承的主要思想就是将子类的对象作为父类的对象来使用。比如王者荣耀的英雄作为父类,后裔作为子类。后裔有所有英雄共有的属性,同时也有自己独特的技能。 多态 多态的定义: 指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用) 简单来说,同样调用攻击这个方法,后裔的普攻和亚瑟的普攻是不一样的。 多态的条件:
多态的好处: 多态对已存在代码具有可替换性。 多态对代码具有可扩充性。 它在应用中体现了灵活多样的操作,提高了使用效率。 多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。 Java中多态的实现方式:
完整讲解入门练习案例2.JavaWeb
HTTP网络请求方式
GET和POST
幂等性
幂等性:指的是同样的请求不管执行多少次,效果都是一样,服务器状态也是一样的。具有幂等性的请求方法没有副作用。(统计用途除外) 如何保证幂等性
1.insert前select 插入数据前先根据某一字段查询一下数据库,如果已经存在就修改,不存在再插入。 2.加锁 加锁可解决一切问题,但也要考虑并发性。 主要包括悲观锁,乐观锁,分布式锁。 悲观锁的并发性较低,更适合使用在防止数据重复的场景,注意幂等性不光是防止重复还需要结果相同。 乐观锁可以很高的提升性能,也就是常说的版本号。 分布式锁应用在高并发场景,主要用redis来实现。 3.唯一索引 通过数据库的唯一索引来保证结果的一致性和数据的不重复。 4.Token 两次请求,第一请求拿到token,第二次带着token去完成业务请求。 常见的网络状态码
1xx(信息性状态码) 代表请求已被接受,需要继续处理。 包括: 这一系列的在实际开发中基本不会遇到,可以略过。 2xx(成功状态码) 表示成功处理了请求的状态代码。
3xx(重定向状态码)
301和302的区别?
4xx(客户端错误状态码)
5xx(服务器错误状态码)
??有时候返回的状态码响应是错误的,比如Web应用程序内部发生错误,状态码依然返回 转发和重定向
1.转发 A找B借钱,B没有钱,B去问C,C有钱,C把钱借给A的过程。 客户浏览器发送http请求,web服务器接受此请求,调用内部的一个方法在容器内部完成请求处理和转发动作,将目标资源发送给客户。 整个转发一个请求,一个响应,地址栏不会发生变化,不能跨域访问。 2.重定向 A找B借钱,B没有钱,B让A去找C,A又和C借钱,C有钱,C把钱借给A的过程。 客户浏览器发送http请求,web服务器接受后发送302状态码响应及对应新的location给客户浏览器,客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址,服务器根据此请求寻找资源并发送给客户。 两个请求,两个响应,可以跨域。 Servlet
之前都是浏览器像服务器请求资源,服务器(tomcat)返回页面,但用户多了之后,每个用户希望带到不用的资源。这时就该
看一下 Servlet的生命周期Servlet生命周期分为三个阶段:
session、cookie、token首先我们要明白HTTP是一种无状态协议,怎么理解呢?很简单
这段对话都熟悉吧,HTTP就是那个大爷,那如果我们就直接把“大爷”放给用户,用户不用干别的了,就不停的登录就行了。 既然“大爷不靠谱”,我们找“大娘”去吧。 哈哈哈,开个玩笑,言归正传。 为了解决用户频繁登录的问题,在服务端和客户端共同维护一个状态——会话,就是所谓 但是服务器可不开心了,因为用户越来越多,都要把 但是问题依然存在,如果兄弟挂了怎么办,兄弟们之间的数据怎么同步,用户1把 这时有人思考,为什么一定要服务端保存呢,让客户端自己保存不就好了,所以就诞生了 但是空口无凭,服务器怎么知道哪个 有人想到了一个办法,用加密令牌,也就是 MVC与三层架构三层架构与MVC的目标一致:都是为了解耦和、提高代码复用。MVC是一种设计模式,而三层架构是一种软件架构。 MVC Model 模型 模型负责各个功能的实现(如登录、增加、删除功能),用 View 视图 用户看到的页面和与用户的交互。包含各种表单。 实现视图用到的技术有html/css/jsp/js等前端技术。 常用的web 容器和开发工具 Controller 控制器 控制器负责将视图与模型一一对应起来。相当于一个模型分发器。接收请求,并将该请求跳转(转发,重定向)到模型进行处理。模型处理完毕后,再通过控制器,返回给视图中的请求处。 三层架构 表现层(UI)(web层)、业务逻辑层(BLL)(service层)、数据访问层(DAL)(dao层) ,再加上实体类库(Model)
完整讲解3.集合
ArrayList底层实现
毋庸置疑,底层由数组组成,那数组的特点就是
扩容
1.初始化容量为10,也可以指定容量创建。
2.数组进行扩容时,是将旧数据拷贝到新的数组中,新数组容量是原容量的1.5倍。(这里用位运算是为了提高运算速度)
3.扩容代价是很高得,因此再实际使用时,我们因该避免数组容量得扩张。尽可能避免数据容量得扩张。尽可能,就至指定容量,避免数组扩容的发生。 为什么扩容是1.5倍?
所以, 线程安全问题
没有加锁,所以线程不安全 在多线程的情况下,插入数据的时可能会造成数据丢失,一个线程在遍历,另一个线程修改,会报 多线程下使用怎么保证线程安全?
1.Vector
找到
2.Collections 注意是
对于Map、Set也有对应的方法 3.CopyOnWrite(写时复制) 写时复制,简称COW,是计算机程序设计领域中的一种通用优化策略。 当有多人同时访问同一资源时,他们会共同获取指向相同的资源的指针,供访问者进行读操作。 当某个调用者修改资源内容时,系统会真正复制一份副本给该调用者,而其他调用者所见到的最初的资源仍然保持不变。修改完成后,再把新的数据写回去。 通俗易懂的讲,假设现在有一份班级名单,但有几个同学还没有填好,这时老师把文件通过微信发送过去让同学们填写(复制一份),但不需要修改的同学此时查看的还是旧的名单,直到有同学修改好发给老师,老师用新的名单替换旧的名单,全班同学才能查看新的名单。 共享读,分开写。读写分离,写时复制。 在java中,通过 平时查询的时候,都不需要加锁,随便访问,只有在更新的时候,才会从原来的数据复制一个副本出来,然后修改这个副本,最后把原数据替换成当前的副本。修改操作的同时,读操作不会被阻塞,而是继续读取旧的数据。
源码里用到了 LinkedList
底层实现
先简单说一下链表这种数据结构,与数组相反,链表是一种物理存储单元上非连续、非顺序的存储结构,一个最简单的链表(单链表)有节点
链表中保存着对最后一个节点的引用,这就是双端链表 在单链表的结点中增加一个指向其前驱的pre指针就是双向链表,一种牺牲空间换时间的做法。 双端链表不同于双向链表,切记! 关于链表更详细代码级讲解会放《糊涂算法》专栏更新。敬请期待! 简单了解过后分析一下链表的特点:
如何解决查询慢的问题? 如果我查找的元素在尾部,则需要遍历整个链表,所以有了双端链表。 即使不在尾部,我如果只能一个方向遍历,也很麻烦,所以有了双向队列,牺牲空间换时间。 那么空间可不可以再牺牲一点? 可以,就是跳跃链表,简称「跳表」。 通过建立多级索引来加快查询速度。 线程安全问题
都没加锁,百分之一百的不安全。 如何解决线程不安全问题 1.ConcurrentLinkedQueue 一个新的类,位于
使用
1.Collections 和 Map:存储双列数据的集合,通过键值对存储数据,存储 的数据是无序的,Key值不能重复,value值可以重复 和ArrayList对比一下共同点:有序,可重复。线程不安全。 不同点:底层架构,查询和删改的速度 完整讲解4.JVM
JVM的体系架构
类装载器classloader
双亲委派机制 当一个类收到类加载请求,他会先把这个请求交给他的父类,只有父类无法完成这个请求时,子加载器才会尝试自己去加载。 双亲委派的好处是保护Java核心类,比如加载位于rt.jar中的java.lang.Object,不管是哪个加载器加载的,最终都会交给启动类加载器,这样就保证了不同的类加载器得到的都是同一个Object对象。 代码举例:查看类是被那个加载器加载的
输出
因为Object是jdk自带的,所以在加载的时候是走Bootstrap启动类加载器,而Bootstrap加载器是C++语言写的,所以在查的时候是null,报了NullPointException();Test类自己写的,走AppClassLoder,他的父类是扩展加载器,再父类是启动类加载器,也输出Null 沙箱安全机制 主要是防止恶意代码污染java源代码,比如定义了一个类名为String所在包为java.lang,因为这个类本来是属于jdk的,如果没有沙箱安全机制的话,这个类将会污染到我所有的String,但是由于沙箱安全机制,所以就委托顶层的bootstrap加载器查找这个类,如果没有的话就委托extsion,extsion没有就到appclassloader,但是由于String就是jdk的源代码,所以在bootstrap那里就加载到了,先找到先使用,所以就使用bootstrap里面的String,后面的一概不能使用,这就保证了不被恶意代码污染。 垃圾回收
判断垃圾
1.引用计数法 非常好理解,引用一次标记一次,没有被标记的就是垃圾。 在堆中存储对象时,在对象头处维护一个 如果一个引用关系失效则 2.引用计数的缺点
3.可达性分析 类似树的树结构,从根结点出发,即GC root,把有关系的对象用一颗树链接起来 那么我们遍历这棵树,没遍历到的对象,就是垃圾 4.有哪些可以做GC Roots的对象?
回收算法
1.标记-清除 两次遍历:
优点:
缺点:
2.标记-复制 将空间分为相等大小的两部分,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。牺牲空间解决碎片问题。 优点:
缺点:
3.标记-整理 同样是为了解决空间碎片提出,区别是通过牺牲时间的方式。 和标记-清除类似,不一样的是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。 优点:
缺点:
完整讲解5.多线程
并行和并发并行:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。 并发:多个处理器或多核处理器同时处理多个任务。 举例: 并发 = 两个队列和一台咖啡机。 并行 = 两个队列和两台咖啡机。 线程和进程一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。 守护线程守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。 创建线程4种方式
synchronized 底层实现synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的基本实现单元。 在 Java 6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。 但在 Java 6 的时候,Java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的 monitor 实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。 synchronized 和 volatile 的区别volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。 volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。 volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。 synchronized 和 Lock 区别synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。 synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁。 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。 synchronized 和 ReentrantLock 区别synchronized 早期的实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大,但是在 Java 6 中对 synchronized 进行了非常多的改进。 主要区别如下: ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作; ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁; ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。 volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。 6.设计模式
原型模式定义 官方定义
通俗解读 在需要创建重复的对象,为了保证性能,本体给外部提供一个克隆体进行使用。 类似我国的印刷术,省去 结构图 代码实现 目录结构
开发场景 假设一条开发了一个替代 一条想到一个解决办法,就是把查过的数据保存起来,下来查相同的数据,直接把保存好的对象返回,也就是缓存的思想。 我们用代码模拟一下: 1.创建
2.创建
3.编写测试类
输出结果 从结果可以看出:
好像是实现了 4.修改对象id 在测试类继续编写
输出结果 重点看第三次查询, 我们在内存修改的数据,导致从数据库查出来的数据也跟着改变,出现脏数据。 怎么解决呢?原型模式正式开始。 5.实现 本体给外部提供一个克隆体进行使用,在缓存中拿到的对象不直接返回,而是复制一份,这样就保证了不会脏缓存。
修改缓存
6.再次测试 不用改测试类,直接看一下结果: 从输出结果可以看出第三次查询 基于原型模式的克隆思想,我可以快速拿到和「本体」一模一样的「克隆体」,而且对象也只被 不知道大家是否好奇对象是怎么被创建出来的,那我们就一起看一下「深拷贝」和「浅拷贝」是怎么回事。 深拷贝和浅拷贝 定义
好比两个兄弟,深拷贝是年轻的时候关系特别好,衣服买一样的,房子住一块。浅拷贝是长大了都成家立业,衣服可以继续买一样的,但房子必须要分开住了。 实现
浅拷贝 1.通过 新建
2.测试1
输出结果 结论:基本类型 3.通过构造方法实现浅拷贝
4.测试2
输出结果 与测试1无异 深拷贝 1.通过对象序列化实现深拷贝
2.测试
输出结果 结论,引用对象也完全复制一个新的,值不变化。 不过要注意的是,如果某个属性被 应用场景
原型模式已经与 Java 融为浑然一体,可以随手拿来使用。 更多设计模式7.SSM框架
ORM 框架?ORM(Object Relation Mapping)对象关系映射,是把数据库中的关系数据映射成为程序中的对象。 使用 ORM 的优点:提高了开发效率降低了开发成本、开发更简单更对象化、可移植更强。 MyBatis 中 #{}和 的区别#是预编译处理,{}的区别是什么?#{}是预编译处理,的区别是什么?#是预编译处理,{}是字符替换。 在使用 #{}时,MyBatis 会将 SQL 中的 #{}替换成“?”,配合 PreparedStatement 的 set 方法赋值,这样可以有效的防止 SQL 注入,保证程序的运行安全。 什么是Springspring 提供 ioc 技术,容器会帮你管理依赖的对象,从而不需要自己创建和管理依赖对象了,更轻松的实现了程序的解耦。 spring 提供了事务支持,使得事务操作变的更加方便。 spring 提供了面向切片编程,这样可以更方便的处理某一类的问题。 更方便的框架集成,spring 可以很方便的集成其他框架,比如 MyBatis、hibernate 等。 什么是 aopaop 是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。 简单来说就是统一处理某一“切面”(类)的问题的编程思想,比如统一处理日志、异常等。 什么是 iocioc:Inversionof Control(中文:控制反转)是 spring 的核心,对于 spring 框架来说,就是由 spring 来负责控制对象的生命周期和对象间的关系。 简单来说,控制指的是当前对象对内部成员的控制权;控制反转指的是,这种控制权不由当前对象管理了,由其他(类,第三方容器)来管理。 spring mvc 运行流程spring mvc 先将请求发送给 DispatcherServlet。 DispatcherServlet 查询一个或多个 HandlerMapping,找到处理请求的 Controller。 DispatcherServlet 再把请求提交到对应的 Controller。 Controller 进行业务逻辑处理后,会返回一个ModelAndView。 Dispathcher 查询一个或多个 ViewResolver 视图解析器,找到 ModelAndView 对象指定的视图对象。 什么是 spring boot?spring boot 是为 spring 服务的,是用来简化新 spring 应用的初始搭建以及开发过程的。 为什么要用 spring boot?配置简单 独立运行 自动装配 无代码生成和 xml 配置 提供应用监控 易上手 提升开发效率 8.Redis
什么是RedisRedis 是一个使用 C 语言开发的高速缓存数据库。 Redis 使用场景:
Redis 的功能
Redis 和 memcache存储方式不同:memcache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小;Redis 有部份存在硬盘上,这样能保证数据的持久性。 数据支持类型:memcache 对数据类型支持相对简单;Redis 有复杂的数据类型。 使用底层模型不同:它们之间底层实现方式,以及与客户端之间通信的应用协议不一样,Redis 自己构建了 vm 机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。 value 值大小不同:Redis 最大可以达到 1gb;memcache 只有 1mb。 Redis 为什么是单线程的因为 cpu 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 cpu 又不会成为瓶颈,那就顺理成章地采用单线程的方案了。 关于 Redis 的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求。 而且单线程并不代表就慢 nginx 和 nodejs 也都是高性能单线程的代表。 缓存穿透缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。 解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 Redis 数据类型Redis 支持的数据类型:string(字符串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)。 9.Zookeeper
什么是zookeeperzookeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 google chubby 的开源实现,是 hadoop 和 hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。 zookeeper 的功能集群管理:监控节点存活状态、运行请求等。 主节点选举:主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程,使用 zookeeper 可以协助完成这个过程。 分布式锁:zookeeper 提供两种锁:独占锁、共享锁。独占锁即一次只能有一个线程使用资源,共享锁是读锁共享,读写互斥,即可以有多线线程同时读同一个资源,如果要使用写锁也只能有一个线程使用。zookeeper可以对分布式锁进行控制。 命名服务:在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。 zookeeper 的部署模式zookeeper 有三种部署模式: 单机部署:一台集群上运行; 集群部署:多台集群运行; 伪集群部署:一台集群启动多个 zookeeper 实例运行。 10.Kafkakafka和zookeeper的关系kafka 不能脱离 zookeeper 单独使用,因为 kafka 使用 zookeeper 管理和协调 kafka 的节点服务器。 kafka的数据保留的策略?kafka 有两种数据保存策略:按照过期时间保留和按照存储的消息大小保留。 kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理? kafka性能瓶颈cpu 性能瓶颈 磁盘读写瓶颈 网络瓶颈 kafka集群集群的数量不是越多越好,最好不要超过 7 个,因为节点越多,消息复制需要的时间就越长,整个群组的吞吐量就越低。 集群数量最好是单数,因为超过一半故障集群就不能用了,设置为单数容错率更高。 11.ES
12.Dubbo
13.SpringCloud
什么是 spring cloudspring cloud 是一系列框架的有序集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 spring boot 的开发风格做到一键启动和部署。 spring cloud 的核心组件Eureka:服务注册于发现。 Feign:基于动态代理机制,根据注解和选择的机器,拼接请求 url 地址,发起请求。 Ribbon:实现负载均衡,从一个服务的多台机器中选择一台。 Hystrix:提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。 Zuul:网关管理,由 Zuul 网关转发请求给对应的服务。 断路器的作用在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。 14.Nginx
15.Netty
16.架构设计
下面拿如何设计一个百万人抽奖系统举例说明架构的演进。 V0——单体架构
两猫一豚走江湖,中奖入库,调通知服务,查库通知,完美! 相信大家学java时可能都做过这种案例,思考🤔一下存在什么问题?
接下来就聊聊怎么解决这些问题? V1——负载均衡
负载均衡就是建立很多很多服务器,组成一个服务器集群,当用户访问网站时,先访问一个中间服务器,好比管家,由他在服务器集群中选择一个压力较小的服务器,然后将该访问请求引入该服务器。 如此以来,用户的每次访问,都会保证服务器集群中的每个服务器压力趋于平衡,分担了服务器压力,避免了服务器崩溃的情况。 负载均衡是用「反向代理」的原理实现的。具体负载均衡算法及其实现方式我们下文再续。 负载均衡虽然解决了单体架构一着不慎满盘皆输的问题,但服务器成本依然不能保护系统周全,我们必须想好一旦服务器宕机,如何保证用户的体验。 即如何缓解开奖一瞬间时的大量请求。 V2——服务限流
在上一小节我们做好了负载均衡来保证集群的可用性,但公司需要需要考虑服务器的成本,不可能无限制的增加服务器数量,一般会经过计算保证日常的使用没问题。 限流的意义就在于我们无法预测未知流量,比如刚提到的抽奖可能遇到的:
其他一些场景:
这些情况都是无法预知的,不知道什么时候会有10倍甚至20倍的流量打进来,如果真碰上这种情况,扩容是根本来不及的(弹性扩容都是虚谈,一秒钟你给我扩一下试试) 明确了限流的意义,我们再来看看如何实现限流 防止用户重复抽奖
如果同一个用户在1分钟之内多次发送请求来进行抽奖,就认为是恶意重复抽奖或者是脚本在刷奖,这种流量是不应该再继续往下请求的,在负载均衡层给直接屏蔽掉。 可以通过 用户的抽奖状态可以通过redis来存储,后面会说。 拦截无效流量
举个例子,假设50万人抽奖,就准备了100台手机,那么50万请求瞬间涌入,其实前500个请求就把手机抢完了,后续的几十万请求就没必要让他再执行业务逻辑,直接暴力拦截返回抽奖结束就可以了。 同时前端在按钮置灰上也可以做一些文章。 那么思考一下如何才能知道奖品抽完了呢,也就是库存和订单之前的数据同步问题。 服务降级和服务熔断
有好多人容易混淆这两个概念,通过一个小例子让大家明白: 假设现在一条粉丝数突破100万,冲上微博热搜,粉丝甲和粉丝乙都打开微博观看,但甲看到了一条新闻发布会的内容,乙却看到”系统繁忙“,过了一会,乙也能看到内容了。 (请允许一条幻想一下😎) 在上述过程中,首先是热点时间造成大量请求,发生了服务熔断,为了保证整个系统可用,牺牲了部分用户乙,乙看到的”系统繁忙“就是服务降级(fallback),过了一会有恢复访问,这也是熔断器的一个特性(hystrix) V3 同步状态
这不得不提到 我们可以基于Redis来实现这种共享抽奖状态,它非常轻量级,很适合两个层次的系统的共享访问。 当然其实用ZooKeeper也是可以的,在负载均衡层可以基于zk客户端监听某个znode节点状态。一旦抽奖结束,抽奖服务更新zk状态,负载均衡层会感知到。 V4线程优化
众所周知,对于进入Tomcat的每个请求,其实都会交给一个独立的工作线程来进行处理,那么Tomcat有多少线程,就决定了并发请求处理的能力。 但是这个线程数量是需要经过压测来进行判断的,因为每个线程都会处理一个请求,这个请求又需要访问数据库之类的外部系统,所以不是每个系统的参数都可以一样的,需要自己对系统进行压测。 但是给一个经验值的话,Tomcat的线程数量不宜过多。因为线程过多,普通服务器的CPU是扛不住的,反而会导致机器CPU负载过高,最终崩溃。 同时,Tomcat的线程数量也不宜太少,因为如果就100个线程,那么会导致无法充分利用Tomcat的线程资源和机器的CPU资源。 所以一般来说,Tomcat线程数量在200~500之间都是可以的,但是具体多少需要自己压测一下,不断的调节参数,看具体的CPU负载以及线程执行请求的一个效率。 在CPU负载尚可,以及请求执行性能正常的情况下,尽可能提高一些线程数量。 但是如果到一个临界值,发现机器负载过高,而且线程处理请求的速度开始下降,说明这台机扛不住这么多线程并发执行处理请求了,此时就不能继续上调线程数量了。 V5业务逻辑
好了,现在该研究一下怎么做抽奖了 在负载均衡那个层面,已经把比如50万流量中的48万都拦截掉了,但是可能还是会有2万流量进入抽奖服务。 因为抽奖活动都是临时服务,可以阿里云租一堆机器,也不是很贵,tomcat优化完了,服务器的问题也解决了,还剩啥呢? Mysql,是的,你的Mysql能抗住2万的并发请求吗? 答案是很难,怎么办呢? 把Mysql给替换成redis,单机抗2万并发那是很轻松的一件事情。 而且redis的一种数据结构 V6流量削峰
思考这个问题:假设抽奖服务在2万请求中有1万请求抽中了奖品,那么势必会造成抽奖服务对礼品服务调用1万次。 那也要和抽奖服务同样处理吗? 其实并不用,因为发送通知不要求及时性,完全可以让一万个请求慢慢发送,这时就要用到消息中间件,进行限流削峰。 也就是说,抽奖服务把中奖信息发送到MQ,然后通知服务慢慢的从MQ中消费中奖消息,最终完成完礼品的发放,这也是我们会延迟一些收到中奖信息或者物流信息的原因。 假设两个通知服务实例每秒可以完成100个通知的发送,那么1万条消息也就是延迟100秒发放完毕罢了。 同样对MySQL的压力也会降低,那么数据库层面也是可以抗住的。 看一下最终结构图: 17.Linux
18.Git
命令大全19.数据结构和算法
排序算法
准备
该段代码内容主要有两个功能:
1.冒泡排序 基本思想 通过对乱序序列从前向后遍历,依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部。 像水底下的气泡一样逐渐向上冒一样。 动图讲解 代码实现
输出结果 逐步分析
这时再回去看动图理解。 耗时测试
时间复杂度: 算法优化 优化点一 外层第一次遍历完,最后一位已经是正确的, 优化点二 因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志 优化代码
优化测试 通过基础测试看到当序列已经排好序,即不发生交换后终止循环。 耗时测试由 2.选择排序 基本思想 选择排序和冒泡排序很像,是从乱序序列的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。 动图讲解 代码实现
输出结果 逐步分析
这时再回去看动图理解。 耗时测试 时间复杂度: 算法优化 上诉代码中使用交换的方式找到较小值,还可以通过移动的方式,即全部比较完只交换一次。 这种对空间的占有率会有些增益,但对时间的增益几乎没有,可忽略,亦不再演示。 3.插入排序 基本思想 把n个乱序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中通过不断往有序表插入元素,获取一个局部正确解,逐渐扩大有序序列的长度,直到完成排序。 动图讲解 代码实现
输出结果 耗时测试 算法优化 见下方希尔排序,就是希尔对插入排序的优化。 4.希尔排序
基本思想 希尔排序是把记录按下标的一定增量分组,对每组使用插入排序; 随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个序列恰被分成一组,算法便终止。 和插入排序一样,从局部到全部,希尔排序是局部再局部。 动图讲解 代码实现
输出结果 耗时测试 算法优化 无 5.快速排序
基本思想 将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。 体现出分治的思想。 动图讲解 代码实现
输出结果
逐步分析
耗时测试 算法优化 优化一 三数取中(median-of-three):我们目前是拿第一个数作为基准数,对于部分有序序列,会浪费循环,可以用三数取中法优化,感性的小伙伴可自行了解。 优化二 快速排序对于长序列非常快,但对于短序列不如插入排序。可以综合使用。 1eetcode刷题
Question 难度:中等
Solution
Code
Result
20.计算机网络
21.操作系统
22.计算机组成原理
23.程序员英语
24.写作能力
25.演讲能力
26.管理能力
配套资料电子书1.Java核心技术卷一、二
2.深入理解Java虚拟机附带读书笔记。 3.java并发编程的艺术
4.阿里巴巴Java开发手册
Redis 深度历险:核心原理与应用实践
Apache Kafka实战
视频1.零基础入门2.JVM3.SpringCloud4.程序员一条
面试集一条整理204道全路线面试题合集Java核心知识点
粉丝福利
|
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 21:05:09- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |