diff --git a/Docs/Dubbo.md b/Dubbo.md similarity index 100% rename from Docs/Dubbo.md rename to Dubbo.md diff --git a/Elasticsearch.md b/Elasticsearch.md new file mode 100644 index 0000000..27e6a25 --- /dev/null +++ b/Elasticsearch.md @@ -0,0 +1,294 @@ +## Elasticsearch + +#### 1.简单介绍下ES? + +ES是一种存储和管理基于文档和半结构化数据的数据库(搜索引擎)。它提供实时搜索(ES最近几个版本才提供实时搜索,以前都是准实时)和分析结构化、半结构化文档、数据和地理空间信息数据。 + +#### 2.简单介绍当前可以下载的ES稳定版本? + +最新的稳定版本是7.10.0 + +#### 3.安装ES前需要安装哪种软件? + +JDK 8或者 Java 1.8.0 + +#### 4.请介绍启动ES服务的步骤? + +**A:**启动步骤如下 + +Windows下进入ES文件夹的bin目录下,点击ElasticSearch.bat开始运行 + +打开本地9200端口http://localhost:9200, 就可以使用ES了 + +#### 5.ES中的倒排索引是什么? + +传统的检索方式是通过文章,逐个遍历找到对应关键词的位置。 +倒排索引,是通过分词策略,形成了词和文章的映射关系表,也称倒排表,这种词典 + 映射表即为**倒排索引**。 + +其中词典中存储词元,倒排表中存储该词元在哪些文中出现的位置。 +有了倒排索引,就能实现 O(1) 时间复杂度的效率检索文章了,极大的提高了检索效率。 + +加分项: +倒排索引的底层实现是基于:FST(Finite State Transducer)数据结构。 + +Lucene 从 4+ 版本后开始大量使用的数据结构是 FST。FST 有两个优点: +1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间; +2)查询速度快。O(len(str)) 的查询时间复杂度。 + +#### 6. ES是如何实现master选举的? + +前置条件: +1)只有是候选主节点(master:true)的节点才能成为主节点。 +2)最小主节点数(min_master_nodes)的目的是防止脑裂。 + +Elasticsearch 的选主是 ZenDiscovery 模块负责的,主要包含 Ping(节点之间通过这个RPC来发现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要 ping 通)这两部分; +获取主节点的核心入口为 findMaster,选择主节点成功返回对应 Master,否则返回 null。 + +选举流程大致描述如下: +第一步:确认候选主节点数达标,elasticsearch.yml 设置的值 discovery.zen.minimum_master_nodes; +第二步:对所有候选主节点根据nodeId字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。 +第三步:如果对某个节点的投票数达到一定的值(候选主节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件。 + +- 补充: + - 这里的 id 为 string 类型。 + - master 节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data 节点可以关闭 http 功能。 + +#### 7. 如何解决ES集群的脑裂问题 + +所谓集群脑裂,是指 Elasticsearch 集群中的节点(比如共 20 个),其中的 10 个选了一个 master,另外 10 个选了另一个 master 的情况。 + +当集群 master 候选数量不小于 3 个时,可以通过设置最少投票通过数量(discovery.zen.minimum_master_nodes)超过所有候选节点一半以上来解决脑裂问题; +当候选数量为两个时,只能修改为唯一的一个 master 候选,其他作为 data 节点,避免脑裂问题。 + +#### 8. 详细描述一下ES索引文档的过程? + +这里的索引文档应该理解为文档写入 ES,创建索引的过程。 + +第一步:客户端向集群某节点写入数据,发送请求。(如果没有指定路由/协调节点,请求的节点扮演协调节点的角色。) +第二步:协调节点接受到请求后,默认使用文档 ID 参与计算(也支持通过 routing),得到该文档属于哪个分片。随后请求会被转到另外的节点。 + +```java +bash# 路由算法:根据文档id或路由计算目标的分片id +shard = hash(document_id) % (num_of_primary_shards) +``` + +第三步:当分片所在的节点接收到来自协调节点的请求后,会将请求写入到 Memory Buffer,然后定时(默认是每隔 1 秒)写入到F ilesystem Cache,这个从 Momery Buffer 到 Filesystem Cache 的过程就叫做 refresh; +第四步:当然在某些情况下,存在 Memery Buffer 和 Filesystem Cache 的数据可能会丢失,ES 是通过 translog 的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到 translog 中,当 Filesystem cache 中的数据写入到磁盘中时,才会清除掉,这个过程叫做 flush; +第五步:在 flush 过程中,内存中的缓冲将被清除,内容被写入一个新段,段的 fsync 将创建一个新的提交点,并将内容刷新到磁盘,旧的 translog 将被删除并开始一个新的 translog。 +第六步:flush 触发的时机是定时触发(默认 30 分钟)或者 translog 变得太大(默认为 512 M)时。 + +![elasticsearch_index_process.jpg](https://www.wenyuanblog.com/medias/blogimages/elasticsearch_index_process.jpg) + + + +- 补充:关于 Lucene 的 Segement + - Lucene 索引是由多个段组成,段本身是一个功能齐全的倒排索引。 + - 段是不可变的,允许 Lucene 将新的文档增量地添加到索引中,而不用从头重建索引。 + - 对于每一个搜索请求而言,索引中的所有段都会被搜索,并且每个段会消耗 CPU 的时钟周、文件句柄和内存。这意味着段的数量越多,搜索性能会越低。 + - 为了解决这个问题,Elasticsearch 会合并小段到一个较大的段,提交新的合并段到磁盘,并删除那些旧的小段。(段合并) + +#### 9. 详细描述一下ES更新和删除文档的过程? + +删除和更新也都是写操作,但是 Elasticsearch 中的文档是不可变的,因此不能被删除或者改动以展示其变更。 + +磁盘上的每个段都有一个相应的 .del 文件。当删除请求发送后,文档并没有真的被删除,而是在 .del 文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在 .del 文件中被标记为删除的文档将不会被写入新段。 + +在新的文档被创建时,Elasticsearch 会为该文档指定一个版本号,当执行更新时,旧版本的文档在 .del 文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。 + +#### 10. 详细描述一下ES搜索的过程? + +搜索被执行成一个两阶段过程,即 Query Then Fetch; +Query阶段: +查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。每个分片在本地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。PS:在搜索的时候是会查询Filesystem Cache的,但是有部分数据还在Memory Buffer,所以搜索是近实时的。 +每个分片返回各自优先队列中 **所有文档的 ID 和排序值** 给协调节点,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。 +Fetch阶段: +协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。 + +#### 11.索引是什么? + +ES集群包含多个索引,每个索引包含一种表,表包含多个文档,并且每个文档包含不同的属性。 + +#### 12.请解释什么是分片(SHARDs)? + +随着索引文件的增加,磁盘容量、处理能力都会变得不够,在这种情况下,将索引数据切分成小段,这就叫分片(SHARDS)。它的出现大大改进了数据查询的效率。 + +#### 13.什么是副本(REPLICA), 他的作用是什么? + +副本是分片的完整拷贝,副本的作用是增加了查询的吞吐率和在极端负载情况下获得高可用的能力。副本有效的帮助处理用户请求。 + +#### 14.在ES集群中增加和创建索引的步骤是什么? + +可以在Kibana中配置新的索引,进行Fields Mapping,设置索引别名。也可以通过HTTP请求来创建索引。 + +#### 15.ES支持哪些类型的查询? + +主要分为匹配(文本)查询和基于Term的查询。 + +文本查询包括基本匹配,match phrase, multi-match, match phrase prefix, common terms, query-string, simple query string. + +Term查询,比如term exists, type, term set, range, prefix, ids, wildcard, regexp, and fuzzy。 + +#### 16.Elasticsearch在部署时,对Linux的设置有哪些优化方法 + +`面试官`:想了解对ES集群的运维能力。 `解答`: + +- 1)关闭缓存swap; +- 2)堆内存设置为:Min(节点内存/2, 32GB); +- 3)设置最大文件句柄数; +- 4)线程池+队列大小根据业务需要做调整; +- 5)磁盘存储raid方式——存储有条件使用RAID10,增加单节点性能以及避免单节点存储故障。 + +#### 17.什么是ElasticSearch中的编译器? + +编译器用于将字符串分解为术语或标记流。一个简单的编译器可能会将字符串拆分为任何遇到空格或标点的地方。Elasticsearch有许多内置标记器,可用于构建自定义分析器。 + +#### 18.拼写纠错是如何实现的? + +1、拼写纠错是基于编辑距离来实现;编辑距离是一种标准的方法,它用来表示经过插入、删除和替换操作从一个字符串转换到另外一个字符串的最小操作步数; + + + +2、编辑距离的计算过程:比如要计算 batyu 和 beauty 的编辑距离,先创建一个7×8 的表(batyu 长度为 5,coffee 长度为 6,各加 2),接着,在如下位置填入黑色数字。其他格的计算过程是取以下三个值的最小值: + + + +如果最上方的字符等于最左方的字符,则为左上方的数字。否则为左上方的数字+1。(对于 3,3 来说为 0) + +左方数字+1(对于 3,3 格来说为 2) + +上方数字+1(对于 3,3 格来说为 2) + + + +最终取右下角的值即为编辑距离的值 3。 + + + +![img](http://res.mianshigee.com/upload/article/20200307/v2-1f5084b94e47d417b3cebd615ef04647_1440w.jpg) + + + +对于拼写纠错,我们考虑构造一个度量空间(Metric Space),该空间内任何关 + +系满足以下三条基本条件: + + + +d(x,y) = 0 -- 假如 x 与 y 的距离为 0,则 x=y + +d(x,y) = d(y,x) -- x 到 y 的距离等同于 y 到 x 的距离 + +d(x,y) + d(y,z) >= d(x,z) -- 三角不等式 + + + +1、根据三角不等式,则满足与 query 距离在 n 范围内的另一个字符转 B,其与 A + +的距离最大为 d+n,最小为 d-n。 + + + +2、BK 树的构造就过程如下:每个节点有任意个子节点,每条边有个值表示编辑距离。所有子节点到父节点的边上标注 n 表示编辑距离恰好为 n。比如,我们有棵树父节点是”book”和两个子节点”cake”和”books”,”book”到”books”的边标号 1,”book”到”cake”的边上标号 4。从字典里构造好树后,无论何 + +时你想插入新单词时,计算该单词与根节点的编辑距离,并且查找数值为d(neweord, root)的边。递归得与各子节点进行比较,直到没有子节点,你就可以创建新的子节点并将新单词保存在那。比如,插入”boo”到刚才上述例子的树中,我们先检查根节点,查找 d(“book”, “boo”) = 1 的边,然后检查标号为1 的边的子节点,得到单词”books”。我们再计算距离 d(“books”, “boo”)=2,则将新单词插在”books”之后,边标号为 2。 + + + +3、查询相似词如下:计算单词与根节点的编辑距离 d,然后递归查找每个子节点标号为 d-n 到 d+n(包含)的边。假如被检查的节点与搜索单词的距离 d 小于 n,则返回该节点并继续查询。比如输入 cape 且最大容忍距离为 1,则先计算和根的编辑距离 d(“book”, “cape”)=4,然后接着找和根节点之间编辑距离为 3 到5 的,这 + +个就找到了 cake 这个节点,计算 d(“cake”, “cape”)=1,满足条件所以返回 **cake**,然后再找和 cake 节点编辑距离是 0 到 2 的,分别找到 cape 和cart 节点,这样就得到 **cape** 这个满足条件的结果。 + + + + + +![img](http://res.mianshigee.com/upload/article/20200307/v2-d5426155b3c3c0a7e49123954f96e347_1440w.jpg) + + + +#### 19.ElasticSearch中的分析器是什么? + +在ElasticSearch中索引数据时,数据由为索引定义的Analyzer在内部进行转换。 分析器由一个Tokenizer和零个或多个TokenFilter组成。编译器可以在一个或多个CharFilter之前。分析模块允许您在逻辑名称下注册分析器,然后可以在映射定义或某些API中引用它们。 + +Elasticsearch附带了许多可以随时使用的预建分析器。或者,您可以组合内置的字符过滤器,编译器和过滤器器来创建自定义分析器。 + +#### 20.是否了解字典树? + +常用字典数据结构如下所示: + + + +![img](http://res.mianshigee.com/upload/article/20200307/v2-aa1a57bbbcbbf04ef089d6681d662ffe_1440w.jpg) + + + +Trie 的核心思想是空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。它有 3 个基本性质: + +1、根节点不包含字符,除根节点外每一个节点都只包含一个字符。 + +2、从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。 + +3、每个节点的所有子节点包含的字符都不相同。 + + + +![img](http://res.mianshigee.com/upload/article/20200307/v2-df4c8cd2e2b1dad444a50bab3f6d9bb2_1440w.jpg) + +1、可以看到,trie 树每一层的节点数是 26^i 级别的。所以为了节省空间,我们还可以用动态链表,或者用数组来模拟动态。而空间的花费,不会超过单词数×单词长度。 + +2、实现:对每个结点开一个字母集大小的数组,每个结点挂一个链表,使用左儿子右兄弟表示法记录这棵树; + +3、对于中文的字典树,每个节点的子节点用一个哈希表存储,这样就不用浪费太大的空间,而且查询速度上可以保留哈希的复杂度 O(1)。 + +#### 21. 在并发情况下,ES如果保证读写一致? + +可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突; +另外对于写操作,一致性级别支持quorum/one/all,默认为quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。 +对于读操作,可以设置replication为sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置replication为async时,也可以通过设置搜索请求参数_preference为primary来查询主分片,确保文档是最新版本。 + +#### 22. ES对于大数据量(上亿量级)的聚合如何实现? + +Elasticsearch 提供的首个近似聚合是cardinality 度量。它提供一个字段的基数,即该字段的distinct或者unique值的数目。它是基于HLL算法的。HLL 会先对我们的输入作哈希运算,然后根据哈希运算的结果中的 bits 做概率估算从而得到基数。其特点是:可配置的精度,用来控制内存的使用(更精确 = 更多内存);小的数据集精度是非常高的;我们可以通过配置参数,来设置去重需要的固定内存使用量。无论数千还是数十亿的唯一值,内存使用量只与你配置的精确度相关。 + +#### 23. 对于GC方面,在使用ES时要注意什么? + +1)倒排词典的索引需要常驻内存,无法GC,需要监控data node上segment memory增长趋势。 +2)各类缓存,field cache, filter cache, indexing cache, bulk queue等等,要设置合理的大小,并且要应该根据最坏的情况来看heap是否够用,也就是各类缓存全部占满的时候,还有heap空间可以分配给其他任务吗?避免采用clear cache等“自欺欺人”的方式来释放内存。 +3)避免返回大量结果集的搜索与聚合。确实需要大量拉取数据的场景,可以采用scan & scroll api来实现。 +4)cluster stats驻留内存并无法水平扩展,超大规模集群可以考虑分拆成多个集群通过tribe node连接。 +5)想知道heap够不够,必须结合实际应用场景,并对集群的heap使用情况做持续的监控。 + +#### 24. 说说你们公司ES的集群架构,索引数据大小,分片有多少,以及一些调优手段? + +根据实际情况回答即可,如果是我的话会这么回答: +我司有多个ES集群,下面列举其中一个。该集群有20个节点,根据数据类型和日期分库,每个索引根据数据量分片,比如日均1亿+数据的,控制单索引大小在200GB以内。  +下面重点列举一些调优策略,仅是我做过的,不一定全面,如有其它建议或者补充欢迎留言。 +部署层面: +1)最好是64GB内存的物理机器,但实际上32GB和16GB机器用的比较多,但绝对不能少于8G,除非数据量特别少,这点需要和客户方面沟通并合理说服对方。 +2)多个内核提供的额外并发远胜过稍微快一点点的时钟频率。 +3)尽量使用SSD,因为查询和索引性能将会得到显著提升。 +4)避免集群跨越大的地理距离,一般一个集群的所有节点位于一个数据中心中。 +5)设置堆内存:节点内存/2,不要超过32GB。一般来说设置export ES_HEAP_SIZE=32g环境变量,比直接写-Xmx32g -Xms32g更好一点。 +6)关闭缓存swap。内存交换到磁盘对服务器性能来说是致命的。如果内存交换到磁盘上,一个100微秒的操作可能变成10毫秒。 再想想那么多10微秒的操作时延累加起来。不难看出swapping对于性能是多么可怕。 +7)增加文件描述符,设置一个很大的值,如65535。Lucene使用了大量的文件,同时,Elasticsearch在节点和HTTP客户端之间进行通信也使用了大量的套接字。所有这一切都需要足够的文件描述符。 +8)不要随意修改垃圾回收器(CMS)和各个线程池的大小。 +9)通过设置gateway.recover_after_nodes、gateway.expected_nodes、gateway.recover_after_time可以在集群重启的时候避免过多的分片交换,这可能会让数据恢复从数个小时缩短为几秒钟。 +索引层面: +1)使用批量请求并调整其大小:每次批量数据 5–15 MB 大是个不错的起始点。 +2)段合并:Elasticsearch默认值是20MB/s,对机械磁盘应该是个不错的设置。如果你用的是SSD,可以考虑提高到100-200MB/s。如果你在做批量导入,完全不在意搜索,你可以彻底关掉合并限流。另外还可以增加 index.translog.flush_threshold_size 设置,从默认的512MB到更大一些的值,比如1GB,这可以在一次清空触发的时候在事务日志里积累出更大的段。 +3)如果你的搜索结果不需要近实时的准确度,考虑把每个索引的index.refresh_interval 改到30s。 +4)如果你在做大批量导入,考虑通过设置index.number_of_replicas: 0 关闭副本。 +5)需要大量拉取数据的场景,可以采用scan & scroll api来实现,而不是from/size一个大范围。 +存储层面: +1)基于数据+时间滚动创建索引,每天递增数据。控制单个索引的量,一旦单个索引很大,存储等各种风险也随之而来,所以要提前考虑+及早避免。 +2)冷热数据分离存储,热数据(比如最近3天或者一周的数据),其余为冷数据。对于冷数据不会再写入新数据,可以考虑定期force_merge加shrink压缩操作,节省存储空间和检索效率。 + + + +#### 参考 + +https://zhuanlan.zhihu.com/p/265399976 + +https://www.wenyuanblog.com/blogs/elasticsearch-interview-questions.html + +http://www.mianshigee.com/question \ No newline at end of file diff --git a/Git.md b/Git.md new file mode 100644 index 0000000..272dcf9 --- /dev/null +++ b/Git.md @@ -0,0 +1,268 @@ +## Git + +#### 1.什么是Git? + +我建议你先通过了解 git 的架构再来回答这个问题,如下图所示,试着解释一下这个图: + +- Git 是分布式版本控制系统(DVCS)。它可以跟踪文件的更改,并允许你恢复到任何特定版本的更改。 +- 与 SVN 等其他版本控制系统(VCS)相比,其分布式架构具有许多优势,一个主要优点是它不依赖于中央服务器来存储项目文件的所有版本。 +- 每个开发人员都可以“克隆”我在图中用“Local repository”标注的存储库的副本,并且在他的硬盘驱动器上具有项目的完整历史记录,因此当服务器中断时,你需要的所有恢复数据都在你队友的本地 Git 存储库中。 +- 还有一个中央云存储库,开发人员可以向其提交更改,并与其他团队成员进行共享,如图所示,所有协作者都在提交更改“远程存储库”。 + +#### 2.Git 工作流程 + +本章节我们将为大家介绍 Git 的工作流程。 + +一般工作流程如下: + +- 克隆 Git 资源作为工作目录。 +- 在克隆的资源上添加或修改文件。 +- 如果其他人修改了,你可以更新资源。 +- 在提交前查看修改。 +- 提交修改。 +- 在修改完成后,如果发现错误,可以撤回提交并再次修改并提交。 + +下图展示了 Git 的工作流程: + +![img](https://www.runoob.com/wp-content/uploads/2015/02/git-process.png) + +#### 3.在 Git 中提交的命令是什么? + +用于写入提交的命令是 **`git commit -a`**。 + +现在解释一下 `-a` 标志, 通过在命令行上加 `-a` 指示 git 提交已修改的所有被跟踪文件的新内容。还要提一下,如果你是第一次需要提交新文件,可以在在 `git commit -a` 之前先 **`git add `**。 + +#### 4.什么是 Git 中的“裸存储库”? + +你应该说明 “工作目录” 和 “裸存储库” 之间的区别。 + +Git 中的 “裸” 存储库只包含版本控制信息而没有工作文件(没有工作树),并且它不包含特殊的 `.git` 子目录。相反,它直接在主目录本身包含 `.git` 子目录中的所有内容,其中工作目录包括: + +1. 一个 `.git` 子目录,其中包含你的仓库所有相关的 Git 修订历史记录。 +2. 工作树,或签出的项目文件的副本。 + +#### 5.Git 是用什么语言编写的? + +你需要说明使用它的原因,而不仅仅是说出语言的名称。我建议你这样回答: + +Git使用 C 语言编写。 GIT 很快,C 语言通过减少运行时的开销来做到这一点。 + +#### 6.在Git中,你如何还原已经 push 并公开的提交? + +There can be two answers to this question and make sure that you include both because any of the below options can be used depending on the situation: 1 +这个问题可以有两个答案,你回答时也要保包含这两个答案,因为根据具体情况可以使用以下选项: + +- 删除或修复新提交中的错误文件,并将其推送到远程存储库。这是修复错误的最自然方式。对文件进行必要的修改后,将其提交到我将使用的远程存储库 + +``` +git commit -m "commit message" +``` + +- 创建一个新的提交,撤消在错误提交中所做的所有更改。可以使用命令: + +``` +git revert +``` + + + +#### 7.git pull 和 git fetch 有什么区别? + +`git pull` 命令从中央存储库中提取特定分支的新更改或提交,并更新本地存储库中的目标分支。 + +`git fetch` 也用于相同的目的,但它的工作方式略有不同。当你执行 `git fetch` 时,它会从所需的分支中提取所有新提交,并将其存储在本地存储库中的新分支中。如果要在目标分支中反映这些更改,必须在 `git fetch` 之后执行`git merge`。只有在对目标分支和获取的分支进行合并后才会更新目标分支。为了方便起见,请记住以下等式: + +
git pull = git fetch + git merge
+ + + +#### 8.git中的“staging area”或“index”是什么? + +For this answer try to explain the below diagram as you can see: +可以通过下图进行解释: + +在完成提交之前,可以在称为“staging area”或“index”的中间区域中对其进行格式化和审查。从图中可以看出,每个更改首先在暂存区域中进行验证,我将其称为“stage file”,然后将更改提交到存储库。 + +![clipboard.png](https://segmentfault.com/img/bVbtc0c?w=655&h=645) + +### 9.什么是 git stash? + +首先应该解释 git stash 的必要性。 + +通常情况下,当你一直在处理项目的某一部分时,如果你想要在某个时候切换分支去处理其他事情,事情会处于混乱的状态。问题是,你不想把完成了一半的工作的提交,以便你以后就可以回到当前的工作。解决这个问题的答案是 git stash。 + +再解释什么是git stash。 + +stash 会将你的工作目录,即修改后的跟踪文件和暂存的更改保存在一堆未完成的更改中,你可以随时重新应用这些更改。 + +#### 10.什么是git stash drop? + +通过说明我们使用 `git stash drop` 的目的来回答这个问题。 + +`git stash drop` 命令用于删除隐藏的项目。默认情况下,它将删除最后添加的存储项,如果提供参数的话,它还可以删除特定项。 + +下面举个例子。 + +如果要从隐藏项目列表中删除特定的存储项目,可以使用以下命令: + +**git stash list:**它将显示隐藏项目列表,如: + +stash@{0}: WIP on master: 049d078 added the index file +stash@{1}: WIP on master: c264051 Revert “added file_size” +stash@{2}: WIP on master: 21d80a5 added number to log + +如果要删除名为 stash@{0} 的项目,请使用命令 **git stash drop stash@{0}**。 + +#### 11.如何找到特定提交中已更改的文件列表? + +对于这个问题,不能仅仅是提供命令,还要解释这个命令究竟做了些什么。 + +要获取特定提交中已更改的列表文件,请使用以下命令: + +**git diff-tree -r {hash}** + +给定提交哈希,这将列出在该提交中更改或添加的所有文件。 `-r` 标志使命令列出单个文件,而不是仅将它们折叠到根目录名称中。 + +你还可以包括下面提到的内容,虽然它是可选的,但有助于给面试官留下深刻印象。 + +输出还将包含一些额外信息,可以通过包含两个标志把它们轻松的屏蔽掉: + +**git diff-tree –no-commit-id –name-only -r {hash}** + +这里 `-no-commit-id` 将禁止提交哈希值出现在输出中,而 `-name-only` 只会打印文件名而不是它们的路径。 + +#### 12.git config 的功能是什么? + +首先说明为什么我们需要 `git config`。 + +git 使用你的用户名将提交与身份相关联。 `git config` 命令可用来更改你的 git 配置,包括你的用户名。 + +下面用一个例子来解释。 + +假设你要提供用户名和电子邮件 ID 用来将提交与身份相关联,以便你可以知道是谁进行了特定提交。为此,我将使用: + +**git config –global user.name "Your Name":** 此命令将添加用户名。 + +**git config –global user.email "Your E-mail Address":** 此命令将添加电子邮件ID。 + +#### 13.提交对象包含什么? + +Commit 对象包含以下组件,你应该提到以下这三点: + +- 一组文件,表示给定时间点的项目状态 +- 引用父提交对象 +- SHAI 名称,一个40个字符的字符串,提交对象的唯一标识。 + +#### 14.Git的工作区域 + +对于任何一个文件,在 Git 内都只有三种区域:工作区,暂存区和本地仓库。 + +`工作区:表示新增或修改了某个文件,但还没有提交保存;` + +`暂存区:表示把已新增或修改的文件,放在下次提交时要保存的清单中;` + +`本地仓库:文件已经被安全地保存在本地仓库中了。` + +#### 15.如果分支是否已合并为master,你可以通过什么手段知道? + +要知道某个分支是否已合并为master,你可以使用以下命令: + +`git branch –merged` 它列出了已合并到当前分支的分支。 + +`git branch –no-merged` 它列出了尚未合并的分支。 + +#### 16.什么是SubGit? + +SubGit 是将 SVN 到 Git迁移的工具。它创建了一个可写的本地或远程 Subversion 存储库的 Git 镜像,并且只要你愿意,可以随意使用 Subversion 和 Git。 + +这样做有很多优点,比如你可以从 Subversion 快速一次性导入到 Git 或者在 Atlassian Bitbucket Server 中使用SubGit。我们可以用 SubGit 创建现有 Subversion 存储库的双向 Git-SVN 镜像。你可以在方便时 push 到 Git 或提交 Subversion。同步由 SubGit 完成。 + +#### 17. 如何把本地仓库的内容推向一个空的远程仓库? + +首先确保本地仓库与远程之间是连同的。如果提交失败,则需要进行下面的命令进行连通: + +``` +git remote add origin XXXX +``` + +注意:XXXX是你的远程仓库地址。 如果是第一次推送,则进行下面命令: + +``` +git push -u origin master +``` + +注意:-u 是指定origin为默认主分支 之后的提交,只需要下面的命令: + +``` +git push origin master +``` + +#### 18.描述一下你所使用的分支策略? + +这个问题被要求用Git来测试你的分支经验,告诉他们你在以前的工作中如何使用分支以及它的用途是什么,你可以参考以下提到的要点: + +- 功能分支(Feature branching) + + 要素分支模型将特定要素的所有更改保留在分支内。当通过自动化测试对功能进行全面测试和验证时,该分支将合并到主服务器中。 + +- 任务分支(Task branching) + + 在此模型中,每个任务都在其自己的分支上实现,任务键包含在分支名称中。很容易看出哪个代码实现了哪个任务,只需在分支名称中查找任务键。 + +- 发布分支(Release branching) + + 一旦开发分支获得了足够的发布功能,你就可以克隆该分支来形成发布分支。创建该分支将会启动下一个发布周期,所以在此之后不能再添加任何新功能,只有错误修复,文档生成和其他面向发布的任务应该包含在此分支中。一旦准备好发布,该版本将合并到主服务器并标记版本号。此外,它还应该再将自发布以来已经取得的进展合并回开发分支。 + +最后告诉他们分支策略因团队而异,所以我知道基本的分支操作,如删除、合并、检查分支等。 + +#### 19.Git 工作区、暂存区和版本库 + +我们先来理解下 Git 工作区、暂存区和版本库概念: + +- **工作区:**就是你在电脑里能看到的目录。 +- **暂存区:**英文叫 stage 或 index。一般存放在 **.git** 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。 +- **版本库:**工作区有一个隐藏目录 **.git**,这个不算工作区,而是 Git 的版本库。 + +下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系: + +![img](https://www.runoob.com/wp-content/uploads/2015/02/1352126739_7909.jpg) + +- 图中左侧为工作区,右侧为版本库。在版本库中标记为 "index" 的区域是暂存区(stage/index),标记为 "master" 的是 master 分支所代表的目录树。 +- 图中我们可以看出此时 "HEAD" 实际是指向 master 分支的一个"游标"。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。 +- 图中的 objects 标识的区域为 Git 的对象库,实际位于 ".git/objects" 目录下,里面包含了创建的各种对象及内容。 +- 当对工作区修改(或新增)的文件执行 **git add** 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。 +- 当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。 +- 当执行 **git reset HEAD** 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。 +- 当执行 **git rm --cached ** 命令时,会直接从暂存区删除文件,工作区则不做出改变。 +- 当执行 **git checkout .** 或者 **git checkout -- ** 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。 +- 当执行 **git checkout HEAD .** 或者 **git checkout HEAD ** 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。 + +#### 20.打标签 + + git tag -a 2.27 -m "release version 201606" + git push origin --tags + 详解:git tag 是命令 + -a 0.1.3是增加 名为0.1.3的标签 + -m 后面跟着的是标签的注释 + + 打标签的操作发生在我们commit修改到本地仓库之后。完整的例子 + git add . + git commit -m “fixed some bugs” + git tag -a 0.1.3 -m “Release version 0.1.3″ + + 分享提交标签到远程服务器上 + git push origin master + git push origin --tags + –tags参数表示提交所有tag至服务器端,普通的git push origin master操作不会推送标签到服务器端。 + + 删除标签的命令 + git push origin local_branch:remote_branch +#### 参考 + +https://segmentfault.com/a/1190000019315509 + +https://www.runoob.com/git/ + +https://www.cnblogs.com/rebackl/p/12990881.html + +https://blog.csdn.net/junwua/article/details/82906002 \ No newline at end of file diff --git a/Docs/IO&NIO.md b/IO&NIO.md similarity index 95% rename from Docs/IO&NIO.md rename to IO&NIO.md index ef262b9..d15183f 100644 --- a/Docs/IO&NIO.md +++ b/IO&NIO.md @@ -163,13 +163,47 @@ Selector(选择器)是一个特殊的组件,用于采集各个通道的状 要使用一个Selector,你要先注册这个Selector的Channels。然后你调用Selector的select()方法。这个方法会阻塞,直到它注册的Channels当中有一个准备好了的事件发生了。当select()方法返回的时候,线程可以处理这些事件,如新的连接的到来,数据收到了等。 -参考: +#### 23.代码示例:如何使用流的基本接口来读写文件内容 -- https://www.cnblogs.com/sharing-java/p/10791802.html +``` +try { + +DataInputStream in = + + new DataInputStream( + + new BufferedInputStream( + + new FileInputStream("Test.java") + + ) + +); + +while ((currentLine = in.readLine()) != null){ + + System.out.println(currentLine); + +} + +} catch (IOException e){ + + System.err.println("Error: " + e); + +} +``` + + + +#### 参考: + +https://www.cnblogs.com/sharing-java/p/10791802.html + +https://blog.csdn.net/zengxiantao1994/article/details/88094910 -- https://blog.csdn.net/zengxiantao1994/article/details/88094910 +https://www.cnblogs.com/xueSpring/p/9513266.html -- https://www.cnblogs.com/xueSpring/p/9513266.html +https://zhuanlan.zhihu.com/p/163506337 -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git a/Docs/JVM.md b/JVM.md similarity index 100% rename from Docs/JVM.md rename to JVM.md diff --git a/Java8.md b/Java8.md new file mode 100644 index 0000000..935e857 --- /dev/null +++ b/Java8.md @@ -0,0 +1,187 @@ +#### Java8 + +#### 1.阐述 Java 7 和 Java 8 的区别 + +实话说,两者有很多不同。如果你能列出最重要的,应该就足够了。你应该解释 Java 8 中的新功能。想要获得完整清单,请访问官网:Java 8 JDK。 + +你应该知道以下几个重点: + +- **lambda 表达式**,Java 8 版本引入的一个新特性。lambda 表达式允许你将功能当作方法参数或将代码当作数据。lambda 表达式还能让你以更简洁的方式表示只有一个方法的接口 (称为函数式接口) 的实例。 +- **方法引用**,为已命名方法提供了易于阅读的 lambda 表达式。 +- 默认方法,支持将新功能添加到类库中的接口,并确保与基于这些接口的旧版本的代码的二进制兼容性。 +- **重复注解**,支持在同一声明或类型上多次应用同一注解类型。 +- **类型注解**,支持在任何使用类型的地方应用注解,而不仅限于声明。此特性与可插入型系统一起使用时,可增强对代码的类型检查。 + +#### 2.Java SE 8中最流行和最著名的最新功能是什么? + +Java SE 8最受欢迎和最著名的最新功能包括以下内容: + +功能接口。集合API增强功能。Lambda表达式。分流器。流API等。 + +#### 3.是什么使Java SE 8优于其他? + +Java SE 8具有以下功能,使其优于其他功能: + +它编写并行代码。它提供了更多可用的代码。它具有改进的性能应用程序。它具有更易读和简洁的代码。它支持编写包含促销的数据库。 + +#### 4.在Java SE 8中定义Lambda表达式? + +Lambda表达式是Java SE 8,是匿名函数的名称,该匿名函数有助于接受一组不同的输入参数,并提供各种结果结果。 + +#### 5.为什么将Lambda Expression创造为代码块? + +Lambda表达式是作为代码块创造的,因为它没有名称,可以带有或不带有参数和结果。 + +#### 6.Lambda表达式和功能接口之间有什么联系? + +当我们使用Lambda表达式时,这意味着我们正在使用功能接口。因此,它们都是相互关联的。这意味着Lambda表达式是Functional接口的一部分,Functional接口是一个承载各种其他功能和表达式的更大平台。 + +#### 7.在Java SE 8中定义Nashorn? + +Nashorn是在Java SE 8的Java平台上使用的最新Javascript处理引擎。 + +#### 8.Map和FlatMap流操作之间的主要区别是什么? + +Map和FlatMap流操作之间的主要区别在于,前者将返回值包装在其序数类型内,而后者则没有。 + +#### 9.Map和Flat map流操作之间的相似之处是什么? + +Map和FlatMap流操作都是中间流操作,它们接收一个函数并将这些函数应用于流的不同元素。 + +#### 10.定义流管道? + +Java SE 8中的流管道用于通过拆分可能在一个流上发生的操作来将操作链接在一起。 + +#### 11.什么是使用Stream Pipeline的强制性? + +使用Stream Pipeline的强制性在于存在终端操作,该操作有助于返回最终值并支持管道的终止。 + +#### 12.新日期和时间API的作用是什么? + +新的日期和时间API是在Java SE 8中的java time软件包下设计的,因此可以避免与JDK或Java.util.date相关的问题。 + +#### 13.Java SE 8的核心API类是什么? + +Java SE 8的核心API类包括LocalDate,LocalTime和LocalDateTime。 + +#### 14.Metaspace与PermGen相比有什么优势? + +PerGen的大小是固定的,不能动态增长,而Metaspace可以动态增长,并且确实具有任何类型的大小约束。 + +#### 15.功能接口和SAM接口之间有什么区别吗? + +不,功能接口和SAM接口之间没有区别。 SAM接口或单一抽象方法接口是Java SE 8 API中定义的一种功能接口。 + +#### 16.接口默认方法和静态方法 + + interface IDefaultFunction{ + default void study(){ + System.out.println("什么时候能找到工作"); + } + static void finJob(){ + System.out.println("开始找工作"); + } + } + + + class DefaultFunction implements IDefaultFunction{ + + } + +Java 8用默认方法与静态方法这两个新概念来扩展接口的声明。默认方法与抽象方法不同之处在于抽象方法必须要求实现,但是默认方法则没有这个要求,就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需在方法名前面加个default关键字即可实现默认方法。为什么要有这个特性?以前当需要修改接口的时候,需要修改全部实现该接口的类。而引进的默认方法的目的是为了解决接口的修改与现有的实现不兼容的问题。 + +#### 17.引入了流Stream + +Stream可以链式书写代码,需要几行搞定的代码,Stream可以一行搞定,Stream是使用内部迭代,而且Stream支持并行操作 + + @Test + public void streamTest(){ + Listlist = new ArrayList<>(); + list.add(2); + list.add(4); + list.add(5); + list.forEach(integer -> { + System.out.println(integer); + }); + System.out.println("=======分割线========="); + List list2 = list.stream().filter((i)-> i>2).collect(Collectors.toList()); + list2.forEach(i-> System.out.println(i)); + } +输出 + +``` +2 +4 +5 +=======分割线========= +4 +5 +``` + +filter:接受lamada表达式,从中截取一符合条件的元素 +limit:截流limit(5),表示截取五个元素 +skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补 +distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素 +map--接收Lambda,将元素转换成其他形式或提取信息 + +#### 18.可以重复注解 + +@Repeatable注解 + +#### 19.集合引入了很多parallel开头的并行操作的方法 + +特别是parallelSort,这个将排序拆分很多小的集合,运用多线程进行排序,最后合并,得到最终想要的结果。 + +#### 20.日期时间 + +Clock + +LocalDate 只保存有ISO-8601日期系统的日期部分,有时区信息 + +LocalTime 只保存ISO-8601日期系统的时间部分,没有时区信息 + +LocalDateTime类合并了LocalDate和LocalTime,它保存有ISO-8601日期系统的日期和时间,但是没有时区信息 + +ZonedDateTime,它保存有ISO-8601日期系统的日期和时间,而且有时区信息 + +Duration类,Duration持有的时间精确到纳秒。它让我们很容易计算两个日期中间的差异 + +#### 21.Nashorn javascript 引擎 + +Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用 + +#### 22.Base64 + +新的Base64API也支持URL和MINE的编码解码 + +#### 23.并行数组 + +Java 8新增加了很多方法支持并行的数组处理**parallelSort()** + +#### 24.并发 + +java.util.concurrent.ConcurrentHashMap中加入了一些新方法来支持聚集操作 + +java.util.concurrent.ForkJoinPool类中加入了一些新方法来支持共有资源池 + +java.util.concurrent.locks.StampedLock类提供一直基于容量的锁,这种锁有三个模型来控制读写操作(它被认为是不太有名的 + +java.util.concurrent.locks.ReadWriteLock类的替代者) + +#### 25.什么是Lambda表达式? + +Lambda Expression可以定义为允许用户将方法作为参数传递的匿名函数。这有助于删除大量的样板代码。Lambda函数没有访问修饰符(私有,公共或受保护),没有返回类型声明和没有名称。 + +Lambda表达式允许用户将“函数”传递给代码。所以,与以前需要一整套的接口/抽象类想必,我们可以更容易地编写代码。例如,假设我们的代码具有一些复杂的循环/条件逻辑或工作流程。使用lambda表达式,在那些有难度的地方,可以得到很好的解决。 + + + +#### 参考 + +https://zhuanlan.zhihu.com/p/215758969 + +https://baijiahao.baidu.com/s?id=1667297300847848263&wfr=spider&for=pc + +https://blog.csdn.net/wangbiao007/article/details/103229950 + +https://www.cnblogs.com/wangwanchao/p/5269648.html \ No newline at end of file diff --git "a/Docs/Java\345\237\272\347\241\200.md" "b/Java\345\237\272\347\241\200.md" similarity index 66% rename from "Docs/Java\345\237\272\347\241\200.md" rename to "Java\345\237\272\347\241\200.md" index e1447c9..9aa4a38 100644 --- "a/Docs/Java\345\237\272\347\241\200.md" +++ "b/Java\345\237\272\347\241\200.md" @@ -1,27 +1,35 @@ -## Java基础题 +## Java基础 #### 1.Java语言的三大特性 -1. 封装: +##### 1.封装: - 首先,属性可用来描述同一类事物的特征,方法可描述一类事物可做的操作。封装就是把属于同一类事物的共性(包括属性与方法)归到一个类中,以方便使用。 +首先,属性可用来描述同一类事物的特征,方法可描述一类事物可做的操作。封装就是把属于同一类事物的共性(包括属性与方法)归到一个类中,以方便使用。 - 1)概念:封装也称为信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。系统的其他部分只有通过包裹在数据外面的被授权的操作来与这个抽象数据类型交流与交互。也就是说,用户无需知道对象内部方法的实现细节,但可以根据对象提供的外部接口(对象名和参数)访问该对象。 - 2)好处:(1)实现了专业的分工。将能实现某一特定功能的代码封装成一个独立的实体后,各程序员可以在需要的时候调用,从而实现了专业的分工。(2)隐藏信息,实现细节。通过控制访问权限可以将可以将不想让客户端程序员看到的信息隐藏起来,如某客户的银行的密码需要保密,只能对该客户开发权限。 -2. 继承: - 就是个性对共性的属性与方法的接受,并加入个性特有的属性与方法 - 1.概念:一个类继承另一个类,则称继承的类为子类,被继承的类为父类。 - 2.目的:实现代码的复用。 - 3.理解:子类与父类的关系并不是日常生活中的父子关系,子类与父类而是一种特殊化与一般化的关系,是is-a的关系,子类是父类更加详细的分类。如class dog extends animal,就可以理解为dog is a animal.注意设计继承的时候,若要让某个类能继承,父类需适当开放访问权限,遵循里氏代换原则,即向修改关闭对扩展开放,也就是开-闭原则。 - 4.结果:继承后子类自动拥有了父类的属性和方法,但特别注意的是,父类的私有属性和构造方法并不能被继承。 - 另外子类可以写自己特有的属性和方法,目的是实现功能的扩展,子类也可以复写父类的方法即方法的重写。 -3. 多态: - 多态的概念发展出来,是以封装和继承为基础的。 - 多态就是在抽象的层面上实施一个统一的行为,到个体(具体)的层面上时,这个统一的行为会因为个体(具体)的形态特征而实施自己的特征行为。(针对一个抽象的事,对于内部个体又能找到其自身的行为去执行。) - 1.概念:相同的事物,调用其相同的方法,参数也相同时,但表现的行为却不同。 - 2.理解:子类以父类的身份出现,但做事情时还是以自己的方法实现。子类以父类的身份出现需要向上转型(upcast),其中向上转型是由JVM自动实现的,是安全的,但向下转型(downcast)是不安全的,需要强制转换。子类以父类的身份出现时自己特有的属性和方法将不能使用。 +概念:封装也称为信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。系统的其他部分只有通过包裹在数据外面的被授权的操作来与这个抽象数据类型交流与交互。也就是说,用户无需知道对象内部方法的实现细节,但可以根据对象提供的外部接口(对象名和参数)访问该对象。 +好处: + +(1)实现了专业的分工。将能实现某一特定功能的代码封装成一个独立的实体后,各程序员可以在需要的时候调用,从而实现了专业的分工。 + +(2)隐藏信息,实现细节。通过控制访问权限可以将可以将不想让客户端程序员看到的信息隐藏起来,如某客户的银行的密码需要保密,只能对该客户开发权限。 + +##### 2.继承: + +就是个性对共性的属性与方法的接受,并加入个性特有的属性与方法 +1.概念:一个类继承另一个类,则称继承的类为子类,被继承的类为父类。 +2.目的:实现代码的复用。 +3.理解:子类与父类的关系并不是日常生活中的父子关系,子类与父类而是一种特殊化与一般化的关系,是is-a的关系,子类是父类更加详细的分类。如class dog extends animal,就可以理解为dog is a animal.注意设计继承的时候,若要让某个类能继承,父类需适当开放访问权限,遵循里氏代换原则,即向修改关闭对扩展开放,也就是开-闭原则。 +4.结果:继承后子类自动拥有了父类的属性和方法,但特别注意的是,父类的私有属性和构造方法并不能被继承。 +另外子类可以写自己特有的属性和方法,目的是实现功能的扩展,子类也可以复写父类的方法即方法的重写。 + +##### 3.多态: + +多态的概念发展出来,是以封装和继承为基础的。 +多态就是在抽象的层面上实施一个统一的行为,到个体(具体)的层面上时,这个统一的行为会因为个体(具体)的形态特征而实施自己的特征行为。(针对一个抽象的事,对于内部个体又能找到其自身的行为去执行。) +1.概念:相同的事物,调用其相同的方法,参数也相同时,但表现的行为却不同。 +2.理解:子类以父类的身份出现,但做事情时还是以自己的方法实现。子类以父类的身份出现需要向上转型(upcast),其中向上转型是由JVM自动实现的,是安全的,但向下转型(downcast)是不安全的,需要强制转换。子类以父类的身份出现时自己特有的属性和方法将不能使用。 #### 2.Java语言主要特性 @@ -82,7 +90,7 @@ String是命令行传进参数的类型,args是指命令行传进的字符串 #### 7.==与equals的区别 -==比较两个对象在内存里是不是同一个对象,就是说在内存里的存储位置一致。两个String对象存储的值是一样的,但有可能在内存里存储在不同的地方 . +==比较两个对象在内存里是不是同一个对象,就是说在内存里的存储位置一致。两个String对象存储的值是一样的,但有可能在内存里存储在不同的地方 。 ==比较的是引用而equals方法比较的是内容。public boolean equals(Object obj) 这个方法是由Object对象提供的,可以由子类进行重写。默认的实现只有当对象和自身进行比较时才会返回true,这个时候和==是等价的。String, BitSet, Date, 和File都对equals方法进行了重写,对两个String对象 而言,值相等意味着它们包含同样的字符序列。对于基本类型的包装类来说,值相等意味着对应的基本类型的值一样。 @@ -638,8 +646,6 @@ Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态 方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。 -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) - #### 26.Java的四种引用 **1、强引用** @@ -688,5 +694,240 @@ Java 序列化就是指将对象转换为字节序列的过程,反序列化是 #### 32.**Java泛型和类型擦除?** -泛型即参数化类型,在创建集合时,指定集合元素的类型,此集合只能传入该类型的参数。类型擦除:java编译器生成的字节码不包含泛型信息,所以在编译时擦除:1.泛型用最顶级父类替换;2.移除。 +泛型即参数化类型,在创建集合时,指定集合元素的类型,此集合只能传入该类型的参数。 + +类型擦除:java编译器生成的字节码不包含泛型信息,所以在编译时擦除:1.泛型用最顶级父类替换;2.移除。 + +#### 33. 如何将字符串反转? + +使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。 + +示例代码: + +```java +// StringBuffer reverse +StringBuffer stringBuffer = new StringBuffer(); +stringBuffer. append("abcdefg"); +System. out. println(stringBuffer. reverse()); // gfedcba +// StringBuilder reverse +StringBuilder stringBuilder = new StringBuilder(); +stringBuilder. append("abcdefg"); +System. out. println(stringBuilder. reverse()); // gfedcba +``` + +#### 34.String 类的常用方法都有那些? + +- indexOf():返回指定字符的索引。 +- charAt():返回指定索引处的字符。 +- replace():字符串替换。 +- trim():去除字符串两端空白。 +- split():分割字符串,返回一个分割后的字符串数组。 +- getBytes():返回字符串的 byte 类型数组。 +- length():返回字符串长度。 +- toLowerCase():将字符串转成小写字母。 +- toUpperCase():将字符串转成大写字符。 +- substring():截取字符串。 +- equals():字符串比较。 + +#### 35.抽象类必须要有抽象方法吗? + +不需要,抽象类不一定非要有抽象方法。 + +示例代码: + +```java +abstract class Cat { + public static void sayHi() { + System. out. println("hi~"); + } +} +``` + +上面代码,抽象类并没有抽象方法但完全可以正常运行。 + +#### 36. 普通类和抽象类有哪些区别? + +- 普通类不能包含抽象方法,抽象类可以包含抽象方法。 +- 抽象类不能直接实例化,普通类可以直接实例化。 + +#### 37.接口和抽象类有什么区别? + +- 实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。 +- 构造函数:抽象类可以有构造函数;接口不能有。 +- 实现数量:类可以实现很多个接口;但是只能继承一个抽象类。 +- 访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。 + +#### 38.以下代码中,s5`==`s2返回值是什么? + +``` +String s1="ab"; +String s2="a"+"b"; +String s3="a"; +String s4="b"; +String s5=s3+s4; +``` + +返回false.在编译过程中,编译器会将s2直接优化为"ab",将其放置在常量池当中;而s5则是被创建在堆区,相当于s5=new String(“ab”); + +#### 39.String 中的 intern() + +Stirng中的intern()是个Native方法,它会首先从常量池中查找是否存在该常量值的字符串,若不存在则先在常量池中创建,否则直接返回常量池已经存在的字符串的引用. 比如 + +``` + String s1="aa"; + String s2=s1.intern(); + System.out.print(s1==s2); +``` + + +上述代码将返回true.因为在"aa"会在编译阶段确定下来,并放置字符串常量池中,因此最终s1和s2引用的是同一个字符串常量对象。 + +#### 40.什么是编译器常量?使用它有什么风险? + +公共静态不可变,即public static final修饰的变量就是我们所说的编译期常量.这里的public可选的.实际上这些变量在编译时会被替换掉,因为编译器明确的能推断出这些变量的值(如果你熟悉C++,那么这里就相当于宏替换). + +编译器常量虽然能够提升性能,但是也存在一定问题:你使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,但是你的客户端没有重新编译,这意味着你仍然在使用被修改之前的常量值. + +#### 41.3*0.1`==`0.3返回值是什么 + +false,因为有些浮点数不能完全精确的表示出来. + +#### 42.a=a+b与a+=b有什么区别吗? + ++=操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换.如: + +``` +byte a = 127; +byte b = 127; +b = a + b; // 报编译错误:cannot convert from int to byte +b += a; +``` + +#### 43.this与super的区别 + +- super:它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参) +- this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名) +- super()和this()类似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。 +- super()和this()均需放在构造方法内第一行。 +- 尽管可以用this调用一个构造器,但却不能调用两个。 +- this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。 +- this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。 +- 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。 + +#### 44.static存在的主要意义 + + static的主要意义是在于创建独立于具体对象的域变量或者方法。 **以致于即使没有创建对象,也能使用属性和调用方法** ! + +static关键字还有一个比较关键的作用就是 **用来形成静态代码块以优化程序性能** 。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。 + +为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。 + + + +#### 45.static的独特之处 + +1、被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法 **不属于任何一个实例对象,而是被类的实例对象所共享** 。 + +怎么理解 “被类的实例对象所共享” 这句话呢?就是说,一个类的静态成员,它是属于大伙的【大伙指的是这个类的多个对象实例,我们都知道一个类可以创建多个实例!】,所有的类对象共享的,不像成员变量是自个的【自个指的是这个类的单个实例对象】…我觉得我已经讲的很通俗了,你明白了咩? + +2、在该类被第一次加载的时候,就会去加载被static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。 + +3、static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的! + +4、被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。 + +#### 46.static应用场景 + +因为static是被类的实例对象所共享,因此如果 某个成员变量是被所有对象所共享的,那么这个成员变量就应该定义为静态变量 。 + +因此比较常见的static应用场景有: + +1、修饰成员变量 + +2、修饰成员方法 + + 3、静态代码块 + + 4、修饰类【只能修饰内部类也就是静态内部类】 + +5、静态导包 + +#### 47.break ,continue ,return 的区别及作用 + +break 跳出总上一层循环,不再执行循环(结束当前的循环体) + +continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件) + +return 程序返回,不再执行下面的代码(结束当前的方法 直接返回) + +#### 48.Java 中,如何跳出当前的多重嵌套循环 + +在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。例如: + +``` +public static void main(String[] args) { + ok: + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + System.out.println("i=" + i + ",j=" + j); + if (j == 5) { + break ok; + } + + } + } +} +``` + +#### 49.在Java中定义一个不做事且没有参数的构造方法的作用 + +Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。 + +#### 50.在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是? + +帮助子类做初始化工作。 + +#### 51.一个类的构造方法的作用是什么?若一个类没有声明构造方法,改程序能正确执行吗?为什么? + +主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。 + +#### 52.构造方法有哪些特性? + +名字与类名相同; + +没有返回值,但不能用void声明构造函数; + +生成类的对象时自动执行,无需调用。 + +#### 53.静态变量和实例变量区别 + +静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。 + +实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。 + +#### 54.静态变量与普通变量区别 + +static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。 + +还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。 + +#### 55.静态方法和实例方法有何不同? + +静态方法和实例方法的区别主要体现在两个方面: + +1. 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。 +2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制 + +#### 56.在一个静态方法内调用一个非静态成员为什么是非法的? + +由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。 + + + +#### 参考: + +https://blog.csdn.net/qq_41701956/article/details/86773940 + +https://zhuanlan.zhihu.com/p/94095050 +https://www.cnblogs.com/Java-JJ/p/12625888.html \ No newline at end of file diff --git "a/Java\345\237\272\347\241\200\357\274\210\344\270\213\357\274\211.md" "b/Java\345\237\272\347\241\200\357\274\210\344\270\213\357\274\211.md" new file mode 100644 index 0000000..5e8790a --- /dev/null +++ "b/Java\345\237\272\347\241\200\357\274\210\344\270\213\357\274\211.md" @@ -0,0 +1,438 @@ +## 基础(下) + +#### 1.Java和C++的区别? + +我知道很多人没学过 C++,但是面试官就是没事喜欢拿咱们 Java 和 C++ 比呀!没办法!!!就算没学过C++,也要记下来! + +都是面向对象的语言,都支持封装、继承和多态 + +Java 不提供指针来直接访问内存,程序内存更加安全 + +Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。 + +Java 有自动内存管理机制,不需要程序员手动释放无用内存 + +#### 2.什么是 Java 程序的主类 应用程序和小程序的主类有何不同? + +一个程序中可以有多个类,但只能有一个类是主类。在 Java 应用程序中,这个主类是指包含 main()方法的类。而在 Java 小程序中,这个主类是一个继承自系统类 JApplet 或 Applet 的子类。应用程序的主类不一定要求是 public 类,但小程序的主类要求必须是 public 类。主类是 Java 程序执行的入口点。 + +#### 3.Java 应用程序与小程序之间有哪些差别? + +简单说应用程序是从主线程启动(也就是 `main()` 方法)。applet 小程序没有 `main()` 方法,主要是嵌在浏览器页面上运行(调用`init()`或者`run()`来启动),嵌入浏览器这点跟 flash 的小游戏类似。 + +#### 4.import java和javax有什么区别? + +刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准API的一部分。 所以,实际上java和javax没有区别。这都是一个名字。 + +#### 5.object-c中的协议和java中的接口概念有何不同? + +OC中的协议有2层含义,官方定义为 formal和informal protocol。前者和Java接口一样。 informal protocol中的方法属于设计模式考虑范畴,不是必须实现的,但是如果有实现,就会改变类的属性。 其实关于正式协议,类别和非正式协议我很早前学习的时候大致看过,也写在了学习教程里 “非正式协议概念其实就是类别的另一种表达方式“这里有一些你可能希望实现的方法,你可以使用他们更好的完成工作”。 这个意思是,这些是可选的。比如我门要一个更好的方法,我们就会申明一个这样的类别去实现。然后你在后期可以直接使用这些更好的方法。 这么看,总觉得类别这玩意儿有点像协议的可选协议。" 现在来看,其实protocal已经开始对两者都统一和规范起来操作,因为资料中说“非正式协议使用interface修饰“, 现在我们看到协议中两个修饰词:“必须实现(@requied)”和“可选实现(@optional)”。 + +#### 6.Javascipt的本地对象,内置对象和宿主对象 + +本地对象: Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError, 简单来说,本地对象就是 ECMA一262 定义的类. + +内置对象: ECMA一262 把内置对象(built一in object)定义为“由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现”。这意味着开发者不必明确实例化内置对象,它已被实例化了。 同样是“独立于宿主环境”。根据定义我们似乎很难分清“内置对象”与“本地对象”的区别。而ECMA一262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。 如此就可以理解了。内置对象是本地对象的一种。而其包含的两种对象中,Math对象我们经常用到,可这个Global对象是啥东西呢? Global对象是ECMAScript中最特别的对象,因为实际上它根本不存在,有点玩人的意思。大家要清楚,在ECMAScript中,不存在独立的函数,所有函数都必须是某个对象的方法。 类似于isNaN()、parseInt()和parseFloat()方法等,看起来都是函数,而实际上,它们都是Global对象的方法。而且Global对象的方法还不止这些. + +宿主对象: ECMAScript中的“宿主”就是我们网页的运行环境,即“操作系统”和“浏览器”。所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象。所有的BOM和DOM对象都是宿主对象。因为其对于不同的“宿主”环境所展示的内容不同。其实说白了就是,ECMAScript官方未定义的对象都属于宿主对象,因为其未定义的对象大多数是自己通过ECMAScript程序创建的对象。自定义的对象也是宿主对象。 + +#### 7.在javascript中什么是伪数组,如何将伪数组转化为标准数组 + +这里把符合以下条件的对象称为伪数组: + +1,具有length属性 + +2,按索引方式存储数据 + +3,不具有数组的push,pop等方法 伪数组(类数组):无法直接调用数组方法或期望length属性有什么特殊的行为,不具有数组的push,pop等方法,但仍可以对真正数组遍历方法来遍历它们。典型的是函数的argument参数,还有像调用document.getElementsByTagName, document.childNodes之类的,它们返回的NodeList对象都属于伪数组。 + +#### 8.请问EJB与JAVA BEAN的区别是什么? + +Java Bean 是可复用的组件,对Java Bean并没有严格的规范,理论上讲,任何一个Java类都可以是一个Bean。但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地进程内COM组件,它是不能被跨进程访问的。EnterpriseJava Bean 相当于DCOM,即分布式组件。它是基于Java的远程方法调用(RMI)技术的,所以EJB可以被远程访问(跨进程、跨计算机)。但EJB必须被布署在诸如Webspere、WebLogic这样的容器中,EJB客户从不直接访问真正的EJB组件,而是通过其容器访问。EJB容器是EJB组件的代理, EJB组件由容器所创建和管理。客户通过容器来访问真正的EJB组件。 + +#### 9.请你说说Java和PHP的区别? + +PHP暂时还不支持像Java那样JIT运行时编译热点代码,但是PHP具有opcache机制,能够把脚本对应的opcode缓存在内存,PHP7中还支持配置opcache.file_cache导出opcode到文件.第三方的Facebook HHVM也支持JIT.另外PHP官方基于LLVM围绕opcache机制构建的Zend JIT分支也正在开发测试中.在php-src/Zend/bench.php测试显示,PHP JIT分支速度是PHP 5.4的10倍. PHP的库函数用C实现,而Java核心运行时类库(jdk/jre/lib/rt.jar,大于60MB)用Java编写(jdk/src.zip), 所以Java应用运行的时候,用户编写的代码以及引用的类库和框架都要在JVM上解释执行. Java的HotSpot机制,直到有方法被执行10000次才会触发JIT编译, 在此之前运行在解释模式下,以避免出现JIT编译花费的时间比方法解释执行消耗的时间还要多的情况. + +PHP内置模板引擎,自身就是模板语言.而Java Web需要使用JSP容器如Tomcat或第三方模板引擎. + +PHP也可以运行在多线程模式下,比如Apache的event MPM和Facebook的HHVM都是多线程架构.不管是多进程还是多线程的PHP Web运行模式,都不需要PHP开发者关心和控制,也就是说PHP开发者不需要写代码参与进程和线程的管理,这些都由PHP-FPM/HHVM/Apache实现.PHP-FPM进程管理和并发实现并不需要PHP开发者关心,而Java多线程编程需要Java开发者编码参与.PHP一个worker进程崩溃,master进程会自动新建一个新的worker进程,并不会导致PHP服务崩溃.而Java多线程编程稍有不慎(比如没有捕获异常)就会导致JVM崩溃退出.对于PHP-FPM和Apache MOD_PHP来说,服务进程常驻内存,但一次请求释放一次资源,这种内存释放非常彻底. PHP基于引用计数的GC甚至都还没发挥作用程序就已经结束了。 + +#### 10.请你谈谈Java中是如何支持正则表达式操作的? + +Java中的String类提供了支持正则表达式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。此外,Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作,如: + +```java +import java.util.regex.Matcher; +import java.util.regex.Pattern; +class RegExpTest { + public static void main(String[] args) { + String str = "成都市(成华区)(武侯区)(高新区)"; + Pattern p = Pattern.compile(".*?(?=\\()"); + Matcher m = p.matcher(str); + if(m.find()) { + System.out.println(m.group()); + } + } +} +``` + +#### 11.请你说明一下,在Java中如何跳出当前的多重嵌套循环? + +在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。(Java中支持带标签的break和continue语句,作用有点类似于C和C++中的goto语句,但是就像要避免使用goto一样,应该避免使用带标签的break和continue,因为它不会让你的程序变得更优雅,很多时候甚至有相反的作用,所以这种语法其实不知道更好),根本不能进行字符串的equals比较,否则会产生NullPointerException异常。 + +#### 12.请说明Java的接口和C++的虚类的相同和不同处 + +由于Java不支持多继承,而有可能某个类或对象要使用分别在几个类或对象里面的方法或属性,现有的单继承机制就不能满足要求。 与继承相比,接口有更高的灵活性,因为接口中没有任何实现代码。当一个类实现了接口以后,该类要实现接口里面所有的方法和属性,并且接口里面的属性在默认状态下面都是public static,所有方法默认情况下是public.一个类可以实现多个接口。 + +#### 13.请说明Java是否支持多继承? + +Java中类不支持多继承,只支持单继承(即一个类只有一个父类)。 但是java中的接口支持多继承,,即一个子接口可以有多个父接口。(接口的作用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)。 + +#### 14.请讲讲Java有哪些特性,并举一个和多态有关的例子。 + +封装、继承、多态。多态:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用) + +#### 15.什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? + +Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。 Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。 + +#### 16.请列举一下,在JAVA虚拟机中,哪些对象可作为ROOT对象? + +虚拟机栈中的引用对象 + +方法区中类静态属性引用的对象 + +方法区中常量引用对象 + +本地方法栈中JNI引用对象 + +#### 17.C++,Java,JavaScript这三种语言的区别 + +参考回答: + +从静态类型还是动态类型来看 + +静态类型,编译的时候就能够知道每个变量的类型,编程的时候也需要给定类型,如Java中的整型int,浮点型float等。C、C++、Java都属于静态类型语言。 + +动态类型,运行的时候才知道每个变量的类型,编程的时候无需显示指定类型,如JavaScript中的var、PHP中的$。JavaScript、Ruby、Python都属于动态类型语言。 + +静态类型还是动态类型对语言的性能有很大影响。 + +对于静态类型,在编译后会大量利用已知类型的优势,如int类型,占用4个字节,编译后的代码就可以用内存地址加偏移量的方法存取变量,而地址加偏移量的算法汇编很容易实现。 + +对于动态类型,会当做字符串通通存下来,之后存取就用字符串匹配。 + +从编译型还是解释型来看 + +编译型语言,像C、C++,需要编译器编译成本地可执行程序后才能运行,由开发人员在编写完成后手动实施。用户只使用这些编译好的本地代码,这些本地代码由系统加载器执行,由操作系统的CPU直接执行,无需其他额外的虚拟机等。 + +源代码=》抽象语法树=》中间表示=》本地代码 + +解释性语言,像JavaScript、Python,开发语言写好后直接将代码交给用户,用户使用脚本解释器将脚本文件解释执行。对于脚本语言,没有开发人员的编译过程,当然,也不绝对。 + +源代码=》抽象语法树=》解释器解释执行。 + +对于JavaScript,随着Java虚拟机JIT技术的引入,工作方式也发生了改变。可以将抽象语法树转成中间表示(字节码),再转成本地代码,如JavaScriptCore,这样可以大大提高执行效率。也可以从抽象语法树直接转成本地代码,如V8 + +Java语言,分为两个阶段。首先像C++语言一样,经过编译器编译。和C++的不同,C++编译生成本地代码,Java编译后,生成字节码,字节码与平台无关。第二阶段,由Java的运行环境也就是Java虚拟机运行字节码,使用解释器执行这些代码。一般情况下,Java虚拟机都引入了JIT技术,将字节码转换成本地代码来提高执行效率。 + +注意,在上述情况中,编译器的编译过程没有时间要求,所以编译器可以做大量的代码优化措施。 + +对于JavaScript与Java它们还有的不同: + +对于Java,Java语言将源代码编译成字节码,这个同执行阶段是分开的。也就是从源代码到抽象语法树到字节码这段时间的长短是无所谓的。 + +对于JavaScript,这些都是在网页和JavaScript文件下载后同执行阶段一起在网页的加载和渲染过程中实施的,所以对于它们的处理时间有严格要求。 + +#### 18.java nextLine 与 next 两个方法的区别? + +1. next 不会接收回车符,tab 或者空格键,在接收有效数据之前会忽略这些符号,若已经读取了有效数据,遇到这些符号会直接退出 +2. nextLine 可以接收空格或者 tab 键,其输入以 enter 键结束 + +#### 19.什么是 JavaConfig? + +Spring JavaConfig 是 Spring 社区的产品,它提供了配置 Spring IoC 容器的纯Java 方法。因此它有助于避免使用 XML 配置。使用 JavaConfig 的优点在于: + +(1)面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean 方法等。 + +(2)减少或消除 XML 配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在 XML 和 Java 之间来回切换。JavaConfig 为开发人员提供了一种纯 Java 方法来配置与 XML 配置概念相似的 Spring 容器。从技术角度来讲,只使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。 + +(3)类型安全和重构友好。JavaConfig 提供了一种类型安全的方法来配置 Spring容器。由于 Java 5.0 对泛型的支持,现在可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。 + +#### 20.停止非循环Java线程 + +这可能是我误读了我所读内容的一种情况,但是在Java中杀死线程的所有示例似乎都表明您必须发出信号以杀死自己。您不能在没有严重风险的情况下从外面杀死它。问题是,所有有关如何“礼貌地”要求线程死亡的示例都有某种循环,因此您要做的就是观察每次迭代中的标志。 + +因此,我得到的是一个线程,该线程执行的操作仅需要一段时间(一系列SQL查询)。我当然可以在每个步骤之后进行检查,但是它们并没有处于循环中,并且我没有一种非常优雅的方式可以解决此问题。这是我正在做的事的一个例子: + +``` +new Thread(new Runnable(){ + public void run(){ + //query 1 + Connection conn = db.getConnection(); + Statement s = conn.createStatement(); + ResultSet rs = s.executeQuery("SELECT ..."); + while(rs.next()){ + //do stuff + } + + //query 2 + rs = s.executeQuery("SELECT ..."); + while(rs.next()){ + //do stuff + } + + //query 3 + rs = s.executeQuery("SELECT ..."); + while(rs.next()){ + //do stuff + } + } +}).start(); +``` + +这是一个示例,我没有使用匿名内部类,但是它说明了我的run()方法无法优雅地停止自身。此外,即使我在每个步骤之后都进行检查,如果特定查询需要很长时间才能运行,则该代码将无法在查询完成后停止。 + +这段代码是针对GUI应用程序的,我真的很想找到一种无需使用Thread.stop()即可快速杀死线程的好方法。 + +**编辑** +-yshavit的回答很有帮助,因为我不知道它的`Statement.cancel()`存在。如果您感到好奇,那么对我的特定问题的答案是建立一个更抽象的数据库访问类。该类必须创建一个子线程以在运行时执行查询和循环,并检查每次迭代是否当前线程(不是子线程)被中断。如果确实被中断,它将仅调用Statement.cancel(),并且子线程将引发异常并死亡。并非所有的JDBC驱动程序都支持`Statement.cancel()`,但是Oracle +11g支持。 + + + +#### 21.在java中使用最简单的方法打印数组内容? + +从Java 5开始,你可以将Arrays.toString(arr)或Arrays.deepToString(arr)用于数组中的数组。 +请注意,Object[]版本会调用.toString()数组中的每个对象。输出甚至按照您的要求进行修饰。 + +**例子:** + +简单数组: + +``` +String[] array = new String[] {"John", "Mary", "Bob"}; +System.out.println(Arrays.toString(array)); +``` + +输出: + +``` +[John, Mary, Bob] +``` + +嵌套数组: + +``` +String[][] deepArray = new String[][] {{"John", "Mary"}, {"Alice", "Bob"}}; +System.out.println(Arrays.toString(deepArray)); +//output: [[Ljava.lang.String;@106d69c, [Ljava.lang.String;@52e922] +System.out.println(Arrays.deepToString(deepArray)); +``` + +输出: + +``` +[[John, Mary], [Alice, Bob]] +``` + +double 数组: + +``` +double[] doubleArray = { 7.0, 9.0, 5.0, 1.0, 3.0 }; +System.out.println(Arrays.toString(doubleArray)); +``` + +输出: + +``` +[7.0, 9.0, 5.0, 1.0, 3.0 ] +``` + +int 数组: + +``` +int[] intArray = { 7, 9, 5, 1, 3 }; +System.out.println(Arrays.toString(intArray)); +``` + +输出: + +``` +[7, 9, 5, 1, 3 ] +``` + +#### 22.为什么打印java对象得到SomeType@2f92e0f4这样的结果? + +##### 背景 + +所有Java对象都有一个`toString()`方法,当你尝试打印该对象时会调用该方法。 + +``` +System.out.println(myObject); // invokes myObject.toString() +``` + +此方法在`Object`类(所有Java对象的超类)中定义。该`Object.toString()`方法返回一个看起来很难看的字符串,该字符串由类的名称,`@`符号和对象的哈希码(十六进制)组成。此代码如下所示: + +``` +// Code of Object.toString() +public String toString() { + return getClass().getName() + "@" + Integer.toHexString(hashCode()); +} +``` + +这样的结果`com.foo.MyType@2f92e0f4`可以解释为: + +- com.foo.MyType -类的名称,即类MyType在package中com.foo。 +- @ -将字符串连接在一起 +- 2f92e0f4 对象的哈希码。 + +数组类的名称看起来有些不同,这在Javadocs for中得到了很好的解释`Class.getName()`。例如,`[Ljava.lang.String`表示: + +- [-一维数组(相对于[[或[[[等) +- L -数组包含一个类或接口 +- java.lang.String -数组中对象的类型 + +##### 自定义输出 + +要在调用时打印不同的内容`System.out.println(myObject)`,必须重写`toString()`自己类中的方法。这是一个简单的例子: + +``` +public class Person { + private String name; + // constructors and other methods omitted + @Override + public String toString() { + return name; + } +} +``` + +现在,如果我们打印一个Person,我们将看到它们的名称,而不是`com.foo.Person@12345678`。 + +请记住,这toString()只是将对象转换为字符串的一种方法。通常,此输出应以简洁明了的方式完全描述你的对象。toString()对于我们的Person班级来说,更好的选择可能是: + +``` +@Override +public String toString() { + return getClass().getSimpleName() + "[name=" + name + "]"; +} +``` + +将打印,例如`Person[name=Henry]`。这对于调试/测试来说是非常有用的数据。 + +如果你只想关注对象的一个方面或包含许多爵士乐的格式,则最好定义一个单独的方法,例如String toElegantReport() {…}。 + +##### 自动生成输出 + +许多IDEtoString()基于类中的字段提供了对自动生成方法的支持。例如,请参阅Eclipse和IntelliJ的文档。 + +一些流行的Java库也提供此功能。一些示例包括: + +- ToStringBuilder来自Apache Commons Lang +- MoreObjects.ToStringHelper来自Google Guava +- @ToString龙目岛项目的注释 + +##### 打印对象组 + +因此,你已经`toString()`为课程创建了一个不错的选择。如果将该类放入数组或集合,会发生什么情况? + +##### 数组 + +如果你有一个对象数组,则可以调用Arrays.toString()以生成该数组内容的简单表示。例如,考虑以下Person对象数组: + +``` +Person[] people = { new Person("Fred"), new Person("Mike") }; +System.out.println(Arrays.toString(people)); +``` + +// Prints: [Fred, Mike] + +注意:这是对Arrays类中调用的静态方法的调用toString(),这与我们上面讨论的内容不同。 + +如果你具有多维数组,则可以用于Arrays.deepToString()实现相同类型的输出。 + +##### 集合 + +大多数集合都会基于.toString()对每个元素的调用而产生漂亮的输出。 + +``` +List people = new ArrayList<>(); +people.add(new Person("Alice")); +people.add(new Person("Bob")); +System.out.println(people); +``` + +// Prints [Alice, Bob] +因此,你只需要确保列表元素定义一个`toString()`如上所述的尼斯。 + +#### 23.如何理解和使用Java中的增强型for循环foreach? + +``` +for (Iterator i = someIterable.iterator(); i.hasNext();) { + String item = i.next(); + System.out.println(item); +} +``` + +请注意,如果你需要`i.remove()`;在循环中使用或以某种方式访问实际的迭代器,则不能使用该`for(:)`惯用语,因为实际的迭代器只是推断出来的。 + +正如Denis Bueno所指出的那样,此代码适用于实现该Iterable接口的任何对象。 + +同样,如果`for(:)`习惯用法的右侧是一个`array`而不是一个`Iterable`对象,则内部代码将使用一个`int`索引计数器并进行检查`array.length`。请参阅Java语言规范。 + +#### 24.在java中为什么 1/3 == 0? + +运行下面的代码结果为0? + +``` +public static void main(String[] args) { + double g = 1 / 3; + System.out.printf("%.2f", g); +} +``` + +#### 25.Java 7中的菱形运算符(<>)有什么意义? + +Java 7中的菱形运算符允许如下代码: + +``` +List list = new LinkedList<>(); +``` + +但是,在Java 5/6中,我可以简单地编写: + +``` +List list = new LinkedList(); +``` + +我对类型擦除的理解是这些完全相同。(无论如何,泛型都会在运行时删除)。 + +``` +List list = new LinkedList(); +``` + +是在左侧,你使用的是通用类型`List`,而在右侧,你使用的是原始类型LinkedList。Java中的原始类型实际上仅存在于与前泛型代码的兼容性,并且除非绝对必要,否则绝对不能在新代码中使用。 + +现在,如果Java从一开始就具有泛型,并且没有LinkedList最初在具有泛型之前创建的类型(例如),则它可能已经做到了,这样泛型类型的构造函数会自动从左侧推断出其类型参数-尽可能在作业的另一侧。但事实并非如此,为了向后兼容,必须对原始类型和泛型类型进行不同的处理。这使得他们需要采取一种稍微不同但同样方便的方式来声明泛型对象的新实例,而不必重复其类型参数……菱形运算符。 + +就你的原始示例而言`List list = new LinkedList()`,编译器会为该分配生成警告,因为它必须这样做。考虑一下: + +``` +List strings = ... // some list that contains some strings + +// Totally legal since you used the raw type and lost all type checking! +List integers = new LinkedList(strings); +``` + +存在泛型以提供编译时保护以防止做错事。在上面的示例中,使用原始类型意味着你没有获得此保护,并且在运行时会收到错误消息。这就是为什么你不应该使用原始类型的原因。 + +``` +// Not legal since the right side is actually generic! +List integers = new LinkedList<>(strings); +``` + +但是,菱形运算符允许将赋值的右侧定义为具有与左侧相同类型参数的真实泛型实例,而不必再次键入这些参数。它使你可以与使用原始类型几乎相同的工作来保持泛型的安全。 + +我认为关键要理解的是,原始类型(不带`<>`)不能与泛型类型相同。声明原始类型时,不会获得任何好处和泛型的类型检查。你还必须记住,泛型是Java语言的通用组成部分 ……它们不仅仅适用于Collections 的无参数构造函数! \ No newline at end of file diff --git "a/Docs/\345\244\232\347\272\277\347\250\213.md" "b/Java\345\244\232\347\272\277\347\250\213.md" similarity index 99% rename from "Docs/\345\244\232\347\272\277\347\250\213.md" rename to "Java\345\244\232\347\272\277\347\250\213.md" index 40ee12c..81adb9a 100644 --- "a/Docs/\345\244\232\347\272\277\347\250\213.md" +++ "b/Java\345\244\232\347\272\277\347\250\213.md" @@ -1,4 +1,4 @@ -## 多线程 +## Java多线程 #### 1.**什么是进程**? diff --git "a/Docs/java\351\233\206\345\220\210.md" "b/Java\351\233\206\345\220\210.md" similarity index 94% rename from "Docs/java\351\233\206\345\220\210.md" rename to "Java\351\233\206\345\220\210.md" index 0740258..c913ba2 100644 --- "a/Docs/java\351\233\206\345\220\210.md" +++ "b/Java\351\233\206\345\220\210.md" @@ -615,21 +615,62 @@ class Key implements Comparable { (5) 还没升级JDK1.8的,现在开始升级吧。HashMap的性能提升仅仅是JDK1.8的冰山一角。 -参考: +#### 19.fail-fast 与 fail-safe 之间的区别? -- https://zhuanlan.zhihu.com/p/21673805 +Fail fast快速地报告任何的failure。无论何时任何一个问题都会引发 fail fast系统fails +在Java Fail fast 迭代器中,迭代objects集合有时会出现并发修改异常,出现这种情况有2个原因 +如果一个线程正在迭代一个集合,而另一个线程同时试图修改这个集合 +在调用remove()方法后,如何我们还试图去修改集合object -- https://www.jianshu.com/p/939b8a672070 +#### 20.ConcurrentHashMap分段锁 -- https://www.jianshu.com/p/c45b6d782e91 +jdk1.7中: -- https://blog.csdn.net/Mrs_chens/article/details/92761868 +ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。Segment 是一种可重入锁 ReentrantLock,在 ConcurrentHashMap 里扮演锁的角色,HashEntry 则用于存储键值对数据。 -- https://blog.csdn.net/riemann_/article/details/87217229 +Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是上面的提到的锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样。 -- https://blog.csdn.net/qq_34626097/article/details/83053004 -- https://blog.csdn.net/u010887744/article/details/50575735 - ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) +![img](https://pic2.zhimg.com/80/v2-d8cc7dcb4af87b8146f591aea276400d_720w.jpg) + + + + + +1.8 中: + +放弃了Segment,直接用 Node数组+链表+红黑树 的数据结构来实现,并发控制使用Synchronized + CAS来操作,整个看起来就像是优化过且线程安全的HashMap。 + + + +![img](https://pic3.zhimg.com/80/v2-f79b2da9b987bf4ca653fa099c4bda32_720w.jpg) + + + + + + + +#### 参考: + +https://zhuanlan.zhihu.com/p/21673805 + +https://www.jianshu.com/p/939b8a672070 + +https://www.jianshu.com/p/c45b6d782e91 + +https://blog.csdn.net/Mrs_chens/article/details/92761868 + +https://blog.csdn.net/riemann_/article/details/87217229 + +https://blog.csdn.net/qq_34626097/article/details/83053004 + +https://blog.csdn.net/u010887744/article/details/50575735 + +https://zhuanlan.zhihu.com/p/267545042 + +https://blog.csdn.net/qq_40434646/article/details/81591239 + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) diff --git a/Jsp.md b/Jsp.md new file mode 100644 index 0000000..b2626fa --- /dev/null +++ b/Jsp.md @@ -0,0 +1,417 @@ +## JSP + +#### 1.浏览器jsp,html之间的关系 + +1.JSP与Java Servlet一样,是在服务器端执行的,通常返回该客户端的就是一个HTML文本,因此客户端只要有浏览器就能浏览 + +2.在大多数Browser/Server结构的Web应用中,浏览器直接通过HTML或者JSP的形式与用户交互,响应用户的请求 + +3.JSP在服务器上执行,并将执行结果输出到客户端浏览器,我们可以说基本上与浏览器无关 + +#### 2.自定义标签要继承哪个类 + +这个类可以继承TagSupport或者BodyTagSupport,两者的差别是前者适用于没有主体的标签,而后者适用于有主体的标签。如果选择继承TagSupport,可以实现doStartTag和doEndTag两个方法实现Tag的功能,如果选择继承BodyTagSupport,可以实现doAfterBody这个方法。 + +#### 3. jsp内置对象和作用? + +有九个内置对象:request、response、out、session、application、pageContext、config、page、exception + +作用如下: + +(1) HttpServletRequest类的Request对象 + +作用:代表请求对象,主要用于接受客户端通过HTTP协议连接传输到服务器端的数据。 + +(2) HttpServletResponse类的Respone对象 + +作用:代表响应对象,主要用于向客户端发送数据 + +(3) JspWriter类的out对象 + +作用:主要用于向客户端输出数据; + +​ Out的基类是JspWriter + +(4) HttpSession类的session对象 + +作用:主要用于来分别保存每个用户信息,与请求关联的会话; + +​ 会话状态维持是Web应用开发者必须面对的问题。 + +(5) ServletContex类的application对象 + +作用:主要用于保存用户信息,代码片段的运行环境; + +​ 它是一个共享的内置对象,即一个容器中的多个用户共享一个application对象,故其保存的信息被所有用户所共享. + +(6) PageContext类的PageContext对象 + +作用:管理网页属性,为JSP页面包装页面的上下文,管理对属于JSP中特殊可见部分中已命名对象的访问,它的创建和初始化都是由容器来完成的。 + +(7) ServletConfig类的Config对象 + +作用:代码片段配置对象,表示Servlet的配置。 + +(8) Object类的Page(相当于this)对象 + +作用:处理JSP网页,是Object类的一个实例,指的是JSP实现类的实例,即它也是JSP本身,只有在JSP页面范围之内才是合法的。 + +(9)Exception + +作用:处理JSP文件执行时发生的错误和异常 + +#### 4.jsp乱码如何解决,几种解决方案 + +一、JSP页面显示乱码 +<%@ page contentType=”text/html; charset=gb2312″%> + +二、表单提交中文时出现乱码 + +request.seCharacterEncoding(̶gb2312″)对请求进行统一编码 + +三、数据库连接出现乱码 +要涉及中文的地方全部是乱码,解决办法:在数据库的数据库URL中加上useUnicode=true&characterEncoding=GBK就OK了。 + +四、通过过滤器完成 + +五、在server.xml中的设置编码格式 + +#### 5.页面间对象传递的方法 + +request,session,application,cookie等 +request.setAttribute(key,value) +session.setAttribute(key,value) +application.setAttribute(key,value) + +#### 6.BS与CS的联系与区别 + +B/S模式是指在TCP/IP的支持下,以HTTP为传输协议,客户端通过Browser访问Web服务器以及与之相连的后台数据库的技术及体系结构。它由浏览器、Web服务器、应用服务器和数据库服务器组成。客户端的浏览器通过URL访问Web服务器,Web服务器请求数据库服务器,并将获得的结果以HTML形式返回客户端浏览器。 + +c/s在系统机构上和B/S相似,不过需要在客户端安装一个客户端软件,由这个软件对服务器的数据进行读写,就像我们常用的qq,就是这种模式。 + +#### 7.描述Jsp页面的运行过程? + +第一步: + +请求进入Web容器,将JSP页面翻译成Servlet代码 + +第二步: + +编译Servlet代码,并将编译过的类文件装入Web容器(JVM)环境 + +第三步: + +Web容器为JSP页面创建一个Servlet类实例,并执行jspInit方法 + +第四步: + +Web容器为该JSP页面调用Servlet实例的_jspService方法;将结果发送给用户 + +#### 8.Jsp工作原理 + +JSP是一种Servlet,但是与HttpServlet的工作方式不太一样。HttpServlet是先由源代码编译为class文件后部署到服务器下,为先编译后部署。而JSP则是先部署后编译。JSP会在客户端第一次请求JSP文件时被编译为HttpJspPage类(接口Servlet的一个子类)。该类会被服务器临时存放在服务器工作目录里面。 + +由于JSP只会在客户端第一次请求的时候被编译 ,因此第一次请求JSP时会感觉比较慢,之后就会感觉快很多。如果把服务器保存的class文件删除,服务器也会重新编译JSP。 + +开发Web程序时经常需要修改JSP。Tomcat能够自动检测到JSP程序的改动。如果检测到JSP源代码发生了改动。Tomcat会在下次客户端请求JSP时重新编译JSP,而不需要重启Tomcat。 + +```java +虽然servlet和jsp本质都是servlet,运行时都是运行.class文件,但是它们的部署方式不一样。 +servlet是先编译后部署,修改完以后,MyEclipse进行编译,然后部署.class文件到servlet容器中。如果web服务器已启动,则之前的.class文件已被servlet容器加载,修改后的.class文件不会被servlet容器执行。 +而jsp是web服务器进行编译,而不是预先编译好,编译后再加载,tomcat会监视jsp文件的改动,改动之后则重新编译、执行,所以jsp改动时不需要重启服务器。 +``` + +#### 9.Jsp包含的部分 + +``` +指令: <%@ %> +java小脚本: <% java代码 %> 语句带封号; +方法声明: <%! %> +表达式: <%= %> 表达式不带分号; +注释: <%-- 注释内容 --%> + java中单行,多行 + html中 +html: +js: +css: +标签: +``` + +#### 10.getAttribute()与getParameter() + +**从获取方向来看:** + +`getParameter()`是获取 POST/GET 传递的参数值; + +`getAttribute()`是获取对象容器中的数据值; + +**从用途来看:** + +`getParameter()`用于客户端重定向时,即点击了链接或提交按扭时传值用,即用于在用表单或url重定向传值时接收数据用。 + +`getAttribute()` 用于服务器端重定向时,即在 sevlet 中使用了 forward 函数。getAttribute 只能收到程序用 setAttribute 传过来的值。 + +另外,可以用 `setAttribute()`,`getAttribute()` 发送接收对象.而 `getParameter()` 显然只能传字符串。 +`setAttribute()` 是应用服务器把这个对象放在该页面所对应的一块内存中去,当你的页面服务器重定向到另一个页面时,应用服务器会把这块内存拷贝另一个页面所对应的内存中。这样`getAttribute()`就能取得你所设下的值,当然这种方法可以传对象。session也一样,只是对象在内存中的生命周期不一样而已。`getParameter()`只是应用服务器在分析你送上来的 request页面的文本时,取得你设在表单或 url 重定向时的值。 + +**总结:** + +`getParameter()`返回的是String,用于读取提交的表单中的值;(获取之后会根据实际需要转换为自己需要的相应类型,比如整型,日期类型啊等等) + +`getAttribute()`返回的是Object,需进行转换,可用`setAttribute()`设置成任意对象,使用很灵活,可随时用 + +#### 11.静态导入与动态导入 + +静态导入: + +```java +<%@include file="validate.jsp" %> +将被导入页面和导入页面,合在一起进行翻译,编译。最后产生一个Servlet,那么两个页面的变量名不能重复。 +``` + +动态导入: + +```java + +动态导入,被导入页面和导入页面分别翻译,编译,产生两个Servlet,所以两个页面的变量名可以重复.都会被执行。 +``` + +#### 12.四种作用域 + +SP中的四种作用域包括page、request、session和application,具体来说: + +- **page**代表与一个页面相关的对象和属性。 +- **request**代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。 +- **session**代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。 +- **application**代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。 + +#### 13.会话跟踪技术 + +**1)使用Cookie** + +向客户端发送Cookie + +```java +Cookie c =new Cookie("name","value"); //创建Cookie +c.setMaxAge(60*60*24); //设置最大时效,此处设置的最大时效为一天 +response.addCookie(c); //把Cookie放入到HTTP响应中 +``` + +从客户端读取Cookie + +```java +String name ="name"; +Cookie[]cookies =request.getCookies(); +if(cookies !=null){ + for(int i= 0;i +``` + +**优点:** Cookie被禁时可以使用 + +**缺点:** 所有页面必须是表单提交之后的结果。 + +**4)HttpSession** + +在所有会话跟踪技术中,HttpSession对象是最强大也是功能最多的。当一个用户第一次访问某个网站时会自动创建 HttpSession,每个用户可以访问他自己的HttpSession。可以通过HttpServletRequest对象的getSession方 法获得HttpSession,通过HttpSession的setAttribute方法可以将一个值放在HttpSession中,通过调用 HttpSession对象的getAttribute方法,同时传入属性名就可以获取保存在HttpSession中的对象。与上面三种方式不同的 是,HttpSession放在服务器的内存中,因此不要将过大的对象放在里面,即使目前的Servlet容器可以在内存将满时将HttpSession 中的对象移到其他存储设备中,但是这样势必影响性能。添加到HttpSession中的值可以是任意Java对象,这个对象最好实现了 Serializable接口,这样Servlet容器在必要的时候可以将其序列化到文件中,否则在序列化时就会出现异常。 + +#### 14.<%…%>和<%!…%>的区别 + +<%…%>用于在JSP页面中嵌入Java脚本 + +<%!…%>用于在JSP页面中申明变量或方法,可以在该页面中的<%…%>脚本中调用,声明的变量相当于Servlet中的定义的成员变量。 + +#### 15.描述Jsp页面的指令标记的功能、写法、并示例 + +指令标记影响JSP页面的翻译阶段 + +<%@ page session=”false” %> + +<%@ include file=”incl/copyright.html” %> + +<%@ taglib %> + +#### 16.描述Jsp页面的声明标记的功能、写法、并示例 + +声明标记允许JSP页面开发人员包含类级声明 + +写法: + +<%! JavaClassDeclaration %> + +```text +例: + +<%! public static final String DEFAULT_NAME = “World”; %> + +<%! public String getName(HttpServletRequest request) { + + return request.getParameter(“name”); + + } + +%> + +<%! int counter = 0; %> +``` + +#### 17.描述Jsp页面翻译成Servlet的规则 + +jsp中的注释标记被翻译成Servlet类中的注释 + +jsp中的指令标记被翻译成Servlet类中的import语句等 + +jsp中的声明标记被翻译成Servlet类中的属性 + +jsp中的脚本标记被转移到Servlet类中service方法中的代码 + +jsp中的表达式标记被翻译成Serlvet类中的write()或者print()方法括号中的代码 + +#### 18.page指令的功能,写法、并示例,并描述它的如下属性的功能和用法:import、session、buffer、errorPage、isErrorPage、ContentType、pageEncoding + +import : import 定义了一组servlet类定义必须导入的类和包,值是一个由逗号分隔的完全类名或包的列表。 + +session : session 定义JSP页面是否参与HTTP会话,值可以为true(缺省)或false。 + +buffer : buffer 定义用于输出流(JspWriter对象)的缓冲区大小,值可以为none或Nkb,缺省为8KB或更大。 + +errorPage: 用来指定由另一个jsp页面来处理所有该页面抛出的异常 + +isErrorPage : 定义JSP页面为其它JSP页面errorPage属性的目标,值为true或false(缺省)。 + +ContentType : 定义输出流的MIME类型,缺省为text/html。 + +pageEncoding :定义输出流的字符编码,缺省为ISO-8859-1 + +#### 19.描述MVC各部分的功能? + +1.Model + +封装应用状态 + +响应状态查询 + +暴露应用的功能 + +2.Controller + +验证HTTP请求的数据 + +将用户数据与模型的更新相映射 + +选择用于响应的视图 + +3.View + +产生HTML响应 + +请求模型的更新 + +提供HTML form用于用户请求 + +#### 20.什么是JavaBean? + +用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用java代码 + +创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其 + +他JavaBean、applet程序或者应用来使用这些对象。 + +#### 21.JavaBean的规则? + +使用get和set方法定义属性 + +一个无参构造方法 + +无public实例变量 + +#### 22.什么是jsp标准动作?包含那些?分别都是什么功能?如何使用? + +JSP页面中使用类似于XML的标记表示运行时的动作 + +jsp:userBean + +jsp:setProperty + +jsp:getProperty + +jsp:parameter + +jsp:include + +jsp:forward + +#### 23.用代码示例如下标准动作的使用:useBean、getProperty、setProperty + +```text + + + + + +``` + +#### 24.描述说明页面上的字段和Bean中属性的对应规则 + +id 指javabean的变量名 + +class指javabean类的全路径 + +scope指javabean的应用范围 + +name指所用到的javabean的变量名 + +property指javabean中的属性 + +#### 25.描述useBean动作的处理过程 + +使用id声明变量 + +试图在指定的范围内查找对象 + +如果没找到 + +创建一个类的实例 + +执行useBean标记体初始化对象 + +如果找到 + +将对象转换为类指定的类型 + +#### 参考: + +https://blog.csdn.net/woshizoe2/article/details/78993677 + +https://zhuanlan.zhihu.com/p/252313998 + +https://www.cnblogs.com/itzlg/p/11380691.html diff --git a/Docs/Kafka.md b/Kafka.md similarity index 74% rename from Docs/Kafka.md rename to Kafka.md index 54627c7..5da8375 100644 --- a/Docs/Kafka.md +++ b/Kafka.md @@ -153,11 +153,26 @@ Producer 、Broker。 - JMXTrans + InfluxDB + Grafana - Confluent Control Center -参考: +#### 26.kafka follower如何与leader同步数据 -- 《Kafka并不难学》 -- 《kafka入门与实践》 -- 极客时间:Kafka核心技术与实战 -- http://kafka.apache.org/ +Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。完全同步复制要求All Alive Follower都复制完,这条消息才会被认为commit,这种复制方式极大的影响了吞吐率。而异步复制方式下,Follower异步的从Leader复制数据,数据只要被Leader写入log就被认为已经commit,这种情况下,如果leader挂掉,会丢失数据,kafka使用ISR的方式很好的均衡了确保数据不丢失以及吞吐率。Follower可以批量的从Leader复制数据,而且Leader充分利用磁盘顺序读以及send file(zero copy)机制,这样极大的提高复制性能,内部批量写磁盘,大幅减少了Follower与Leader的消息量差。 + +#### 27.什么情况下一个 broker 会从 isr中踢出去 + +leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护 ,如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除 。 + + + +#### 参考: + +《Kafka并不难学》 + +《kafka入门与实践》 + +极客时间:Kafka核心技术与实战 + +http://kafka.apache.org/ + +https://blog.csdn.net/qq_28900249/article/details/90346599 ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) diff --git a/Docs/Linux.md b/Linux.md similarity index 99% rename from Docs/Linux.md rename to Linux.md index 25d5ad2..0daf456 100644 --- a/Docs/Linux.md +++ b/Linux.md @@ -783,6 +783,4 @@ The lion said to the bear, “I caught this kid first, and so this is mine.” - 百度百科 -- ​ 公众号:《马里奥玩Python》 - - ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file + ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) diff --git a/Maven.md b/Maven.md new file mode 100644 index 0000000..55544c9 --- /dev/null +++ b/Maven.md @@ -0,0 +1,318 @@ +## Maven + +#### 1.Maven 是什么? + +Maven 主要服务于基于 Java 平台的项目构建、依赖管理和项目信息管理。 + +Maven 的主要功能主要分为 5 点: + +- 依赖管理系统 +- 多模块构建 +- 一致的项目结构 +- 一致的构建模型和插件机制 + +#### 2.什么选用 Maven 进行构建? + +- 首先,Maven 是一个优秀的项目构建工具。使用 maven,可以很方便的对项目进行分模块构建,这样在开发和测试打包部署时,效率会提高很多。 +- 其次,Maven 可以进行依赖的管理。使用 Maven ,可以将不同系统的依赖进行统一管理,并且可以进行依赖之间的传递和继承。 + +#### 3. Maven 规约是什么? + +- `/src/main/java/` :Java 源码。 +- `/src/main/resource` :Java 配置文件,资源文件。 +- `/src/test/java/` :Java 测试代码。 +- `/src/test/resource` :Java 测试配置文件,资源文件。 +- `/target` :文件编译过程中生成的 `.class` 文件、jar、war 等等。 +- `pom.xml` :配置文件 + +Maven 要负责项目的自动化构建,以编译为例,Maven 要想自动进行编译,那么它必须知道 Java 的源文件保存在哪里,这样约定之后,不用我们手动指定位置,Maven 能知道位置,从而帮我们完成自动编译。 + +遵循 **“约定>>> 配置 >>> 编码”**。即能进行配置的不要去编码指定,能事先约定规则的不要去进行配置。这样既减轻了劳动力,也能防止出错。 + +#### 4.Maven 常用命令 + +- `mvn archetype:create` :创建 Maven 项目。 +- `mvn compile` :编译源代码。 +- `mvn deploy` :发布项目。 +- `mvn test-compile` :编译测试源代码。 +- `mvn test` :运行应用程序中的单元测试。 +- `mvn site` :生成项目相关信息的网站。 +- `mvn clean` :清除项目目录中的生成结果。 +- `mvn package` :根据项目生成的 jar/war 等。 +- `mvn install` :在本地 Repository 中安装 jar 。 +- `mvn eclipse:eclipse` :生成 Eclipse 项目文件。 +- `mvn jetty:run` 启动 Jetty 服务。 +- `mvn tomcat:run` :启动 Tomcat 服务。 +- `mvn clean package -Dmaven.test.skip=true` :清除以前的包后重新打包,跳过测试类。 + +用到最多的命令 + +- `mvn eclipse:clean` :清除 Project 中以前的编译的东西,重新再来。 +- `mvn eclipse:eclipse` :开始编译 Maven 的 Project 。 +- `mvn clean package` :清除以前的包后重新打包。 + +#### 5.Maven 有哪些优点和缺点 + +##### 1)优点 + +- 简化了项目依赖管理。 + + 当年,多少人被 SSH 整合搞死搞活,很多时候,是因为依赖不完整,或者版本不正确。自从 Maven 出来后,终于可以无痛了~ 当然,也有一部分功劳是 Spring Boot ,这是后话。 + +- 易于上手,对于新手可能一个 `mvn clean package` 命令就可能满足我们的工作。 + +- 便于与持续集成工具 (Jenkins) 整合。 + +- 便于项目升级,无论是项目本身升级还是项目使用的依赖升级。 + +- 有助于多模块项目的开发,一个模块开发好后,发布到仓库,依赖该模块时可以直接从仓库更新,而不用自己去编译。 + +- Maven 有很多插件,便于功能扩展,比如生产站点,自动发布版本等。 + +##### 2)缺点 + +- Maven 是一个庞大的构建系统,学习难度大。 + + 这里的学习,更多指的完整学习。如果基本使用,并不会存在该问题。 + +- Maven 采用约定优于配置的策略 (convention over configuration),虽然上手容易,但是一旦出了问题,难于调试。 + + 这个确实,略微痛苦。 + +- 当依赖很多时,m2eclipse 老是搞得 Eclipse 很卡。 + + 使用 IDEA ,而不是 Eclipse ,完美解决。 + +- 中国的网络环境差,很多 repository 无法访问,比如 Google Code、 JBoss 仓库无法访问等。 + + 这个也好解决,在 `` 中增加阿里巴巴的 Maven 私服,具体可以参见 [《提高 Maven 速度 —— Maven 仓库修改成国内阿里巴巴地址》](https://my.oschina.net/af8991/blog/833513) 文章。 + +#### 6.什么是Maven的坐标 + +Maven的坐标通过groupId,artifactId,version唯一标志一个构件。groupId通常为公司或组织名字,artifactId通常为项目名称,versionId为版本号。 + +#### 7.通过坐标如何定位地址 + +加上groupId为org.codehaus.mojo,artifactId为myproject,versionId为v1.0.0,则对应地址为:仓库目录(.m2)/org/codehaus/mojo/myproject/v1.0.0 + +#### 8.Maven的依赖范围有哪些(在scope中指定) + +compile:默认范围,如果未指定任何范围,则使用该范围。编译依赖项在所有(编译,测试,运行)类路径中都可用。此外,这些依赖关系会传播到依赖的项目 + +provided:这很像compile,但表示您希望JDK或容器在运行时提供它。它只在编译和测试类路径上可用,不可传递。 + +runtime:此范围表示编译不需要依赖项,但需要执行依赖项。它在运行时和测试类路径中,但不在编译类路径中。(servlet-api) + +test:表示应用程序的正常使用不需要依赖项,并且仅在测试编译和执行阶段可用。它不是传递的。(jdbc) + +system:系统依赖范围。该依赖与三种classpath的关系和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植。 + +#### 9.Maven生命周期 + + 有三套什么周期,分别为clean,default,site + +   clean: + +    此生命周期旨在给工程做清理工作,它主要包含以下阶段: + +    pre-clean - 执行项目清理前所需要的工作。 + +    clean - 清理上一次build项目生成的文件。 + +    post-clean - 执行完成项目清理所需的工作。 + +   default: + +    validate - 验证项目是否正确且所有必要的信息都可用。 + +    initialize - 初始化构建工作,如:设置参数,创建目录等。 + +    generate-sources - 为包含在编译范围内的代码生成源代码. + +    process-sources - 处理源代码, 如过滤值. + +    generate-resources - + +    process-resources - 复制并处理资源文件,至目标目录,准备打包。 + +    compile - 编译项目中的源代码. + +    process-classes - 为编译生成的文件做后期工作, 例如做Java类的字节码增强. + +    generate-test-sources - 为编译内容生成测试源代码. + +    process-test-sources - 处理测试源代码。 + +    generate-test-resources - + +    process-test-resources - 复制并处理资源文件,至目标测试目录。 + +    test-compile - 将需测试源代码编译到路径。一般来说,是编译/src/test/java目录下的java文件至目标输出的测试classpath目录中。 + +    process-test-classes - + +    test - 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。 + +    prepare-package - + +    package - 接受编译好的代码,打包成可发布的格式,如 JAR 。 + +    pre-integration-test - + +    integration-test - 按需求将发布包部署到运行环境。 + +    post-integration-test - + +    verify - + +    install -将包安装到本地仓库,给其他本地引用提供依赖。 + +    deploy -完成集成和发布工作,将最终包复制到远程仓库以便分享给其他开发人员。 + +   site: + +    pre-site - 执行一些生成项目站点前的准备工作。 + +    site - 生成项目站点的文档。 + +    post-site - 执行需完成站点生成的工作,如站点部署的准备工作。 + +    site-deploy - 向制定的web服务器部署站点生成文件。 + +#### 10.Maven命令 + + mvn archetype:generate 创建Maven项目 + +   mvn compile 编译源代码 + +   mvn deploy 发布项目 + +   mvn test-compile 编译测试源代码 + +   mvn test 运行应用程序中的单元测试 + +   mvn site 生成项目相关信息的网站 + +   mvn clean 清除项目目录中的生成结果 + +   mvn package 根据项目生成的jar + +   mvn install 在本地Repository中安装jar + +   mvn eclipse:eclipse 生成eclipse项目文件 + +   mvn[jetty](https://baike.baidu.com/item/jetty):run 启动jetty服务 + +   mvn[tomcat](https://baike.baidu.com/item/tomcat):run 启动tomcat服务 + +   mvn clean package -Dmaven.test.skip=true:清除以前的包后重新打包,跳过测试 + +#### 11.依赖的解析机制 + +当依赖的范围是 system 的时候,Maven 直接从本地文件系统中解析构件。 + +根据依赖坐标计算仓库路径,尝试直接从本地仓库寻找构件,如果发现对应的构件,就解析成功。 + +如果在本地仓库不存在相应的构件,就遍历所有的远程仓库,发现后,下载并解析使用。 + +如果依赖的版本是 RELEASE 或 LATEST,就基于更新策略读取所有远程仓库的元数据文件(groupId/artifactId/maven-metadata.xml),将其与本地仓库的对应元合并后,计算出 RELEASE 或者 LATEST 真实的值,然后基于该值检查本地仓库,或者从远程仓库下载。 + +如果依赖的版本是 SNAPSHOT,就基于更新策略读取所有远程仓库的元数据文件,将它与本地仓库对应的元数据合并,得到最新快照版本的值,然后根据该值检查本地仓库,或从远程仓库下载。 + +如果最后解析得到的构件版本包含有时间戳,先将该文件下载下来,再将文件名中时间戳信息删除,剩下 SNAPSHOT 并使用(以非时间戳的形式使用)。 + +#### 12.创建Maven的普通Java项目 + + mvn archetype:create -DgroupId=packageName -DartifactId=projectName + +#### 13.创建 Maven 的 Web 项目 + + mvn archetype:create -DgroupId=packageName -DartifactId=webappName -DarchetypeArtifactId=maven-archetype-webapp + +#### 14.反向生成 maven 项目的骨架 + + mvn artifacttype:generate + +#### 15.编译源代码 + + mvn compile + +#### 16.编译测试代码 + + mvn test-compile + +#### 17.运行测试 + + mvn test + +#### 18.产生 site + + mvn site + +#### 19.打包 + + mvn package + +#### 20.在本地 Repository 中安装 jar + + mvn install(例:installing D:\xxx\xx.jar to D:\xx\xxxx) + +#### 21.清除产生的项目 + + mvn clean + +#### 22.生成 Eclipse 项目/idea项目 + +eclipse项目 + + mvn eclipse:eclipse + + idea 项目 + + mvn idea:idea + +#### 23.组合使用 goal 命令,如只打包不测试 + + mvn -Dtest package + +#### 24.编译测试的内容 + + mvn test-compile + +#### 25.只打 jar 包 + + mvn jar:jar + +#### 26.只测试而不编译,也不测试编译 + + mvn test -skipping compile -skipping test-compile + +#### 27.清除 eclipse 的一些系统设置 + + mvn eclipse:clean + +#### 28.查找当前项目已被解析的依赖 + + mvn dependency:list + +#### 29.上传到私服 + + mvn deploy + +#### 30.强制检查更新,由于快照版本的更新策略(一天更新几次、隔断时间更新一次)存在,如果想强制更新就会用到此命令 + + mvn clean install-U + +#### 31.源码打包 + + mvn source:jar + + 或 + + mvn source:jar-no-fork + +#### 参考 + +https://blog.csdn.net/a303549861/article/details/93752178 + +https://www.cnblogs.com/lin0/p/14153982.html diff --git a/MongoDB.md b/MongoDB.md new file mode 100644 index 0000000..24c76a6 --- /dev/null +++ b/MongoDB.md @@ -0,0 +1,609 @@ +## MongoDB + +#### 1.什么是MongoDB + +``` +MongoDB是一个文档数据库,提供好的性能,领先的非关系型数据库。采用BSON存储文档数据。 +BSON()是一种类json的一种二进制形式的存储格式,简称Binary JSON. +相对于json多了date类型和二进制数组。 +``` + +#### 2.MongoDB的优势有哪些 + +- 面向文档的存储:以 JSON 格式的文档保存数据。 +- 任何属性都可以建立索引。 +- 复制以及高可扩展性。 +- 自动分片。 +- 丰富的查询功能。 +- 快速的即时更新。 + +#### 3.什么是数据库 + +``` +数据库可以看成是一个电子化的文件柜,用户可以对文件中的数据运行新增、检索、更新、删除等操作。数据库是一个 +所有集合的容器,在文件系统中每一个数据库都有一个相关的物理文件。 +``` + +#### 4.什么是集合(表) + +``` +集合就是一组 MongoDB 文档。它相当于关系型数据库(RDBMS)中的表这种概念。集合位于单独的一个数据库中。 +一个集合内的多个文档可以有多个不同的字段。一般来说,集合中的文档都有着相同或相关的目的。 +``` + +#### 5 什么是文档(记录) + +``` +  文档由一组key value组成。文档是动态模式,这意味着同一集合里的文档不需要有相同的字段和结构。在关系型 +数据库中table中的每一条记录相当于MongoDB中的一个文档 +``` + +#### 6 MongoDB和关系型数据库术语对比图 + +![img](https://img2018.cnblogs.com/blog/1521877/201904/1521877-20190429170250020-1693717595.png) + +#### 7.什么是非关系型数据库 + +``` + 非关系型数据库的显著特点是不使用SQL作为查询语言,数据存储不需要特定的表格模式。 +``` + +#### 8.为什么用MOngoDB? + +- 架构简单 +- 没有复杂的连接 +- 深度查询能力,MongoDB支持动态查询。 +- 容易调试 +- 容易扩展 +- 不需要转化/映射应用对象到数据库对象 +- 使用内部内存作为存储工作区,以便更快的存取数据。 + +#### 9.MongoDB中的命名空间是什么意思? + +``` +MongoDB内部有预分配空间的机制,每个预分配的文件都用0进行填充。 + +数据文件每新分配一次,它的大小都是上一个数据文件大小的2倍,每个数据文件最大2G。 + +MongoDB每个集合和每个索引都对应一个命名空间,这些命名空间的元数据集中在16M的*.ns文件中,平均每个命名占用约 628 字节,也即整个数据库的命名空间的上限约为24000。 + +如果每个集合有一个索引(比如默认的_id索引),那么最多可以创建12000个集合。如果索引数更多,则可创建的集合数就更少了。同时,如果集合数太多,一些操作也会变慢。 + +要建立更多的集合的话,MongoDB 也是支持的,只需要在启动时加上“--nssize”参数,这样对应数据库的命名空间文件就可以变得更大以便保存更多的命名。这个命名空间文件(.ns文件)最大可以为 2G。 + +每个命名空间对应的盘区不一定是连续的。与数据文件增长相同,每个命名空间对应的盘区大小都是随分配次数不断增长的。目的是为了平衡命名空间浪费的空间与保持一个命名空间数据的连续性。 + +需要注意的一个命名空间$freelist,这个命名空间用于记录不再使用的盘区(被删除的Collection或索引)。每当命名空间需要分配新盘区时,会先查看$freelist是否有大小合适的盘区可以使用,如果有就回收空闲的磁盘空间。 +``` + +#### 10.在哪些场景使用MongoDB + +- 大数据 +- 内容管理系统 +- 移动端Apps +- 数据管理 + +#### 11.monogodb 中的分片什么意思 + +``` +分片是将数据水平切分到不同的物理节点。当应用数据越来越大的时候,数据量也会越来越大。当数据量增长 +时,单台机器有可能无法存储数据或可接受的读取写入吞吐量。利用分片技术可以添加更多的机器来应对数据量增加 +以及读写操作的要求。 +``` + +#### 12.为什么要在MongoDB中使用分析器 + +``` +mongodb中包括了一个可以显示数据库中每个操作性能特点的数据库分析器.通过这个分析器你可以找到比预期慢 +的查询(或写操作);利用这一信息,比如,可以确定是否需要添加索引。 +``` + +#### 13.MongoDB支持主键外键关系吗 + +``` +默认MongoDB不支持主键和外键关系。 用Mongodb本身的API需要硬编码才能实现外键关联,不够直观且难度较大 +``` + +#### 14.MongoDB支持哪些数据类型 + + +- String +- Integer +- Double +- Boolean +- Object +- Object ID +- Arrays +- Min/Max Keys +- Datetime +- Code +- Regular Expression等 + +#### 15.为什么要在MongoDB中用"Code"数据类型 + +``` +"Code"类型用于在文档中存储 JavaScript 代码。 +``` + +#### 16.32位系统上有什么细微差别? + +journaling会激活额外的内存映射文件。这将进一步抑制32位版本上的数据库大小。因此,现在journaling在32位系统上默认是禁用的。 + +#### 17.为什么在MongoDB中使用"Object ID"数据类型 + +``` +"ObjectID"数据类型用于存储文档id +``` + +#### 18."ObjectID"有哪些部分组成 + +``` +一共有四部分组成:时间戳、客户端ID、客户进程ID、三个字节的增量计数器 + +_id是一个 12 字节长的十六进制数,它保证了每一个文档的唯一性。在插入文档时,需要提供_id。如果你不提供,那么 MongoDB 就会为每一文档提供一个唯一的 id。_id的头 4 个字节代表的是当前的时间戳,接着的后 3 个字节表示的是机器 id 号,接着的 2 个字节表示 MongoDB 服务器进程 id,最后的 3 个字节代表递增值。 +``` + +#### 19.在MongoDb中什么是索引 + +``` +索引用于高效的执行查询,没有索引的MongoDB将扫描整个集合中的所有文档,这种扫描效率很低,需要处理大量的数据. +索引是一种特殊的数据结构,将一小块数据集合保存为容易遍历的形式.索引能够存储某种特殊字段或字段集的值,并按照索引指定的方式将字段值进行排序. +``` + +#### 20.如何添加索引 + +``` +使用db.collection.createIndex()在集合中创建一个索引 +``` + +#### 21.如何查询集合中的文档 + +``` +db.collectionName.find({key:value}) +``` + +#### 22.用什么方法可以格式化输出结果 + +``` +db.collectionName.find().pretty() +``` + +#### 23.如何使用"AND"或"OR"条件循环查询集合中的文档 + +``` +db.mycol.find( + { + $or: [ + {key1: value1}, {key2:value2} + ] + } +).pretty() +``` + +#### 24.你怎么比较MongoDB、CouchDB及CouchBase? + +MongoDB和CouchDB都是面向文档的数据库。MongoDB和CouchDB都是开源NoSQL数据库的最典型代表。 除了都以文档形式存储外它们没有其他的共同点。MongoDB和CouchDB在数据模型实现、接口、对象存储以及复制方法等方面有很多不同。 + +细节可以参见下面的链接: + +MongDB vs CouchDB + +CouchDB vs CouchBase + +#### 25.名字空间(namespace)是什么? + +MongoDB存储BSON对象在丛集(collection)中。数据库名字和丛集名字以句点连结起来叫做名字空间(namespace)。 + +#### 26.如果用户移除对象的属性,该属性是否从存储层中删除? + +是的,用户移除属性然后对象会重新保存(re-save())。 + +#### 27.什么是聚合 + +聚合操作能够处理数据记录并返回计算结果。聚合操作能将多个文档中的值组合起来,对成组数据执行各种操作,返回单一的结果。它相当于 SQL 中的 count(*) 组合 group by。对于 MongoDB 中的聚合操作,应该使用`aggregate()`方法。 + +``` +db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION) +``` + +#### 28.在MongoDB中什么是副本集(避免单点故障) + +``` +在MongoDB中副本集由一组MongoDB实例组成,包括一个主节点多个次节点,MongoDB客户端的所有数据都 +写入主节点(Primary),副节点从主节点同步写入数据,以保持所有复制集内存储相同的数据,提高数据可用性。 +``` + +#### 29.什么是NoSQL数据库?NoSQL和RDBMS有什么区别?在哪些情况下使用和不使用NoSQL数据库? + +``` +NoSQL是非关系型数据库,NoSQL = Not Only SQL。 关系型数据库采用的结构化的数据,NoSQL采用的是键值对的方式存储数据。 + +在处理非结构化/半结构化的大数据时;在水平方向上进行扩展时;随时应对动态增加的数据项时可以优先考虑使用NoSQL数据库。 + +在考虑数据库的成熟度;支持;分析和商业智能;管理及专业性等问题时,应优先考虑关系型数据库。 +``` + +#### 30.MongoDB支持存储过程吗?如果支持的话,怎么用? + +``` +MongoDB支持存储过程,它是javascript写的,保存在db.system.js表中。 +``` + +#### 31.如何理解MongoDB中的GridFS机制,MongoDB为何使用GridFS来存储文件? + +GridFS是一种将大型文件存储在MongoDB中的文件规范。使用GridFS可以将大文件分隔成多个小文档存放,这样我们能够有效的保存大文档,而且解决了BSON对象有限制的问题。 + +#### 32.如何执行事务/加锁? + +MongoDB没有使用传统的锁或者复杂的带回滚的事务,因为它设计的宗旨是轻量,快速以及可预计的高性能。可以把它类比成MySQL MylSAM的自动提交模式。通过精简对事务的支持,性能得到了提升,特别是在一个可能会穿过多个服务器的系统里。 + +#### 33.启用备份故障恢复需要多久? + +从备份数据库声明主数据库宕机到选出一个备份数据库作为新的主数据库将花费10到30秒时间。这期间在主数据库上的操作将会失败--包括写入和强一致性读取(strong consistent read)操作。然而,你还能在第二数据库上执行最终一致性查询(eventually consistent query)(在slaveOk模式下),即使在这段时间里。 + +#### 34.我应该启动一个集群分片(sharded)还是一个非集群分片的 MongoDB 环境? + +为开发便捷起见,我们建议以非集群分片(unsharded)方式开始一个 MongoDB 环境,除非一台服务器不足以存放你的初始数据集。从非集群分片升级到集群分片(sharding)是无缝的,所以在你的数据集还不是很大的时候没必要考虑集群分片(sharding)。 + +#### 35.分片(sharding)和复制(replication)是怎样工作的? + +每一个分片(shard)是一个分区数据的逻辑集合。分片可能由单一服务器或者集群组成,我们推荐为每一个分片(shard)使用集群。 + +#### 36.数据在什么时候才会扩展到多个分片(shard)里? + +MongoDB 分片是基于区域(range)的。所以一个集合(collection)中的所有的对象都被存放到一个块(chunk)中。只有当存在多余一个块的时候,才会有多个分片获取数据的选项。现在,每个默认块的大小是 64Mb,所以你需要至少 64 Mb 空间才可以实施一个迁移。 + +#### 37.我可以把moveChunk目录里的旧文件删除吗? + +没问题,这些文件是在分片(shard)进行均衡操作(balancing)的时候产生的临时文件。一旦这些操作已经完成,相关的临时文件也应该被删除掉。但目前清理工作是需要手动的,所以请小心地考虑再释放这些文件的空间。 + +#### 38.分片(sharding)和复制(replication)是怎样工作的? + +每一个分片(shard)是一个分区数据的逻辑集合.分片可能由单一服务器或者集群组成,我们推荐为每一个分片(shard)使用集群。 + +#### 39.如果块移动操作(movechunk)失败了,我需要手动清除部分转移的文档吗? + + 不需要,移动操作是一致(consistent)并且是确定性的(deterministic);一次失败后,移动操作会不断重试;当完成后,数据只会出现在新的分片里(shard)。 + +#### 40.mongodb是否支持事务 + +MongoDB 4.0的新特性——事务(Transactions):MongoDB 是不支持事务的,因此开发者在需要用到事务的时候,不得不借用其他工具,在业务代码层面去弥补数据库的不足。 + +事务和会话(Sessions)关联,一个会话同一时刻只能开启一个事务操作,当一个会话断开,这个会话中的事务也会结束。 + +#### 41.哪些语言支持MongoDB? + +- C +- C++ +- C# +- Java +- Node.js +- Perl +- Php 等 + +#### 42.如何使用"AND"或"OR"条件循环查询集合中的文档 + +在`find()`方法中,如果传入多个键,并用逗号(`,`)分隔它们,那么 MongoDB 会把它看成是**AND**条件。 + +```text +>db.mycol.find({key1:value1, key2:value2}).pretty() +``` + +若基于**OR**条件来查询文档,可以使用关键字**$or**。 + +```text +>db.mycol.find( + { + $or: [ + {key1: value1}, {key2:value2} + ] + } +).pretty() +``` + +#### 43.如何删除文档 + +MongoDB 利用 `remove()` 方法 清除集合中的文档。它有 2 个可选参数: + +- deletion criteria:(可选)删除文档的标准。 +- justOne:(可选)如果设为 true 或 1,则只删除一个文档。 + +```text +>db.collectionName.remove({key:value}) +``` + +#### 44.在MongoDB中如何排序 + +MongoDB 中的文档排序是通过`sort()`方法来实现的。`sort()`方法可以通过一些参数来指定要进行排序的字段,并使用 1 和 -1 来指定排序方式,其中 1 表示升序,而 -1 表示降序。 + +```text +>db.connectionName.find({key:value}).sort({columnName:1}) +``` + +#### 45.举例说明您将从Redis和MongoDB一起使用中受益的情况? + +Redis和MongoDB可以一起使用,效果很好。Craiglist是一家以运行MongoDB和Redis(以及MySQL和Sphinx)而闻名的公司。请参阅Jeremy +Zawodny的[演示文稿](http://www.slideshare.net/jzawodn/living-with-sql-and-nosql-at-craigslist-a-pragmatic-approach)。 + +MongoDB对于以各种方式索引的持久性,面向文档的数据很有趣。对于易失性数据或对延迟敏感的半永久性数据,Redis更有趣。 + +以下是在MongoDB之上具体使用Redis的一些示例。 + +- 2.2版之前的MongoDB还没有到期机制。上限集合不能真正用于实现真正的TTL。Redis具有基于TTL的过期机制,可以方便地存储易失性数据。例如,用户会话通常存储在Redis中,而用户数据将存储在MongoDB中并建立索引。请注意,MongoDB 2.2在集合级别引入了一种低精度的过期机制(例如,用于清除数据)。 +- Redis提供了一种方便的集合数据类型及其关联的操作(联合,交集,多个集合的差等)。在此功能之上实现基本的多面搜索或标记引擎非常容易,这是对MongoDB更传统的索引功能的有趣补充。 +- Redis支持有效地阻止列表上的弹出操作。这可用于实现临时分布式排队系统。它比MongoDB可尾游标IMO更具灵活性,因为后端应用程序可以在超时的情况下侦听多个队列,原子地将项目转移到另一个队列,等等…如果应用程序需要排队,则将队列存储在Redis中是有意义的,并将持久性功能数据保留在MongoDB中。 +- Redis还提供了发布/订阅机制。在分布式应用程序中,事件传播系统可能会有用。对于持久性数据保留在MongoDB中而言,这也是Redis的绝佳用例。 + +由于使用MongoDB设计数据模型要比使用Redis容易得多(Redis更底层),因此可以从MongoDB的主要持久性数据灵活性和Redis提供的额外功能(低延迟)中受益。 +,项目到期,队列,发布/订阅,原子块等)。这确实是一个很好的组合。 + +请注意,您永远不要在同一台机器上运行Redis和MongoDB服务器。MongoDB内存被设计为可以换出,Redis不是。如果MongoDB触发某些交换活动,则Redis的性能将是灾难性的。它们应该在不同的节点上隔离。 + +#### 46.MongoDB + Azure + Android:com.mongodb.WriteConcernException err:“非主用户”代码:“ 10058” + +**背景** : + +嗨,我正在Azure上运行MongoDB副本集,并已从Android应用程序中远程连接到它。我已使读取在所有实例上都能很好地工作(已更新:因为允许它们在主节点和辅助节点上读取)。但是,对数据库的写入仍然会出现间歇性错误,并出现以下错误,因为写入必须仅在主节点上进行。 + +另外,如果您可以提供更多具体资源来解决此问题,那么这也将非常有帮助。我已经阅读了大多数文档,并搜索了很多此错误。 + +**问题** : + +如何防止此错误并允许100%的时间写入? + +``` +E/AndroidRuntime(): com.mongodb.WriteConcernException: { + "serverUsed" : "/:27017" , "err" : "not master" , + "code" : 10058 , "n" : 0 , "lastOp" : { "$ts" : 0 , "$inc" : 0} , + "connectionId" : 1918 , "ok" : 1.0} +``` + +**堆栈跟踪** : + +``` +E/AndroidRuntime(13731): FATAL EXCEPTION: Thread-7629 +E/AndroidRuntime(13731): Process: com.myapplication.examplemongodb, PID: 13731 +E/AndroidRuntime(13731): com.mongodb.WriteConcernException: { "serverUsed" : "/:27017" , "err" : "not master" , "code" : 10058 , "n" : 0 , "lastOp" : { "$ts" : 0 , "$inc" : 0} , "connectionId" : 1918 , "ok" : 1.0} +E/AndroidRuntime(13731): at com.mongodb.CommandResult.getException(CommandResult.java:77) +E/AndroidRuntime(13731): at com.mongodb.CommandResult.throwOnError(CommandResult.java:110) +E/AndroidRuntime(13731): at com.mongodb.DBTCPConnector._checkWriteError(DBTCPConnector.java:102) +E/AndroidRuntime(13731): at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:142) +E/AndroidRuntime(13731): at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:115) +E/AndroidRuntime(13731): at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:248) +E/AndroidRuntime(13731): at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:204) +E/AndroidRuntime(13731): at com.mongodb.DBCollection.insert(DBCollection.java:76) +E/AndroidRuntime(13731): at com.mongodb.DBCollection.insert(DBCollection.java:60) +E/AndroidRuntime(13731): at com.mongodb.DBCollection.insert(DBCollection.java:105) +E/AndroidRuntime(13731): at com.myapplication.examplemongodb.ActivityMain$1.run(ActivityMain.java:83) +E/AndroidRuntime(13731): at java.lang.Thread.run(Thread.java:841) +``` + +**注意事项** : + +- 我正在使用[mongo-java-driver v2.11.3](http://central.maven.org/maven2/org/mongodb/mongo-java-driver/)。 + +- 我使用了 + + mongo-azure库 + + 来帮助创建具有两个工作角色的MongoDB副本集。 + + - (如果您还有其他资源,那么我很乐意阅读。我已经阅读了GitHub自述文件[this](http://docs.mongodb.org/ecosystem/tutorial/deploy-mongodb-worker-roles-in-azure/),[this](http://docs.mongodb.org/ecosystem/tutorial/configure-worker-roles-in-azure/)和其他一些与MongoDB / Azure不相关的内容。但是,这些资源不是更新,也不详细。) + +**可能的解决方案** : + +- 我认为这与设置副本集有关。 +- 我不确定这种情况是否会发生,因为我只有两个实例副本集(一个主要副本和一个次要副本),并且他们正在为谁想成为主要副本而进行争夺(阅读:投票)。也许需要仲裁员?但是,我目前不知道该怎么做。 + +**更新** : + +- 感谢@David Makogon的帮助,我非常确定问题在于如何建立与Azure的连接以及如何访问辅助角色。因此,这是我关于系统配置方式的最新注释: + - 两个工作角色(MongoDB.WindowsAzure.MongoDBRole),我通过`TCP Input Endpoint`Android应用程序通过端口27017 直接连接到这些角色。正如@David所说,我目前无法控制连接到哪个实例。 + - 我不做任何事情的一个Web角色(MongoDB.WindowsAzure.Manager)`HTTP Input Endpoint`在端口80上有一个。默认情况下,这是我上面提到的mongo-azure库的默认设置。我不确定是否应该对此做任何事情。 + +#### 47.使用Spring Security + Spring数据+ MongoDB进行身份验证 + +我想将Spring安全性与MongoDB结合使用(使用Spring数据),并从我自己的数据库中检索用户以获取Spring安全性。但是,由于我的用户服务类型似乎不受支持,所以我不能这样做。 + +这是我的UserService类: + +``` +public class UserService { + private ApplicationContext applicationContext; + private MongoOperations mongoOperations; + + public UserService() { + applicationContext = new AnnotationConfigApplicationContext(MongoConfig.class); + mongoOperations = (MongoOperations) applicationContext.getBean("mongoTemplate"); + } + + public User find(String username) { + return mongoOperations.findOne(Query.query(Criteria.where("username").is(username)), User.class); + } +} +``` + +和我的SecurityConfig类: + +``` +@Configuration +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + UserService userService; + + @Autowired + public void configAuthBuilder(AuthenticationManagerBuilder builder) throws Exception { + builder.userDetailsService(userService); //THIS DOES NOT WORK + builder.inMemoryAuthentication().withUser("username").password("password").roles("USER"); + } + +} +``` + +我评论的那句话说: + +``` +The inferred type UserService is not a valid substitute for the bounded parameter products; +} + +@Document(collection = "album") +public class Album extends Product { + @DBRef(lazy = true) + public List songs; +} + +@Document(collection = "single") +public class Single extends Product { + @DBRef(lazy = true) + public List songs; +} + +@Document(collection = "song") +public class Song { + @Id + public String id; + + public String title; +} + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, + property = "productType", + include = JsonTypeInfo.As.EXTERNAL_PROPERTY) +@JsonSubTypes(value = { + @JsonSubTypes.Type(value = Single.class), + @JsonSubTypes.Type(value = Album.class) +}) +public abstract class Product { + @Id + public String id; +} +``` + +**生成的JSON** + +``` +{ + "id": "someId1", + "products": [ + { + "id": "someId2", + "songs": [ + { + "id": "someId3", + "title": "Some title", + "target": { + "id": "someId3", + "title": "Some title" + } + } + ] + } + ] +} +``` + +#### 50.表示MongoDB中具有属性的多对多关系的最佳模型 + +代表具有属性的多对多关系的最“ mongo”方式是什么? + +因此,例如: + +##### 介绍 + +------ + +MYSQL表 + +``` +people` => `firstName, lastName, ... +Movies` => `name, length .. +peopleMovies` => `movieId, personId, language, role +``` + +##### 解决方案1 + +------ + +将人们嵌入电影中…? + +在MongoDB中,我知道这样`denormalize and embed`做很好,但是我不想让`embed`人们看电影,从逻辑上讲这没有任何意义。因为人们不一定只属于电影。 + +##### 解决方案2 + +------ + +``` +People`并且`Movies`将两个单独的集合。 `People`=>嵌入`[{movieId: 12, personId: 1, language: "English", role: "Main"} ...] +Movies` =>嵌入 `[{movieId: 12, personId: 1, language: "English", role: "Main"} ...] +``` + +该解决方案的问题在于,当我们要`role`为特定对象更新人员时,`movie`我们需要运行两个更新查询以确保两个集合中的数据同步。 + +##### 解决方案3 + +------ + +我们还可以做一些与关系有关的事情,最后得到三个集合 + +``` +People`=> `firstName, lastName, ...` `Movies`=> `name, length ..` +`Castings`=>`movieId, personId, language, role +``` + +问题在于,由于MongoDB中缺少join语句,因此需要`3 queries`从人那里去->电影,反之亦然。 + +这是我的问题,还有什么其他方式可以对此类事物进行建模`MongoDB`以及更多`NoSQL`方式。就提供的解决方案而言,在mongo的性能和约定方面哪一种是最好的。 + +#### 参考 + +https://www.cnblogs.com/angle6-liu/p/10791875.html + +https://www.jb51.net/article/179908.htm + +https://zhuanlan.zhihu.com/p/37437657 + +https://blog.csdn.net/weixin_45669794/article/details/102991806 + +http://www.zzvips.com/article/69641.html + +https://funyan.cn/p/6173.html + +http://www.mianshigee.com/question/132761aes/ + +http://www.mianshigee.com/question/65730fcs/ + +http://www.mianshigee.com/question/67979kov/ + +http://www.mianshigee.com/question/159947bya/ \ No newline at end of file diff --git a/Docs/MySql.md "b/MySql-\344\270\212\345\215\267.md" similarity index 99% rename from Docs/MySql.md rename to "MySql-\344\270\212\345\215\267.md" index c07b842..3b60733 100644 --- a/Docs/MySql.md +++ "b/MySql-\344\270\212\345\215\267.md" @@ -1,6 +1,4 @@ -## Mysql - - +## Mysql(上) #### 1.什么是数据库? diff --git "a/MySql-\344\270\213\345\215\267.md" "b/MySql-\344\270\213\345\215\267.md" new file mode 100644 index 0000000..b566e7c --- /dev/null +++ "b/MySql-\344\270\213\345\215\267.md" @@ -0,0 +1,584 @@ +## Mysql(下) + +#### 1.能说下myisam 和 innodb的区别吗? + +myisam引擎是5.1版本之前的默认引擎,支持全文检索、压缩、空间函数等,但是不支持事务和行级锁,所以一般用于有大量查询少量插入的场景来使用,而且myisam不支持外键,并且索引和数据是分开存储的。 + +innodb是基于聚簇索引建立的,和myisam相反它支持事务、外键,并且通过MVCC来支持高并发,索引和数据存储在一起。 + + +#### 2.说下mysql的索引有哪些吧,聚簇和非聚簇索引又是什么? + +索引按照数据结构来说主要包含 B + 树和 Hash 索引。 + +假设我们有张表,结构如下: + +``` +create table user( +id int(11) not null, +age int(11) not null, +primary key(id), key(age) +); +``` + +B + 树是左小右大的顺序存储结构,节点只包含 id 索引列,而叶子节点包含索引列和数据,这种数据和索引在一起存储的索引方式叫做聚簇索引,一张表只能有一个聚簇索引。假设没有定义主键,InnoDB 会选择一个唯一的非空索引代替,如果没有的话则会隐式定义一个主键作为聚簇索引。 + +![img](https://img-blog.csdnimg.cn/20201007140904245.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_16,color_FFFFFF,t_70#pic_center) + + +这是主键聚簇索引存储的结构,那么非聚簇索引的结构是什么样子呢?非聚簇索引 (二级索引) 保存的是主键 id 值,这一点和 myisam 保存的是数据地址是不同的。 + +![img](https://img-blog.csdnimg.cn/20201007141110124.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_1,color_FFFFFF,t_70#pic_center) + + +最终,我们一张图看看 InnoDB 和 Myisam 聚簇和非聚簇索引的区别 + +![img](https://img-blog.csdnimg.cn/20201007141238754.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_1,color_FFFFFF,t_70#pic_center) + + + +#### 3.那你知道什么是覆盖索引和回表吗? + +覆盖索引指的是在一次查询中,如果一个索引包含或者说覆盖所有需要查询的字段的值,我们就称之为覆盖索引,而不再需要回表查询。 + +而要确定一个查询是否是覆盖索引,我们只需要 explain sql 语句看 Extra 的结果是否是 “Using index” 即可。 + +以上面的 user 表来举例,我们再增加一个 name 字段,然后做一些查询试试。 + +``` +explain select * from user where age=1; // 查询的 name 无法从索引数据获取 +explain select id,age from user where age=1; // 可以直接从索引获取 +``` + +#### 4、锁的类型有哪些呢 + +mysql 锁分为共享锁和排他锁,也叫做读锁和写锁。 + +读锁是共享的,可以通过 lock in share mode 实现,这时候只能读不能写。 + +写锁是排他的,它会阻塞其他的写锁和读锁。从颗粒度来区分,可以分为表锁和行锁两种。 + +表锁会锁定整张表并且阻塞其他用户对该表的所有读写操作,比如 alter 修改表结构的时候会锁表。 + +行锁又可以分为乐观锁和悲观锁,悲观锁可以通过 for update 实现,乐观锁则通过版本号实现。 + +#### 5、你能说下事务的基本特性和隔离级别吗? + +事务基本特性 ACID 分别是: + +原子性指的是一个事务中的操作要么全部成功,要么全部失败。 + +一致性指的是数据库总是从一个一致性的状态转换到另外一个一致性的状态。比如 A 转账给 B100 块钱,假设中间 sql 执行过程中系统崩溃 A 也不会损失 100 块,因为事务没有提交,修改也就不会保存到数据库。 + +隔离性指的是一个事务的修改在最终提交前,对其他事务是不可见的。 + +持久性指的是一旦事务提交,所做的修改就会永久保存到数据库中。 + +而隔离性有 4 个隔离级别,分别是: + +read uncommit 读未提交,可能会读到其他事务未提交的数据,也叫做脏读。 + +用户本来应该读取到 id=1 的用户 age 应该是 10,结果读取到了其他事务还没有提交的事务,结果读取结果 age=20,这就是脏读。 + +![img](https://img-blog.csdnimg.cn/20201007141450216.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_1,color_FFFFFF,t_70#pic_center) + + +read commit 读已提交,两次读取结果不一致,叫做不可重复读。 + +不可重复读解决了脏读的问题,他只会读取已经提交的事务。 + +用户开启事务读取 id=1 用户,查询到 age=10,再次读取发现结果 = 20,在同一个事务里同一个查询读取到不同的结果叫做不可重复读。 + +![img](https://img-blog.csdnimg.cn/20201007141612150.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_16,color_FFFFFF,t_70#pic_center) + + +repeatable read 可重复复读,这是 mysql 的默认级别,就是每次读取结果都一样,但是有可能产生幻读。 + +serializable 串行,一般是不会使用的,他会给每一行读取的数据加锁,会导致大量超时和锁竞争的问题。 + +#### 6、那 ACID 靠什么保证的呢? + +A 原子性由 undo log 日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的 sql + +C 一致性一般由代码层面来保证 + +I 隔离性由 MVCC 来保证 + +D 持久性由内存 + redo log 来保证,mysql 修改数据同时在内存和 redo log 记录这次操作,事务提交的时候通过 redo log 刷盘,宕机的时候可以从 redo log 恢复 + +#### 7、那你说说什么是幻读,什么是 MVCC? + +要说幻读,首先要了解 MVCC,MVCC 叫做多版本并发控制,实际上就是保存了数据在某个时间节点的快照。 + +我们每行数实际上隐藏了两列,创建时间版本号,过期 (删除) 时间版本号,每开始一个新的事务,版本号都会自动递增。 + +还是拿上面的 user 表举例子,假设我们插入两条数据,他们实际上应该长这样。 + +a-draft-type=“table” data-size=“normal” data-row-style=“normal”> +id name create_version delete_version +这时候假设小明去执行查询,此时 current_version=3 + +select * from user where id<=3; + +同时,小红在这时候开启事务去修改 id=1 的记录,current_version=4 + +update user set name=‘张三三’ where id=1; + +执行成功后的结果是这样的 + +a-draft-type=“table” data-size=“normal” data-row-style=“normal”> +id name create_version delete_version +如果这时候还有小黑在删除 id=2 的数据,current_version=5,执行后结果是这样的。 + +a-draft-type=“table” data-size=“normal” data-row-style=“normal”> +id name create_version delete_version +由于 MVCC 的原理是查找创建版本小于或等于当前事务版本,删除版本为空或者大于当前事务版本,小明的真实的查询应该是这样 + +select * from user where id<=3 and create_version<=3 and (delete_version>3 or delete_version is null); + +所以小明最后查询到的 id=1 的名字还是’张三’,并且 id=2 的记录也能查询到。这样做是为了保证事务读取的数据是在事务开始前就已经存在的,要么是事务自己插入或者修改的。 + +明白 MVCC 原理,我们来说什么是幻读就简单多了。举一个常见的场景,用户注册时,我们先查询用户名是否存在,不存在就插入,假定用户名是唯一索引。 + +小明开启事务 current_version=6 查询名字为’王五’的记录,发现不存在。 +小红开启事务 current_version=7 插入一条数据,结果是这样: +a-draft-type=“table” data-size=“normal” data-row-style=“normal”> +id Name create_version delete_version +小明执行插入名字’王五’的记录,发现唯一索引冲突,无法插入,这就是幻读。 + +#### 8、 那你知道什么是间隙锁吗? + +间隙锁是可重复读级别下才会有的锁,结合 MVCC 和间隙锁可以解决幻读的问题。我们还是以 user 举例,假设现在 user 表有几条记录 + +e data-draft-node=“block” data-draft-type=“table” data-size=“normal” data-row-style=“normal”> +当我们执行: + +begin; + +select * from user where age=20 for update; + + begin; + + insert into user(age) values(10); #成功 + + insert into user(age) values(11); #失败 + + insert into user(age) values(20); #失败 + +insert into user(age) values(21); #失败 + + insert into user(age) values(30); #失败 + + + +只有 10 可以插入成功,那么因为表的间隙 mysql 自动帮我们生成了区间 (左开右闭) + +(negative infinity,10],(10,20],(20,30],(30,positive infinity) + +由于 20 存在记录,所以 (10,20],(20,30] 区间都被锁定了无法插入、删除。 + +如果查询 21 呢?就会根据 21 定位到 (20,30) 的区间(都是开区间)。 + +需要注意的是唯一索引是不会有间隙索引的。 + +#### 9、你们数据量级多大?分库分表怎么做的? + +首先分库分表分为垂直和水平两个方式,一般来说我们拆分的顺序是先垂直后水平。 + +**垂直分库** + +基于现在微服务拆分来说,都是已经做到了垂直分库了。 + +![img](https://img-blog.csdnimg.cn/20201007142110579.png#pic_center) + + +**垂直分表** + +如果表字段比较多,将不常用的、数据较大的等等做拆分。 + +![img](https://img-blog.csdnimg.cn/20201007142229828.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_1,color_FFFFFF,t_70#pic_center) +**水平分表** + +首先根据业务场景来决定使用什么字段作为分表字段 (sharding_key),比如我们现在日订单 1000 万,我们大部分的场景来源于 C 端,我们可以用 user_id 作为 sharding_key,数据查询支持到最近 3 个月的订单,超过 3 个月的做归档处理,那么 3 个月的数据量就是 9 亿,可以分 1024 张表,那么每张表的数据大概就在 100 万左右。 + +比如用户 id 为 100,那我们都经过 hash(100),然后对 1024 取模,就可以落到对应的表上了。 + +#### 10、那分表后的 ID 怎么保证唯一性的呢? + +因为我们主键默认都是自增的,那么分表之后的主键在不同表就肯定会有冲突了。有几个办法考虑: + +1. 设定步长,比如 1-1024 张表我们分别设定 1-1024 的基础步长,这样主键落到不同的表就不会冲突了。 +2. 分布式 ID,自己实现一套分布式 ID 生成算法或者使用开源的比如雪花算法这种。 +3. 分表后不使用主键作为查询依据,而是每张表单独新增一个字段作为唯一主键使用,比如订单表订单号是唯一的,不管最终落在哪张表都基于订单号作为查询依据,更新也一样。 + +#### 11、 分表后非 sharding_key 的查询怎么处理呢? + +1. 可以做一个 mapping 表,比如这时候商家要查询订单列表怎么办呢?不带 user_id 查询的话你总不能扫全表吧?所以我们可以做一个映射关系表,保存商家和用户的关系,查询的时候先通过商家查询到用户列表,再通过 user_id 去查询。 +2. 打宽表,一般而言,商户端对数据实时性要求并不是很高,比如查询订单列表,可以把订单表同步到离线(实时)数仓,再基于数仓去做成一张宽表,再基于其他如 es 提供查询服务。 +3. 数据量不是很大的话,比如后台的一些查询之类的,也可以通过多线程扫表,然后再聚合结果的方式来做。或者异步的形式也是可以的。 + +``` +List>> taskList = Lists.newArrayList(); for (int shardingIndex = 0; shardingIndex < 1024; shardingIndex++) { taskList.add(() -> (userMapper.getProcessingAccountList(shardingIndex))); } List list = null; try { list = taskExecutor.executeTask(taskList); } catch (Exception e) { //do something } public class TaskExecutor { public List executeTask(Collection> tasks) throws Exception { List result = Lists.newArrayList(); List> futures = ExecutorUtil.invokeAll(tasks); for (Future future : futures) { result.add(future.get()); } return result; } } +``` + +#### 12、说说 mysql 主从同步怎么做的吧? + +首先先了解 mysql 主从同步的原理 + +1. master 提交完事务后,写入 binlog + +2. slave 连接到 master,获取 binlog + +3. master 创建 dump 线程,推送 binglog 到 slave + +4. slave 启动一个 IO 线程读取同步过来的 master 的 binlog,记录到 relay log 中继日志中 + +5. slave 再开启一个 sql 线程读取 relay log 事件并在 slave 执行,完成同步 + +6. slave 记录自己的 binglog + + ![img](https://img-blog.csdnimg.cn/20201007142848225.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_1,color_FFFFFF,t_70#pic_center) + + 由于 mysql 默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念。 + +**全同步复制** + +主库写入 binlog 后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响。 + +**半同步复制** + +和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回 ACK 确认给主库,主库收到至少一个从库的确认就认为写操作完成。 + +#### 13、那主从的延迟怎么解决呢? + +这个问题貌似真的是个无解的问题,只能是说自己来判断了,需要走主库的强制走主库查询。 + +#### 14.Mysql中有哪几种锁? + +1.表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。 + +2.行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 + +3.页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。 + +#### 15.主键和候选键有什么区别? + +表格的每一行都由主键唯一标识,一个表只有一个主键。 + +主键也是候选键。按照惯例,候选键可以被指定为主键,并且可以用于任何外键引用。 + +#### 16.myisamchk是用来做什么的? + +它用来压缩MyISAM表,这减少了磁盘或内存使用。 + +#### 17.MyISAM Static和MyISAM Dynamic有什么区别? + +在MyISAM Static上的所有字段有固定宽度。动态MyISAM表将具有像TEXT,BLOB等字段,以适应不同长度的数据类型。 + +MyISAM Static在受损情况下更容易恢复。 + +#### 18.如果一个表有一列定义为TIMESTAMP,将发生什么? + +每当行被更改时,时间戳字段将获取当前时间戳。 + +#### 19.列设置为AUTO INCREMENT时,如果在表中达到最大值,会发生什么情况? + +它会停止递增,任何进一步的插入都将产生错误,因为密钥已被使用。 + +#### 20.怎样才能找出最后一次插入时分配了哪个自动增量? + +LAST_INSERT_ID将返回由Auto_increment分配的最后一个值,并且不需要指定表名称。 + +#### 21.你怎么看到为表格定义的所有索引? + +索引是通过以下方式为表格定义的: + +SHOW INDEX FROM +; + +#### 22.LIKE声明中的%和_是什么意思? + +%对应于0个或更多字符,_只是LIKE语句中的一个字符。 + +#### 23.如何在Unix和Mysql时间戳之间进行转换? + +UNIX_TIMESTAMP是从Mysql时间戳转换为Unix时间戳的命令 +FROM_UNIXTIME是从Unix时间戳转换为Mysql时间戳的命令 + +#### 24.列对比运算符是什么? + +在SELECT语句的列比较中使用=,<>,<=,<,> =,>,<<,>>,<=>,AND,OR或LIKE运算符。 + +#### 25.BLOB和TEXT有什么区别? + +BLOB是一个二进制对象,可以容纳可变数量的数据。TEXT是一个不区分大小写的BLOB。 + +BLOB和TEXT类型之间的唯一区别在于对BLOB值进行排序和比较时区分大小写,对TEXT值不区分大小写。 + +#### 26.mysql_fetch_array和mysql_fetch_object的区别是什么? + +以下是mysql_fetch_array和mysql_fetch_object的区别: + +mysql_fetch_array() – 将结果行作为关联数组或来自数据库的常规数组返回。 + +mysql_fetch_object – 从数据库返回结果行作为对象。 + +#### 27.MyISAM表格将在哪里存储,并且还提供其存储格式? + +每个MyISAM表格以三种格式存储在磁盘上: + +·“.frm”文件存储表定义 + +·数据文件具有“.MYD”(MYData)扩展名 + +索引文件具有“.MYI”(MYIndex)扩展名 + +#### 28.Mysql如何优化DISTINCT? + +DISTINCT在所有列上转换为GROUP BY,并与ORDER BY子句结合使用。 + +1 + +SELECT DISTINCT t1.a FROM t1,t2 where t1.a=t2.a; + +#### 29.如何显示前50行? + +在Mysql中,使用以下代码查询显示前50行: + +SELECT*FROM + +LIMIT 0,50; + +#### 30.可以使用多少列创建索引? + +任何标准表最多可以创建16个索引列。 + +#### 31.NOW()和CURRENT_DATE()有什么区别? + +NOW()命令用于显示当前年份,月份,日期,小时,分钟和秒。 + +CURRENT_DATE()仅显示当前年份,月份和日期。 + +#### 32.什么是非标准字符串类型? + +1. TINYTEXT +2. TEXT +3. MEDIUMTEXT +4. LONGTEXT + +#### 33.什么是通用SQL函数? + +1. CONCAT(A, B) – 连接两个字符串值以创建单个字符串输出。通常用于将两个或多个字段合并为一个字段。 +2. FORMAT(X, D)- 格式化数字X到D有效数字。 +3. CURRDATE(), CURRTIME()- 返回当前日期或时间。 +4. NOW() – 将当前日期和时间作为一个值返回。 +5. MONTH(),DAY(),YEAR(),WEEK(),WEEKDAY() – 从日期值中提取给定数据。 +6. HOUR(),MINUTE(),SECOND() – 从时间值中提取给定数据。 +7. DATEDIFF(A,B) – 确定两个日期之间的差异,通常用于计算年龄 +8. SUBTIMES(A,B) – 确定两次之间的差异。 +9. FROMDAYS(INT) – 将整数天数转换为日期值。 + +#### 34.mysql里记录货币用什么字段类型好 + +NUMERIC和DECIMAL类型被Mysql实现为同样的类型,这在SQL92标准允许。他们被用于保存值,该值的准确精度是极其重要的值,例如与金钱有关的数据。当声明一个类是这些类型之一时,精度和规模的能被(并且通常是)指定。 + +例如: + +salary DECIMAL(9,2) + +在这个例子中,9(precision)代表将被用于存储值的总的小数位数,而2(scale)代表将被用于存储小数点后的位数。 + +因此,在这种情况下,能被存储在salary列中的值的范围是从-9999999.99到9999999.99。 + +#### 35.mysql有关权限的表都有哪几个? + +Mysql服务器通过权限表来控制用户对数据库的访问,权限表存放在mysql数据库里,由mysql_install_db脚本初始化。这些权限表分别user,db,table_priv,columns_priv和host。 + +#### 36.列的字符串类型可以是什么? + +字符串类型是: + +1. SET +2. BLOB +3. ENUM +4. CHAR +5. TEXT + +#### 37.MySQL数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化? + +a. 设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率。 +b. 选择合适的表字段数据类型和存储引擎,适当的添加索引。 +c. mysql库主从读写分离。 +d. 找规律分表,减少单表中的数据量提高查询速度。 +e。添加缓存机制,比如memcached,apc等。 +f. 不经常改动的页面,生成静态页面。 +g. 书写高效率的SQL。比如 SELECT * FROM TABEL 改为 SELECT field_1, field_2, field_3 FROM TABLE. + +#### 38.锁的优化策略 + +1. 读写分离 + +2. 分段加锁 + +3. 减少锁持有的时间 + +4. 多个线程尽量以相同的顺序去获取资源 + +不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一次加一把大锁。 + +#### 39.索引的底层实现原理和优化 + +B+树,经过优化的B+树 + +主要是在所有的叶子结点中增加了指向下一个叶子节点的指针,因此InnoDB建议为大部分表使用默认自增的主键作为主索引。 + +#### 40.什么情况下设置了索引但无法使用 + +​ 1.以“%”开头的LIKE语句,模糊匹配 + +2. OR语句前后没有同时使用索引 + +3. 数据类型出现隐式转化(如varchar不加单引号的话可能会自动转换为int型) + +#### 41.实践中如何优化MySQL + +最好是按照以下顺序优化: + +1.SQL语句及索引的优化 + +2.数据库表结构的优化 + +3.系统配置的优化 + +4.硬件的优化 + +详细可以查看 [阿里P8架构师谈:MySQL慢查询优化、索引优化、以及表等优化总结](https://link.zhihu.com/?target=http%3A//youzhixueyuan.com/mysql-slow-query-optimization-index-optimization.html) + +#### 42.优化数据库的方法 + +1. 选取最适用的字段属性,尽可能减少定义字段宽度,尽量把字段设置NOTNULL,例如’省份’、’性别’最好适用ENUM +2. 使用连接(JOIN)来代替子查询 +3. 适用联合(UNION)来代替手动创建的临时表 +4. 事务处理 +5. 锁定表、优化事务处理 +6. 适用外键,优化锁定表 +7. 建立索引 +8. 优化查询语句 + +#### 43.简单描述mysql中,索引,主键,唯一索引,联合索引的区别,对数据库的性能有什么影响(从读写两方面) + +索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。 + +普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度。 + +普通索引允许被索引的数据列包含重复的值。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用关键字UNIQUE把它定义为一个唯一索引。也就是说,唯一索引可以保证数据记录的唯一性。 + +主键,是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字 PRIMARY KEY 来创建。 + +索引可以覆盖多个数据列,如像INDEX(columnA, columnB)索引,这就是联合索引。 + +索引可以极大的提高数据的查询速度,但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要操作索引文件。 + +#### 44.SQL注入漏洞产生的原因?如何防止? + +SQL注入产生的原因:程序开发过程中不注意规范书写sql语句和对特殊字符进行过滤,导致客户端可以通过全局变量POST和GET提交一些sql语句正常执行。 + +防止SQL注入的方式: +开启配置文件中的magic_quotes_gpc 和 magic_quotes_runtime设置 + +执行sql语句时使用addslashes进行sql语句转换 + +Sql语句书写尽量不要省略双引号和单引号。 + +过滤掉sql语句中的一些关键词:update、insert、delete、select、 * 。 + +提高数据库表和字段的命名技巧,对一些重要的字段根据程序的特点命名,取不易被猜到的。 + +#### 45.为表中得字段选择合适得数据类型 + +字段类型优先级: 整形>date,time>enum,char>varchar>blob,text +优先考虑数字类型,其次是日期或者二进制类型,最后是字符串类型,同级别得数据类型,应该优先选择占用空间小的数据类型 + +**存储时期** + +Datatime:以 YYYY-MM-DD HH:MM:SS 格式存储时期时间,精确到秒,占用8个字节得存储空间,datatime类型与时区无关 +Timestamp:以时间戳格式存储,占用4个字节,范围小1970-1-1到2038-1-19,显示依赖于所指定得时区,默认在第一个列行的数据修改时可以自动得修改timestamp列得值 +Date:(生日)占用得字节数比使用字符串.[http://datatime.int](https://link.zhihu.com/?target=http%3A//datatime.int)储存要少,使用date只需要3个字节,存储日期月份,还可以利用日期时间函数进行日期间得计算 +Time:存储时间部分得数据 +注意:不要使用字符串类型来存储日期时间数据(通常比字符串占用得储存空间小,在进行查找过滤可以利用日期得函数) +使用int存储日期时间不如使用timestamp类型 + +#### 46.对于关系型数据库而言,索引是相当重要的概念,请回答有关索引的几个问题: + +1.索引的目的是什么? +快速访问数据表中的特定信息,提高检索速度 +创建唯一性索引,保证数据库表中每一行数据的唯一性。 +加速表和表之间的连接 + +使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间 + +2.索引对数据库系统的负面影响是什么? +负面影响: +创建索引和维护索引需要耗费时间,这个时间随着数据量的增加而增加;索引需要占用物理空间,不光是表需要占用数据空间,每个索引也需要占用物理空间;当对表进行增、删、改、的时候索引也要动态维护,这样就降低了数据的维护速度。 + +3.为数据表建立索引的原则有哪些? +在最频繁使用的、用以缩小查询范围的字段上建立索引。 + +在频繁使用的、需要排序的字段上建立索引 + +4.什么情况下不宜建立索引? +对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引。 + +对于一些特殊的数据类型,不宜建立索引,比如文本字段(text)等 + +#### 47.解释MySQL外连接、内连接与自连接的区别 + +先说什么是交叉连接: 交叉连接又叫笛卡尔积,它是指不使用任何条件,直接将一个表的所有记录和另一个表中的所有记录一一匹配。 + +内连接 则是只有条件的交叉连接,根据某个条件筛选出符合条件的记录,不符合条件的记录不会出现在结果集中,即内连接只连接匹配的行。 +外连接 其结果集中不仅包含符合连接条件的行,而且还会包括左表、右表或两个表中 +的所有数据行,这三种情况依次称之为左外连接,右外连接,和全外连接。 + +左外连接,也称左连接,左表为主表,左表中的所有记录都会出现在结果集中,对于那些在右表中并没有匹配的记录,仍然要显示,右边对应的那些字段值以NULL来填充。右外连接,也称右连接,右表为主表,右表中的所有记录都会出现在结果集中。左连接和右连接可以互换,MySQL目前还不支持全外连接。 + +#### 48.Myql中的事务回滚机制概述 + +事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位,事务回滚是指将该事务已经完成的对数据库的更新操作撤销。 + +要同时修改数据库中两个不同表时,如果它们不是一个事务的话,当第一个表修改完,可能第二个表修改过程中出现了异常而没能修改,此时就只有第二个表依旧是未修改之前的状态,而第一个表已经被修改完毕。而当你把它们设定为一个事务的时候,当第一个表修改完,第二表修改出现异常而没能修改,第一个表和第二个表都要回到未修改的状态,这就是所谓的事务回滚 + +#### 49.SQL语言包括哪几部分?每部分都有哪些操作关键字? + +SQL语言包括数据定义(DDL)、数据操纵(DML),数据控制(DCL)和数据查询(DQL)四个部分。 + +数据定义:Create Table,Alter Table,Drop Table, Craete/Drop Index等 + +数据操纵:Select ,insert,update,delete, + +数据控制:grant,revoke + +数据查询:select + +#### 50.完整性约束包括哪些? + +数据完整性(Data Integrity)是指数据的精确(Accuracy)和可靠性(Reliability)。 + +**分为以下四类:** + +1) 实体完整性:规定表的每一行在表中是惟一的实体。 + +2) 域完整性:是指表中的列必须满足某种特定的数据类型约束,其中约束又包括取值范围、精度等规定。 + +3) 参照完整性:是指两个表的主关键字和外关键字的数据应一致,保证了表之间的数据的一致性,防止了数据丢失或无意义的数据在数据库中扩散。 + +4) 用户定义的完整性:不同的关系数据库系统根据其应用环境的不同,往往还需要一些特殊的约束条件。用户定义的完整性即是针对某个特定关系数据库的约束条件,它反映某一具体应用必须满足的语义要求。 + +与表有关的约束:包括列约束(NOT NULL(非空约束))和表约束(PRIMARY KEY、foreign key、check、UNIQUE) 。 + +#### 参考 + +https://zhuanlan.zhihu.com/p/59838091 + +https://blog.csdn.net/m0_45270667/article/details/108950184 + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git a/Docs/Mybatis.md b/Mybatis.md similarity index 84% rename from Docs/Mybatis.md rename to Mybatis.md index 5e1579c..6b35aaf 100644 --- a/Docs/Mybatis.md +++ b/Mybatis.md @@ -221,24 +221,34 @@ EhCache是一个纯牌的 Java进程内的缓存框架,具有快速、精干 - 快速。 - 简单。 - 多种缓存策略 。 - - 缓存数据有内存和磁盘两级,无须担心容量问题 。 - - 缓存数据会在虚拟机重启 的过程中写入磁盘。 - - 可 以通过 RMI、可插入 API 等方式进行分布式缓存。 - - .具有缓存和缓存管理器的侦 昕接口。 - - 支持多缓存管理器实例 以及一个实例的多个缓存区域。 -参考: +#### 29.说一下resultMap和resultType + +resultmap是手动提交,人为提交,resulttype是自动提交 +MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType是直接表示返回类型的,而resultMap则是对外部ResultMap的引用,但是resultType跟resultMap不能同时存在。 +在MyBatis进行查询映射时,其实查询出来的每一个属性都是放在一个对应的Map里面的,其中键是属性名,值则是其对应的值。 +1.当提供的返回类型属性是resultType时,MyBatis会将Map里面的键值对取出赋给resultType所指定的对象对应的属性。所以其实MyBatis的每一个查询映射的返回类型都是ResultMap,只是当提供的返回类型属性是resultType的时候,MyBatis对自动的给把对应的值赋给resultType所指定对象的属性。 +2.当提供的返回类型是resultMap时,因为Map不能很好表示领域模型,就需要自己再进一步的把它转化为对应的对象,这常常在复杂查询中很有作用。 + +#### 30.Mybatis动态sql有什么用?执行原理?有哪些动态sql? + +有九种动态sql标签:trim,where,set,foreach,if,choose,when,bind,otherwise +Mybatis的动态sql可以在xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值,完成逻辑判断并动态拼接sql的功能! + +#### 参考 + +《 MyBatis从入门到精通》 -- 《 MyBatis从入门到精通》 +《深入浅出 MyBatis技术原理与实战》 -- 《深入浅出 MyBatis技术原理与实战》 +《MyBatis技术》 -- 《MyBatis技术》 +https://zhuanlan.zhihu.com/p/60257737 ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) diff --git a/Netty.md b/Netty.md new file mode 100644 index 0000000..0140557 --- /dev/null +++ b/Netty.md @@ -0,0 +1,587 @@ +## Netty + +#### 1.Netty的特点? + +- 一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持 +- 使用更高效的socket底层,对epoll空轮询引起的cpu占用飙升在内部进行了处理,避免了直接使用NIO的陷阱,简化了NIO的处理方式。 +- 采用多种decoder/encoder 支持,对TCP粘包/分包进行自动化处理 +- 可使用接受/处理线程池,提高连接效率,对重连、心跳检测的简单支持 +- 可配置IO线程数、TCP参数, TCP接收和发送缓冲区使用直接内存代替堆内存,通过内存池的方式循环利用ByteBuf +- 通过引用计数器及时申请释放不再引用的对象,降低了GC频率 +- 使用单线程串行化的方式,高效的Reactor线程模型 +- 大量使用了volitale、使用了CAS和原子类、线程安全类的使用、读写锁的使用 + +#### 2.Netty的线程模型? + +- Netty通过Reactor模型基于多路复用器接收并处理用户请求,内部实现了两个线程池,boss线程池和work线程池,其中boss线程池的线程负责处理请求的accept事件,当接收到accept事件的请求时,把对应的socket封装到一个NioSocketChannel中,并交给work线程池,其中work线程池负责请求的read和write事件,由对应的Handler处理。 +- 单线程模型:所有I/O操作都由一个线程完成,即多路复用、事件分发和处理都是在一个Reactor线程上完成的。既要接收客户端的连接请求,向服务端发起连接,又要发送/读取请求或应答/响应消息。一个NIO 线程同时处理成百上千的链路,性能上无法支撑,速度慢,若线程进入死循环,整个程序不可用,对于高负载、大并发的应用场景不合适。 +- 多线程模型:有一个NIO 线程(Acceptor) 只负责监听服务端,接收客户端的TCP 连接请求;NIO 线程池负责网络IO 的操作,即消息的读取、解码、编码和发送;1 个NIO 线程可以同时处理N 条链路,但是1 个链路只对应1 个NIO 线程,这是为了防止发生并发操作问题。但在并发百万客户端连接或需要安全认证时,一个Acceptor 线程可能会存在性能不足问题。 +- 主从多线程模型:Acceptor 线程用于绑定监听端口,接收客户端连接,将SocketChannel 从主线程池的Reactor 线程的多路复用器上移除,重新注册到Sub 线程池的线程上,用于处理I/O 的读写等操作,从而保证mainReactor只负责接入认证、握手等操作; + +#### 3.TCP 粘包/拆包的原因及解决方法? + +- TCP是以流的方式来处理数据,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送。 +- TCP粘包/分包的原因: + - 应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象,而应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包现象; + - 进行MSS大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆包 + - 以太网帧的payload(净荷)大于MTU(1500字节)进行ip分片。 +- 解决方法 + - 消息定长:FixedLengthFrameDecoder类 + - 包尾增加特殊字符分割:行分隔符类:LineBasedFrameDecoder或自定义分隔符类 :DelimiterBasedFrameDecoder + - 将消息分为消息头和消息体:LengthFieldBasedFrameDecoder类。分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。 + +#### 4.了解哪几种序列化协议? + +- 序列化(编码)是将对象序列化为二进制形式(字节数组),主要用于网络传输、数据持久化等;而反序列化(解码)则是将从网络、磁盘等读取的字节数组还原成原始对象,主要用于网络传输对象的解码,以便完成远程调用。 +- 影响序列化性能的关键因素:序列化后的码流大小(网络带宽的占用)、序列化的性能(CPU资源占用);是否支持跨语言(异构系统的对接和开发语言切换)。 +- Java默认提供的序列化:无法跨语言、序列化后的码流太大、序列化的性能差 +- XML,优点:人机可读性好,可指定元素或特性的名称。缺点:序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息;只能序列化公共属性和字段;不能序列化方法;文件庞大,文件格式复杂,传输占带宽。适用场景:当做配置文件存储数据,实时数据转换。 +- JSON,是一种轻量级的数据交换格式,优点:兼容性高、数据格式比较简单,易于读写、序列化后数据较小,可扩展性好,兼容性好、与XML相比,其协议比较简单,解析速度比较快。缺点:数据的描述性比XML差、不适合性能要求为ms级别的情况、额外空间开销比较大。适用场景(可替代XML):跨防火墙访问、可调式性要求高、基于Web browser的Ajax请求、传输数据量相对小,实时性要求相对低(例如秒级别)的服务。 +- Fastjson,采用一种“假定有序快速匹配”的算法。优点:接口简单易用、目前java语言中最快的json库。缺点:过于注重快,而偏离了“标准”及功能性、代码质量不高,文档不全。适用场景:协议交互、Web输出、Android客户端 +- Thrift,不仅是序列化协议,还是一个RPC框架。优点:序列化后的体积小, 速度快、支持多种语言和丰富的数据类型、对于数据字段的增删具有较强的兼容性、支持二进制压缩编码。缺点:使用者较少、跨防火墙访问时,不安全、不具有可读性,调试代码时相对困难、不能与其他传输层协议共同使用(例如HTTP)、无法支持向持久层直接读写数据,即不适合做数据持久化序列化协议。适用场景:分布式系统的RPC解决方案 +- Avro,Hadoop的一个子项目,解决了JSON的冗长和没有IDL的问题。优点:支持丰富的数据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可压缩的二进制数据形式、可以实现远程过程调用RPC、支持跨编程语言实现。缺点:对于习惯于静态类型语言的用户不直观。适用场景:在Hadoop中做Hive、Pig和MapReduce的持久化数据格式。 +- Protobuf,将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性。优点:序列化后码流小,性能高、结构化数据存储格式(XML JSON等)、通过标识字段的顺序,可以实现协议的前向兼容、结构化的文档更容易管理和维护。缺点:需要依赖于工具生成代码、支持的语言相对较少,官方只支持Java 、C++ 、python。适用场景:对性能要求高的RPC调用、具有良好的跨防火墙的访问属性、适合应用层对象的持久化 +- 其它 + - protostuff 基于protobuf协议,但不需要配置proto文件,直接导包即可 + - Jboss marshaling 可以直接序列化java类, 无须实java.io.Serializable接口 + - Message pack 一个高效的二进制序列化格式 + - Hessian 采用二进制协议的轻量级remoting onhttp工具 + - kryo 基于protobuf协议,只支持java语言,需要注册(Registration),然后序列化(Output),反序列化(Input) + +#### 5.如何选择序列化协议? + +- 具体场景 + - 对于公司间的系统调用,如果性能要求在100ms以上的服务,基于XML的SOAP协议是一个值得考虑的方案。 + - 基于Web browser的Ajax,以及Mobile app与服务端之间的通讯,JSON协议是首选。对于性能要求不太高,或者以动态类型语言为主,或者传输数据载荷很小的的运用场景,JSON也是非常不错的选择。 + - 对于调试环境比较恶劣的场景,采用JSON或XML能够极大的提高调试效率,降低系统开发成本。 + - 当对性能和简洁性有极高要求的场景,Protobuf,Thrift,Avro之间具有一定的竞争关系。 + - 对于T级别的数据的持久化应用场景,Protobuf和Avro是首要选择。如果持久化后的数据存储在hadoop子项目里,Avro会是更好的选择。 + - 对于持久层非Hadoop项目,以静态类型语言为主的应用场景,Protobuf会更符合静态类型语言工程师的开发习惯。由于Avro的设计理念偏向于动态类型语言,对于动态语言为主的应用场景,Avro是更好的选择。 + - 如果需要提供一个完整的RPC解决方案,Thrift是一个好的选择。 + - 如果序列化之后需要支持不同的传输层协议,或者需要跨防火墙访问的高性能场景,Protobuf可以优先考虑。 +- protobuf的数据类型有多种:bool、double、float、int32、int64、string、bytes、enum、message。protobuf的限定符:required: 必须赋值,不能为空、optional:字段可以赋值,也可以不赋值、repeated: 该字段可以重复任意次数(包括0次)、枚举;只能用指定的常量集中的一个值作为其值; +- protobuf的基本规则:每个消息中必须至少留有一个required类型的字段、包含0个或多个optional类型的字段;repeated表示的字段可以包含0个或多个数据;[1,15]之内的标识号在编码的时候会占用一个字节(常用),[16,2047]之内的标识号则占用2个字节,标识号一定不能重复、使用消息类型,也可以将消息嵌套任意多层,可用嵌套消息类型来代替组。 +- protobuf的消息升级原则:不要更改任何已有的字段的数值标识;不能移除已经存在的required字段,optional和repeated类型的字段可以被移除,但要保留标号不能被重用。新添加的字段必须是optional或repeated。因为旧版本程序无法读取或写入新增的required限定符的字段。 +- 编译器为每一个消息类型生成了一个.java文件,以及一个特殊的Builder类(该类是用来创建消息类接口的)。如:UserProto.User.Builder builder = UserProto.User.newBuilder();builder.build(); +- Netty中的使用:ProtobufVarint32FrameDecoder 是用于处理半包消息的解码类;ProtobufDecoder(UserProto.User.getDefaultInstance())这是创建的UserProto.java文件中的解码类;ProtobufVarint32LengthFieldPrepender 对protobuf协议的消息头上加上一个长度为32的整形字段,用于标志这个消息的长度的类;ProtobufEncoder 是编码类 +- 将StringBuilder转换为ByteBuf类型:copiedBuffer()方法 + +#### 6.Netty的零拷贝实现? + +- Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。堆内存多了一次内存拷贝,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。ByteBuffer由ChannelConfig分配,而ChannelConfig创建ByteBufAllocator默认使用Direct Buffer +- CompositeByteBuf 类可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。addComponents方法将 header 与 body 合并为一个逻辑上的 ByteBuf, 这两个 ByteBuf 在CompositeByteBuf 内部都是单独存在的, CompositeByteBuf 只是逻辑上是一个整体 +- 通过 FileRegion 包装的FileChannel.tranferTo方法 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环write方式导致的内存拷贝问题。 +- 通过 wrap方法, 我们可以将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操作。 +- Selector BUG:若Selector的轮询结果为空,也没有wakeup或新消息处理,则发生空轮询,CPU使用率100%, +- Netty的解决办法:对Selector的select操作周期进行统计,每完成一次空的select操作进行一次计数,若在某个周期内连续发生N次空轮询,则触发了epoll死循环bug。重建Selector,判断是否是其他线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上去除注册,重新注册到新的Selector上,并将原来的Selector关闭。 + +#### 7.Netty的高性能表现在哪些方面? + +- **心跳**,对服务端:会定时清除闲置会话inactive(netty5),对客户端:用来检测会话是否断开,是否重来,检测网络延迟,其中idleStateHandler类 用来检测会话状态 +- **串行无锁化设计**,即消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁。表面上看,串行化设计似乎CPU利用率不高,并发程度不够。但是,通过调整NIO线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。 +- **可靠性**,链路有效性检测:链路空闲检测机制,读/写空闲超时机制;内存保护机制:通过内存池重用ByteBuf;ByteBuf的解码保护;优雅停机:不再接收新消息、退出前的预处理操作、资源的释放操作。 +- **Netty安全性**:支持的安全协议:SSL V2和V3,TLS,SSL单向认证、双向认证和第三方CA认证。 +- **高效并发编程的体现**:volatile的大量、正确使用;CAS和原子类的广泛使用;线程安全容器的使用;通过读写锁提升并发性能。IO通信性能三原则:传输(AIO)、协议(Http)、线程(主从多线程) +- **流量整型**的作用(变压器):防止由于上下游网元性能不均衡导致下游网元被压垮,业务流中断;防止由于通信模块接受消息过快,后端业务线程处理不及时导致撑死问题。 +- **TCP参数配置**:SO_RCVBUF和SO_SNDBUF:通常建议值为128K或者256K;SO_TCPNODELAY:NAGLE算法通过将缓冲区内的小封包自动相连,组成较大的封包,阻止大量小封包的发送阻塞网络,从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法; + +#### 8.客户端关闭的时候会抛出异常,死循环 + +解决方案 + + + int read = channel.read(buffer); + if(read > 0){ + byte[] data = buffer.array(); + String msg = new String(data).trim(); + System.out.println(“服务端收到信息:” + msg); + //回写数据 + ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes()); + channel.write(outBuffer);// 将消息回送给客户端 + }else{ + System.out.println("客户端关闭"); + key.cancel(); + } + + +#### 9、selector.select();阻塞,那为什么说nio是非阻塞的IO? + +``` +selector.select() +selector.select(1000);不阻塞 会继续往下执行程序 +selector.wakeup();也可以唤醒selector 继续往下执行程序 +selector.selectNow();也可以立马返回程序 死循环 +``` + +#### 10、SelectionKey.OP_WRITE是代表什么意思 + +OP_WRITE表示底层缓冲区是否有空间,是则响应返还true +netty版本大致版本分为 netty3.x 和 netty4.x、netty5.x + +netty可以运用在那些领域? + +#### 11.分布式进程通信 + +例如: hadoop、dubbo、akka等具有分布式功能的框架,底层RPC通信都是基于netty实现的,这些框架使用的版本通常都还在用netty3.x + +#### 12、游戏服务器开发 + +最新的游戏服务器有部分公司可能已经开始采用netty4.x 或 netty5.x + +#### 13、netty服务端hello world案例 + +``` +SimpleChannelHandler 处理消息接收和写 +{ +messageReceived接收消息 + +channelConnected新连接,通常用来检测IP是否是黑名单 + +channelDisconnected链接关闭,可以再用户断线的时候清楚用户的缓存数据等 +} +``` + +#### 14、netty客户端hello world案例 + +channelDisconnected与channelClosed的区别? + +channelDisconnected只有在连接建立后断开才会调用 +channelClosed无论连接是否成功都会调用关闭资源 + +#### 15.一个NIO是不是只能有一个selector? + +不是,一个系统可以有多个selector + +#### 16.selector是不是只能注册一个ServerSocketChannel? + +不是,可以注册多个 +一个thread + 队列 == 一个单线程线程池 =====> 线程安全的,任务是线性串行执行的 +线程安全,不会产生阻塞效应 ,使用对象组 +线程不安全,会产生阻塞效应, 使用对象池 + +#### 17.心跳其实就是一个普通的请求,特点数据简单,业务也简单 + +心跳对于服务端来说,定时清除闲置会话inactive(netty5) channelclose(netty3) +心跳对客户端来说,用来检测会话是否断开,是否重连! 用来检测网络延时! + +#### 18.Netty IdleStateHandler出现问题-我是否以错误的方式对其进行了测试? + +我有一个玩具Netty服务器,并且尝试在客户端的通道未发生任何事件时向其发送心跳消息。我正在通过telnet到服务器,编写消息然后不发送任何内容来对此进行测试,但是我听不到任何声音! + +安慰: + +``` +>>telnet localhost 6969 +Trying 127.0.0.1... +Connected to localhost. +Escape character is '^]'. +>>foo +Did you say 'foo'? +``` + +MyPipelineFactory.java + +``` +public class MyPipelineFactory implements ChannelPipelineFactory { + private final Timer timer; + private static final ChannelHandler stringDecoder = new StringDecoder(); + private static final ChannelHandler stringEncoder = new StringEncoder(); + private final ChannelHandler idleStateHandler; + + public MyPipelineFactory(Timer t) { + this.timer = t; + this.idleStateHandler = new IdleStateHandler(timer, 5, 5, 5); + } + + public ChannelPipeline getPipeline() { + // create default pipeline from static method + ChannelPipeline pipeline = Channels.pipeline(); + pipeline.addLast("idleStateHandler", this.idleStateHandler); // heartbeat + pipeline.addLast("framer", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter())); + //pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024,0,1)); // get header from message + pipeline.addLast("stringDecoder", stringDecoder); + pipeline.addLast("stringEncoder", stringEncoder); + pipeline.addLast("ServerHandler", new ServerHandler()); // goes at the end + + return pipeline; + } +} +``` + +HeartbeatHandler.java + +``` +public class HeartbeatHandler extends IdleStateAwareChannelHandler { + + @Override + public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) { + if (e.getState() == IdleState.READER_IDLE) { + System.out.println("Reader idle, closing channel"); + //e.getChannel().close(); + e.getChannel().write("heartbeat-reader_idle"); + } + else if (e.getState() == IdleState.WRITER_IDLE) { + System.out.println("Writer idle, sending heartbeat"); + e.getChannel().write("heartbeat-writer_idle"); + } + else if (e.getState() == IdleState.ALL_IDLE) { + System.out.println("All idle, sending heartbeat"); + e.getChannel().write("heartbeat-all_idle"); + } + } +} +``` + +------ + +固定: + +我忘记了HeartbeatHandler,它需要IdleStateHandler(这部分对我来说并不明显)。这样可行。 + +``` +public class MyPipelineFactory implements ChannelPipelineFactory { + private final Timer timer; + private static final ChannelHandler stringDecoder = new StringDecoder(); + private static final ChannelHandler stringEncoder = new StringEncoder(); + private final ChannelHandler idleStateHandler; + private final ChannelHandler heartbeatHandler; + + public MyPipelineFactory(Timer t) { + this.timer = t; + this.idleStateHandler = new IdleStateHandler(timer, 5, 5, 5); + this.heartbeatHandler = new HeartbeatHandler(); + } + + public ChannelPipeline getPipeline() { + // create default pipeline from static method + ChannelPipeline pipeline = Channels.pipeline(); + pipeline.addLast("idleStateHandler", this.idleStateHandler); + pipeline.addLast("heartbeatHandler", this.heartbeatHandler); // heartbeat + pipeline.addLast("framer", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter())); + //pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024,0,1)); // get header from message + pipeline.addLast("stringDecoder", stringDecoder); + pipeline.addLast("stringEncoder", stringEncoder); + pipeline.addLast("ServerHandler", new ServerHandler()); // goes at the end + + return pipeline; + } +} +``` + +#### 19.Netty如何使用线程池? + +您能解释一下Netty如何使用线程池工作吗?我是否正确理解,线程池有两种:老板线程和工人线程。老板用于执行I / +O,而worker用于调用用户回调(messageReceived)来处理数据? + + + +这是来自NioServerSocketChannelFactory文档 + +> 一个ServerSocketChannelFactory,它创建一个基于NIO的服务器端ServerSocketChannel。它利用NIO引入的非阻塞I +> / O模式来有效地服务许多并发连接。 +> +> 线程如何工作 +> NioServerSocketChannelFactory中有两种类型的线程:一个是老板线程,另一个是工作线程。 +> +> 老板线程 +> +> 每个绑定的ServerSocketChannel都有自己的老板线程。例如,如果您打开了两个服务器端口(例如80和443),则将有两个老板线程。Boss线程接受传入的连接,直到未绑定端口。一旦成功接受了连接,老板线程就将接受的Channel传递给NioServerSocketChannelFactory管理的工作线程之一。 +> +> 工作线程 +> 一个NioServerSocketChannelFactory可以具有一个或多个工作线程。工作线程以非阻塞模式对一个或多个通道执行非阻塞读写。 + +在Nio模型中,bossThread照顾所有有界套接字(监听套接字),workerThread照顾Accepted- +socket(包括IO和调用messageMethod等接收事件的方法)。 + +#### 20.Netty IllegalReferenceCountException + +尽管我的业务逻辑没有问题,但事实证明我没有使用Netty +`ByteBuf`。更新要使用的测试代码后`ByteBuf`,我遇到了[IllegalReferenceCountException](http://netty.io/wiki/reference-counted-objects.html)的无尽循环。我承认对Netty还是陌生的,但这并不能证明在手动分配和释放资源的日子里回来。创建GC就是为了避免这种混乱。迪斯科,有人吗?那贝尔底呢? + +``` +public class StringDecoder extends AbstractDecoder { + private static final IntPredicate NEWLINE_DELIMITER = b -> b == '\n' || b == '\r'; + + @Override + public Flux decode(Publisher publisher, ResolvableType elementType, MimeType mimeType, Map hints) { + return Flux.from(publisher) + .scan(Tuples., Optional>of(Flux.empty(), Optional.empty()), + (acc, buffer) -> { + List results = new ArrayList<>(); + int startIdx = 0, endIdx = 0, limit = buffer.readableByteCount(); + Optional incomplete = acc.getT2(); + + while (startIdx < limit && endIdx != -1) { + endIdx = buffer.indexOf(NEWLINE_DELIMITER, startIdx); + int length = (endIdx == -1 ? limit : endIdx) - startIdx; + DataBuffer slice = buffer.slice(startIdx, length); + DataBuffer tmp = incomplete.map(b -> b.write(slice)) + .orElse(buffer.factory().allocateBuffer(length).write(slice)); + tmp = DataBufferUtils.retain(tmp); + + if (endIdx != -1) { + startIdx = endIdx + 1; + results.add(tmp); + incomplete = Optional.empty(); + } else { + incomplete = Optional.of(tmp); + } + } + releaseBuffer(buffer); + + return Tuples.of(Flux.fromIterable(results), incomplete); + }) + .flatMap(t -> { + t.getT2().ifPresent(this::releaseBuffer); + return t.getT1(); + }) + .map(buffer -> { + // charset resolution should in general use supplied mimeType + String s = UTF_8.decode(buffer.asByteBuffer()).toString(); + releaseBuffer(buffer); + + return s; + }) + .log(); + } + + private void releaseBuffer(DataBuffer buffer) { + boolean release = DataBufferUtils.release(buffer); + if (release) { + System.out.println("Buffer was released."); + } + } +} + +public class StringDecoderTest { + private StringDecoder stringDecoder = new StringDecoder(); + DataBufferFactory dataBufferFactory = new NettyDataBufferFactory(UnpooledByteBufAllocator.DEFAULT); + + @Test + public void testDecode() { + Flux pub = Flux.just("abc\n", "abc", "def\n", "abc", "def\nxyz\n", "abc", "def", "xyz\n") + .map(s -> dataBufferFactory.wrap(s.getBytes(UTF_8))); + + StepVerifier.create(stringDecoder.decode(pub, null, null, null)) + .expectNext("abc", "abcdef", "abcdef", "xyz", "abcdefxyz") + .verifyComplete(); + } +} +``` + +我不断得到: + +``` +[ERROR] (main) onError(io.netty.util.IllegalReferenceCountException: refCnt: 0) +[ERROR] (main) - io.netty.util.IllegalReferenceCountException: refCnt: 0 +io.netty.util.IllegalReferenceCountException: refCnt: 0 + at io.netty.buffer.AbstractByteBuf.ensureAccessible(AbstractByteBuf.java:1415) + at io.netty.buffer.UnpooledHeapByteBuf.nioBuffer(UnpooledHeapByteBuf.java:314) + at io.netty.buffer.AbstractUnpooledSlicedByteBuf.nioBuffer(AbstractUnpooledSlicedByteBuf.java:434) + at io.netty.buffer.CompositeByteBuf.nioBuffers(CompositeByteBuf.java:1496) + at io.netty.buffer.CompositeByteBuf.nioBuffer(CompositeByteBuf.java:1468) + at io.netty.buffer.AbstractByteBuf.nioBuffer(AbstractByteBuf.java:1205) + at org.springframework.core.io.buffer.NettyDataBuffer.asByteBuffer(NettyDataBuffer.java:234) + at org.abhijitsarkar.java.StringDecoder.lambda$decode$4(StringDecoder.java:61) +``` + +工作代码: + +``` +public class StringDecoder extends AbstractDecoder { + private static final IntPredicate NEWLINE_DELIMITER = b -> b == '\n' || b == '\r'; + + @Override + public Flux decode(Publisher publisher, ResolvableType elementType, MimeType mimeType, Map hints) { + DataBuffer incomplete = new NettyDataBufferFactory(UnpooledByteBufAllocator.DEFAULT).allocateBuffer(0); + + return Flux.from(publisher) + .scan(Tuples., DataBuffer>of(Flux.empty(), retain(incomplete)), + (acc, buffer) -> { + List results = new ArrayList<>(); + int startIdx = 0, endIdx = 0, limit = buffer.readableByteCount(); + + while (startIdx < limit && endIdx != -1) { + endIdx = buffer.indexOf(NEWLINE_DELIMITER, startIdx); + int length = (endIdx == -1 ? limit : endIdx) - startIdx; + + DataBuffer slice = buffer.slice(startIdx, length); + byte[] slice1 = new byte[length]; + slice.read(slice1, 0, slice1.length); + + if (endIdx != -1) { + byte[] slice2 = new byte[incomplete.readableByteCount()]; + incomplete.read(slice2, 0, slice2.length); + // call retain to match release during decoding to string later + results.add(retain( + incomplete.factory().allocateBuffer() + .write(slice2) + .write(slice1) + )); + startIdx = endIdx + 1; + } else { + incomplete.write(slice1); + } + } + + return Tuples.of(Flux.fromIterable(results), incomplete); + }) + .flatMap(Tuple2::getT1) + .map(buffer -> { + // charset resolution should in general use supplied mimeType + String s = UTF_8.decode(buffer.asByteBuffer()).toString(); + + return s; + }) + .doOnTerminate(() -> release(incomplete)) + .log(); + } +} +``` + +该代码可能更[简洁](https://jira.spring.io/browse/SPR-16351)一些,但是适用于Spring bug +[SPR-16351](https://jira.spring.io/browse/SPR-16351)。 + +#### 21.Java Netty负载测试问题 + +我编写了使用文本协议接受连接和轰炸消息(〜100字节)的服务器,并且我的实现能够与3rt客户端发送约400K / +sec的回送消息。我为此任务选择了Netty,即SUSE 11 RealTime,JRockit +RTS。但是,当我开始基于Netty开发自己的客户端时,吞吐量却急剧下降(从400K msg / sec降低到1.3K msg / +sec)。客户端的代码非常简单。能否请您提供建议或示例,说明如何编写更有效的客户。实际上,我实际上更关心延迟,但是从吞吐量测试开始,我认为在环回中以1.5Kmsg +/ sec的速度正常是不正常的。PS客户端的目的只是接收来自服务器的消息,很少发送心跳信号。 + + + +``` +Client.java + +public class Client { + +private static ClientBootstrap bootstrap; +private static Channel connector; +public static boolean start() +{ + ChannelFactory factory = + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + ExecutionHandler executionHandler = new ExecutionHandler( new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576)); + + bootstrap = new ClientBootstrap(factory); + + bootstrap.setPipelineFactory( new ClientPipelineFactory() ); + + bootstrap.setOption("tcpNoDelay", true); + bootstrap.setOption("keepAlive", true); + bootstrap.setOption("receiveBufferSize", 1048576); + ChannelFuture future = bootstrap + .connect(new InetSocketAddress("localhost", 9013)); + if (!future.awaitUninterruptibly().isSuccess()) { + System.out.println("--- CLIENT - Failed to connect to server at " + + "localhost:9013."); + bootstrap.releaseExternalResources(); + return false; + } + + connector = future.getChannel(); + + return connector.isConnected(); +} +public static void main( String[] args ) +{ + boolean started = start(); + if ( started ) + System.out.println( "Client connected to the server" ); +} + +} + +ClientPipelineFactory.java + +public class ClientPipelineFactory implements ChannelPipelineFactory{ + +private final ExecutionHandler executionHandler; +public ClientPipelineFactory( ExecutionHandler executionHandle ) +{ + this.executionHandler = executionHandle; +} +@Override +public ChannelPipeline getPipeline() throws Exception { + ChannelPipeline pipeline = pipeline(); + pipeline.addLast("framer", new DelimiterBasedFrameDecoder( + 1024, Delimiters.lineDelimiter())); + pipeline.addLast( "executor", executionHandler); + pipeline.addLast("handler", new MessageHandler() ); + + return pipeline; +} + +} + +MessageHandler.java +public class MessageHandler extends SimpleChannelHandler{ + +long max_msg = 10000; +long cur_msg = 0; +long startTime = System.nanoTime(); +@Override +public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { + cur_msg++; + + if ( cur_msg == max_msg ) + { + System.out.println( "Throughput (msg/sec) : " + max_msg* NANOS_IN_SEC/( System.nanoTime() - startTime ) ); + cur_msg = 0; + startTime = System.nanoTime(); + } +} + +@Override +public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { + e.getCause().printStackTrace(); + e.getChannel().close(); +} + +} +``` + +更新。在服务器端,存在一个定期线程,该线程写入已接受的客户端通道。而且该频道很快就无法写入。更新N2。在管道中添加了OrderedMemoryAwareExecutor,但是吞吐量仍然很低(大约4k +msg / sec) + +固定。我将执行程序放在整个管道堆栈的前面,结果成功了! + + + +如果服务器正在发送固定大小(〜100字节)的消息,则可以将ReceiveBufferSizePredictor设置为客户端引导程序,这将优化读取 + +``` +bootstrap.setOption("receiveBufferSizePredictorFactory", + new AdaptiveReceiveBufferSizePredictorFactory(MIN_PACKET_SIZE, INITIAL_PACKET_SIZE, MAX_PACKET_SIZE)); +``` + +根据您发布的代码段:客户端的nio工作线程正在做管道中的所有事情,因此它将忙于解码和执行消息处理程序。您必须添加一个执行处理程序。 + +您已经说过,通道从服务器端变得不可写,因此您可能必须在服务器引导程序中调整水印大小。您可以定期监视写缓冲区大小(写队列大小),并确保由于消息无法写到网络而使通道变得不可写。可以通过以下类似的util类来完成。 + +``` +package org.jboss.netty.channel.socket.nio; + +import org.jboss.netty.channel.Channel; + +public final class NioChannelUtil { + public static long getWriteTaskQueueCount(Channel channel) { + NioSocketChannel nioChannel = (NioSocketChannel) channel; + return nioChannel.writeBufferSize.get(); + } +} +``` + +#### 22. + +#### 参考 + +https://www.cnblogs.com/xuxinstyle/p/9872915.html + +https://blog.csdn.net/qq_38772518/article/details/105834573 + +http://www.mianshigee.com/ \ No newline at end of file diff --git a/Docs/Nginx.md b/Nginx.md similarity index 73% rename from Docs/Nginx.md rename to Nginx.md index 47aca8b..32de863 100644 --- a/Docs/Nginx.md +++ b/Nginx.md @@ -1,10 +1,10 @@ ## Nginx -#### 1.什么是nginx? +#### 1.什么是Nginx? Nginx是一个高性能的HTTP和反向代理服务器。同时也是一个 IMAP/POP3/SMTP 代理服务器。 官方网站:http://nginx.org。 -#### 2.nginx主要特征? +#### 2.Nginx主要特征? 处理静态文件,索引文件以及自动索引;打开文件描述符缓冲. 无缓存的反向代理加速,简单的负载均衡和容错. FastCGI,简单的负载均衡和容错.模块化的结构。包括 gzipping, byte ranges, chunked responses,以及 SSI-filter 等filter。如果由 FastCGI 或其它代理服务器处理单页中存在的多个 SSI,则这项处理可以并行 运行,而不需要相互等待。 @@ -24,7 +24,7 @@ Nginx 采用了一些 os 提供的最新特性如对 sendfile (Linux2.2+),acce 免费开源,可以做高并发负载均衡。 -#### 3.nginx 常用命令? +#### 3.Nginx 常用命令? 启动 nginx 。 停止 nginx -s stop 或 nginx -s quit 。 @@ -47,7 +47,7 @@ worker_connections 1024;#单个后台 worker process 进程的最大并发链接 } ``` -#### 5.nginx负载均衡几种算法? +#### 5.Nginx负载均衡几种算法? 5种。 @@ -122,7 +122,7 @@ location ^~/path/ { ``` -#### 13.nginx负载均衡实现过程? +#### 13.Nginx负载均衡实现过程? 首先在 http 模块中配置使用 upstream 模块定义后台的 webserver 的池子,名为 proxy-web,在池子中我们可以添加多台后台 webserver,其中状态 检查、调度算法都是在池子中配置;然后在 serverr 模块中定义虚拟主机,但是这个虚拟主 机不指定自己的 web 目录站点,它将使用 location 匹配 url 然后转发到上面定义好的 web 池子中,最后根据调度策略再转发到后台 web server 上 。 @@ -173,7 +173,49 @@ http { 坏处:但是由于是在服务器上进行压缩,会消耗服务器起源 -参考: +#### 17.Nginx配置文件nginx.conf有哪些属性模块? + +``` +worker_processes 1; # worker进程的数量 +events { # 事件区块开始 + worker_connections 1024; # 每个worker进程支持的最大连接数 +} # 事件区块结束 +http { # HTTP区块开始 + include mime.types; # Nginx支持的媒体类型库文件 + default_type application/octet-stream; # 默认的媒体类型 + sendfile on; # 开启高效传输模式 + keepalive_timeout 65; # 连接超时 + server { # 第一个Server区块开始,表示一个独立的虚拟主机站点 + listen 80; # 提供服务的端口,默认80 + server_name localhost; # 提供服务的域名主机名 + location / { # 第一个location区块开始 + root html; # 站点的根目录,相当于Nginx的安装目录 + index index.html index.htm; # 默认的首页文件,多个用空格分开 + } # 第一个location区块结果 + error_page 500502503504 /50x.html; # 出现对应的http状态码时,使用50x.html回应客户 + location = /50x.html { # location区块开始,访问50x.html + root html; # 指定对应的站点目录为html + } + } + ...... + +``` + +#### 18.Nginx静态资源? + + 静态资源访问,就是存放在nginx的html页面,我们也可以编写 + +#### 19.如何用Nginx解决前端跨域问题? + +- 使用Nginx转发请求。把跨域的接口写成调本域的接口,然后将这些接口转发到真正的请求地址。 + +#### 20.Nginx虚拟主机怎么配置? + +- 1、基于域名的虚拟主机,通过域名来区分虚拟主机——应用:外部网站 +- 2、基于端口的虚拟主机,通过端口来区分虚拟主机——应用:公司内部网站,外部网站的管理后台 +- 3、基于ip的虚拟主机。 + +#### 参考: - 《Nginx从入门到精通》 diff --git a/RabbitMQ.md b/RabbitMQ.md new file mode 100644 index 0000000..3ae64f6 --- /dev/null +++ b/RabbitMQ.md @@ -0,0 +1,215 @@ +## RabbitMQ + + + +#### 1、什么是 RabbitMQ?为什么使用 RabbitMQ? + +RabbitMQ 是一款开源的,Erlang 编写的,基于 AMQP 协议的,消息中间件; + +可以用它来:解耦、异步、削峰。 + +#### 2、RabbitMQ 有什么优缺点? + +优点:解耦、异步、削峰; + +缺点:降低了系统的稳定性:本来系统运行好好的,现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低; + +增加了系统的复杂性:加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,复杂性增大。 + +#### 3.rabbitmq 的使用场景 + +(1)服务间异步通信 + +(2)顺序消费 + +(3)定时任务 + +(4)请求削峰 + +#### 4.RabbitMQ基本概念 + + Broker: 简单来说就是消息队列服务器实体 +Exchange: 消息交换机,它指定消息按什么规则,路由到哪个队列 +Queue: 消息队列载体,每个消息都会被投入到一个或多个队列 +Binding: 绑定,它的作用就是把exchange和queue按照路由规则绑定起来 +Routing Key: 路由关键字,exchange根据这个关键字进行消息投递 +VHost: vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的 queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。 +Producer: 消息生产者,就是投递消息的程序 +Consumer: 消息消费者,就是接受消息的程序 +Channel: 消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务 +由Exchange、Queue、RoutingKey三个才能决定一个从Exchange到Queue的唯一的线路。 + +#### 5.RabbitMQ 中的 broker 是指什么?cluster 又是指什么? + +`broker` 是指一个或多个 `erlang node` 的逻辑分组,且 `node` 上运行着 `RabbitMQ` 应用程序。 +`cluster` 是在 `broker` 的基础之上,增加了 `node` 之间共享元数据的约束。 + +#### 6、RabbitMQ 概念里的 channel、exchange 和 queue 是逻辑概念,还是对应着进程实体?分别起什么作用? + +**Queue** 具有自己的 **erlang** 进程;**exchange** 内部实现为保存 binding 关系的查找表;channel 是实际进行路由工作的实体,即负责按照 routing_key 将 message 投递给 queue 。由 AMQP 协议描述可知,channel 是真实 TCP 连接之上的虚拟连接,所有 AMQP 命令都是通过 channel 发送的,且每一个 channel 有唯一的 ID。一个 channel 只能被单独一个操作系统线程使用,故投递到特定 channel 上的 message 是有顺序的。但一个操作系统线程上允许使用多个 channel 。 + +#### 7 vhost 是什么?起什么作用? + +`vhost` 可以理解为虚拟 `broker` ,即 `mini-RabbitMQ server`。其内部均含有独立的 `queue`、`exchange` 和 `binding` 等,但最最重要的是,其拥有独立的权限系统,可以做到 `vhost` 范围的用户控制。当然,从 `RabbitMQ` 的全局角度,`vhost` 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 `vhost` 中)。 + +#### 8. 消息基于什么传输? + +由于TCP连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制。 + +#### 9. 消息如何分发? + +若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。 + +#### 10. 消息怎么路由? + +从概念上来说,消息路由必须有三部分:**交换器**、**路由**、**绑定**。生产者把消息发布到交换器上;绑定决定了消息如何从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。 + +消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。 +通过队列路由键,可以把队列绑定到交换器上。 +消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则)。如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到任何队列,消息将进入 “黑洞”。 + +常用的交换器主要分为一下三种: + +- direct:如果路由键完全匹配,消息就被投递到相应的队列 +- fanout:如果交换器收到消息,将会广播到所有绑定的队列上 +- topic:可以使来自不同源头的消息能够到达同一个队列。使用topic交换器时,可以使用通配符。 + 比如:“*” 匹配特定位置的任意文本, “.” 把路由键分为了几部分,“#” 匹配所有规则等。 + 特别注意:发往topic交换器的消息不能随意的设置选择键(routing_key),必须是由"."隔开的一系列的标识符组成。 + +#### 11. 什么是元数据?元数据分为哪些类型?包括哪些内容?与 cluster 相关的元数据有哪些?元数据是如何保存的?元数据在 cluster 中是如何分布的? + +在非 `cluster` 模式下,元数据主要分为 `Queue` 元数据(queue 名字和属性等)、`Exchange`元数据(exchange 名字、类型和属性等)、`Binding` 元数据(存放路由关系的查找表)、`Vhost`元数据(vhost 范围内针对前三者的名字空间约束和安全属性设置)。 +在 `cluster` 模式下,还包括 cluster 中 node 位置信息和 node 关系信息。元数据按照 erlang node 的类型确定是仅保存于 RAM 中,还是同时保存在 RAM 和 disk 上。元数据在 cluster 中是全 node 分布的。 + +#### 12. 在单node 系统和多 node 构成的 cluster 系统中声明 queue、exchange ,以及进行 binding 会有什么不同? + +答:当你在单 node 上声明 queue 时,只要该 node 上相关元数据进行了变更,你就会得到 Queue.Declare-ok 回应;而在 cluster 上声明 queue ,则要求 cluster 上的全部 node 都要进行元数据成功更新,才会得到 Queue.Declare-ok 回应。另外,若 node 类型为 RAM node 则变更的数据仅保存在内存中,若类型为 disk node 则还要变更保存在磁盘上的数据。 + +死信队列&死信交换器:DLX 全称(Dead-Letter-Exchange),称之为死信交换器,当消息变成一个死信之后,如果这个消息所在的队列存在x-dead-letter-exchange参数,那么它会被发送到x-dead-letter-exchange对应值的交换器上,这个交换器就称之为死信交换器,与这个死信交换器绑定的队列就是死信队列。 + +#### 13. 如何确保消息正确地发送至RabbitMQ? + +**RabbitMQ**使用发送方确认模式,确保消息正确地发送到**RabbitMQ**。 +发送方确认模式:将信道设置成`confirm`模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID。一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID)。如果`RabbitMQ`发生内部错误从而导致消息丢失,会发送一条`nack`(not acknowledged,未确认)消息。发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。 + +#### 14. 如何确保消息接收方消费了消息? + +接收方消息确认机制:消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,`RabbitMQ`才能安全地把消息从队列中删除。这里并没有用到超时机制,`RabbitMQ`仅通过`Consumer`的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,`RabbitMQ`给了`Consumer`足够长的时间来处理消息。 + +下面罗列几种特殊情况: + +- 如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要根据bizId去重) +- 如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发更多的消息。 + +#### 15、如何保证 RabbitMQ 不被重复消费? + +先说为什么会重复消费:正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除; + +但是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。 + +针对以上问题,一个解决思路是:保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;保证消息等幂性; + +比如:在写入消息队列的数据做唯一标示,消费消息时,根据唯一标识判断是否消费过; + +#### 16、如何保证 RabbitMQ 消息的可靠传输? + +消息不可靠的情况可能是消息丢失,劫持等原因; + +丢失又分为:生产者丢失消息、消息列表丢失消息、消费者丢失消息; + +生产者丢失消息:从生产者弄丢数据这个角度来看,RabbitMQ 提供 transaction 和 confirm 模式来确保生产者不丢消息; + +transaction 机制就是说:发送消息前,开启事务(channel.txSelect()), 然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback()), 如果发送成功则提交事务(channel.txCommit())。然而,这种方式有个缺点:吞吐量下降; + +confirm 模式用的居多:一旦 channel 进入 confirm 模式,所有在该信道上发布的消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被投递到所有匹配的队列之后; + +rabbitMQ 就会发送一个 ACK 给生产者(包含消息的唯一 ID),这就使得生产者知道消息已经正确到达目的队列了; + +如果 rabbitMQ 没能处理该消息,则会发送一个 Nack 消息给你,你可以进行重试操作。 + +消息队列丢数据:消息持久化。 + +处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。 + +这个持久化配置可以和 confirm 机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个 Ack 信号。 + +这样,如果消息持久化磁盘之前,rabbitMQ 阵亡了,那么生产者收不到 Ack 信号,生产者会自动重发。 + +那么如何持久化呢? + +这里顺便说一下吧,其实也很容易,就下面两步 + +1. 将 queue 的持久化标识 durable 设置为 true, 则代表是一个持久的队列 +2. 发送消息的时候将 deliveryMode=2 + +这样设置以后,即使 rabbitMQ 挂了,重启后也能恢复数据 + +消费者丢失消息:消费者丢数据一般是因为采用了自动确认消息模式,改为手动确认消息即可! + +消费者在收到消息之后,处理消息之前,会自动回复 RabbitMQ 已收到消息; + +如果这时处理消息失败,就会丢失该消息; + +解决方案:处理消息成功后,手动回复确认消息。 + +#### 17、如何保证 RabbitMQ 消息的顺序性? + +单线程消费保证消息的顺序性;对消息进行编号,消费者处理消息是根据编号处理消息; + +#### 18. 死信队列和延迟队列的使用? + +**死信消息:** + +消息被拒绝(Basic.Reject或Basic.Nack)并且设置 requeue 参数的值为 false +消息过期了 +队列达到最大的长度 + +**过期消息:** + +在 rabbitmq 中存在2种方可设置消息的过期时间,第一种通过对队列进行设置,这种设置后,该队列中所有的消息都存在相同的过期时间,第二种通过对消息本身进行设置,那么每条消息的过期时间都不一样。如果同时使用这2种方法,那么以过期时间小的那个数值为准。当消息达到过期时间还没有被消费,那么那个消息就成为了一个 死信 消息。 + +**队列设置:**在队列申明的时候使用 x-message-ttl 参数,单位为 毫秒 + +**单个消息设置:**是设置消息属性的 expiration 参数的值,单位为 毫秒 + +**延时队列:**在rabbitmq中不存在延时队列,但是我们可以通过设置消息的过期时间和死信队列来模拟出延时队列。消费者监听死信交换器绑定的队列,而不要监听消息发送的队列。 + +------ + +有了以上的基础知识,我们完成以下需求: + +需求:用户在系统中创建一个订单,如果超过时间用户没有进行支付,那么自动取消订单。 + +分析: + +1、上面这个情况,我们就适合使用延时队列来实现,那么延时队列如何创建 +2、延时队列可以由 过期消息+死信队列 来时间 +3、过期消息通过队列中设置 x-message-ttl 参数实现 +4、死信队列通过在队列申明时,给队列设置 x-dead-letter-exchange 参数,然后另外申明一个队列绑定x-dead-letter-exchange对应的交换器。 + +#### 19. 使用了消息队列会有什么缺点? + +1.系统可用性降低:你想啊,本来其他系统只要运行好好的,那你的系统就是正常的。现在你非要加个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性降低 + +2.系统复杂性增加:要多考虑很多方面的问题,比如一致性问题、如何保证消息不被重复消费,如何保证保证消息可靠传输。因此,需要考虑的东西更多,系统复杂性增大。 + +#### 20. 多个消费者监听一个队列时,消息如何分发? + +- 轮询: 默认的策略,消费者轮流,平均地接收消息 +- 公平分发: 根据消费者的能力来分发消息,给空闲的消费者发送更多消息 + +当消费者有x条消息没有响应ACK时,不再给这个消费者发送消息 + +```java +channel.basicQos(int x) +``` + + + +#### 参考 + +https://www.cnblogs.com/woadmin/p/10537174.html + +https://blog.csdn.net/thinkwon/article/details/104588612/ + +https://www.cnblogs.com/coder-programming/p/12465314.html \ No newline at end of file diff --git a/Readme.md b/Readme.md index 1b36cf4..59686b2 100644 --- a/Readme.md +++ b/Readme.md @@ -1,31 +1,20 @@ -最近很多童鞋在到处搜集面试题,但是面试题都比较零散,所以博主整理了一下,前4个专题多来源于互联网,从第4部分往后,基本是小编从阅读电子书以及专栏学习所得。目前有17个专题。全文阅读时间可能几个小时,但是整理和创作要花的时间就要比阅读的时间多出至少五十倍,目前全文大概15万字左右,166页。 - -小编后面阅读电子书以及专栏等所得,请不要直接发到互联网申请原创,如果要发请注明来源。小编会持续的更新维护。大家可以加我微信进入群内,vx:baiseyumaoxx。一旦更新,我会通知大家。 - -并且还会整理成一个题库,本来想将md文件上传到github上,但由于文件太大有的都无法显示所以直接整理成一个PDF,供大家学习。 - -好了,来了,本手册目前为第一版,内容有以下板块: - -- [**Java基础**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Java基础.md) -- [**Java集合**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/java集合.md) -- [**异常&反射**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/%20异常%26反射.md) -- [**IO&NIO**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/IO%26NIO.md) -- [**多线程**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/多线程.md) -- [**JVM**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/JVM.md) -- [**Linux**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Linux.md) -- [**MySql**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/MySql.md) -- [**Spring**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Spring.md) -- [**Mybatis**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Mybatis.md) -- [**Nginx**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Nginx.md) -- [**Redis**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Redis.md) -- [**Dubbo**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Dubbo.md) -- [**SpringBoot**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/SpringBoot.md) -- [**Kafka**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Kafka.md) -- [**Spring Cloud**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/SpringCloud.md) -- [**简历**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/简历.md) +最近很多小伙伴问我要面试题,但面试题大多都比较零散,所以博主整理了一下,有一部分是从互联网收集整理所得,还有一部分是小咖自己从专栏、书籍学习所得。 +目前有31个专题,277页。全文阅读时间可能几个小时,但是整理和创作要花的时间就要比阅读的时间多出至少十倍。 + +小编后面阅读书籍以及专栏等所得的部分,请不要直接发到互联网申请原创,如果要发请注明来源。 +小编会持续的更新维护。大家可以加我微信进入群内,vx:baiseyumaoxx。一旦更新,我会通知大家。 + +手册是第二个版本了已经,原来是17个专题,新增了14个专题,希望对大家有用。 + +目前有如下专题: + +Java基础、Java集合、异常&反射、IO&NIO、Java多线程、JVM、Linux、MySql、Spring、Mybatis、Nginx、Redis、Dubbo、SpringBoot、Kafka、Spring Cloud、简历 + +Netty、计算机基础、MongoDB、JSP、Servlet、Elasticsearch、Web安全、Tomcat、Zookeeper、Maven、Git、RabbitMQ、Java8、JVM + 具体大家需要看的部分点击目录即可查看。pdf给大家准备好了。小编会持续更新... -扫描二维码回复:“面试”即可获取pdf +扫描二维码回复:“面试2”即可获取pdf -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) diff --git a/Docs/Redis.md "b/Redis-\344\270\212\345\215\267.md" similarity index 100% rename from Docs/Redis.md rename to "Redis-\344\270\212\345\215\267.md" diff --git "a/Redis-\344\270\213\345\215\267.md" "b/Redis-\344\270\213\345\215\267.md" new file mode 100644 index 0000000..5947af1 --- /dev/null +++ "b/Redis-\344\270\213\345\215\267.md" @@ -0,0 +1,359 @@ +## Redis + +#### 1.查看配置语法 + +``` +CONFIG GET CONFIG_SETTING_NAME +``` + +#### 2.获取所有配置项 + +``` +127.0.0.1:6379> config get * + 1) "dbfilename" + 2) "dump.rdb" + 3) "requirepass" + 4) "" + 5) "masterauth" + 6) "" + 7) "cluster-announce-ip" + 8) "" + 9) "unixsocket" + 10) "" + 11) "logfile" + 12) "" +... +``` + +#### 3.设置字符串 + +``` +127.0.0.1:6379> set xk www.javaxks.com +OK +``` + +#### 4.获取字符串 + +``` +127.0.0.1:6379> get xk +"www.javaxks.com" +``` + +#### 5.获取随机key + +``` +127.0.0.1:6379> randomkey +"xk" +``` + +#### 6.获取key存储的类型 + +``` +127.0.0.1:6379> randomkey +"xk" +``` + +#### 7.判断key是否存在 + +``` +127.0.0.1:6379> randomkey +"xk" +``` + +#### 8.修改key的名称 + +``` +127.0.0.1:6379> randomkey +"xk" +``` + +#### 9.返回key存储的字符串的长度 + +``` +127.0.0.1:6379> randomkey +"xk" +``` + +#### 10.同时设置多个kv对 + +``` +127.0.0.1:6379> mset date 5.21 mood haha +OK +``` + +#### 11.获取多个key的值 + +``` +127.0.0.1:6379> mget date mood +1) "5.21" +2) "haha" +``` + +#### 12.设置key 10秒后过期 + +``` +127.0.0.1:6379> expire date 10 +(integer) 1 +``` + +#### 13.查看当前还剩几秒过期 + +``` +127.0.0.1:6379> ttl date +(integer) 4 +``` + +#### 14.过期后获取key + +``` +127.0.0.1:6379> get date +(nil) +``` + +#### 15.列表最多可以存多少元素 + +2的32次方 - 1 + +#### 16.从左向列表中添加元素 + +``` +127.0.0.1:6379> lpush fruit apple +(integer) 1 +127.0.0.1:6379> lpush fruit strawberry +(integer) 2 +127.0.0.1:6379> lpush fruit pear +(integer) 3 +127.0.0.1:6379> lpush fruit orange +(integer) 4 +``` + +#### 17.获取列表中所有的元素 + +``` +127.0.0.1:6379> lrange fruit 0 -1 +1) "orange" +2) "pear" +3) "strawberry" +4) "apple" +``` + +#### 18.从左弹出元素 + +``` +127.0.0.1:6379> lpop fruit +"orange" +127.0.0.1:6379> lrange fruit 0 -1 +1) "pear" +2) "strawberry" +3) "apple" +``` + +#### 19.获取指定位置的元素 + +``` +127.0.0.1:6379> lindex fruit 2 +"apple" +``` + +#### 20.获取列表长度 + +``` +127.0.0.1:6379> llen fruit +(integer) 3 +``` + +#### 21.集合 + +set是string类型的无序集合,集合中的元素都是唯一的。 + +#### 22.向集合中添加元素 + +``` +127.0.0.1:6379> sadd books java +(integer) 1 +127.0.0.1:6379> sadd books python +(integer) 1 +127.0.0.1:6379> sadd books php +(integer) 1 +127.0.0.1:6379> sadd books go +(integer) 1 +127.0.0.1:6379> sadd books ruby +(integer) 1 +``` + +#### 23.获取集合中的元素 + +``` +127.0.0.1:6379> smembers books +1) "go" +2) "python" +3) "java" +4) "php" +5) "ruby" +``` + +#### 24.删除集合中的指定元素 + +``` +127.0.0.1:6379> srem books ruby +(integer) 1 +127.0.0.1:6379> smembers books +1) "php" +2) "java" +3) "python" +4) "go" +``` + +#### 25.zset类型 + +有序的集合 + +元素为string类型 + +元素唯一,不重复。每个元素有自己的权重。 + +#### 26.向有序集合中添加元素 + +``` +127.0.0.1:6379> zadd animal 1 monkey +(integer) 1 +127.0.0.1:6379> zadd animal 2 chicken +(integer) 1 +127.0.0.1:6379> zadd animal 3 mouse +(integer) 1 +127.0.0.1:6379> zadd animal 4 horse +(integer) 1 +``` + +#### 27.获取有序集合元素个数 + +``` +127.0.0.1:6379> zcard animal +(integer) 4 +``` + +#### 28.获取所有元素 + +``` +127.0.0.1:6379> zrange animal 0 -1 +1) "monkey" +2) "chicken" +3) "mouse" +4) "horse" +``` + +#### 29.获取所有元素并带上分值 + +``` +127.0.0.1:6379> zrange animal 0 -1 withscores +1) "monkey" +2) "1" +3) "chicken" +4) "2" +5) "mouse" +6) "3" +7) "horse" +8) "4" +``` + +#### 30.HyperLogLog + +Redis 在 2.8.9 版本添加了 HyperLogLog 结构。 + +Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。 + +在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。 + +但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。 + +#### 31.向HyperLogLog中添加元素 + +``` +127.0.0.1:6379> pfadd color blue +(integer) 1 +127.0.0.1:6379> pfadd color yellow +(integer) 1 +127.0.0.1:6379> pfadd color pink +(integer) 1 +127.0.0.1:6379> pfadd color white +(integer) 1 +127.0.0.1:6379> pfadd color black +(integer) 1 +``` + +#### 32.获取HyperLogLog元素个数 + +``` +127.0.0.1:6379> pfcount color +(integer) 5 +``` + +#### 33.发布订阅 + +种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。 + +Redis 客户端可以订阅任意数量的频道。 + +#### 34.创建订阅频道 + +``` +127.0.0.1:6379> subscribe everydayNews +Reading messages... (press Ctrl-C to quit) +1) "subscribe" +2) "everydayNews" +3) (integer) 1 +1) "message" +``` + +#### 35.在对应频道发布消息 + +``` +127.0.0.1:6379> publish everydayNews "morning" +(integer) 1 +``` + +订阅者收到: + +``` +Reading messages... (press Ctrl-C to quit) +1) "subscribe" +2) "everydayNews" +3) (integer) 1 +1) "message" +2) "everydayNews" +3) "morning" +``` + +#### 36.非后台执行备份 + +``` +127.0.0.1:6379> save +OK +``` + +#### 37.后台执行备份 + +``` +127.0.0.1:6379> BGSAVE +Background saving started +``` + +#### 38.查看是否需要密码验证 + +``` +127.0.0.1:6379> config get requirepass +1) "requirepass" +2) "" +``` + + + +#### 参考: + +https://redis.io/ + +http://redisdoc.com/ + +https://www.runoob.com/redis/ + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git a/Servlet.md b/Servlet.md new file mode 100644 index 0000000..87eeb69 --- /dev/null +++ b/Servlet.md @@ -0,0 +1,247 @@ +## Servlet + +#### 1.Servlet生命周期 + +servlet的生命周期是初始化(init)、服务(service)、销毁(destroy) + +初始化(init):默认第一次请求前,只初始化一次。修改web.xml,允许服务器启动时初始化。 +服务(service):方法被调用时进行服务,在项目启动期间可以进行多次服务(请求一次执行一次) +销毁(destory):当服务器关闭时进行销毁。只销毁一次 +Servlet接口中声明3个方法,tomcat在不同的时候将调用不同的方法。 +init 初始化方法,2种情况被调用 +情况1:默认,第一次请求前 +情况2:在web项目核心配置文件web.xml中,配置初始化,将在服务器启动时初始化。 +每次请求时,调用服务 +服务器关闭时,调用销毁。 + +#### 2.什么是jsp?jsp和Servlet有什么区别? + +Servlet是服务器端的程序 +JSP是服务器页面程序 +JSP本质上就是一个Servlet,在访问jsp时,在服务器端会将jsp先转换成servlet,再将生产的servlet的结果响应给浏览器。 +jsp是html页面中内嵌Java代码,侧重页面显示;Servlet是中书写Java代码,侧重逻辑控制; + +![img](https://img-blog.csdnimg.cn/20200910144535754.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI1ODU0NTk5,size_16,color_FFFFFF,t_70#pic_center) + +#### 3.Servlet 接口中有哪些方法? + +1. init(ServletConfig):初始化方法,默认第一次请求前执行,完成 servlet 初始化工作 + +2. service(ServletRequest,ServletResponse):执行方法,一次请求执行一次。 + +3. destroy():销毁方法,Servlet 对象应该从服务中被移除的时候,容器会调用该方法进行销毁操作 + +4. getServletConfig():获得 ServletConfig 配置对象,包括初始化参数等。 + +5. getServletInfo():获得 Servlet 描述,一般没有用。 + + ![img](https://img-blog.csdnimg.cn/20200910144604142.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI1ODU0NTk5,size_16,color_FFFFFF,t_70#pic_center) + + + +#### 4.Servlet 3.0 中的异步处理指的是什么? + +异步处理允许 Servlet 重新发起一条新线程去调用 耗时业务方法,这样就可以避免等待 + +![img](https://img-blog.csdnimg.cn/2020091014463133.png#pic_center) + +#### 5.Servlet 中如何获取用户提交的查询参数或表单数据? + +1. request.getParameterValues(“参数”); // 获得指定参数名的一组参数值 (String[]) +2. request.getParameter(“参数”); // 获得指定参数名的一个参数值(String) , UserServlet?username=jack , 通过 username 获得值 jack + +``` +public class TestRequestParam { + private HttpServletRequest request; + + public void testDemo01(){ + //请求数据:index.html?username=jack&hobby=抽烟&hobby=喝酒&hobby=烫头 + + // 获得username的值,一个值 + String username = request.getParameter("username"); + + // 获得hobby的值,一组值 + String[] hobbyArr = request.getParameterValues("hobby"); + + // 所有值 , map.key 参数名称,map.value 参数的值 + Map map = request.getParameterMap(); + + } +} +``` + +#### 6.区别请求的转发与重定向? + + 可以从以下三个方面进行比较 + +1.地址栏: + +转发: 显示的是请求的URL + +重定向: 显示的不是请求的URL, 而是重定向指向的新的URL + +2.浏览器发了几次请求? + +转发: 1次请求 + +重定向: 2次请求 + +1. 是否可以进行Request的数据共享? + +转发: 两个资源之间是同一个request对象, 可以共享request中的数据 + +重定向: 两个资源之间不是同一个request对象, 不可以共享 + +**经典现实案例:** + +![Servlet相关技术常见面试题](https://img-blog.csdnimg.cn/img_convert/48ff7f8346d74c66da6806855a4b4cb7.png) + +#### 7. 比较一下Servlet与Filter + +从四个方面来区分: + +**概念** + +  servlet是一种运行在服务器端的Java应用程序,独立于平台和协议,可以动态的生成web页面,它工作于客户端请求和服务器的中间层 + +  filter是一个可以复用的代码片段,可以用来转换请求,响应以及头信息,filter不能产生请求和响应,他只能在请求到达servlet之前对请求进行修改,或者在请求返回客户端之前对响应进行处理 + +**生命周期** + +  servlet是在系统启动或者请求到达servlet时,通过init()方法进行初始化,一旦被装入了web服务器,一般不会从Web服务器删除,直到服务器关闭才会调用  destroy()方法进行销毁。每次请求,Request都会被初始化,响应请求后,请求被销毁。但是servlet不会随着请求的销毁而销毁 + +  如果某个Servlet配置了 1 ,该Servlet也是在Tomcat(Servlet容器)启动时初始化。 +  如果Servlet没有配置1 ,该Servlet不会在Tomcat启动时初始化,而是在请求到来时初始化。 + +  filter + +    是在系统启动的时候通过init()初始化的,每次请求都只会调用dofiter方法进行处理,服务器停止的时候调用destroy()进行销毁 + +**注意**:服务器关闭时,servlet和filter依次销毁 + +**职责** + +  **servlet** + +    可以动态创建基于客户请求的页面;可以读取客户端发来的隐藏数据和显示数据;可以和其他的服务器资源进行通讯;通过状态代码和响应头向客户端返回数据。 + +  **filter** + +    主要是对请求到达servlet之前对请求和请求头信息进行前处理,和对数据返回客户端之前进行后处理 + +**区别** + +  servlet的流程比较短,url来了之后就对其进行处理,处理完就返回数据或者转向另一个页面 + +  filter的流程比较长,在一个filter处理之后还可以转向另一个filter进行处理,然后再交给servlet,但是servlet处理之后不能向下传递了。 + +  filter可用来进行字符编码的过滤,检测用户是否登陆的过滤,禁止页面缓存等 + +#### 8.我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串? + +``` + Public String translate (String str) { +    String tempStr = ""; + +    try { +      tempStr = new String(str.getBytes("ISO-8859-1"), "GBK"); + +      tempStr = tempStr.trim(); + +    } + +    catch (Exception e) { +      System.err.println(e.getMessage()); + +    } + +    return tempStr; + +  } +``` + +#### 9.Servlet执行时一般实现哪几个方法? + +public void init(ServletConfig config) + +public ServletConfig getServletConfig() + +public String getServletInfo() + +public void service(ServletRequest request,ServletResponse response) + +public void destroy() + + +#### 10.描述Cookie和Session的作用,区别和各自的应用范围,Session工作原理。 + +1)cookie 是一种发送到客户浏览器的文本串句柄,并保存在客户机硬盘上,可以用来在某个WEB站点会话间持久的保持数据。 + +2)session其实指的就是访问者从到达某个特定主页到离开为止的那段时间。 Session其实是利用Cookie进行信息处理的,当用户首先进行了请求后,服务端就在用户浏览器上创建了一个Cookie,当这个Session结束时,其实就是意味着这个Cookie就过期了。 + +注:为这个用户创建的Cookie的名称是aspsessionid。这个Cookie的唯一目的就是为每一个用户提供不同的身份认证。 + +3)cookie和session的共同之处在于:cookie和session都是用来跟踪浏览器用户身份的会话方式。 + +4)cookie 和session的区别是:cookie数据保存在客户端,session数据保存在服务器端。 + +5)session工作原理:session技术中所有的数据都保存在服务器上,客户端每次请求服务器的时候会发送当前会话的sessionid,服务器根据当前sessionid判断相应的用户数据标志,以确定用户是否登录或具有某种权限。 + +#### 11.Applet和Servlet有什么区别? + +Applet是运行在客户端主机的浏览器上的客户端Java程序。而Servlet是运行在web服务器上的服务端的组件。applet可以使用用户界面类,而Servlet没有用户界面,相反,Servlet是等待客户端的HTTP请求,然后为请求产生响应。 + +#### 12.GenericServlet和HttpServlet有什么区别? + +GenericServlet是一个通用的协议无关的Servlet,它实现了Servlet和ServletConfig接口。继承自GenericServlet的Servlet应该要覆盖service()方法。最后,为了开发一个能用在网页上服务于使用HTTP协议请求的Servlet,你的Servlet必须要继承自HttpServlet。这里有Servlet的例子。 + +#### 13.什么是服务端包含(Server Side Include)? + +服务端包含(SSI)是一种简单的解释型服务端脚本语言,大多数时候仅用在Web上,用servlet标签嵌入进来。SSI最常用的场景把一个或多个文件包含到Web服务器的一个Web页面中。当浏览器访问Web页面的时候,Web服务器会用对应的servlet产生的文本来替换Web页面中的servlet标签。 + +#### 14.什么是Servlet链(Servlet Chaining)? + +Servlet链是把一个Servlet的输出发送给另一个Servlet的方法。第二个Servlet的输出可以发送给第三个Servlet,依次类推。链条上最后一个Servlet负责把响应发送给客户端。 + +#### 15.如何知道是哪一个客户端的机器正在请求你的Servlet + +ServletRequest类可以找出客户端机器的IP地址或者是主机名。getRemoteAddr()方法获取客户端主机的IP地址,getRemoteHost()可以获取主机名。看下这里的例子。 + +#### 16.什么是cookie?session和cookie有什么区别? + +cookie是Web服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个Web服务器存储cookie。以后浏览器在给特定的Web服务器发请求的时候,同时会发送所有为该服务器存储的cookie。下面列出了session和cookie的区别: + +• 无论客户端浏览器做怎么样的设置,session都应该能正常工作。客户端可以选择禁用cookie,但是,session仍然是能够工作的,因为客户端无法禁用服务端的session。 + +• 在存储的数据量方面session和cookies也是不一样的。session能够存储任意的Java对象,cookie只能存储String类型的对象。 + +#### 17.浏览器和Servlet通信使用的是什么协议? + +浏览器和Servlet通信使用的是HTTP协议。 + +#### 18.什么是URL编码和URL解码? + +URL编码是负责把URL里面的空格和其他的特殊字符替换成对应的十六进制表示,反之就是解码。 + +JSP + +#### 19.什么是Scriptlets? + +JSP技术中,scriptlet是嵌入在JSP页面中的一段Java代码。scriptlet是位于标签内部的所有的东西,在标签与标签之间,用户可以添加任意有效的scriplet。 + +#### 20.声明(Decalaration)在哪里? + +声明跟Java中的变量声明很相似,它用来声明随后要被表达式或者scriptlet使用的变量。添加的声明必须要用开始和结束标签包起来。 + +#### + +#### 参考: + +https://blog.csdn.net/qq_25854599/article/details/108513815 + +https://blog.csdn.net/msjhw_com/article/details/113240556 + +https://www.cnblogs.com/htyj/p/8619198.html + +https://blog.csdn.net/Jeff_Seid/article/details/80761076 + diff --git a/Docs/Spring.md b/Spring.md similarity index 100% rename from Docs/Spring.md rename to Spring.md diff --git a/Docs/SpringBoot.md b/SpringBoot.md similarity index 100% rename from Docs/SpringBoot.md rename to SpringBoot.md diff --git a/Docs/SpringCloud.md b/SpringCloud.md similarity index 100% rename from Docs/SpringCloud.md rename to SpringCloud.md diff --git a/Tomcat.md b/Tomcat.md new file mode 100644 index 0000000..465d994 --- /dev/null +++ b/Tomcat.md @@ -0,0 +1,440 @@ +## Tomcat + +#### 1.Tomcat的缺省端口是多少,怎么修改? + +1)找到Tomcat目录下的conf文件夹 + +2)进入conf文件夹里面找到server.xml文件 + +3)打开server.xml文件 + +4)在server.xml文件里面找到下列信息 + + +port="8080"改成你想要的端口 + +#### 2.tomcat 有哪几种Connector 运行模式(优化)? + +bio:传统的Java I/O操作,同步且阻塞IO。 +maxThreads="150"//Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数。默认值200。可以根据机器的时期性能和内存大小调整,一般可以在400-500。最大可以在800左右。 +minSpareThreads="25"---Tomcat初始化时创建的线程数。默认值4。如果当前没有空闲线程,且没有超过maxThreads,一次性创建的空闲线程数量。Tomcat初始化时创建的线程数量也由此值设置。 +maxSpareThreads="75"--一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。默认值50。一旦创建的线程超过此数值,Tomcat会关闭不再需要的线程。线程数可以大致上用 “同时在线人数*每秒用户操作次数*系统平均操作时间” 来计算。 +acceptCount="100"----指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。默认值10。如果当前可用线程数为0,则将请求放入处理队列中。这个值限定了请求队列的大小,超过这个数值的请求将不予处理。 +connectionTimeout="20000" --网络连接超时,默认值20000,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。 + +nio:JDK1.4开始支持,同步阻塞或同步非阻塞IO。 +指定使用NIO模型来接受HTTP请求 +protocol="org.apache.coyote.http11.Http11NioProtocol" 指定使用NIO模型来接受HTTP请求。默认是BlockingIO,配置为protocol="HTTP/1.1" +acceptorThreadCount="2" 使用NIO模型时接收线程的数目 + +aio(nio.2):JDK7开始支持,异步非阻塞IO。 + +apr:Tomcat将以JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地 提高Tomcat对静态文件的处理性能。 + + + + + + + + + + +其他配置 + +maxHttpHeaderSize="8192" http请求头信息的最大程度,超过此长度的部分不予处理。一般8K。 +URIEncoding="UTF-8" 指定Tomcat容器的URL编码格式。 +disableUploadTimeout="true" 上传时是否使用超时机制 +enableLookups="false"--是否反查域名,默认值为true。为了提高处理能力,应设置为false +compression="on" 打开压缩功能 +compressionMinSize="10240" 启用压缩的输出内容大小,默认为2KB +noCompressionUserAgents="gozilla, traviata" 对于以下的浏览器,不启用压缩 +compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" 哪些资源类型需要压缩 + +#### 3.Tomcat有几种部署方式? + +1)直接把Web项目放在webapps下,Tomcat会自动将其部署 + +2)在server.xml文件上配置节点,设置相关的属性即可 + +3)通过Catalina来进行配置:进入到conf\Catalina\localhost文件下,创建一个xml文件,该文件的名字就是站点的名字。 + +编写XML的方式来进行设置。 + +#### 4.tomcat容器是如何创建servlet类实例?用到了什么原理? + +当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件,然后对xml文件进行解析, + +并读取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过反射的方式实例化。 + +(有时候也是在第一次请求时实例化)在servlet注册时加上如果为正数,则在一开始就实例化, + +如果不写或为负数,则第一次请求实例化。 + +#### 5.tomcat 如何优化? + +1、优化连接配置.这里以tomcat7的参数配置为例,需要修改conf/server.xml文件,修改连接数,关闭客户端dns查询。 + +参数解释: + +URIEncoding=”UTF-8″ :使得tomcat可以解析含有中文名的文件的url,真方便,不像apache里还有搞个mod_encoding,还要手工编译 + +maxSpareThreads : 如果空闲状态的线程数多于设置的数目,则将这些线程中止,减少这个池中的线程总数。 + +minSpareThreads : 最小备用线程数,tomcat启动时的初始化的线程数。 + +enableLookups : 这个功效和Apache中的HostnameLookups一样,设为关闭。 + +connectionTimeout : connectionTimeout为网络连接超时时间毫秒数。 + +maxThreads : maxThreads Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数,即最大并发数。 + +acceptCount : acceptCount是当线程数达到maxThreads后,后续请求会被放入一个等待队列,这个acceptCount是这个队列的大小,如果这个队列也满了,就直接refuse connection + +maxProcessors与minProcessors : 在 Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最 大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。 + +通常Windows是1000个左右,Linux是2000个左右。 + +useURIValidationHack: + +我们来看一下tomcat中的一段源码: + +【security】 + +``` +if (connector.getUseURIValidationHack()) { + +String uri = validate(request.getRequestURI()); + +if (uri == null) { + +res.setStatus(400); + +res.setMessage(“Invalid URI”); + +throw new IOException(“Invalid URI”); + +} else { + +req.requestURI().setString(uri); + +// Redoing the URI decoding + +req.decodedURI().duplicate(req.requestURI()); + +req.getURLDecoder().convert(req.decodedURI(), true); +``` + +可以看到如果把useURIValidationHack设成”false”,可以减少它对一些url的不必要的检查从而减省开销。 + +enableLookups=”false” : 为了消除DNS查询对性能的影响我们可以关闭DNS查询,方式是修改server.xml文件中的enableLookups参数值。 + +disableUploadTimeout :类似于Apache中的keeyalive一样 + +给Tomcat配置gzip压缩(HTTP压缩)功能 + +compression=”on” compressionMinSize=”2048″ + +compressableMimeType=”text/html,text/xml,text/JavaScript,text/css,text/plain” + +HTTP 压缩可以大大提高浏览网站的速度,它的原理是,在客户端请求网页后,从服务器端将网页文件压缩,再下载到客户端,由客户端的浏览器负责解压缩并浏览。相对于普通的浏览过程HTML,CSS,javascript , Text ,它可以节省40%左右的流量。更为重要的是,它可以对动态生成的,包括CGI、PHP , JSP , ASP , Servlet,SHTML等输出的网页也能进行压缩,压缩效率惊人。 + +1)compression=”on” 打开压缩功能 + +2)compressionMinSize=”2048″ 启用压缩的输出内容大小,这里面默认为2KB + +3)noCompressionUserAgents=”gozilla, traviata” 对于以下的浏览器,不启用压缩 + +4)compressableMimeType=”text/html,text/xml” 压缩类型 + +最后不要忘了把8443端口的地方也加上同样的配置,因为如果我们走https协议的话,我们将会用到8443端口这个段的配置,对吧? + +``` + + + +``` + + + +#### 6.内存调优 + +内存方式的设置是在catalina.sh中,调整一下JAVA_OPTS变量即可,因为后面的启动参数会把JAVA_OPTS作为JVM的启动参数来处理。 +具体设置如下: +JAVA_OPTS="$JAVA_OPTS -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4" +其各项参数如下: +-Xmx3550m:设置JVM最大可用内存为3550M。 +-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。 +-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。 +-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。 +-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5 +-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6 +-XX:MaxPermSize=16m:设置持久代大小为16m。 +-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。 + + + +#### 7.垃圾回收策略调优 + +垃圾回收的设置也是在catalina.sh中,调整JAVA_OPTS变量。 +具体设置如下: +JAVA_OPTS="$JAVA_OPTS -Xmx3550m -Xms3550m -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100" +具体的垃圾回收策略及相应策略的各项参数如下: +串行收集器(JDK1.5以前主要的回收方式) +-XX:+UseSerialGC:设置串行收集器 +并行收集器(吞吐量优先) +示例: +java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 + +-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。 +-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。 +-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集 +-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。 +-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。 +并发收集器(响应时间优先) +示例:java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC +-XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。 +-XX:+UseParNewGC: 设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。 +-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。 + +#### 8.添加JMS远程监控 + +对于部署在局域网内其它机器上的Tomcat,可以打开JMX监控端口,局域网其它机器就可以通过这个端口查看一些常用的参数(但一些比较复杂的功能不支持),同样是在JVM启动参数中配置即可,配置如下: +-Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false +-Djava.rmi.server.hostname=192.168.71.38 设置JVM的JMS监控监听的IP地址,主要是为了防止错误的监听成127.0.0.1这个内网地址 +-Dcom.sun.management.jmxremote.port=1090 设置JVM的JMS监控的端口 +-Dcom.sun.management.jmxremote.ssl=false 设置JVM的JMS监控不实用SSL +-Dcom.sun.management.jmxremote.authenticate=false 设置JVM的JMS监控不需要认证 + +#### 9.专业点的分析工具有 + +IBM ISA,JProfiler、probe 等,具体监控及分析方式去网上搜索即可 + +#### 10.关于Tomcat的session数目 + +这个可以直接从Tomcat的web管理界面去查看即可 ; 或者借助于第三方工具Lambda Probe来查看,它相对于Tomcat自带的管理稍微多了点功能,但也不多 ; + +#### 11.监视Tomcat的内存使用情况 + +使用JDK自带的jconsole可以比较明了的看到内存的使用情况,线程的状态,当前加载的类的总量等; JDK自带的jvisualvm可以下载插件(如GC等),可以查看更丰富的信息。如果是分析本地的Tomcat的话,还可以进行内存抽样等,检查每个类的使用情况 + +#### 12.打印类的加载情况及对象的回收情况 + +这个可以通过配置JVM的启动参数,打印这些信息(到屏幕(默认也会到catalina.log中)或者文件),具体参数如下: + +``` +-XX:+PrintGC:输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs] +-XX:+PrintGCDetails:输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs] +-XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用,输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs] +-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用。输出形式:Application time: 0.5291524 seconds +-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用。输出形式:Total time for which application threads were stopped: 0.0468229 seconds +-XX:PrintHeapAtGC: 打印GC前后的详细堆栈信息 +-Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析 +-verbose:class 监视加载的类的情况 +-verbose:gc 在虚拟机发生内存回收时在输出设备显示信息 +-verbose:jni 输出native方法调用的相关情况,一般用于诊断jni调用错误信息 +``` + +#### 13.Tomcat一个请求的完整过程 + +Ng:(nginx) + +upstream yy_001{ + server 10.99.99.99:8080; + server 10.99.99.100:8080; + + hash $**; + + healthcheck_enabled; + healthcheck_delay 3000; + healthcheck_timeout 1000; + healthcheck_failcount 2; + healthcheck_send 'GET /healthcheck.html HTTP/1.0' 'Host: wo.com' 'Connection: close'; + } + + server { + include base.conf; + server_name wo.de.tian; + ... + location /yy/ { + proxy_pass http://yy_001; + } + +首先 dns 解析 wo.de.tian机器,一般是ng服务器ip地址 +然后 ng根据server的配置,寻找路径为 yy/的机器列表,ip和端口 +最后 选择其中一台机器进行访问—->下面为详细过程 + +1) 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得 +2) Connector把该请求交给它所在的Service的Engine来处理,并等待来自Engine的回应 +3) Engine获得请求localhost/yy/index.jsp,匹配它所拥有的所有虚拟主机Host +4) Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机) +5) localhost Host获得请求/yy/index.jsp,匹配它所拥有的所有Context +6) Host匹配到路径为/yy的Context(如果匹配不到就把该请求交给路径名为”“的Context去处理) +7) path=”/yy”的Context获得请求/index.jsp,在它的mapping table中寻找对应的servlet +8) Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类 +9) 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法 +10)Context把执行完了之后的HttpServletResponse对象返回给Host +11)Host把HttpServletResponse对象返回给Engine +12)Engine把HttpServletResponse对象返回给Connector +13)Connector把HttpServletResponse对象返回给客户browser + + +#### 14.Tomcat工作模式? + +笔者回答:Tomcat是一个JSP/Servlet容器。其作为Servlet容器,有三种工作模式:独立的Servlet容器、进程内的Servlet容器和进程外的Servlet容器。 + +进入Tomcat的请求可以根据Tomcat的工作模式分为如下两类: + +Tomcat作为应用程序服务器:请求来自于前端的web服务器,这可能是Apache, IIS, Nginx等; + +Tomcat作为独立服务器:请求来自于web浏览器; + +#### 15.tomcat结构目录有哪些? + +①bin:启动和关闭tomcat的bat文件。 + +②conf:配置文件。 + +③server.xml该文件用于配置server相关的信息,比如tomcat启动的端口号,配置主机(Host)。 + +④web.xml文件配置与web应用(web应用相当于一个web站点) + +⑤tomcat-user.xml配置用户名密码和相关权限。 + +⑥lib:该目录放置运行tomcat运行需要的jar包。 + +⑦logs:存放日志,当我们需要查看日志的时候,可以查询信息。 + +⑧webapps:放置我们的web应用。 + +⑨work工作目录:该目录用于存放jsp被访问后生成对应的server文件和.class文件。 + +#### 16.如何配置Tomcat虚拟目录? + +1、在server.xml中的节点下添加如下代码。path表示的是访问时输入的web项目名,docBase表示的是站点目录的绝对路径。 + +2、进入到confCatalinalocalhost文件下,创建一个xml文件,该文件的名字就是站点的名字。 + +#### 17.Tomcat体系结构是怎样的? + +浏览器 -> tomcat server-> service ->connector -> engine(引擎) -> host(主机) -> web应用。 + +#### 18.Web请求在Tomcat请求中的请求流程是怎么样的? + +①浏览器输入URL地址; + +②查询本机hosts文件寻找IP; + +③查询DNS服务器寻找IP; + +④向该IP发送Http请求; + +⑤Tomcat容器解析主机名; + +⑥Tomcat容器解析Web应用; + +⑦Tomcat容器解析资源名称; + +⑧Tomcat容器获取资源; + +⑨Tomcat响应浏览器。 + +#### 19.如何在tomcat集群中实现Session共享 + +Apache集群实现Tomcat的Session共享配置其实很简单,在Tomcat自带的文档中有详细的说明( /docs/cluster-howto.html ),只不过是英语的,所以联合下面根据说下怎么配置吧: + +1、既然是集群肯定要多准备几个Tomcat来模拟,比如分别为Tomcat01、Tomcat02、Tomcat03。 + +如果各Tomcat程序放在不同的机器上,那么就不会有端口的冲突。如果是放在同一台机器上的话,那就简单改几个端口,防止端口占用造成的冲突。打开conf文件夹中的server.xml文件,需要修改的端口有: + + 1、 + + 2、 + + 3、 + +以上port需要修改,至于修改成什么样子,看你自己了,只要不出现端口冲突就可以了,要保证各个Tomcat实例间没有端口冲突。 + +#### 20.tomcat有哪些Connector? + +Tomcat的Web服务器连接器支持两种协议:AJP和HTTP,它们均定义了以二进制格式在Web服务器和Tomcat之间进行数据传输,并提供相应的控制命令。 + +AJP(Apache JServ Protocol)协议:目前正在使用的AJP协议的版本是通过JK和JK2连接器提供支持的AJP13,它基于二进制的格式在Web服务器和Tomcat之间传输数据,而此前的版本AJP10和AJP11则使用文本格式传输数据。 + +HTTP协议:诚如其名称所表示,其是使用HTTP或HTTPS协议在Web服务器和Tomcat之间建立通信,此时,Tomcat就是一个完全功能的HTTP服务器,它需要监听在某端口上以接收来自于商前服务器的请求。 + +#### 21.tomcat的Valve的作用是什么? + +Valve类似于过滤器,它可以工作于Engine和Host/Context之间、Host和Context之间以及Context和Web应用程序的某资源之间。一个容器内可以建立多个Valve,而且Valve定义的次序也决定了它们生效的次序。 + +#### 22.Webserver和 Application Server的区别是什么? + +最大区别,WebServer 一般仅仅指Web(如servlet,jsp)的应用服务器,ApplicationServer不仅可以是Web,还可以是Ejb等其它的应用服务器。 + +web server可以是application server的一部分,也可以是单独存在。 + +#### 23.Tomcat的缺省端口是多少,怎么修改? + +1)找到Tomcat目录下的conf文件夹; + +2)进入conf文件夹里面找到server.xml文件; + +3)打开server.xml文件; + +4)在server.xml文件里面找到下列信息; + +port=“8080”改成你想要的端口 + +#### 24.Tomcat 有几种部署方式? + +1)直接把Web项目放在webapps下,Tomcat会自动将其部署 + +2)在server.xml文件上配置节点,设置相关的属性即可 + +3)通过Catalina来进行配置:进入到conf\Catalina\localhost文件下,创建一个xml文件,该文件的名字就是站点的名字。编写XML的方式来进行设置。 + +#### 25.tomcat容器是如何创建servlet类实例?用到了什么原理? + +当容器启动时,会读取在webapps 目录下所有的web应用中的web.xml 文件,然后对xml文件进行解析,并读取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过反射的方式实例化。(有时候也是在第一次请求时实例化)在servlet注册时加上如果为正数,则在一开始就实例化,如果不写或为负数,则第一次请求实例化。 + +#### 参考 + +https://blog.csdn.net/qq_25934401/article/details/81536958 + +http://blog.itpub.net/69902581/viewspace-2673221/ + + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) + diff --git "a/Web\345\256\211\345\205\250.md" "b/Web\345\256\211\345\205\250.md" new file mode 100644 index 0000000..fe8ce08 --- /dev/null +++ "b/Web\345\256\211\345\205\250.md" @@ -0,0 +1,602 @@ +## Web安全 + +#### 1.CIA三元组知道吗? + +- 机密性(Confidentiality) +- 完整性(Integrity) +- 可用性(Availability) + +#### 2. XSS攻击是如何产生的? + +黑客在你的浏览器中插入一段恶意 JavaScript 脚本,窃取你的隐私信息、冒充你的身份进行操作。这就是 XSS 攻击(Cross-Site Scripting,跨站脚本攻击) + +因为浏览器无法区分脚本是被恶意注入的还是正常的内容,它都会执行,况且 HTML 非常灵活,可以在任何时候对它进行修改。 + +#### 3.知道XSS有哪几种类型吗? + +- 反射型 XSS (也叫非持久型) +- 基于 DOM 的 XSS +- 存储型 XSS (也叫持久型 XSS) + +#### 4.反射型 XSS实现原理 + +顾名思义,恶意 JavaScript 脚本属于用户发送给网站请求中的一部分,随后网站又将这部分返回给用户,恶意脚本在页面中被执行。一般发生在前后端一体的应用中,服务端逻辑会改变最终的网页代码。 + +#### 5.DOM型实现原理 + +目前更流行前后端分离的项目,反射型 XSS 无用武之地。 + +但这种攻击不需要经过服务器,我们知道,网页本身的 JavaScript 也是可以改变 HTML 的,黑客正是利用这一点来实现插入恶意脚本。 + +#### 6.存储型实现原理 + +又叫持久型 XSS,顾名思义,黑客将恶意 JavaScript 脚本长期保存在服务端数据库中,用户一旦访问相关页面数据,恶意脚本就会被执行。常见于搜索、微博、社区贴吧评论等。 + +#### 7.说下3者的区别 + +反射型的 XSS 的恶意脚本存在 URL 里,存储型 XSS 的恶意代码存在数据库里。 + +而基于DOM型的XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,其他两种 XSS 都属于服务端的安全漏洞。 + + + +**反射型** + +![image-20210622131957034](C:\Users\qq\AppData\Roaming\Typora\typora-user-images\image-20210622131957034.png) + +**基于DOM型** + +[![img](https://s2.51cto.com/oss/202006/24/8ca9d116817109c5db78883ae65ee7c6.jpg)](https://s2.51cto.com/oss/202006/24/8ca9d116817109c5db78883ae65ee7c6.jpg) + +**存储型** + +[![img](https://s3.51cto.com/oss/202006/24/63a8d3a9d932ba7084930621a2a28983.jpg)](https://s3.51cto.com/oss/202006/24/63a8d3a9d932ba7084930621a2a28983.jpg) + + + +#### 8.黑客可以通过XSS攻击做哪些事儿? + +- 盗取用户 Cookie +- 未授权操作 +- 修改 DOM +- 刷浮窗广告 +- 发动 XSS 蠕虫攻击 +- 劫持用户行为,进一步渗透内网 + +#### 9.XSS攻击如何进行防护? + +- 一切用户输入皆不可信,在输出时进行验证 +- 将 HTML 元素内容、属性以及 URL 请求参数、CSS 值进行编码 +- 当编码影响业务时,使用白名单规则进行检测和过滤 +- 使用 W3C 提出的 CSP (Content Security Policy,内容安全策略),定义域名白名单 +- 设置 Cookie 的 HttpOnly 属性 + +#### 10.知道哪些XSS攻击案例简单说一下 + +- 2005年,年仅19岁的 Samy Kamkar 发起了对 MySpace.com 的 XSS Worm 攻击。 + + Samy Kamkar 的蠕虫在短短几小时内就感染了100万用户——它在每个用户的自我简介后边加了一句话:“but most of all, Samy is my hero.”(Samy是我的偶像)。这是 Web 安全史上第一个重量级的 XSS Worm,具有里程碑意义。 + +- 2007年12月,百度空间收到蠕虫攻击,用户之间开始转发垃圾短消息。 +- QQ 邮箱 m.exmail.qq.com 域名被发现反射型 XSS 漏洞 +- 2011年新浪微博曾被黑客 XSS 攻击,黑客诱导用户点击一个带有诱惑性的链接,便会自动发送一条带有同样诱惑性链接微博。攻击范围层层扩大,也是一种蠕虫攻击。 + +#### 11.什么是CSRF攻击? + +CSRF 英文全称是 Cross-site request forgery,又称为“跨站请求伪造”。 + +顾名思义,CSRF 攻击就是黑客引诱用户打开黑客的网站,利用用户的登陆状态发起跨站请求。 + +降维解释:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。 + +利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证, 达到冒充用户对被攻击的网站执行某项操作的目的。 + +#### 12.CSRF攻击一般怎么实现? + +- 最容易实现的是 Get 请求,一般进入黑客网站后,可以通过设置 img的 src 属性来自动发起请求 +- 在黑客的网站中,构造隐藏表单来自动发起 Post 请求 +- 通过引诱链接诱惑用户点击触发请求,利用 a 标签的 href。 + +#### 13.CSRF攻击和XSS攻击有什么区别? + +CSRF 攻击不需要将恶意代码注入用户的页面,仅仅是利用服务器的漏洞和用户的登录状态来实施攻击。 + +CSRF 攻击成本也比 XSS 低,用户每天都要访问大量网页,无法确认每一个网页的合法性, + +从用户角度来说,无法彻底防止 CSRF 攻击。 + +#### 14.那应该如何防范CSRF攻击? + +- 针对实际情况,设置关键 Cookie 的 SameSite 属性为 Strict 或 Lax +- 服务端验证请求来源站点(Referer、Origin) +- 使用 CSRF Token,服务端随机生成返回给浏览器的 Token,每一次请求都会携带不同的 CSRF Token +- 加入二次验证(独立的支付密码) + +#### 15.字符集处理过程中可能出现的安全问题有哪些? + +1. 上下层使用的字符集不一致,导致数据的意义出现问题 +2. 处理多字节字符集时出现问题 +3. 对于非法数据的处理上理解不一致 +4. 某些字符集的天生缺陷 +5. 数据库里的校对规则(前后使用字符集不一致就出现问题) + +#### 16.如何测试网站是否存在跨站点脚本漏洞? + +关键字:Web到目前为止,对于跨站点脚本攻击具有很大的威胁这一点大家并无异议。如果您很精通 XSS 并且只想看看有什么好的测试方法可供借鉴,那么请直接跳到本文的测试部分。如果您对此一无所知,请按顺序认真阅读!如果某个怀有恶意的人(攻击者)可以强迫某个不知情的用户(受害者)运行攻击者选择的客户端脚本,那么便会发生跨站点脚本攻击。“跨站点脚本”这个词应该属于用词不当的情况,因为它不仅与脚本有关,而且它甚至不一定是跨站点的。所以,它就是一个在发现这种攻击时起的一个名字,并且一直沿用至今。从现在开始,我们将使用它常见的缩写名称“XSS”。 + + XSS 攻击的过程涉及以下三方: + + ? 攻击者 + + ? 受害者 + + ? 存在漏洞的网站(攻击者可以使用它对受害者采取行动) + + + +​ 在这三方之中,只有受害者会实际运行攻击者的代码。网站仅仅是发起攻击的一个载体,一般不会受到影响。可以用多种方式发起 XSS 攻击。例如,攻击者可通过电子邮件、IM 或其他途径向受害者发送一个经过经心构造的恶意 URL。当受害者在 Web 浏览器中打开该 URL 的时侯,网站会显示一个页面并在受害者的计算机上执行脚本。 + +###### XSS 漏洞是什么样的呢? + +​ 作为一名 Web 开发人员或测试人员,您肯定知道 Web 应用程序的技术基础是由 HTTP 和 HTML 组成的。HTTP 协议是 HTML 的传输机制,可使用代码设计 Web 页面布局和生成页面。 + + + +​ 如果 Web 应用程序接受用户通过 HTTP 请求(如 GET 或 POST)提交的输入信息,然后使用输出 HTML 代码在某些地方显示这些信息,便可能存在 XSS 漏洞。下面是一个最简单的例子: + +###### 1.Web 请求如下所示: + + GET http://www.somesite.com/page.asp?pageid=10&lang=en&title=Section%20Title + +###### 2. 在发出请求后,服务器返回的 HTML 内容包括:Section Title + + 可以看到,传递给“title”查询字符串参数的用户输入可能被保存在一个字符串变量中并且由 Web 应用程序插入到标记中。通过提供输入内容,攻击者可以控制 HTML。 + +###### 3.现在,如果站点没有在服务器端对用户输入加以过滤(因为总是可以绕过客户端控件),那么恶意用户便可以使用许多手段对此漏洞加以滥用: + + + + 攻击者可以通过摆脱标记来注入代码: + + http://www.somesite.com/page.asp?pageid=10&lang=en&title=Section%20Title + + + +< SCRIPT>alert(‘XSS%20attack’)< /SCRIPT> + + 这个请求的 HTML 输出将为:Section Title + +< SCRIPT>alert(‘XSS attack’)< /SCRIPT> + + + + 即便是这个最简单的例子,攻击者也可以利用此连接完成数不清的事情。让我们看看会有哪些潜在的威胁,然后讨论一些更高级的测试方法。 + + + +###### XSS 攻击的威胁有多么严重? + +​ 由于能够在生成的 Web 页面中注入代码,能想到的威胁有多么严重,就可以有多么严重的威胁。攻击者可以使用 XSS 漏洞窃取 Cookie,劫持帐户,执行 ActiveX,执行 Flash 内容,强迫您下载软件,或者是对硬盘和数据采取操作。 + + + +​ 只要您点击了某些 URL,这一切便有可能发生。每天之中,在阅读来自留言板或新闻组的受信任的电子邮件的时侯,您会多少次地单击其中的 URL? + + + +​ 网络钓鱼攻击通常利用 XSS 漏洞来装扮成合法站点。可以看到很多这样的情况,比如您的银行给你发来了一封电子邮件,向您告知对您的帐户进行了一些修改并诱使您点击某些超链接。如果仔细观察这些 URL,它们实际上可能利用了银行网站中存在的漏洞,它们的形式类似于 http://mybank.com/somepage?redirect= < SCRIPT>alert(‘XSS’)< /SCRIPT> ,这里利用了“redirect”参数来执行攻击。 + + + +​ 如果您足够狡猾的话,可以将管理员定为攻击目标,您可以发送一封具有如下主题的邮件:“求救!这个网站地址总是出现错误!”在管理员打开该 URL 后,便可以执行许多恶意操作,例如窃取他(或她)的凭证。 + +​ 好了,现在我们已经理解了它的危害性 — 危害用户,危害管理员,给公司带来坏的公共形象。现在,让我们看看本文的重点 — 测试您的网站是否存在这些问题。 + +###### 测试 XSS 漏洞 + +​ 多年以来,我一直是一名全职的安全顾问,已经做过无数次的这种测试了。我将好的测试计划归结为两个字:彻底。对于你我来说,查找这些漏洞与能够有机会在 Bugtraq 或 Vulnwatch 上吹嘘一番没有任何关系;它只与如何出色完成负责的工作有关。如果这意味着对应用程序中所有的单个查询字符串参数、cookie 值 以及 POST 数据值进行检查,那么这只能表明我们的工作还不算太艰巨。 + + + +​ 显然,一次完整的安全性检查所涉及的内容通常远远超出寻找 XSS 漏洞那样简单;它需要建立整体的威胁模型,测试溢出漏洞、信息泄漏、错误处理、SQL 注入、身份验证和授权错误。好在执行这样彻底的工作时,各个领域之间都存在重叠。比如,在测试 XSS 漏洞时,经常会同时找出错误处理或信息泄漏问题。 + + + +​ 我假设您属于某个负责对 Web 应用程序进行开发和测试的小组。在这个幸运的位置上,您可以混合使用黑盒和白盒方法。每种方法都有它自己的优点,结合使用时甚至能相互提供支持。 + + + +​ \1. 按顺序准备您的工具包 + +​ 测试工作也可以是自动化的,但是我们在这里只讨论手动操作。手动测试的必备工具包括: + + + + ? Paros proxy (http://www.parosproxy.org),用于截获 HTTP 通信数据 + + + + ? Fiddler (http://www.fiddlertool.com/fiddler),用于截获 HTTP 通信数据 + + + + ? Burp proxy (http://www.portswigger.net/proxy/) + + + + ? TamperIE (http://www.bayden.com/dl/TamperIESetup.exe),用于修改 GET 和 POST + + + + + +​ 我们以上至少列出了三种 Web 代理软件。也可以寻找其他不同的类似产品,因为每种产品都有它自己的独到之处。下面,您需要在 Web 浏览器发出 HTTP 请求之前截获这些请求,并修改它们以注入 XSS 测试代码。上面所有这些工具都可以完成这项任务,某些工具还会显示返回的 HTML 源代码(如果您选择了截获服务器响应)。 + + + +​ 截获客户端发出的 GET 和 POST 请求非常重要。这样可以绕过所有的客户端 javascript 输入验证代码。我在这里要提醒所有 Web 开发人员 — 客户端安全控制是靠不住的。应该总是在服务器端执行有效性验证。 + + + +​ \2. 确定站点及其功能 — 与开发人员和 PM 交流 + +​ 绘制一些简单的数据流图表,对站点上的页面及其功能进行描述。此时,可以安排一些与开发人员和项目经理的会议来建立威胁模型。在会议上尽可能对应用程序进行深入探讨。站点公开了 Web 服务吗?是否有身份验证表单?有留言板吗?有用户设置页面吗?确保列出了所有这些页面。 + + + +​ \3. 找出并列出所有由用户提供输入的点 + +​ 对站点地图进行进一步细化。我通常会为此创建一个电子表格。对于每个页面,列出所有查询字符串参数、cookie 值、自定义 HTTP 标头、POST 数据值和以其他形式传递的用户输入。不要忘记搜索 Web 服务和类似的 SOAP 请求,并找出所有允许用户输入的字段。 + + + +​ 分别列出每个输入参数,因为下面需要独立测试每个参数。这可能是最重要的一个步骤!如果阅读下面的电子表格,您会看到我已经在示例站点中找出了一大堆这样的东西。如 forwardURL 和 lang 这样的查询字符串。如 name、password、msgBody、msgTitle 和这样的 POST 数据,甚至某些 Cookie 值。所有这些都是我们感兴趣的重要测试内容。 + + + +​ \4. 认真思考并列出测试用例 + +​ 使用已经得到的电子表格并列出各种用来测试 XSS 漏洞的方法。我们稍候将讨论各种方法,但是现在先让我们看看我的电子表格的屏幕截图,请注意,我列出了页面上允许的每个值以及每个值的所有测试类型。这种记录测试的方法仅是我自己的习惯,您可以使用自己的方法。我喜欢记录所有东西,以便我能知道已经做了哪些工作和哪些工作没有做。 + + + +​ \5. 开始测试并注意输出结果 + +​ 在查找漏洞的过程中,最重要的部分并不是您是否找到了漏洞。而是您是否真正知道究竟发生了哪些事情。对于 XSS,只需检查 HTML 输出并看看您输入的内容在什么地方。它在一个 HREF 标记中吗?是否在 IFRAME 标记中?它在 CLSID 标记中吗?在 IMG SRC 中吗?某些 Flash 内容的 PARAM NAME 是怎样的? + + + +​ 我会检查所有这些情况,如果您对所输入内容的目的十分了解,可以调整您的测试来找出问题。这意味着您可能需要添加一个额外的封闭括号“>”来让某个标记变得完整,或者添加一个双引号来关闭标记内的一个元素。或者,您可能需要使用 URL 或 HTML 来编码您的字符,例如将双引号变为 %22 或 。 + +​ 嗯,并不那么容易,这个站点看来防范比较严密。现在该怎么办呢? + +​ 那么,也许您的简单测试用例 < SCRIPT>alert(‘hi’)< /SCRIPT> 并不能产生期望中的警告对话框。仔细想想这个问题并在可能的情况下与开发人员进行交流。也许他们对输入中的尖括号、单引号或圆括号进行了过滤。也许他们会过滤“script”这个词。重新研究为何输入会产生这样的输出,并理解每个值(查询字符串、cookie、POST 数据)的作用。“pageId=10”这样的查询字符串值可能对输出没有影响,因此不值得花费时间测试它。有时,最好试着注入单个字符(例如尖括号、双引号标记或者圆括号),看看应用程序是否过滤这些字符。然后,便可以知道您面对的过滤级别究竟如何。接着,可以调整测试方法,对这些字符进行编码并重试,或者寻找其他注入办法。 + + + +###### 改变测试用例 + + + +​ 我恐怕无法充分对此进行说明:研究输入的值会输出什么样的 HTML 页面非常重要。如果它们不能产生输出,那么不要在它们上面浪费时间。如果可以,请进行研究,因为您需要根据输出对测试进行相应的修改。我使用了各种变化形式来找出能接受和显示脚本代码的参数。因为这涉及太多内容,因此在这里无法一一进行讨论,但是请务必注意以下几种情况: + + + +​ 有许多变化形式可以尝试。关键在于了解程序究竟使用何种方式处理输入和显示输出页面。如同这些例子所展示的,常见的变化形式经常是在脚本代码前面加上 “>””,以尝试封闭网站可能在输出中生成的标记。还可以对代码进行 URL 编码,尝试绕过服务器端的输入过滤功能。此外,因为尖括号“<>”通常会在输入时被过滤和从输出中删除,所以还必须尝试不需要尖括号的 XSS,例如 ”&{alert(XSS)};” + + + +###### 持久和动态 + +​ 找出一个成功的 XSS 颇费周折,因为在开始时 XSS 攻击可能并不是那么显而易见的。随便举一个例子,如果向网站添加一条新留言并在“msgTitle”值中注入代码,在提交数据后,您可能不会立即看到脚本代码被执行。但是,当您访问留言板的时侯,将会在 HTML 页面中使用“msgTitle”值并将其作为脚本代码执行。这种情况被称作持久性 XSS 攻击,如果包含脚本代码的值将被保存到客户端或者后端系统并在稍候的时间被执行,便会发生此种攻击。 + + + +​ 而与此相对的是动态 XSS 攻击,这种攻击会立即执行并只发生一次。比如,如果某个链接或 GET 请求在某个用来控制页面输出的查询字符串中包含了脚本代码,那么在点击链接后会立即显示输出。 + +#### 17.在网站测试中应该如何进行安全性测试? + +安全性测试(security testing)是有关验证应用程序的安全服务和识别潜在安全性缺陷的过程。 + + + +  注意:安全性测试并不最终证明应用程序是安全的,而是用于验证所设立策略的有效性,这些对策是基于威胁分析阶段所做的假设而选择的。 + + + +  以下是我读<<软件评测试教程>>中的Web安全性测试章节内容,并进行修改的笔记,前面看了好多朋友写的,不过不是很全,希望对大家有所帮助,建议大家还是买本<<软件评测试教程>>此书绝对物超所值^_^ + + + +WEB安全性测试 + +  一个完整的WEB安全性测试可以从部署与基础结构、输入验证、身份验证、授权、配置管理、敏感数据、会话管理、加密。参数操作、异常管理、审核和日志记录等几个方面入手。 + +\1. 安全体系测试 + +1) 部署与基础结构 + +  网络是否提供了安全的通信 + +  部署拓扑结构是否包括内部的防火墙 + +  部署拓扑结构中是否包括远程应用程序服务器 + +  基础结构安全性需求的限制是什么 + +  目标环境支持怎样的信任级别 + +2) 输入验证 + +l 如何验证输入 + +A. 是否清楚入口点 + +B. 是否清楚信任边界 + +C. 是否验证Web页输入 + +D. 是否对传递到组件或Web服务的参数进行验证 + +E. 是否验证从数据库中检索的数据 + +F. 是否将方法集中起来 + +G. 是否依赖客户端的验证 + +H. 应用程序是否易受SQL注入攻击 + +I. 应用程序是否易受XSS攻击 + +l 如何处理输入 + +3) 身份验证 + +  是否区分公共访问和受限访问 + +  是否明确服务帐户要求 + +  如何验证调用者身份 + +  如何验证数据库的身份 + +  是否强制试用帐户管理措施 + +4) 授权 + +  如何向最终用户授权 + +  如何在数据库中授权应用程序 + +  如何将访问限定于系统级资源 + +5) 配置管理 + +  是否支持远程管理 + +  是否保证配置存储的安全 + +  是否隔离管理员特权 + +6) 敏感数据 + +  是否存储机密信息 + +  如何存储敏感数据 + +  是否在网络中传递敏感数据 + +  是否记录敏感数据 + +7) 会话管理 + +  如何交换会话标识符 + +  是否限制会话生存期 + +  如何确保会话存储状态的安全 + +8) 加密 + +  为何使用特定的算法 + +  如何确保加密密钥的安全性 + +9) 参数操作 + +  是否验证所有的输入参数 + +  是否在参数过程中传递敏感数据 + +  是否为了安全问题而使用HTTP头数据 + +10) 异常管理 + +  是否使用结构化的异常处理 + +  是否向客户端公开了太多的信息 + +11) 审核和日志记录 + +  是否明确了要审核的活动 + +  是否考虑如何流动原始调用这身份 + +\2. 应用及传输安全 + +  WEB应用系统的安全性从使用角度可以分为应用级的安全与传输级的安全,安全性测试也可以从这两方面入手。 + +  应用级的安全测试的主要目的是查找Web系统自身程序设计中存在的安全隐患,主要测试区域如下。 + +  注册与登陆:现在的Web应用系统基本采用先注册,后登录的方式。 + +A. 必须测试有效和无效的用户名和密码 + +B. 要注意是否存在大小写敏感, + +C. 可以尝试多少次的限制 + +D. 是否可以不登录而直接浏览某个页面等。 + +  在线超时:Web应用系统是否有超时的限制,也就是说,用户登陆一定时间内(例如15分钟)没有点击任何页面,是否需要重新登陆才能正常使用。 + +  操作留痕:为了保证Web应用系统的安全性,日志文件是至关重要的。需要测试相关信息是否写进入了日志文件,是否可追踪。 + +  备份与恢复:为了防范系统的意外崩溃造成的数据丢失,备份与恢复手段是一个Web系统的必备功能。备份与恢复根据Web系统对安全性的要求可以采用多种手段,如数据库增量备份、数据库完全备份、系统完全备份等。出于更高的安全性要求,某些实时系统经常会采用双机热备或多级热备。除了对于这些备份与恢复方式进行验证测试以外,还要评估这种备份与恢复方式是否满足Web系统的安全性需求。 + +  传输级的安全测试是考虑到Web系统的传输的特殊性,重点测试数据经客户端传送到服务器端可能存在的安全漏洞,以及服务器防范非法访问的能力。一般测试项目包括以下几个方面。 + +  HTTPS和SSL测试:默认的情况下,安全HTTP(Soure HTTP)通过安全套接字SSL(Source Socket Layer)协议在端口443上使用普通的HTTP。HTTPS使用的公共密钥的加密长度决定的HTTPS的安全级别,但从某种意义上来说,安全性的保证是以损失性能为代价的。除了还要测试加密是否正确,检查信息的完整性和确认HTTPS的安全级别外,还要注意在此安全级别下,其性能是否达到要求。 + +  服务器端的脚本漏洞检查:存在于服务器端的脚本常常构成安全漏洞,这些漏洞又往往被黑客利用。所以,还要测试没有经过授权,就不能在服务器端放置和编辑脚本的问题。 + +  防火墙测试:防火墙是一种主要用于防护非法访问的路由器,在Web系统中是很常用的一种安全系统。防火墙测试是一个很大很专业的课题。这里所涉及的只是对防火墙功能、设置进行测试,以判断本Web系统的安全需求。 + + + +另推荐安全性测试工具: + +  Watchfire AppScan:商业网页漏洞扫描器(此工具好像被IBM收购了,所以推荐在第一位) + +  AppScan按照应用程序开发生命周期进行安全测试,早在开发阶段就进行单元测试和安全保证。Appscan能够扫描多种常见漏洞,例如跨网站脚本、HTTP应答切开、参数篡改、隐藏值篡改、后门/调试选项和缓冲区溢出等等。 + +  Acunetix Web Vulnerability Scanner:商业漏洞扫描器(目前用的比较多,不过这东东N占内存) + +Acunetix WVS自动检查您的网页程序漏洞,例如SQL注入、跨网站脚本和验证页面弱密码破解。Acunetix WVS有着非常友好的用户界面,还可以生成个性化的网站安全评估报告。 + +#### 18.Web应用攻击中目录遍历攻击的方法及如何预防? + +对于一个安全的Web服务器来说,对Web内容进行恰当的访问控制是极为关键的。目录遍历是Http所存在的一个安全漏洞,它使得攻击者能够访问受限制的目录,并在Web服务器的根目录以外执行命令。 + +  Web服务器主要提供两个级别的安全机制: + + + +  访问控制列表——就是我们常说的ACL   根目录访问 + +  访问控制列表是用于授权过程的,它是一个Web服务器的管理员用来说明什么用户或用户组能够在服务器上访问、修改和执行某些文件的列表,同时也包含了其他的一些访问权限内容。 + + + +  根目录是服务器文件系统中一个特定目录,它往往是一个限制,用户无法访问位于这个目录之上的任何内容。 + + + +  例如:在Windows的IIS其默认的根目录是C:\Inetpub\wwwroot,那么用户一旦通过了ACL的检查,就可以访问C:\Inetpub\wwwroot\news目录以及其他位于这个根目录以下的所有目录和文件,但无法访问C:\Windows目录。 + + + +  根目录的存在能够防止用户访问服务器上的一些关键性文件,譬如在Windows平台上的cmd.exe或是Linux/Unix平台上的口令文件。 + + + +  这个漏洞可能存在于Web服务器软件本身,也可能存在于Web应用程序的代码之中。 + + + +  要执行一个目录遍历攻击,攻击者所需要的只是一个web浏览器,并且有一些关于系统的一些缺省文件和目录所存在的位置的知识即可。 + + + +  如果你的站点存在这个漏洞,攻击者可以用它来做些什么? + + + +  利用这个漏洞,攻击者能够走出服务器的根目录,从而访问到文件系统的其他部分,譬如攻击者就能够看到一些受限制的文件,或者更危险的,攻击者能够执行一些造成整个系统崩溃的指令。 + + + +  依赖于web站点的访问是如何设置的,攻击者能够仿冒成站点的其他用户来执行操作,而这就依赖系统对Web站点的用户是如何授权的。 + + + +  利用Web应用代码进行目录遍历攻击的实例 + + + +  在包含动态页面的Web应用中,输入往往是通过GET或是POST的请求方法从浏览器获得,以下是一个GET的Http URL请求示例: + + + +  http://test.webarticles.com/show.asp?view=oldarchive.html + + + +  利用这个URL,浏览器向服务器发送了对动态页面show.asp的请求,并且伴有值为oldarchive.html的view参数,当请求在Web服务器端执行时,show.asp会从服务器的文件系统中取得oldarchive.html文件,并将其返回给客户端的浏览器,那么攻击者就可以假定show.asp能够从文件系统中获取文件并编制如下的URL: + + + +  http://test.webarticles.com/show.asp?view=../../../../../Windows/system.ini + + + +  那么,这就能够从文件系统中获取system.ini文件并返回给用户,../的含义这里就不用多说了,相信大家都会明白。攻击者不得不去猜测需要往上多少层才能找到Windows目录,但可想而知,这其实并不困难,经过若干次的尝试后总会找到的。 + + + + 利用Web服务器进行目录遍历攻击的实例: + + + +  除了Web应用的代码以外,Web服务器本身也有可能无法抵御目录遍历攻击。这有可能存在于Web服务器软件或是一些存放在服务器上的示例脚本中。 + + + +  在最近的Web服务器软件中,这个问题已经得到了解决,但是在网上的很多Web服务器仍然使用着老版本的IIS和Apache,而它们则可能仍然无法抵御这类攻击。即使你使用了已经解决了这个漏洞的版本的Web服务器软件,你仍然可能会有一些对黑客来说是很明显的存有敏感缺省脚本的目录。 + + + +  例如,如下的一个URL请求,它使用了IIS的脚本目录来移动目录并执行指令:http://server.com/scripts/..%5c../Windows/System32/cmd.exe?/c dir c:\ + + + +  这个请求会返回C:\目录下所有文件的列表,它使通过调用cmd.exe然后再用dir c:\来实现的,%5c是web服务器的转换符,用来代表一些常见字符,这里表示的是“\” + + + +  新版本的Web服务器软件会检查这些转换符并限制它们通过,但对于一些老版本的服务器软件仍然存在这个问题。 + + + +  如何判断是否存在目录遍历漏洞? + + + +  最好的方式就是使用Web漏洞扫描器,Web漏洞扫描器能够遍历你Web站点的所有目录以判断是否存在目录遍历漏洞,如果有它会报告该漏洞并给出解决的方法,除了目录遍历漏洞以外,Web应用扫描还能检查SQL注入、跨站点脚本攻击以及其他的漏洞。 + +#### 19.介绍一下DDoS攻击新中的反弹技术 + +反弹技术就是利用反弹服务器实现攻击的技术。 +所谓反弹服务器(Reflector)是指当收到一个请求数据报后就会产生一个回应数据报的主机.例如,所有的Web服务器,DNS服务器和路由服务器都是反弹服务器.攻击者可以利用这些回应的数据报对目标机器发动DDoS攻击。 +反弹技术原理 +反弹服务器攻击过程和传统的DDoS攻击过程相似,如前面所述的4个步骤中,只是第4步改为:攻击者锁定大量的可以作为反弹服务器的服务器群,攻击命令发出后,代理守护进程向已锁定的反弹服务器群发送大量的欺骗请求数据包,其原地址为受害服务器或目标服务器. +反弹技术实现DDoS攻击与传统DDoS攻击的区别: +1.反弹技术实现DDoS攻击比传统DDoS攻击更加难以抵御.实际上它的攻击网络结构和传统的相比多了第四层——被锁定的反弹服务器层.反弹服务器的数量可以远比驻有守护进程的代理服务器多,故反弹技术可以使攻击时的洪水流量变弱,最终才在目标机汇合为大量的洪水,其攻击规模也比传统DDoS攻击大得多。 +2.目标机更难追查到攻击来源.目标机接收到的攻击数据报的源IP是真实的,反弹服务器追查到的数据报源IP是假的.又由于反弹服务器上收发数据报的流量较小(远小于代理服务器发送的数量),所以,服务器根据网络流量来自动检测是否为DDoS攻击源的这种机制将不起作用。 + +#### 20.请介绍一下盗号木马和网页木马的原理和机制 + +**盗号木马**是指隐秘在电脑中的一种恶意程序,跟灰鸽子不同,这是以盗号为目的并且能够伺机盗取各种需要密码的账户(游戏,应用程序等)的木马病毒。 + +- 在传统的远程控制木马基础上发展出的以窃取敏感信息为目标的专用木马。 +- QQ盗号木马:数十款,流行网游:均发现相应的盗号木马 +- 免杀机制:继承可执行程序加壳/变形等技术方法 + +**网页木马**就是表面上伪装成普通的网页文件或是将恶意的代码直接插入到正常的网页文件中,当有人访问时,网页木马就会利用对方系统或者浏览器的漏洞自动将配置好的木马的服务端下载到访问者的电脑上来自动执行。 + +网页木马实际上是一个HTML网页,与其它网页不同的是该网页是黑客精心制作的,用户一旦访问了该网页就会中木马。为什么说是黑客精心制作的呢?因为嵌入在这个网页中的脚本恰如其分地利用了IE浏览器的漏洞,让IE在后台自动下载黑客放置在网络上的木马并运行(安装)这个木马,也就是说,这个网页能下载木马到本地并运行(安装)下载到本地电脑上的木马,整个过程都在后台运行,用户一旦打开这个网页,下载过程和运行(安装)过程就自动开始。 + +- 网页木马本质上并非木马,而是Web方式的渗透攻击代码 +- 网页木马一般以JavaScript, VBScript等脚本语言实现 +- 免杀机制:1)通过大小写变换、十六进制编码、unicode编码、base64编码、escape编码等方法对网页木马进行编码混淆 2)通过通用(screnc等)或定制的加密工具(xxtea等)对网页木马进行加密 3)修改网页木马文件掩码、混淆文件结构、分割至多个文件等 + +#### 参考: + +https://developer.51cto.com/art/202006/619575.htm + +http://www.100mian.com/category/webanquan/ + + + + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git a/Zookeeper.md b/Zookeeper.md new file mode 100644 index 0000000..7c35eae --- /dev/null +++ b/Zookeeper.md @@ -0,0 +1,448 @@ +## Zookeeper + +#### 1.ZooKeeper 是什么? + +ZooKeeper 是一个开源的分布式协调服务。它是一个为分布式应用提供一致性服务的软件,分布式应用程序可以基于 Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。 + +ZooKeeper 的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 + +Zookeeper 保证了如下分布式一致性特性: + +(1)顺序一致性 + +(2)原子性 + +(3)单一视图 + +(4)可靠性 + +(5)实时性(最终一致性) + +客户端的读请求可以被集群中的任意一台机器处理,如果读请求在节点上注册了监听器,这个监听器也是由所连接的 zookeeper 机器来处理。对于写请求,这些请求会同时发给其他 zookeeper 机器并且达成一致后,请求才会返回成功。因此,随着 zookeeper 的集群机器增多,读请求的吞吐会提高但是写请求的吞吐会下降。 + +有序性是 zookeeper 中非常重要的一个特性,所有的更新都是全局有序的,每个更新都有一个唯一的时间戳,这个时间戳称为 zxid(Zookeeper Transaction Id)。而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个zookeeper 最新的 zxid。 + + +#### 2.ZooKeeper 提供了什么? + +- 文件系统 +- 通知机制 + +#### 3.Zookeeper 文件系统 + +Zookeeper 提供一个多层级的节点命名空间(节点称为 znode)。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。 + +Zookeeper 为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得 Zookeeper 不能用于存放大量的数据,每个节点的存放数据上限为1M。 + + +#### 4.Zookeeper 怎么保证主从节点的状态同步? + + Zookeeper 的核心是原子广播机制,这个机制保证了各个 server 之间的同步。实现这个机制的协议叫做 Zab 协议。Zab 协议有两种模式,它们分别是恢复模式和广播模式。 + +##### 恢复模式 + +当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数 server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 server 具有相同的系统状态。 + +##### 广播模式 + +一旦 leader 已经和多数的 follower 进行了状态同步后,它就可以开始广播消息了,即进入广播状态。这时候当一个 server 加入 ZooKeeper 服务中,它会在恢复模式下启动,发现 leader,并和 leader 进行状态同步。待到同步结束,它也参与消息广播。ZooKeeper 服务一直维持在 Broadcast 状态,直到 leader 崩溃了或者 leader 失去了大部分的 followers 支持。 + + +#### 5.四种类型的数据节点 Znode + +(1)PERSISTENT-持久节点 + 除非手动删除,否则节点一直存在于 Zookeeper 上 + +(2)EPHEMERAL-临时节点 + 临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与zookeeper 连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。 + +(3)PERSISTENT_SEQUENTIAL-持久顺序节点 + 基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。 + +(4)EPHEMERAL_SEQUENTIAL-临时顺序节点 + 基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。 + +#### 6.Zookeeper Watcher 机制 – 数据变更通知 + +Zookeeper 允许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。 + +工作机制: + +(1)客户端注册 watcher + +(2)服务端处理 watcher + +(3)客户端回调 watcher + +#### 7.Watcher 特性总结 + +##### 一次性 + +无论是服务端还是客户端,一旦一个 Watcher 被 触 发 ,Zookeeper 都会将其从相应的存储中移除。这样的设计有效的减轻了服务端的压力,不然对于更新非常频繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的压力都非常大。 + +##### 客户端串行执行 + +客户端 Watcher 回调的过程是一个串行同步的过程。 + +##### 轻量 + +3.1、Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。 +3.2、客户端向服务端注册 Watcher 的时候,并不会把客户端真实的 Watcher 对象实体传递到服务端,仅仅是在客户端请求中使用 boolean 类型属性进行了标记。 + +watcher event 异步发送 watcher 的通知事件从 server 发送到 client 是异步的,这就存在一个问题,不同的客户端和服务器之间通过 socket 进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于 Zookeeper 本身提供了 ordering guarantee,即客户端监听事件后,才会感知它所监视 znode发生了变化。所以我们使用 Zookeeper 不能期望能够监控到节点每次的变化。Zookeeper 只能保证最终的一致性,而无法保证强一致性。 + +注册 watcher getData、exists、getChildren + +触发 watcher create、delete、setData + +当一个客户端连接到一个新的服务器上时,watch 将会被以任意会话事件触发。当与一个服务器失去连接的时候,是无法接收到 watch 的。而当 client 重新连接时,如果需要的话,所有先前注册过的 watch,都会被重新注册。通常这是完全透明的。只有在一个特殊情况下,watch 可能会丢失:对于一个未创建的 znode的 exist watch,如果在客户端断开连接期间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这个 watch 事件可能会被丢失。 + +#### 8.客户端注册 Watcher 实现 + +(1)调用 getData()/getChildren()/exist()三个 API,传入 Watcher 对象 + +(2)标记请求 request,封装 Watcher 到 WatchRegistration + +(3)封装成 Packet 对象,发服务端发送 request + +(4)收到服务端响应后,将 Watcher 注册到 ZKWatcherManager 中进行管理 + +(5)请求返回,完成注册。 + +#### 9.服务端处理 Watcher 实现 + +##### 1.服务端接收 Watcher 并存储 + +接收到客户端请求,处理请求判断是否需要注册 Watcher,需要的话将数据节点的节点路径和 ServerCnxn(ServerCnxn 代表一个客户端和服务端的连接,实现了 Watcher 的 process 接口,此时可以看成一个 Watcher 对象)存储在WatcherManager 的 WatchTable 和 watch2Paths 中去。 + +##### 2.Watcher 触发 + +以服务端接收到 setData() 事务请求触发 NodeDataChanged 事件为例: + +2.1 封装 WatchedEvent +将通知状态(SyncConnected)、事件类型(NodeDataChanged)以及节点路径封装成一个 WatchedEvent 对象 + +2.2 查询 Watcher +从 WatchTable 中根据节点路径查找 Watcher + +2.3 没找到;说明没有客户端在该数据节点上注册过 Watcher + +2.4 找到;提取并从 WatchTable 和 Watch2Paths 中删除对应 Watcher(从这里可以看出 Watcher 在服务端是一次性的,触发一次就失效了) + +##### 3.调用 process 方法来触发 Watcher + +这里 process 主要就是通过 ServerCnxn 对应的 TCP 连接发送 Watcher 事件通知。 + +#### 10.客户端回调 Watcher + +- 客户端 SendThread 线程接收事件通知,交由 EventThread 线程回调 Watcher。 +- 客户端的 Watcher 机制同样是一次性的,一旦被触发后,该 Watcher 就失效了。 + +#### 11.ACL 权限控制机制 + +UGO(User/Group/Others) + +目前在 Linux/Unix 文件系统中使用,也是使用最广泛的权限控制方式。是一种粗粒度的文件系统权限控制模式。 + +ACL(Access Control List)访问控制列表 + +包括三个方面: + +权限模式(Scheme) +(1)IP:从 IP 地址粒度进行权限控制 +(2)Digest:最常用,用类似于 username:password 的权限标识来进行权限配置,便于区分不同应用来进行权限控制 +(3)World:最开放的权限控制方式,是一种特殊的 digest 模式,只有一个权限标识“world:anyone” +(4)Super:超级用户 + +授权对象 +授权对象指的是权限赋予的用户或一个指定实体,例如 IP 地址或是机器灯。 + +权限 Permission +(1)CREATE:数据节点创建权限,允许授权对象在该 Znode 下创建子节点 +(2)DELETE:子节点删除权限,允许授权对象删除该数据节点的子节点 +(3)READ:数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等 +(4)WRITE:数据节点更新权限,允许授权对象对该数据节点进行更新操作 +(5)ADMIN:数据节点管理权限,允许授权对象对该数据节点进行 ACL 相关设置操作 + +#### 12.Chroot 特性 + +3.2.0 版本后,添加了 Chroot 特性,该特性允许每个客户端为自己设置一个命名空间。如果一个客户端设置了 Chroot,那么该客户端对服务器的任何操作,都将会被限制在其自己的命名空间下。 + +通过设置 Chroot,能够将一个客户端应用于 Zookeeper 服务端的一颗子树相对应,在那些多个应用公用一个 Zookeeper 进群的场景下,对实现不同应用间的相互隔离非常有帮助。 + +#### 13.会话管理 + +分桶策略:将类似的会话放在同一区块中进行管理,以便于 Zookeeper 对会话进行不同区块的隔离处理以及同一区块的统一处理。 + +分配原则:每个会话的“下次超时时间点”(ExpirationTime) + +计算公式: + +ExpirationTime_ = currentTime + sessionTimeout + +ExpirationTime = (ExpirationTime_ / ExpirationInrerval + 1) * + +ExpirationInterval , ExpirationInterval 是指 Zookeeper 会话超时检查时间间隔,默认 tickTime + +#### 14.服务器角色 + +Leader + +(1)事务请求的唯一调度和处理者,保证集群事务处理的顺序性 + +(2)集群内部各服务的调度者 + +Follower + +(1)处理客户端的非事务请求,转发事务请求给 Leader 服务器 + +(2)参与事务请求 Proposal 的投票 + +(3)参与 Leader 选举投票 + +Observer + +(1)3.0 版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力 + +(2)处理客户端的非事务请求,转发事务请求给 Leader 服务器 + +(3)不参与任何形式的投票 + + +#### 15.Zookeeper 下 Server 工作状态 + +服务器具有四种状态,分别是 LOOKING、FOLLOWING、LEADING、OBSERVING。 + +(1)LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。 + +(2)FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。 + +(3)LEADING:领导者状态。表明当前服务器角色是 Leader。 + +(4)OBSERVING:观察者状态。表明当前服务器角色是 Observer。 + +#### 16.Leader 选举 + +Leader 选举是保证分布式数据一致性的关键所在。当 Zookeeper 集群中的一台服务器出现以下两种情况之一时,需要进入 Leader 选举。 + + + +  (1) 服务器初始化启动。 + +  (2) 服务器运行期间无法和 Leader 保持连接。 + +  下面就两种情况进行分析讲解。 + +  1. 服务器启动时期的 Leader 选举 + +  若进行 Leader 选举,则至少需要两台机器,这里选取 3 台机器组成的服务器集群为例。在集群初始化阶段,当有一台服务器 Server1 启动时,其单独无法进行和完成 Leader 选举,当第二台服务器 Server2 启动时,此时两台机器可以相互通信,每台机器都试图找到 Leader,于是进入 Leader 选举过程。选举过程如下 + +  (1) 每个 Server 发出一个投票。由于是初始情况,Server1 和 Server2 都会将自己作为 Leader 服务器来进行投票,每次投票会包含所推举的服务器的 myid 和 ZXID,使用 (myid, ZXID) 来表示,此时 Server1 的投票为(1, 0),Server2 的投票为(2, 0),然后各自将这个投票发给集群中其他机器。 + +  (2) 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自 LOOKING 状态的服务器。 + +  (3) 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行 PK,PK 规则如下 + +    · 优先检查 ZXID。ZXID 比较大的服务器优先作为 Leader。 + +    · 如果 ZXID 相同,那么就比较 myid。myid 较大的服务器作为 Leader 服务器。 + +  对于 Server1 而言,它的投票是 (1, 0),接收 Server2 的投票为 (2, 0),首先会比较两者的 ZXID,均为 0,再比较 myid,此时 Server2 的 myid 最大,于是更新自己的投票为 (2, 0),然后重新投票,对于 Server2 而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。 + +  (4) 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于 Server1、Server2 而言,都统计出集群中已经有两台机器接受了 (2, 0) 的投票信息,此时便认为已经选出了 Leader。 + +  (5) 改变服务器状态。一旦确定了 Leader,每个服务器就会更新自己的状态,如果是 Follower,那么就变更为 FOLLOWING,如果是 Leader,就变更为 LEADING。 + +  2. 服务器运行时期的 Leader 选举 + +  在 Zookeeper 运行期间,Leader 与非 Leader 服务器各司其职,即便当有非 Leader 服务器宕机或新加入,此时也不会影响 Leader,但是一旦 Leader 服务器挂了,那么整个集群将暂停对外服务,进入新一轮 Leader 选举,其过程和启动时期的 Leader 选举过程基本一致。假设正在运行的有 Server1、Server2、Server3 三台服务器,当前 Leader 是 Server2,若某一时刻 Leader 挂了,此时便开始 Leader 选举。选举过程如下 + +  (1) 变更状态。Leader 挂后,余下的非 Observer 服务器都会讲自己的服务器状态变更为 LOOKING,然后开始进入 Leader 选举过程。 + +  (2) 每个 Server 会发出一个投票。在运行期间,每个服务器上的 ZXID 可能不同,此时假定 Server1 的 ZXID 为 123,Server3 的 ZXID 为 122;在第一轮投票中,Server1 和 Server3 都会投自己,产生投票 (1, 123),(3, 122),然后各自将投票发送给集群中所有机器。 + +  (3) 接收来自各个服务器的投票。与启动时过程相同。 + +  (4) 处理投票。与启动时过程相同,此时,Server1 将会成为 Leader。 + +  (5) 统计投票。与启动时过程相同。 + +  (6) 改变服务器的状态。与启动时过程相同。 + +  2.2 Leader 选举算法分析 + +  在 3.4.0 后的 Zookeeper 的版本只保留了 TCP 版本的 FastLeaderElection 选举算法。当一台机器进入 Leader 选举时,当前集群可能会处于以下两种状态 + +    · 集群中已经存在 Leader。 + +    · 集群中不存在 Leader。 + +  对于集群中已经存在 Leader 而言,此种情况一般都是某台机器启动得较晚,在其启动之前,集群已经在正常工作,对这种情况,该机器试图去选举 Leader 时,会被告知当前服务器的 Leader 信息,对于该机器而言,仅仅需要和 Leader 机器建立起连接,并进行状态同步即可。而在集群中不存在 Leader 情况下则会相对复杂,其步骤如下 + +  (1) 第一次投票。无论哪种导致进行 Leader 选举,集群的所有机器都处于试图选举出一个 Leader 的状态,即 LOOKING 状态,LOOKING 机器会向所有其他机器发送消息,该消息称为投票。投票中包含了 SID(服务器的唯一标识)和 ZXID(事务 ID),(SID, ZXID) 形式来标识一次投票信息。假定 Zookeeper 由 5 台机器组成,SID 分别为 1、2、3、4、5,ZXID 分别为 9、9、9、8、8,并且此时 SID 为 2 的机器是 Leader 机器,某一时刻,1、2 所在机器出现故障,因此集群开始进行 Leader 选举。在第一次投票时,每台机器都会将自己作为投票对象,于是 SID 为 3、4、5 的机器投票情况分别为 (3, 9),(4, 8), (5, 8)。 + +  (2) 变更投票。每台机器发出投票后,也会收到其他机器的投票,每台机器会根据一定规则来处理收到的其他机器的投票,并以此来决定是否需要变更自己的投票,这个规则也是整个 Leader 选举算法的核心所在,其中术语描述如下 + +    · vote_sid:接收到的投票中所推举 Leader 服务器的 SID。 + +    · vote_zxid:接收到的投票中所推举 Leader 服务器的 ZXID。 + +    · self_sid:当前服务器自己的 SID。 + +    · self_zxid:当前服务器自己的 ZXID。 + +  每次对收到的投票的处理,都是对 (vote_sid, vote_zxid) 和(self_sid, self_zxid)对比的过程。 + +    规则一:如果 vote_zxid 大于 self_zxid,就认可当前收到的投票,并再次将该投票发送出去。 + +    规则二:如果 vote_zxid 小于 self_zxid,那么坚持自己的投票,不做任何变更。 + +    规则三:如果 vote_zxid 等于 self_zxid,那么就对比两者的 SID,如果 vote_sid 大于 self_sid,那么就认可当前收到的投票,并再次将该投票发送出去。 + +    规则四:如果 vote_zxid 等于 self_zxid,并且 vote_sid 小于 self_sid,那么坚持自己的投票,不做任何变更。 + +  结合上面规则,给出下面的集群变更过程。 + +  (3) 确定 Leader。经过第二轮投票后,集群中的每台机器都会再次接收到其他机器的投票,然后开始统计投票,如果一台机器收到了超过半数的相同投票,那么这个投票对应的 SID 机器即为 Leader。此时 Server3 将成为 Leader。 + +  由上面规则可知,通常那台服务器上的数据越新(ZXID 会越大),其成为 Leader 的可能性越大,也就越能够保证数据的恢复。如果 ZXID 相同,则 SID 越大机会越大。 + +  2.3 Leader 选举实现细节 + +  1. 服务器状态 + +  服务器具有四种状态,分别是 LOOKING、FOLLOWING、LEADING、OBSERVING。 + +  LOOKING:寻找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。 + +  FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。 + +  LEADING:领导者状态。表明当前服务器角色是 Leader。 + +  OBSERVING:观察者状态。表明当前服务器角色是 Observer。 + +  2. 投票数据结构 + +  每个投票中包含了两个最基本的信息,所推举服务器的 SID 和 ZXID,投票(Vote)在 Zookeeper 中包含字段如下 + +  id:被推举的 Leader 的 SID。 + +  zxid:被推举的 Leader 事务 ID。 + +  electionEpoch:逻辑时钟,用来判断多个投票是否在同一轮选举周期中,该值在服务端是一个自增序列,每次进入新一轮的投票后,都会对该值进行加 1 操作。 + +  peerEpoch:被推举的 Leader 的 epoch。 + +  state:当前服务器的状态。 + +  3. QuorumCnxManager:网络 I/O + +  每台服务器在启动的过程中,会启动一个 QuorumPeerManager,负责各台服务器之间的底层 Leader 选举过程中的网络通信。 + +  (1) 消息队列。QuorumCnxManager 内部维护了一系列的队列,用来保存接收到的、待发送的消息以及消息的发送器,除接收队列以外,其他队列都按照 SID 分组形成队列集合,如一个集群中除了自身还有 3 台机器,那么就会为这 3 台机器分别创建一个发送队列,互不干扰。 + +    · recvQueue:消息接收队列,用于存放那些从其他服务器接收到的消息。 + +    · queueSendMap:消息发送队列,用于保存那些待发送的消息,按照 SID 进行分组。 + +    · senderWorkerMap:发送器集合,每个 SenderWorker 消息发送器,都对应一台远程 Zookeeper 服务器,负责消息的发送,也按照 SID 进行分组。 + +    · lastMessageSent:最近发送过的消息,为每个 SID 保留最近发送过的一个消息。 + +  (2) 建立连接。为了能够相互投票,Zookeeper 集群中的所有机器都需要两两建立起网络连接。QuorumCnxManager 在启动时会创建一个 ServerSocket 来监听 Leader 选举的通信端口 (默认为 3888)。开启监听后,Zookeeper 能够不断地接收到来自其他服务器的创建连接请求,在接收到其他服务器的 TCP 连接请求时,会进行处理。为了避免两台机器之间重复地创建 TCP 连接,Zookeeper 只允许 SID 大的服务器主动和其他机器建立连接,否则断开连接。在接收到创建连接请求后,服务器通过对比自己和远程服务器的 SID 值来判断是否接收连接请求,如果当前服务器发现自己的 SID 更大,那么会断开当前连接,然后自己主动和远程服务器建立连接。一旦连接建立,就会根据远程服务器的 SID 来创建相应的消息发送器 SendWorker 和消息接收器 RecvWorker,并启动。 + +  (3) 消息接收与发送。消息接收:由消息接收器 RecvWorker 负责,由于 Zookeeper 为每个远程服务器都分配一个单独的 RecvWorker,因此,每个 RecvWorker 只需要不断地从这个 TCP 连接中读取消息,并将其保存到 recvQueue 队列中。消息发送:由于 Zookeeper 为每个远程服务器都分配一个单独的 SendWorker,因此,每个 SendWorker 只需要不断地从对应的消息发送队列中获取出一个消息发送即可,同时将这个消息放入 lastMessageSent 中。在 SendWorker 中,一旦 Zookeeper 发现针对当前服务器的消息发送队列为空,那么此时需要从 lastMessageSent 中取出一个最近发送过的消息来进行再次发送,这是为了解决接收方在消息接收前或者接收到消息后服务器挂了,导致消息尚未被正确处理。同时,Zookeeper 能够保证接收方在处理消息时,会对重复消息进行正确的处理。 + +  4. FastLeaderElection:选举算法核心 + +  · 外部投票:特指其他服务器发来的投票。 + +  · 内部投票:服务器自身当前的投票。 + +  · 选举轮次:Zookeeper 服务器 Leader 选举的轮次,即 logicalclock。 + +  · PK:对内部投票和外部投票进行对比来确定是否需要变更内部投票。 + +  (1) 选票管理 + +  · sendqueue:选票发送队列,用于保存待发送的选票。 + +  · recvqueue:选票接收队列,用于保存接收到的外部投票。 + +  · WorkerReceiver:选票接收器。其会不断地从 QuorumCnxManager 中获取其他服务器发来的选举消息,并将其转换成一个选票,然后保存到 recvqueue 中,在选票接收过程中,如果发现该外部选票的选举轮次小于当前服务器的,那么忽略该外部投票,同时立即发送自己的内部投票。 + +  · WorkerSender:选票发送器,不断地从 sendqueue 中获取待发送的选票,并将其传递到底层 QuorumCnxManager 中。 + +  (2) 算法核心 + +![img](https://images2018.cnblogs.com/blog/632316/201808/632316-20180803082744195-266416769.png) + +  上图展示了 FastLeaderElection 模块是如何与底层网络 I/O 进行交互的。Leader 选举的基本流程如下 + +  1. 自增选举轮次。Zookeeper 规定所有有效的投票都必须在同一轮次中,在开始新一轮投票时,会首先对 logicalclock 进行自增操作。 + +  2. 初始化选票。在开始进行新一轮投票之前,每个服务器都会初始化自身的选票,并且在初始化阶段,每台服务器都会将自己推举为 Leader。 + +  3. 发送初始化选票。完成选票的初始化后,服务器就会发起第一次投票。Zookeeper 会将刚刚初始化好的选票放入 sendqueue 中,由发送器 WorkerSender 负责发送出去。 + +  4. 接收外部投票。每台服务器会不断地从 recvqueue 队列中获取外部选票。如果服务器发现无法获取到任何外部投票,那么就会立即确认自己是否和集群中其他服务器保持着有效的连接,如果没有连接,则马上建立连接,如果已经建立了连接,则再次发送自己当前的内部投票。 + +  5. 判断选举轮次。在发送完初始化选票之后,接着开始处理外部投票。在处理外部投票时,会根据选举轮次来进行不同的处理。 + +    · 外部投票的选举轮次大于内部投票。若服务器自身的选举轮次落后于该外部投票对应服务器的选举轮次,那么就会立即更新自己的选举轮次 (logicalclock),并且清空所有已经收到的投票,然后使用初始化的投票来进行 PK 以确定是否变更内部投票。最终再将内部投票发送出去。 + +    · 外部投票的选举轮次小于内部投票。若服务器接收的外选票的选举轮次落后于自身的选举轮次,那么 Zookeeper 就会直接忽略该外部投票,不做任何处理,并返回步骤 4。 + +    · 外部投票的选举轮次等于内部投票。此时可以开始进行选票 PK。 + +  6. 选票 PK。在进行选票 PK 时,符合任意一个条件就需要变更投票。 + +    · 若外部投票中推举的 Leader 服务器的选举轮次大于内部投票,那么需要变更投票。 + +    · 若选举轮次一致,那么就对比两者的 ZXID,若外部投票的 ZXID 大,那么需要变更投票。 + +    · 若两者的 ZXID 一致,那么就对比两者的 SID,若外部投票的 SID 大,那么就需要变更投票。 + +  7. 变更投票。经过 PK 后,若确定了外部投票优于内部投票,那么就变更投票,即使用外部投票的选票信息来覆盖内部投票,变更完成后,再次将这个变更后的内部投票发送出去。 + +  8. 选票归档。无论是否变更了投票,都会将刚刚收到的那份外部投票放入选票集合 recvset 中进行归档。recvset 用于记录当前服务器在本轮次的 Leader 选举中收到的所有外部投票(按照服务队的 SID 区别,如 {(1, vote1), (2, vote2)...})。 + +  9. 统计投票。完成选票归档后,就可以开始统计投票,统计投票是为了统计集群中是否已经有过半的服务器认可了当前的内部投票,如果确定已经有过半服务器认可了该投票,则终止投票。否则返回步骤 4。 + +  10. 更新服务器状态。若已经确定可以终止投票,那么就开始更新服务器状态,服务器首选判断当前被过半服务器认可的投票所对应的 Leader 服务器是否是自己,若是自己,则将自己的服务器状态更新为 LEADING,若不是,则根据具体情况来确定自己是 FOLLOWING 或是 OBSERVING。 + +  以上 10 个步骤就是 FastLeaderElection 的核心,其中步骤 4-9 会经过几轮循环,直到有 Leader 选举产生。 + +#### 17.zookeeper 负载均衡和 nginx 负载均衡区别 + +- zk 的负载均衡是可以调控,nginx 只是能调权重,其他需要可控的都需要自己写插件;但是 nginx 的吞吐量比 zk 大很多,应该说按业务选择用哪种方式。 + +#### 18.集群支持动态添加机器吗? + + 其实就是水平扩容了,Zookeeper 在这方面不太好。两种方式: + +全部重启:关闭所有 Zookeeper 服务,修改配置之后启动。不影响之前客户端的会话。 + +逐个重启:在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供服务。这是比较常用的方式。 + +3.5 版本开始支持动态扩容。 + +#### 19.集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗? + + 集群规则为 2N+1 台,N>0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用 + +#### 20.Zookeeper 对节点的 watch 监听通知是永久的吗?为什么不是永久的? + +不是。官方声明:一个 Watch 事件是一个一次性的触发器,当被设置了 Watch 的数据发生了改变的时候,则服务器将这个改变发送给设置了 Watch 的客户端,以便通知它们。 + + + +为什么不是永久的,举个例子,如果服务端变动频繁,而监听的客户端很多情况下,每次变动都要通知到所有的客户端,给网络和服务器造成很大压力。 +一般是客户端执行 getData(“/ 节点 A”,true),如果节点 A 发生了变更或删除,客户端会得到它的 watch 事件,但是在之后节点 A 又发生了变更,而客户端又没有设置 watch 事件,就不再给客户端发送。 +在实际应用中,很多情况下,我们的客户端不需要知道服务端的每一次变动,我只要最新的数据即可。 + +#### 参考 + +https://blog.csdn.net/weixin_43122090/article/details/103645642 + +https://www.cnblogs.com/lanqiu5ge/p/9405601.html \ No newline at end of file diff --git "a/Docs/ \345\274\202\345\270\270&\345\217\215\345\260\204.md" "b/\345\274\202\345\270\270&\345\217\215\345\260\204.md" similarity index 51% rename from "Docs/ \345\274\202\345\270\270&\345\217\215\345\260\204.md" rename to "\345\274\202\345\270\270&\345\217\215\345\260\204.md" index fbc8555..5cd7859 100644 --- "a/Docs/ \345\274\202\345\270\270&\345\217\215\345\260\204.md" +++ "b/\345\274\202\345\270\270&\345\217\215\345\260\204.md" @@ -61,18 +61,68 @@ throws: - throw是语句抛出异常,出现于函数内部,用来抛出一个具体异常实例,throw被执行后面的语句不起作用,直接转入异常处理阶段。 - throws是函数方法抛出异常,一般写在方法的头部,抛出异常,给方法的调用者进行解决。 -#### 7.什么是Java反射机制? +#### 7.常见的异常 + +NullPointException:空指针异常,对象是null时会抛出,在调用传入对象时尽量判断是否为null,Jdk8里面可以用Optional对象来避免 + +IndexOutOfBoundsException:数组下标越界,数组的下标超过了最大值时会抛出,在迭代循环时检查下标是否越界 + +NumberFormatException:数字类型转化异常,将非数字类型转成数字类型,将类型转化的代码catch住 + +ClassCastException:类型转换异常,发生在强转时,将不同类型转成同一类型,尽量少用强转,或用instanceof(判断继承中子类的实例是否是父类的实现)做类型判断,或多用泛型 + +FileNotFoundException:找不到指定文件,文件路径错误或文件不存在,可能用了绝对路径检查文件是否存在,路径是否写错,多用相对路径 + +ClassNotFoundException:在classpath中找不到引用的类缺乏引用当前类的jar或没有设置classpath或jar损坏-,找到jar并放入classpath中或检查jar是否损坏 + +OutOfMemoryError:内存溢出异常,产生对象太多,内存不够->不要在循环体重创建大量对象,或对象及时回收,增大初始化堆:-Xms 增加最大值:-Xmx + +NoClassDefFoundError:找不到相应的类错误,缺乏当前引用类的jar或jar版本不对->找到jar并放入classpath中或找到合适的版本 + +ConcurrentModificationException:并发修改异常,在集合迭代时修改里面的元素->在迭代时不要修改集合或用并发集合做遍历(如:ConcurrentHashMap) + +NoSuchMethodError:类里找不到相应的方法,一般是jar版本不对,当前引用的jar版本中没有这个方法->检查jar版本是否正确 + +UnsupportedClassVersionError:版本不支持错误,编译class的jdk和运行时候的jdk版本不一致或比较高->将低版本换成高版本 + +StackOverflowError:栈溢出错误,一般是函数的死循环,或递归调用无法退出->检查死循环的代码,或让递归有退出值,或加大栈初始化参数 + +#### 8.异常打印信息组成 + +所处线程名字、异常类名、异常信息、异常堆栈、异常的源码,包名,类名,方法名,行数 + +#### 9.常见方法 + +getMessage:错误信息的字符串解释 + +getCause:返回异常产生的原因,一般是原始异常如果不知道原因返回null + +printStackTrace:打印异常出现的位置或原因 + +toString:返回String格式的Throwable信息,此信息包括Throwable的名字和本地化信息 + +initCause:初始化原始异常 + +PrintStream和PrintWriter作为产生实现重载,这样就能实现打印栈轨迹到文件或流中 + +#### 10.如何自定义异常 + +继承Exception是检查性异常,继承RuntimeException是非检查性异常,一般要复写两个构造方法,用throw抛出新异常 + +如果同时有很多异常抛出,那可能就是异常链,就是一个异常引发另一个异常,另一个异常引发更多异常,一般我们会找它的原始异常来解决问题,一般会在开头或结尾,异常可通过initCause串起来,可以通过自定义异常 + +#### 11.什么是Java反射机制? Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。 -#### 8.举例什么地方用到反射机制? +#### 12.举例什么地方用到反射机制? 1. JDBC中,利用反射动态加载了数据库驱动程序。 2. Web服务器中利用反射调用了Sevlet的服务方法。 3. Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。 4. 很多框架都用到反射机制,注入属性,调用方法,如Spring。 -#### 9.java反射机制的作用 +#### 13.java反射机制的作用 - 在运行时判定任意一个对象所属的类 - 在运行时构造任意一个类的对象; @@ -80,7 +130,7 @@ Java的反射(reflection)机制是指在程序的运行状态中,可以构 - 在运行时调用任意一个对象的方法; - 生成动态代理; -#### 10.Java反射机制类 +#### 14.Java反射机制类 ``` java.lang.Class; //类 @@ -90,20 +140,28 @@ java.lang.reflect.Method;//类的方法 java.lang.reflect.Modifier;//访问权限 ``` -#### 11.反射机制优缺点? +#### 15.反射机制优缺点? 优点:运行期类型的判断,动态加载类,提高代码灵活度。 缺点:性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。 -#### 12.利用反射创建对象? +#### 16.利用反射创建对象? + +1.通过一个全限类名创建一个对象 Class.forName(“全限类名”); + +例如:com.mysql.jdbc.Driver Driver类已经被加载到 jvm中,并且完成了类的初始化工作就行了 类名.class; 获取Class<?> clz 对象 对象.getClass(); + +2.获取构造器对象,通过构造器new出一个对象 Clazz.getConstructor([String.class]); Con.newInstance([参数]); 3.通过class对象创建一个实例对象(就相当与new类名()无参构造器) Cls.newInstance(); + + -1.通过一个全限类名创建一个对象 Class.forName(“全限类名”); 例如:com.mysql.jdbc.Driver Driver类已经被加载到 jvm中,并且完成了类的初始化工作就行了 类名.class; 获取Class<?> clz 对象 对象.getClass(); 2.获取构造器对象,通过构造器new出一个对象 Clazz.getConstructor([String.class]); Con.newInstance([参数]); 3.通过class对象创建一个实例对象(就相当与new类名()无参构造器) Cls.newInstance(); +#### 参考: -参考: +https://blog.csdn.net/qq_37875585/article/details/89340495 -- https://blog.csdn.net/qq_37875585/article/details/89340495 +https://www.cnblogs.com/whoislcj/p/6038511.html -- https://www.cnblogs.com/whoislcj/p/6038511.html +https://blog.csdn.net/Yang_Hui_Liang/article/details/90238678 - ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git "a/Docs/\347\256\200\345\216\206.md" "b/\347\256\200\345\216\206.md" similarity index 97% rename from "Docs/\347\256\200\345\216\206.md" rename to "\347\256\200\345\216\206.md" index 6ce6348..7d2a44a 100644 --- "a/Docs/\347\256\200\345\216\206.md" +++ "b/\347\256\200\345\216\206.md" @@ -1,8 +1,6 @@ ## 简历 -几个制作简历不错的网站: -https://mp.weixin.qq.com/s/z0k922U6jwXe5VYlz33gaA 原文链接:https://www.zhihu.com/question/25002833 ThoughtWorks中国回答 @@ -159,9 +157,9 @@ java大数据工程师— 2013.4月-2015.12月 基本就这些了,希望对大家能有帮助,看起简历来几十秒,码字还是个体力活。 -​ 扫码回复“404”获取404份简历模版 +##### 微信搜索Java小咖秀 回复 “简历1” 获取404份简历模版 + -![getqrcode](https://gitee.com/yizhibuerdai/Imagetools/raw/85449e4e4b14d08e02a5c7683d06518a869880f4/images/getqrcode.jpg) 写在最后: 希望大家都能拿到自己心仪的offer。面试笔记会继续更新优化。 diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200.md" new file mode 100644 index 0000000..579808d --- /dev/null +++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200.md" @@ -0,0 +1,287 @@ +# 计算机基础 + +#### 1.ICMP 是什么协议?处于哪一层? + +ICMP是(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议簇的一个子协议,用于在IP主机、路由器之间传递控制消息。属于网络层协议 + +控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。 + + + +#### 2.什么是程序局部性?为什么会有程序的空间局部性? + +程序局部性是指程序在运行时呈现出局部性规律,在一段时间间隔内,程序的执行是局限在某个部份,所访问的存储空间也只局限在某个区域。 + +程序的空间局部性是指若一个存储单元被访问,那么它附近的单元也可能被访问,这是由于程序的顺序执行引起的。 + + + +#### 3.谈一谈 TCP 与 UDP 的区别。 + +TCP与UDP都是传输层的协议,且都用端口号标识数据所达的进程。 + +TCP提供的是面向连接服务,提供可靠交付。且具有流量控制和拥塞控制。可用于可靠要求高的场合如:SMTP,FTP,HTTP等 + +UDP提供的是无连接服务,提供不可靠交付,且无确认机制。主要用于即时强的场合如:视频聊天,语音电话等。 + + + +#### 4.网络协议的三个核心要素是什么?各有什么作用? + +答: + +语法,定义了数据与控制信息的格式; + +语义,定义了需要发出何种控制信息,完成何种响应动作以及作出何种响应; + +同步,定义了事件实现顺序的详细说明; + + + +#### 5.为了实现重定位,需要哪些硬件? + +答: + +最简单的方式是在系统中增设一个重定位寄存器,用来存放正在执行作业的内存地址,每次访问数据时,由硬件自动将相对地址与重定位寄存器中的起始地址相加,形成实际的特理地址。当然在分页式与分段式系统中,具地址变换机构,以及快表等硬件。 + + + +#### 6.在交互式系统中,非剥夺是不是一个好的策略?为什么? + +答: + +非剥夺方式:分派程序一旦把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生某事件而阻塞时,才把处理机分配给另一个进程。 + +剥夺方式:当一个进程正在运行时,系统可以基于某种原则,剥夺已分配给它的处理机,将之分配给其它进程。剥夺原则有:优先权原则、短进程、优先原则、时间片原则。 + +在分时系统中不剥夺并不是一个好的策略。因为,在分时系统中,除了交互性以外,及时性是很重要的性能因素。当一个作业被阻塞后,CPU就完全空闲了,别的用户的及时性就无法保证了,而完全可以把这些时间分配给别的作业运行。以提高整体的吞吐量。 + + + +#### 7.何为死锁?何为系统调用? + +答: + +死锁:指多个有关进程由于争夺资源而造成的一种僵局,在无外力的情况下这些进程都将无法再向前推进的状态。 + +系统调用:系统调用是OS与应用程序之间的接口,它是用户程序取得OS服务的惟一途径。 + +它与一般的过程调用的区别: + +运行在不同的系统状态。调用程序在运行在用户态,而被调用的程序运行在系统态; + +通过软中断机制,先由用户态转为系统态,经枋心分析后,才能转向相应的系统调用处理子程序;一般的过程调用返回后继续执行,但对系统调用,当调用的进程仍具有最高优先权时,才返回到调用进程继续处理;否则只能等被重新调度; + + + +#### 8.CPU 不执行程序的时候在干什么? + +答: + +当没有被任何程序使用的时候,计算机的处理器被认为是空闲的。当然下面提到的空闲任务不在此列中。当有程序利用CPU空闲时间的时候,就意味着它以较低的优先权运行着,以便不会影响那有正常优先权的程序运行。一般来讲,这会引起CPU消耗更多的电能,而大多数的现代CPU当它们空闲的时候是能够进入省电模式的。大多数操作系统都有个空闲任务,它是一个特殊的任务。仅当CPU无事可做的时候由操作系统调度器载入它。在现代的处理器中,HLT停机指令节省了大量的电能与执量,而空闲任务几乎总是由一个重复执行HLT停机指令的循环组成。 + + + +#### 9.试举例解释一下同步和互斥 + +答: + +同步表现为直接制约,如管道通信,一个进程写,一个进程读,它们是相互制约的。 + +互斥表现为间接制约,比如多个进程同时请求打印机(没使用SPOOLing技术)、多个进程同时请求一张网卡发送数据包等。 + + + +#### 10.在可变分区管理中,需要哪些硬件机制? + +答: + +采用可变分区方式管理时,一般均采用动态重定位方式装入作业。地址变换要靠硬件支持,主要是两个寄存器:基址寄存器和限长寄存器,限长寄存器存放作业所占分区的长度,基址寄存器则存放作业所占分区的起始地址,这两个值确定了一个分区的位置和大小。 + +转换时根据逻辑地址与限长值比较,如果不有超过这个值,表示访问地址合法,再加上基址寄存器中的值就得到了绝对地址了,否则形成“地址越界”中断。 + +#### 11.谈一谈中断和陷入的区别 + +外中断时指来自处理机和内存外部的中断,如I/O中断、定时器中断、外部信号中断等。狭义上也叫中断; + +内中断主要指在处理机和内存内部产生的中断,也称陷入,如校验错、页面失效、溢出、除数为零等; + +中断和陷阱的主要区别: + +(1)陷入通常由处理机正在执行的现行指令引起,而中断则是由与现行指令无关的中断源引起的。 + +(2) 陷阱处理程序提供的服务为当前进程所用,而中断处理程序提供的服务则不是为了当前进程的。 + +(3) CPU在执行完一条指令之后,下一条指令开始之前响应中断,而在一条指令执行中也可以响应陷阱。 + +#### 12.数据库系统和文件系统相比有什么优点? + +答: + +文件系统 + +数据库管理系统 + +某一应用 + +现实世界 + +共享性差,冗余度大 + +共享性高,冗余度小 + +记录内有结构,整体无结构 + +整体结构化,用数据模型描述 + +应用程序自己控制 + +由数据库管理系统提供数据安全性,完整性,并发控制和恢复能力 + +独立性差 + +具有高度的物理独立性和一定的逻辑独立性 + +#### 13.谈一谈计算机网络和分布式计算机系统的区别 + +两者在计算机硬件连接、系统拓朴结构和通信控制等方面基本都是一样的,它们都具有通信和资源共享的功能。 + +区别关键在于:分布式计算机系统是在分布式计算机操作系统支持下,进行分布式数据库处理的,也就是说各互联的计算机可以互相协调工作,共同完成一项任务,多台计算机上并行运行。且具有透明性,用户不知道数据、资源的具体位置,整个网络中所有计算机就像是一台计算机一样;而计算机网络却不具备这种功能,计算机网络系统中的各计算机通常是各自独立进行工作的。 + +#### 14.为什么要引入多道程序技术? + +因为引入多道程序技术后,可以进一步提高了CPU利用率(阻塞),提高内存和I/O设备利用率(小作业把内存浪费了),增加系统吞吐量(两都提高后的必然)。 + +#### 15.何为管态和目态?它们与进程运行状态的关系是什么? + +CPU交替执行操作系统程序和用户程序。管态又叫特权态,系统态或核心态。CPU在管态下可以执行指令系统的全集。通常,操作系统在管态下运行。 + +目态又叫常态或用户态。机器处于目态时,程序只能执行非特权指令。用户程序只能在目态下运行,如果用户程序在目态下执行特权指令,硬件将发生中断,由操作系统获得控制,特权指令执行被禁止,这样可以防止用户程序有意或无意的破坏系统。 + +#### 16.何为网络延时?何为完整性约束? + +时延(delay或latency)是指一个报文或分组从一个网络(或一条链路)的一端传送到另一端所需的时间。 + +数据完整性约束指的是为了防止不符合规范的数据进入数据库,在用户对数据进行插入、修改、删除等操作时,DBMS自动按照一定的约束条件对数据进行监测,使不符合规范的数据不能进入数据库,以确保数据库中存储的数据正确、有效、相容。 + +简单理解:按照一定的约束条件防止不符合规范的数据进入数据库 + +#### 17.谈一谈你对当前 5G 技术和云计算技术的理解 + +5G,第五代移动通信技术,有三个关键特征,超高速率,实现每秒10Gb的下载速率,是4G的100倍。超可靠超低时延,实现1ms的低时延,是4G时延的40分之一;超大连接,实现每平方公里100万的连接数,是4G的100倍。 + +云计算技术:分布式计算的一种,指的是通过网络“云”将巨大的数据计算处理程序分解成无数个小程序,然后,通过多部服务器组成的系统进行处理和分析这些小程序得到结果并返回给用户。云计算具有很强的扩展性和需要性,可以为用户提供一种全新的体验,云计算的核心是可以将很多的计算机资源协调在一起,因此,使用户通过网络就可以获取到无限的资源,同时获取的资源不受时间和空间的限制。 + +#### 18.点对点和端对端工作在哪层?工作机制是什么? + +点对点协议(Point to Point Protocol)的缩写为PPP,是TCP/IP网络协议包的一个成员。PPP是TCP/IP的扩展,它增加了两个额外的功能组: + +(1)它可以通过串行接口传输TCP/IP包; + +(2)它可以安全登录。 + +数据传输的可靠性是通过数据链路层和网络层的点对点和传输层的端对端保证的。点对点是基于MAC地址或者IP地址,是指一个设备发数据给另外一个设备,这些设备是指直连设备包括网卡,路由器,交换机。端对端是网络连接,应用程序之间的远程通信。端对端不需要知道底层是如何传输的,是一条逻辑链路。 + +端到端与点到点是针对网络中传输的两端设备间的关系而言的。端到端传输指的是在数据传输前,经过各种各样的交换设备,在两端设备问建立一条链路,就像它们是直接相连的一样,链路建立后,发送端就可以发送数据,直至数据发送完毕,接收端确认接收成功。点到点系统指的是发送端把数据传给与它直接相连的设备,这台设备在合适的时候又把数据传给与之直接相连的下一台设备,通过一台一台直接相连的设备,把数据传到接收端。 端到端传输的优点是链路建立后,发送端知道接收设备一定能收到,而且经过中间交换设备时不需要进行存储转发,因此传输延迟小。端到端传输的缺点是直到接收端收到数据为止,发送端的设备一直要参与传输。如果整个传输的延迟很长,那么对发送端的设备造成很大的浪费。端到端传输的另一个缺点是如果接收设备关机或故障,那么端到端传输不可能实现。 点到点传输的优点是发送端设备送出数据后,它的任务已经完成,不需要参与整个传输过程,这样不会浪费发送端设备的资源。另外,即使接收端设备关机或故障,点到点传输也可以采用存储转发技术进行缓冲。点到点传输的缺点是发送端发出数据后,不知道接收端能否收到或何时能收到数据。 在一个网络系统的不同分层中,可能用到端到端传输,也可能用到点到点传输。如Internet网,IP及以下各层采用点到点传输,IP层以上采用端到端传输。 + +端对端,点对点,只是称为问题,本质区别很小 + +端对端,主要服务于Application Layer,是说两台主机(终端),跨过网络直接连接 + +点对点,是说两台主机(终端)在局域网中传输。 + +#### 19.DBMS 支持哪几种数据模型?SQL 的四个组成部分是什么? + +常用的是层次模型,网状模型和关系模型(最重要) + +SQL的四个组成部分: + +1、数据库模式定义语言DDL:create用来创建数据库中的各种对象——表、视图、索引、同义词、聚簇等 + +2、数据查询语言dql:基本结构是由SELECT子句,FROM子句和WHERE子句组成的查询块 + +3、数据操纵语言dml:插入INSERT、更新UPDATE和删除DELETE + +4、数据控制语言dcl:用来授予或回收访问数据库的某种特权,并控制数据库操纵事物发生的时间和效果,对数据库实行监视等 + +#### 20.谈一谈网络时延由哪几个部分组成?各产生于何处? + +网络时延主要由发送时延,传播时延,处理时延组成。发送时延是指结点在发送数据时使数据块从结点进入到传输媒体所需的时间,也就是从数据块的第一个比特开始发送算起,到最后一个比特发送完毕所需的时间。发送时延又称为传输时延,它的计算公式是: + +发送时延=数据块长度/信道带宽 + +信道带宽就是数据在信道上的发送速率,它也常称为数据在信道上的传输速率。 + +传播时延是指从发送端发送数据开始,到接收端收到数据(或者从接收端发送确认帧,到发送端收到确认帧),总共经历的时间。 + +传播时延 = d/s + +d = 物理链路的长度 + +s = 介质的信号传播速度 (~2x108 m/sec) + +处理时延是指计算机处理数据所需的时间,与计算机CPU的性能有关。 + +#### 21.TCP/IP 网络协议的核心是什么?如何引出"over everything"和"everythingover"。 + +TCP/IP协议的核心是TCP、UDP和IP协议 + +分层次画出具体的协议来表示TCP/IP协议族,它的特点是上下两头大而中间小:应用层和网络接口都有很多协议,而中间的IP层很小,上层的各种协议都向下汇聚到一个IP协议中。这种很像沙漏计时器形状的TCP/IP协议族表明:TCP/IP协议可以为各种各样的应用提供服务(everything over ip) 同时TCP/IP协议也允许IP协议在各种各样的网络构成的互联网上运行(IP over everything)。 + +#### 22.谈一谈 ARP 地址解析协议的工作原理。 + +网络层以上的协议用IP地址来标识网络接口,但以太数据帧传输时,以物理地址来标识网络接口。因此我们需要进行IP地址与物理地址之间的转化。 + +对于IPv4来说,我们使用ARP地址解析协议来完成IP地址与物理地址的转化(IPv6使用邻居发现协议进行IP地址与物理地址的转化,它包含在ICMPv6中)。 + +ARP协议提供了网络层地址(IP地址)到物理地址(mac地址)之间的动态映射。ARP协议 是地址解析的通用协议。 + +ARP地址解析实现原理 + +每个主机都会在自己的 ARP 缓冲区中建立一个 ARP 列表,以表示 IP 地址和 MAC 地址之间的对应关系。 +主机(网络接口)新加入网络时(也可能只是mac地址发生变化,接口重启等), 会发送免费ARP报文把自己IP地址与Mac地址的映射关系广播给其他主机。 +网络上的主机接收到免费ARP报文时,会更新自己的ARP缓冲区。将新的映射关系更新到自己的ARP表中。 +某个主机需要发送报文时,首先检查 ARP 列表中是否有对应 IP 地址的目的主机的 MAC 地址,如果有,则直接发送数据,如果没有,就向本网段的所有主机发送 ARP 数据包,该数据包包括的内容有:源主机 IP 地址,源主机 MAC 地址,目的主机的 IP 地址等。 +当本网络的所有主机收到该 ARP 数据包时: + +(A)首先检查数据包中的 IP 地址是否是自己的 IP 地址,如果不是,则忽略该数据包。 + +(B)如果是,则首先从数据包中取出源主机的 IP 和 MAC 地址写入到 ARP 列表中,如果已经存在,则覆盖。 + + (C) 然后将自己的 MAC 地址写入 ARP 响应包中,告诉源主机自己是它想要找的 MAC 地址。 + + 6.源主机收到 ARP 响应包后。将目的主机的 IP 和 MAC 地址写入 ARP 列表,并利用此信息发送数据。如果源主机一直没有收到 ARP 响应数据包,表示 ARP 查询失败。 + +#### 23.谈一谈 DNS 域名系统的工作原理 + +DNS(Domain Name System,域名系统) + +因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析) + +每个IP地址都可以有一个主机名,主机名由一个或多个字符串组成,字符串之间用小数点隔开。有了主机名,就不要死记硬背每台IP设备的IP地址,只要记住相对直观有意义的主机名就行了。这就是DNS协议所要完成的功能。 + +主机名到IP地址的映射有两种方式:DNS + +1)静态映射,每台设备上都配置主机到IP地址的映射,各设备独立维护自己的映射表,而且只供本设备使用; + +2)动态映射,建立一套域名解析系统(DNS),只在专门的DNS服务器上配置主机到IP地址的映射,网络上需要使用主机名通信的设备,首先需要到DNS服务器查询主机所对应的IP地址。 + +通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)。在解析域名时,可以首先采用静态域名解析的方法,如果静态域名解析不成功,再采用动态域名解析的方法。可以将一些常用的域名放入静态域名解析表中,这样可以大大提高域名解析效率。 + + 比如我们游览一个网站的时候,打开一个网址, 这个时候 我们的电脑会首先发送一个数据包个 DNS系统 ,DNS系统回应一个数据包给我们,然后再转到我们游览的网站! 那个数据包里面就包含这个 我们访问的这个网站,然后返回来的数据包是 解析成了IP地址,然后就能通过TCP/IP协议通信了! + + 再比如我们发送baidu.com到DNS服务器,DNS服务器发给我们百度的服务器的IP,如果我们直接输入IP就绕开了解析这一个步骤 + +#### 24.何为网桥?防火墙的端口防护是指什么? + +网桥:网桥(Bridge)像一个聪明的中继器。中继器从一个网络电缆里接收信号, 放大它们,将其送入下一个电缆。相比较而言,网桥对从关卡上传下来的信息更敏锐一些。网桥是一种对帧进行转发的技术,根据MAC分区块,可隔离碰撞。网桥将网络的多个网段在数据链路层连接起来。 + +网桥也叫桥接器,是连接两个局域网的一种存储/转发设备,它能将一个大的LAN分割为多个网段,或将两个以上的LAN互联为一个逻辑LAN,使LAN上的所有用户都可访问服务器。 + +防火墙端口防护:指通过对防火墙的端口开关的设置,关闭一些非必需端口,达到一定安全防护目的行为。 + + + +#### 参考 + +https://blog.csdn.net/lingess/article/details/98479704 \ No newline at end of file