├── .gitignore ├── LICENSE ├── README.md ├── books ├── 2.分布式配置中心架构分享-邵俊雄@数人云.pdf ├── HBase_ebook.pdf ├── JVM内存区域划分.pdf ├── JVM性能调优-JVM内存整理及GC回收.pdf ├── Java+Tuning+GuideV1.8.pdf ├── Netty 入门与实战:仿写微信 IM 即时通讯系统.pdf ├── Redis深度历险:核心原理和应用实践.pdf ├── Scalable IO in Java.pdf ├── district_2020.sql ├── nettyBookSourceV2.zip ├── 阿里巴巴Java开发手册(v1.1.0版).pdf ├── 阿里巴巴Java开发手册(v1.2.0).pdf ├── 阿里巴巴Java开发手册_终极版(1.3.0).pdf └── 阿里巴巴Java开发手册v1.4.0.pdf ├── contents ├── Algorithm │ └── README.md ├── Archtecture │ ├── About-Monitor.md │ ├── Cap_Paxos_Alg_Intro.md │ ├── High_Concurrency_Cache.md │ ├── High_Concurrency_Degrade.md │ ├── High_Concurrency_RateLimit.md │ ├── Internet_Company_Archtecture.md │ ├── RBAC_Model.md │ ├── README.md │ ├── Redis_Distributed_Lock_Implementation.md │ ├── Solid_Principle.md │ ├── TCC.md │ └── high_available_arch.md ├── Concurrent │ ├── README.md │ └── Thread_Lifecycle.md ├── Design-Patterns │ └── README.md ├── Elasticsearch │ └── README.md ├── Git │ └── README.md ├── Golang │ └── README.md ├── Gradle │ └── README.md ├── HBase │ └── README.md ├── Interview │ ├── Advanced_Topics.md │ ├── Algorithm_Advanced.md │ ├── Algorithms │ │ └── README.md │ ├── Concurrent_Programming │ │ └── README.md │ ├── Distributed_Transactions.md │ ├── Elasticsearch │ │ └── README.md │ ├── HR_Interview │ │ └── README.md │ ├── Interview_ Highlights.md │ ├── Interview_Final.md │ ├── JVM │ │ └── README.md │ ├── Java_Interview_Summary.md │ ├── Microservices │ │ └── README.md │ ├── MySQL │ │ └── README.md │ ├── Mybatis │ │ └── README.md │ ├── README.md │ ├── Redis │ │ └── README.md │ ├── Spring │ │ └── README.md │ ├── SpringCloud │ │ ├── Eureka.md │ │ ├── Feign.md │ │ ├── Hystrix.md │ │ ├── README.md │ │ └── Ribbon.md │ ├── TCP_HTTP │ │ ├── Cookie_Session.md │ │ ├── HTTP.md │ │ ├── README.md │ │ ├── TCP.md │ │ └── UDP.md │ └── Writing_Code.md ├── JDK Source │ └── README.md ├── JVM │ ├── README.md │ ├── jmm_model.md │ ├── jvm_classloader.md │ └── jvm_space.md ├── Java │ ├── Alibaba_Java_Coding_Guidelines.md │ ├── Apache_Tika_In_Action.md │ ├── CPU_100_Analysis.md │ ├── ConcurrentHashMap_Theory.md │ ├── CopyOnWriteArrayList_Analysis.md │ ├── Filter_Invisible_ASCII_Character.md │ ├── Guava_Join_Split_Usage.md │ ├── HashMap_Theory_Analysis.md │ ├── JDK_New_Features.md │ ├── Java_Env_Config.md │ ├── Java_OOM_Analysis.md │ ├── LinkedList_Analysis.md │ ├── README.md │ └── ThreadLocal_Analysis.md ├── Linux │ └── README.md ├── MQ │ └── README.md ├── Microservices │ ├── Hystrix_Isolation.md │ ├── MSA.md │ └── Short_url.md ├── MyBatis │ └── README.md ├── MySQL │ └── README.md ├── Netty │ └── README.md ├── Others │ └── README.md ├── Redis │ ├── README.md │ ├── Redis_Hash_Datastructure.md │ ├── Redis_List_Datastructure.md │ ├── Redis_Set_Datastructure.md │ └── Redis_Sorted_Set_Datastructure.md ├── Security │ └── README.md ├── Spring Boot │ ├── README.md │ ├── Spring-Boot-Custom-Your-Starter.md │ ├── SpringBoot-Theory-AutoConfigure.md │ └── Spring_Boot_Profile_Usage.md ├── Spring MVC │ └── README.md ├── Spring │ ├── README.md │ ├── Spring Framework-Introduction.md │ ├── Spring4.x-Conditional-Annotation.md │ ├── Spring4.x-Java-Based-Configuration.md │ ├── Spring_AOP_Analysis_Part1.md │ ├── Spring_AOP_Analysis_Part2.md │ ├── Spring_Annotation_Scan.md │ ├── Spring_Bean_Lifecycle.md │ ├── Spring_IOC_Analysis_Part1.md │ └── Spring_Transaction_Analysis.md └── Tools │ ├── Atom_Markdown_Config.md │ ├── Mac_Env_Config.md │ └── Mac_Useful_Skills.md ├── docs └── image │ ├── Http │ ├── README.md │ ├── htt2_binary_frame.jpg │ ├── http2_multiplexing.jpg │ ├── http_request.png │ ├── http_response.png │ ├── tcp_3_handshake.png │ └── tcp_4_bye.png │ ├── Hystrix │ ├── HystrixRollingNumber_bucket.png │ ├── HystrixRollingNumber_window.png │ └── README.md │ ├── Interview │ ├── README.md │ └── System_Archtecture.png │ ├── JAVA_HOME.png │ ├── JDK_Source │ ├── HashMap_jdk8.jpg │ ├── LinkedList_jdk8.png │ ├── README.md │ ├── concurrenthashmap_jdk7.png │ └── concurrenthashmap_jdk8.png │ ├── JVM │ ├── Jvm_Area.jpg │ ├── Jvm_GC_Root.png │ ├── Jvm_collector.png │ ├── Jvm_heap_area.png │ └── README.md │ ├── RBAC │ ├── RBAC_extension.jpg │ ├── RBAC_group.jpg │ ├── RBAC_permission_category.jpg │ └── RBAC_permission_model.jpg │ ├── Redis │ ├── README.md │ ├── redis_ds2.jpg │ └── redis_ds_1.jpg │ ├── Spring-Framework-overview.png │ ├── Spring │ ├── AspectJAwareAdvisorAutoProxyCreator.png │ ├── ClassPathXmlApplicationContext.png │ ├── DefaultListableBeanFactory.png │ └── README.md │ ├── alipay │ └── README.md │ ├── cap.png │ ├── cpu_cache.png │ ├── gbf40 │ ├── README.md │ ├── gbf40_danti.jpg │ └── gbf40_msa.jpg │ ├── git_flow.png │ ├── logo.jpg │ ├── maven_home.png │ ├── tas_list_alipay.jpeg │ ├── task_list_iqj.jpeg │ └── zfb_zqhb.jpg ├── font ├── README.md ├── fangsong.zip └── kaiti.zip └── ppt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | *target/ 3 | !tools/*.jar 4 | *.war 5 | *.zip 6 | *.tar 7 | *.tar.gz 8 | 9 | # Java class files 10 | *.class 11 | 12 | # eclipse ignore 13 | *.settings/ 14 | .project 15 | .classpath 16 | 17 | # idea ignore 18 | .idea/ 19 | *.ipr 20 | *.iml 21 | *.iws 22 | 23 | # temp ignore 24 | *.log 25 | *.log.* 26 | *.cache 27 | *.diff 28 | *.patch 29 | *.tmp 30 | 31 | # system ignore 32 | .DS_Store 33 | Thumbs.db 34 | 35 | sublime-project 36 | sublime-project.sublime-workspace 37 | 38 | coverage-report/ 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FX_SKY 2 | [![License](https://img.shields.io/badge/license-Apache%202-green.svg)](https://www.apache.org/licenses/LICENSE-2.0) 3 | 4 | ## 2020 5 | **快乐工作,认真生活** 6 | 7 | ### August 8 | 八月秋高风怒号,卷我屋上三重茅。 9 | * August 10, 2020 >>> [【泛彩票类】抽奖码生成算法](https://github.com/TFdream/system-design-notes/issues/3) 10 | 11 | 12 | ## 2018 13 | 「但行好事,莫问前程」 14 | 15 | ### December 16 | 日晏霜浓十二月,林疏石瘦第三溪。 17 | * December 20, 2018 >>> [抽奖码生成算法 ](https://github.com/TFdream/blog/issues/50) 18 | * December 10, 2018 >>> [签到系统设计](https://github.com/TFdream/blog/issues/32) 19 | 20 | ### October 21 | 金秋风景如画,十月天高云淡。 22 | * October 11, 2018 >>> [排行榜系统设计](https://github.com/TFdream/blog/issues/21) 23 | 24 | ### September 25 | 星河璀璨,凤舞九天。 26 | * September 27, 2018 >>> [邀请码生成算法](https://github.com/TFdream/blog/issues/20) 27 | * September 19, 2018 >>> [基于Redis的分布式锁实现](https://github.com/TFdream/blog/issues/16) 28 | 29 | ### June 30 | 六月荷花香满湖,红衣绿扇映清波。 31 | * June 04, 2018 >>> [Java中的Copy-On-Write容器](contents/Java/CopyOnWriteArrayList_Analysis.md) 32 | * June 02, 2018 >>> [“异地双活”架构 案例集锦](contents/Archtecture/high_available_arch.md) 33 | 34 | ### May 35 | 五月榴花照眼明,枝间时见子初成。 36 | * May 30, 2018 >>> [Mac 开发环境配置](contents/Tools/Mac_Env_Config.md) 37 | * May 24, 2018 >>> [过滤ASCII码中的不可见字符](contents/Java/Filter_Invisible_ASCII_Character.md) 38 | * May 20, 2018 >>> [Spring Boot 教程 - 编写自定义的Starter](contents/Spring%20Boot/Spring-Boot-Custom-Your-Starter.md) 39 | * May 13, 2018 >>> [jvm OOM异常排查](contents/Java/JVM_OOM_Analysis.md) 40 | * May 12, 2018 >>> [Linux CPU使用率100% 异常排查实践](contents/Java/CPU_100_Analysis.md) 41 | * May 6, 2018 >>> [JDK各个版本的新特性(JDK1.5 - JDK9)](contents/Java/JDK_New_Features.md) 42 | 43 | ### April 44 | 人间四月芳菲尽,山寺桃花始盛开。 45 | * April 28, 2018 >>> [MySQL关于不可重复读与幻读的解决方案](contents/MySQL/MVCC.md) 46 | * April 25, 2018 >>> [从CAP 理论到Paxos 算法](contents/Archtecture/Cap_Paxos_Alg_Intro.md) 47 | * April 24, 2018 >>> [短网址(Short URL)原理及其实现](contents/Microservices/Short_url.md) 48 | * April 8, 2018 >>> [Hystrix 隔离策略: 线程 vs 信号量](contents/Microservices/Hystrix_Isolation.md) 49 | * April 2, 2018 >>> [Spring源码分析系列 — @Transactional实现源码分析](contents/Spring/Spring_Transaction_Analysis.md) 50 | * April 1, 2018 >>> [Spring源码分析系列 — AOP实现源码分析](contents/Spring/Spring_AOP_Analysis_Part1.md) 51 | 52 | ### March 53 | 风光三月连樱笋,美人踌躇白日静。 54 | * March 24, 2018 >>> [Spring源码分析系列 — Spring Bean的生命周期](contents/Spring/Spring_Bean_Lifecycle.md) 55 | * March 16, 2018 >>> [RBAC权限管理](contents/Archtecture/RBAC_Model.md) 56 | * March 15, 2018 >>> [面向对象设计的SOLID原则](contents/Archtecture/Solid_Principle.md) 57 | * March 3, 2018 >>> [Java面试 - 并发篇](contents/Interview/Java_Interview_Concurrent.md) 58 | * March 1, 2018 >>> [Java面试知识点集锦](contents/Interview/Java_Interview_Summary.md) 59 | 60 | ### February 61 | 不知细叶谁裁出,二月春风似剪刀。 62 | * February 8, 2018 >>> [高并发系统设计 - 限流](contents/Archtecture/High_Concurrency_RateLimit.md) 63 | * February 1, 2018 >>> [高并发系统设计 - 降级](contents/Archtecture/High_Concurrency_Degrade.md) 64 | 65 | ### January 66 | 一月秋晴一月泥,南翁此谚似可疑。 67 | * January 29, 2018 >>> [分布式事务之TCC](contents/Archtecture/TCC.md) 68 | * January 1, 2018 >>> [Spring扫描自定义Annotation](contents/Spring/Spring_Annotation_Scan.md) 69 | 70 | ## 2017 71 | 「Stay hungry. Stay foolish.」 72 | 73 | ### December 74 | 日晏霜浓十二月,林疏石瘦第三溪。 75 | * December 25, 2017 >>> [高并发系统设计 - 缓存](contents/Archtecture/High_Concurrency_Cache.md) 76 | 77 | ### November 78 | 江城山寺十一月,北风吹沙雪纷纷。 79 | * November 30, 2017 >>> [监控杂谈](contents/Archtecture/About-Monitor.md) 80 | * November 16, 2017 >>> [Guava教程(一) — Joiner和Splitter](contents/Java/Guava_Join_Split_Usage.md) 81 | * November 15, 2017 >>> [Spring Boot使用@Profile注解实现多环境下配置参数的切换](contents/Spring%20Boot/Spring_Boot_Profile_Usage.md) 82 | * November 3, 2017 >>> [Java后端高级开发面试题集锦之并发篇](contents/Interview/Java_Backend_Interview_Concurrent.md) 83 | * November 1, 2017 >>> [基于Redis实现分布式锁](contents/Archtecture/Redis_Distributed_Lock_Implementation.md) 84 | 85 | ### October 86 | 金秋风景如画,十月天高云淡。 87 | * October 16, 2017 >>> [阿里巴巴Java开发规约插件全球首发](contents/Java/Alibaba_Java_Coding_Guidelines.md) 88 | * October 11, 2017 >>> [使用Apache Tika 检测上传文件类型](contents/Java/Apache_Tika_In_Action.md) 89 | 90 | ### September 91 | 星河璀璨,凤舞九天。 92 | * September 1, 2017 >>> [Java ConcurrentHashMap 源码分析(基于1.7、1.8版本)](contents/Java/ConcurrentHashMap_Theory.md) 93 | 94 | ### August 95 | 八月中秋月正圆,送君吟上木兰船。 96 | 97 | * August 20, 2017 >>> [Spring Boot 教程 - MyBatis多数据源配置]() 98 | 99 | ### July 100 | 七月七日长生殿,夜半无人私语时。 101 | 102 | * July 6, 2017 >>> [Java HashMap 源码分析](contents/Java/HashMap_Theory_Analysis.md) 103 | * July 1, 2017 >>> [Java LinkedList 源码分析](contents/Java/LinkedList_Analysis.md) 104 | 105 | ### June 106 | 六月荷花香满湖,红衣绿扇映清波。 107 | * June 20, 2017 >>> [线程的生命周期](contents/Concurrent/Thread_Lifecycle.md) 108 | * June 2, 2017 >>> [Java ThreadLocal 源码分析](contents/Java/ThreadLocal_Analysis.md) 109 | 110 | 111 | -------------------------------------------------------------------------------- /books/2.分布式配置中心架构分享-邵俊雄@数人云.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/2.分布式配置中心架构分享-邵俊雄@数人云.pdf -------------------------------------------------------------------------------- /books/HBase_ebook.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/HBase_ebook.pdf -------------------------------------------------------------------------------- /books/JVM内存区域划分.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/JVM内存区域划分.pdf -------------------------------------------------------------------------------- /books/JVM性能调优-JVM内存整理及GC回收.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/JVM性能调优-JVM内存整理及GC回收.pdf -------------------------------------------------------------------------------- /books/Java+Tuning+GuideV1.8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/Java+Tuning+GuideV1.8.pdf -------------------------------------------------------------------------------- /books/Netty 入门与实战:仿写微信 IM 即时通讯系统.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/Netty 入门与实战:仿写微信 IM 即时通讯系统.pdf -------------------------------------------------------------------------------- /books/Redis深度历险:核心原理和应用实践.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/Redis深度历险:核心原理和应用实践.pdf -------------------------------------------------------------------------------- /books/Scalable IO in Java.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/Scalable IO in Java.pdf -------------------------------------------------------------------------------- /books/nettyBookSourceV2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/nettyBookSourceV2.zip -------------------------------------------------------------------------------- /books/阿里巴巴Java开发手册(v1.1.0版).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/阿里巴巴Java开发手册(v1.1.0版).pdf -------------------------------------------------------------------------------- /books/阿里巴巴Java开发手册(v1.2.0).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/阿里巴巴Java开发手册(v1.2.0).pdf -------------------------------------------------------------------------------- /books/阿里巴巴Java开发手册_终极版(1.3.0).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/阿里巴巴Java开发手册_终极版(1.3.0).pdf -------------------------------------------------------------------------------- /books/阿里巴巴Java开发手册v1.4.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/books/阿里巴巴Java开发手册v1.4.0.pdf -------------------------------------------------------------------------------- /contents/Algorithm/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/Archtecture/About-Monitor.md: -------------------------------------------------------------------------------- 1 | 监控对于市面上大多数公司来讲主要分为基础、业务、链路,在监控的基础上要去做一些报警等。 2 | 3 | ## 基础监控 4 | 5 | ## 业务监控 6 | 7 | ## 链路监控 8 | 9 | ## 报警 10 | 11 | 12 | -------------------------------------------------------------------------------- /contents/Archtecture/Cap_Paxos_Alg_Intro.md: -------------------------------------------------------------------------------- 1 | ## CAP 理论 2 | [CAP](https://zh.wikipedia.org/wiki/CAP%E5%AE%9A%E7%90%86)定理是由加州大学伯克利分校Eric Brewer教授提出来的,他指出WEB服务无法同时满足一下3个属性: 3 | * 一致性(Consistency) : 客户端知道一系列的操作都会同时发生(生效) 4 | * 可用性(Availability) : 每个操作都必须以可预期的响应结束 5 | * 分区容错性(Partition tolerance) : 即使出现单个组件无法可用,操作依然可以完成 6 | 7 | 根据定理,分布式系统只能满足三项中的两项而不可能满足全部三项。 8 | 9 | ## 2PC & 3PC 10 | 11 | ## Paxos 算法 12 | 13 | ## Raft 算法 14 | 15 | ## 一致性哈希 16 | 17 | ## 康威定律 18 | 在康威的这篇文章中,最有名的一句话就是: 19 | ``` 20 | Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations. - Melvin Conway(1967) 21 | ``` 22 | 中文直译大概的意思就是:设计系统的组织,其产生的设计等同于组织之内、组织之间的沟通结构。 23 | 24 | 25 | ## 参考资料 26 | [从 CAP 理论到 Paxos 算法](http://blog.longjiazuo.com/archives/5369) 27 | [多IDC的数据分布设计(二)](https://timyang.net/tag/paxos/) 28 | -------------------------------------------------------------------------------- /contents/Archtecture/High_Concurrency_Cache.md: -------------------------------------------------------------------------------- 1 | 在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。缓存的目的是提升系统访问速度和增大系统能处理的容量,可谓是抗高并发流量的银弹;而降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉,待高峰或者问题解决后再打开; 2 | 而有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀、抢购)、写服务(如评论、下单)、频繁的复杂查询(评论的最后几页), 3 | 因此需有一种手段来限制这些场景的并发/请求量,即限流。 4 | -------------------------------------------------------------------------------- /contents/Archtecture/High_Concurrency_Degrade.md: -------------------------------------------------------------------------------- 1 | 在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。缓存的目的是提升系统访问速度和增大系统能处理的容量,可谓是抗高并发流量的银弹;而降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉,待高峰或者问题解决后再打开;而有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀、抢购)、写服务(如评论、下单)、频繁的复杂查询(评论的最后几页),因此需有一种手段来限制这些场景的并发/请求量,即限流。 2 | -------------------------------------------------------------------------------- /contents/Archtecture/High_Concurrency_RateLimit.md: -------------------------------------------------------------------------------- 1 | ## 概述 2 | 在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。缓存的目的是提升系统访问速度和增大系统能处理的容量,可谓是抗高并发流量的银弹;而降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉,待高峰或者问题解决后再打开;而有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀、抢购)、写服务(如评论、下单)、频繁的复杂查询(评论的最后几页),因此需有一种手段来限制这些场景的并发/请求量,即限流。 3 | 4 | ## 目的 5 | 限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)。 6 | 7 | ## 应用场景 8 | 一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池、线程池)、限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率);其他还有如限制远程接口调用速率、限制MQ的消费速率。另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。 9 | 10 | 11 | ## 实战 12 | ### 1. RedisRateLimiter 13 | ``` 14 | package redis.ratelimit; 15 | 16 | import demo.pandora.redis.RedisRateLimiterDemo; 17 | import pandora.redis.RedisTemplate; 18 | import java.io.BufferedReader; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.InputStreamReader; 22 | import java.time.Instant; 23 | import java.util.Arrays; 24 | import java.util.List; 25 | 26 | /** 27 | * @author Ricky Fung
28 | * Created on 2018-04-11
29 | */ 30 | public class RedisRateLimiter { 31 | private static final String PREFIX = "META-INF/scripts/"; 32 | 33 | private RedisTemplate redisTemplate; 34 | private final String redisKeyPrefix; 35 | 36 | /** How many requests per second do you want a user to be allowed to do? **/ 37 | private final int replenishRate; 38 | /** How much bursting do you want to allow **/ 39 | private final int burstCapacity; 40 | /** Lua script**/ 41 | private String script; 42 | 43 | public RedisRateLimiter(RedisTemplate redisTemplate, String redisKeyPrefix, 44 | int replenishRate) { 45 | this(redisTemplate, redisKeyPrefix, replenishRate, replenishRate); 46 | } 47 | 48 | public RedisRateLimiter(RedisTemplate redisTemplate, String redisKeyPrefix, 49 | int replenishRate, int burstCapacity) { 50 | this.redisTemplate = redisTemplate; 51 | this.redisKeyPrefix = redisKeyPrefix; 52 | this.replenishRate = replenishRate; 53 | this.burstCapacity = burstCapacity; 54 | this.script = loadScript(); 55 | } 56 | 57 | public Response acquire(String id, int permits) { 58 | // Make a unique key per user. 59 | String prefix = redisKeyPrefix+".{" + id+"}"; 60 | 61 | // You need two Redis keys for Token Bucket. 62 | List keys = Arrays.asList(prefix + ".tokens", prefix + ".timestamp"); 63 | // The arguments to the LUA script. time() returns unixtime in seconds. 64 | List scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", Instant.now().getEpochSecond() + "", permits+""); 65 | 66 | List results; 67 | try { 68 | //执行 Redis Lua 脚本,获取令牌。返回结果为 [是否获取令牌成功, 剩余令牌数] ,其中,1 代表获取令牌成功,0 代表令牌获取失败。 69 | results = (List) redisTemplate.eval(script, keys, scriptArgs); 70 | } catch (Exception e) { 71 | //当 Redis Lua 脚本过程中发生异常,忽略异常,返回 Arrays.asList(1L, -1L) ,即认为获取令牌成功。 72 | // 为什么?在 Redis 发生故障时,我们不希望限流器对 Reids 是强依赖,并且 Redis 发生故障的概率本身就很低。 73 | results = Arrays.asList(1L, -1L); 74 | } 75 | boolean allowed = results.get(0) == 1L; 76 | Long tokensLeft = results.get(1); 77 | Response response = new Response(allowed, tokensLeft); 78 | return response; 79 | } 80 | 81 | private String loadScript() { 82 | try { 83 | String fullName = PREFIX + "request_rate_limiter.lua"; 84 | InputStream in = RedisRateLimiterDemo.class.getClassLoader().getResourceAsStream(fullName); 85 | BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8")); 86 | 87 | String separator = System.getProperty("line.separator"); 88 | 89 | StringBuilder sb = new StringBuilder(2048); 90 | String line = null; 91 | while ((line=br.readLine()) !=null) { 92 | sb.append(line).append(separator); 93 | } 94 | return sb.toString(); 95 | } catch (IOException e) { 96 | e.printStackTrace(); 97 | } 98 | return null; 99 | } 100 | } 101 | 102 | ``` 103 | 104 | ### 2. Redis Lua 脚本 105 | ```META-INF/scripts/request_rate_limiter.lua```,Redis Lua 脚本,实现基于令牌桶算法实现限流。代码如下 : 106 | ``` 107 | local tokens_key = KEYS[1] 108 | local timestamp_key = KEYS[2] 109 | 110 | local rate = tonumber(ARGV[1]) 111 | local capacity = tonumber(ARGV[2]) 112 | local now = tonumber(ARGV[3]) 113 | local requested = tonumber(ARGV[4]) 114 | 115 | local fill_time = capacity/rate 116 | local ttl = math.floor(fill_time*2) 117 | 118 | local last_tokens = tonumber(redis.call("get", tokens_key)) 119 | if last_tokens == nil then 120 | last_tokens = capacity 121 | end 122 | 123 | local last_refreshed = tonumber(redis.call("get", timestamp_key)) 124 | if last_refreshed == nil then 125 | last_refreshed = 0 126 | end 127 | 128 | local delta = math.max(0, now-last_refreshed) 129 | local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) 130 | local allowed = filled_tokens >= requested 131 | local new_tokens = filled_tokens 132 | local allowed_num = 0 133 | if allowed then 134 | new_tokens = filled_tokens - requested 135 | allowed_num = 1 136 | end 137 | 138 | redis.call("setex", tokens_key, ttl, new_tokens) 139 | redis.call("setex", timestamp_key, ttl, now) 140 | 141 | return { allowed_num, new_tokens } 142 | ``` 143 | 144 | * 第 1 至 2 行 :KEYS 方法参数 : 145 | - 第一个参数 :request_rate_limiter.${id}.tokens ,令牌桶剩余令牌数。 146 | - 第二个参数 :request_rate_limiter.${id}.timestamp ,令牌桶最后填充令牌时间,单位:秒。 147 | * 第 4 至 7 行 :ARGV 方法参数 : 148 | - 第一个参数 :replenishRate 。 149 | - 第二个参数 :burstCapacity 。 150 | - 第三个参数 :得到从 1970-01-01 00:00:00 开始的秒数。 151 | - 第四个参数 :消耗令牌数量,默认 1 。 152 | 153 | * 第 9 行 :计算令牌桶填充满令牌需要多久时间,单位:秒。 154 | * 第 10 行 :计算 request_rate_limiter.${id}.tokens / request_rate_limiter.${id}.timestamp 的 ttl 。* 2 保证时间充足。 155 | * 第 12 至 20 行 :调用 get 命令,获得令牌桶剩余令牌数( last_tokens ) ,令牌桶最后填充令牌时间(last_refreshed) 。 156 | * 第 22 至 23 行 :填充令牌,计算新的令牌桶剩余令牌数( filled_tokens )。填充不超过令牌桶令牌上限。 157 | * 第 24 至 30 行 :获取令牌是否成功。 158 | - 若成功,令牌桶剩余令牌数(new_tokens) 减消耗令牌数( requested ),并设置获取成功( allowed_num = 1 ) 。 159 | - 若失败,设置获取失败( allowed_num = 0 ) 。 160 | 161 | * 第 32 至 33 行 :设置令牌桶剩余令牌数( new_tokens ) ,令牌桶最后填充令牌时间(now) 。 162 | * 第 35 行 :返回数组结果,[是否获取令牌成功, 剩余令牌数] 。 163 | 164 | 165 | ### RedisRateLimiter测试 166 | ``` 167 | package demo.redis; 168 | 169 | import redis.ratelimit.RedisRateLimiter; 170 | import redis.ratelimit.Response; 171 | import org.springframework.context.support.ClassPathXmlApplicationContext; 172 | import pandora.core.util.JsonUtils; 173 | import pandora.redis.RedisTemplate; 174 | 175 | /** 176 | * @author Ricky Fung
177 | * Created on 2018-04-11
178 | */ 179 | public class RedisRateLimiterDemo { 180 | 181 | public static void main(String[] args) { 182 | 183 | new RedisRateLimiterDemo().testRateLimit(); 184 | } 185 | 186 | public void testRateLimit() { 187 | 188 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-redis.xml"); 189 | 190 | RedisTemplate redisTemplate = (RedisTemplate) context.getBean("redisTemplate"); 191 | 192 | String userId = "15"; 193 | String keyPrefix = "request_rate_limiter"; 194 | RedisRateLimiter redisRateLimiter = new RedisRateLimiter(redisTemplate, keyPrefix, 20, 30); 195 | 196 | for (int i=0; i<50; i++) { 197 | Response response = redisRateLimiter.acquire(userId, 1); 198 | System.out.println(JsonUtils.toJson(response)); 199 | } 200 | } 201 | 202 | } 203 | 204 | ``` 205 | 打印结果: 206 | ``` 207 | {"allowed":true,"tokensRemaining":29} 208 | {"allowed":true,"tokensRemaining":28} 209 | {"allowed":true,"tokensRemaining":27} 210 | {"allowed":true,"tokensRemaining":26} 211 | {"allowed":true,"tokensRemaining":25} 212 | {"allowed":true,"tokensRemaining":24} 213 | {"allowed":true,"tokensRemaining":23} 214 | {"allowed":true,"tokensRemaining":22} 215 | {"allowed":true,"tokensRemaining":21} 216 | {"allowed":true,"tokensRemaining":20} 217 | {"allowed":true,"tokensRemaining":19} 218 | {"allowed":true,"tokensRemaining":18} 219 | {"allowed":true,"tokensRemaining":17} 220 | {"allowed":true,"tokensRemaining":16} 221 | {"allowed":true,"tokensRemaining":15} 222 | {"allowed":true,"tokensRemaining":14} 223 | {"allowed":true,"tokensRemaining":13} 224 | {"allowed":true,"tokensRemaining":12} 225 | {"allowed":true,"tokensRemaining":11} 226 | {"allowed":true,"tokensRemaining":10} 227 | {"allowed":true,"tokensRemaining":9} 228 | {"allowed":true,"tokensRemaining":8} 229 | {"allowed":true,"tokensRemaining":7} 230 | {"allowed":true,"tokensRemaining":6} 231 | {"allowed":true,"tokensRemaining":5} 232 | {"allowed":true,"tokensRemaining":4} 233 | {"allowed":true,"tokensRemaining":3} 234 | {"allowed":true,"tokensRemaining":2} 235 | {"allowed":true,"tokensRemaining":1} 236 | {"allowed":true,"tokensRemaining":0} 237 | {"allowed":false,"tokensRemaining":0} 238 | {"allowed":false,"tokensRemaining":0} 239 | {"allowed":false,"tokensRemaining":0} 240 | {"allowed":false,"tokensRemaining":0} 241 | {"allowed":false,"tokensRemaining":0} 242 | {"allowed":false,"tokensRemaining":0} 243 | {"allowed":false,"tokensRemaining":0} 244 | {"allowed":false,"tokensRemaining":0} 245 | {"allowed":false,"tokensRemaining":0} 246 | {"allowed":false,"tokensRemaining":0} 247 | {"allowed":false,"tokensRemaining":0} 248 | {"allowed":false,"tokensRemaining":0} 249 | {"allowed":false,"tokensRemaining":0} 250 | {"allowed":false,"tokensRemaining":0} 251 | {"allowed":false,"tokensRemaining":0} 252 | {"allowed":false,"tokensRemaining":0} 253 | {"allowed":false,"tokensRemaining":0} 254 | {"allowed":false,"tokensRemaining":0} 255 | {"allowed":false,"tokensRemaining":0} 256 | {"allowed":false,"tokensRemaining":0} 257 | ``` 258 | 259 | 260 | ## 参考资料 261 | * [Scaling your API with rate limiters](https://stripe.com/blog/rate-limiters) 262 | * [Scaling your API with rate limiters Lua Scripts](https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d) 263 | * [Spring-Cloud-Gateway 源码解析 —— 过滤器 (4.10) 之 RequestRateLimiterGatewayFilterFactory 请求限流](http://www.iocoder.cn/Spring-Cloud-Gateway/filter-request-rate-limiter/) 264 | -------------------------------------------------------------------------------- /contents/Archtecture/Internet_Company_Archtecture.md: -------------------------------------------------------------------------------- 1 | # 各大互联网公司架构演进之路汇总 2 | 3 | ## 各大互联网公司架构演进之路汇总 4 | [各大互联网公司架构演进之路汇总](http://www.hollischuang.com/archives/1036) 5 | 6 | ## 今日头条 7 | [今日头条架构演进之路——高压下的架构演进专题(含PPT)](https://www.toutiao.com/i6304145761982480897/) 8 | -------------------------------------------------------------------------------- /contents/Archtecture/RBAC_Model.md: -------------------------------------------------------------------------------- 1 | RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系。(如下图) 2 | ![RBAC_Permission_Model](https://github.com/TFdream/blog/blob/master/docs/image/RBAC/RBAC_permission_model.jpg) 3 | 4 | 角色是什么?可以理解为一定数量的权限的集合,权限的载体。例如:一个论坛系统,“超级管理员”、“版主”都是角色。版主可管理版内的帖子、可管理版内的用户等,这些是权限。要给某个用户授予这些权限,不需要直接将权限授予用户,可将“版主”这个角色赋予该用户。 5 | 6 | 当用户的数量非常大时,要给系统每个用户逐一授权(授角色),是件非常烦琐的事情。这时,就需要给用户分组,每个用户组内有多个用户。除了可给用户授权外,还可以给用户组授权。这样一来,用户拥有的所有权限,就是用户个人拥有的权限与该用户所在用户组拥有的权限之和。(下图为用户组、用户与角色三者的关联关系) 7 | ![RBAC_group](https://github.com/TFdream/blog/blob/master/docs/image/RBAC/RBAC_group.jpg) 8 | 9 | 在应用系统中,权限表现成什么?对功能模块的操作,对上传文件的删改,菜单的访问,甚至页面上某个按钮、某个图片的可见性控制,都可属于权限的范畴。有些权限设计,会把功能操作作为一类,而把文件、菜单、页面元素等作为另一类,这样构成“用户-角色-权限-资源”的授权模型。而在做数据表建模时,可把功能操作和资源统一管理,也就是都直接与权限表进行关联,这样可能更具便捷性和易扩展性。(见下图) 10 | ![RBAC_Permission_Category](https://github.com/TFdream/blog/blob/master/docs/image/RBAC/RBAC_permission_category.jpg) 11 | 12 | 13 | 请留意权限表中有一列“权限类型”,我们根据它的取值来区分是哪一类权限,如“MENU”表示菜单的访问权限、“OPERATION”表示功能模块的操作权限、“FILE”表示文件的修改权限、“ELEMENT”表示页面元素的可见性控制等。 14 | 15 | 这样设计的好处有二。其一,不需要区分哪些是权限操作,哪些是资源,(实际上,有时候也不好区分,如菜单,把它理解为资源呢还是功能模块权限呢?)。其二,方便扩展,当系统要对新的东西进行权限控制时,我只需要建立一个新的关联表“权限XX关联表”,并确定这类权限的权限类型字符串。 16 | 17 | 这里要注意的是,权限表与权限菜单关联表、权限菜单关联表与菜单表都是一对一的关系。(文件、页面权限点、功能操作等同理)。也就是每添加一个菜单,就得同时往这三个表中各插入一条记录。这样,可以不需要权限菜单关联表,让权限表与菜单表直接关联,此时,须在权限表中新增一列用来保存菜单的ID,权限表通过“权限类型”和这个ID来区分是种类型下的哪条记录。 18 | 19 | 到这里,RBAC权限模型的扩展模型的完整设计图如下: 20 | ![RBAC_Extension](https://github.com/TFdream/blog/blob/master/docs/image/RBAC/RBAC_extension.jpg) 21 | 22 | 随着系统的日益庞大,为了方便管理,可引入角色组对角色进行分类管理,跟用户组不同,角色组不参与授权。例如:某电网系统的权限管理模块中,角色就是挂在区局下,而区局在这里可当作角色组,它不参于权限分配。另外,为方便上面各主表自身的管理与查找,可采用树型结构,如菜单树、功能树等,当然这些可不需要参于权限分配。 23 | 24 | 25 | 26 | ## 参考资料 27 | [RBAC权限管理](https://blog.csdn.net/painsonline/article/details/7183613/) 28 | 29 | 30 | -------------------------------------------------------------------------------- /contents/Archtecture/README.md: -------------------------------------------------------------------------------- 1 | # Archtecture 2 | Archtecture. 3 | -------------------------------------------------------------------------------- /contents/Archtecture/Redis_Distributed_Lock_Implementation.md: -------------------------------------------------------------------------------- 1 | 2 | ## 什么是分布式锁 3 | 4 | ## 实现方式 5 | 分布式锁一般有三种实现方式: 6 | 1. 基于数据库乐观锁; 7 | 2. 基于Redis的分布式锁; 8 | 3. 基于ZooKeeper的分布式锁。 9 | 10 | 本篇博客将介绍第二种方式,基于Redis实现分布式锁。 11 | 本篇博客将详细介绍如何正确地实现Redis分布式锁。 12 | 13 | ## 满足条件 14 | 实现分布式锁需要满足哪些条件呢?我们至少要确保锁的实现同时满足以下条件: 15 | * 高性能: 16 | * 互斥性:在任意时刻,只有一个客户端能持有锁,同一个方法在同一时间只能被一台机器上的一个线程执行。 17 | * 避免发生死锁:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁;另外这把锁是一把可重入锁。 18 | * 容错性:只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。 19 | 20 | ## Redis实现分布式锁 21 | Redis通常可以使用setnx来实现分布式锁。 22 | 23 | 首先我们要通过Maven引入Jedis开源组件,在pom.xml文件加入下面的代码: 24 | ``` 25 | 26 | redis.clients 27 | jedis 28 | 2.9.0 29 | 30 | ``` 31 | 32 | ### 基本版 33 | 1. 加锁 34 | 35 | ``` 36 | public void lock(){ 37 | while(true){ 38 | ret = tryLock(); 39 | if(ret){ //获取到了锁 40 | return; 41 | } 42 | sleep(100); 43 | } 44 | } 45 | ``` 46 | 47 | ``` 48 | public boolean tryLock(){ 49 | Long result = jedis.setnx(lockKey, requestId); 50 | if (result == 1) { 51 | return true; 52 | } 53 | return false; 54 | } 55 | ``` 56 | 2. 解锁 57 | ``` 58 | public void unlock(){ 59 | jedis.del(lockKey); 60 | } 61 | ``` 62 | 63 | setnx来创建一个key,如果key不存在则创建成功返回1,如果key已经存在则返回0。依照上述来判定是否获取到了锁,获取到锁的执行业务逻辑,完毕后删除lock_key,来实现释放锁。 64 | 65 | ### 改进版 66 | 1. 加锁 67 | ``` 68 | public boolean tryLock(long leaseTime, TimeUnit unit) { 69 | String resp = jedis.set(resourceName, getLockName(Thread.currentThread().getId()), "NX", "PX", unit.toMillis(leaseTime)); 70 | if ("OK".equals(resp)) { 71 | return true; 72 | } 73 | return false; 74 | } 75 | 76 | protected String getLockName(long threadId) { 77 | return String.format("%s:%d", id, threadId); 78 | } 79 | ``` 80 | 81 | 2. 解锁 82 | ``` 83 | public void unlock() { 84 | String holder = jedis.get(resourceName); 85 | if (getLockName(Thread.currentThread().getId()).equals(holder)) { //release 86 | jedis.del(resourceName); 87 | } 88 | } 89 | ``` 90 | 91 | ### 终极版 92 | 1. 加锁 93 | ``` 94 | import java.util.concurrent.TimeUnit; 95 | 96 | /** 97 | * Distributed Lock 98 | * @author Ricky Fung 99 | */ 100 | public interface DLock { 101 | 102 | void lock(); 103 | 104 | boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException; 105 | 106 | boolean isLocked(); 107 | 108 | boolean isHeldByCurrentThread(); 109 | 110 | void forceUnlock(); 111 | 112 | void unlock(); 113 | } 114 | ``` 115 | 116 | RedisLock 117 | ``` 118 | import pandora.distlock.DLock; 119 | import pandora.redis.RedisTemplate; 120 | import java.util.ArrayList; 121 | import java.util.List; 122 | import java.util.concurrent.TimeUnit; 123 | 124 | /** 125 | * 参考:[Distributed locks with Redis](https://redis.io/topics/distlock) 126 | * @author Ricky Fung 127 | */ 128 | public class RedisLock implements DLock { 129 | 130 | private String id; //唯一标识id 131 | private String resourceName; //资源名称 132 | private RedisTemplate redisTemplate; 133 | private LuaScript luaScript = new LuaScript(); 134 | 135 | private final List keys = new ArrayList<>(1); 136 | 137 | public RedisLock(String id, String resourceName, RedisTemplate redisTemplate) { 138 | this.id = id; 139 | this.resourceName = resourceName; 140 | this.redisTemplate = redisTemplate; 141 | this.keys.add(resourceName); 142 | } 143 | 144 | @Override 145 | public void lock() { 146 | 147 | } 148 | 149 | @Override 150 | public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { 151 | long time = unit.toMillis(waitTime); 152 | long current = System.currentTimeMillis(); 153 | boolean res = tryAcquire(leaseTime, unit); 154 | // lock acquired 155 | if (res) { 156 | return true; 157 | } 158 | time -= (System.currentTimeMillis() - current); 159 | if (time <= 0) { 160 | return false; 161 | } 162 | while (true) { 163 | long currentTime = System.currentTimeMillis(); 164 | res = tryAcquire(leaseTime, unit); 165 | // lock acquired 166 | if (res) { 167 | return true; 168 | } 169 | time -= (System.currentTimeMillis() - currentTime); 170 | if (time <= 0) { 171 | return false; 172 | } 173 | } 174 | } 175 | 176 | @Override 177 | public boolean isLocked() { 178 | return redisTemplate.exists(resourceName); 179 | } 180 | 181 | @Override 182 | public boolean isHeldByCurrentThread() { 183 | List args = new ArrayList<>(1); 184 | args.add(getLockValue()); 185 | Long update = (Long) redisTemplate.eval(luaScript.buildHeldByCurrentThreadScript(), keys, 186 | args); 187 | if (update==1) { 188 | return true; 189 | } 190 | return false; 191 | } 192 | 193 | @Override 194 | public void forceUnlock() { 195 | redisTemplate.del(resourceName); 196 | } 197 | 198 | @Override 199 | public void unlock() { 200 | List args = new ArrayList<>(1); 201 | args.add(getLockValue()); 202 | Long update = (Long) redisTemplate.eval(luaScript.buildUnLockScript(), keys, args); 203 | 204 | } 205 | 206 | private boolean tryAcquire(long leaseTime, TimeUnit unit) { 207 | List args = new ArrayList<>(4); 208 | args.add(getLockValue()); 209 | args.add("NX"); 210 | args.add("PX"); 211 | args.add(String.valueOf(unit.toMillis(leaseTime))); 212 | Long update = (Long) redisTemplate.eval(luaScript.buildLockScript(), keys, args); 213 | if (update==1) { 214 | return true; 215 | } 216 | return false; 217 | } 218 | 219 | protected String getLockValue() { 220 | return String.format("%s:%d", id, Thread.currentThread().getId()); 221 | } 222 | 223 | } 224 | 225 | ``` 226 | 227 | LuaScript 228 | ``` 229 | /** 230 | * @author Ricky Fung 231 | */ 232 | public class LuaScript { 233 | 234 | /** 235 | * SET resource_name my_random_value NX PX 30000 236 | * @return 237 | */ 238 | public String buildLockScript() { 239 | StringBuilder sb = new StringBuilder(120); 240 | sb.append("if redis.call('set', KEYS[1], ARGV[1], ARGV[2], ARGV[3], tonumber(ARGV[4])) == 1 then ") 241 | .append("\t return 1 ") 242 | .append("end ") 243 | .append("if redis.call('get', KEYS[1]) == ARGV[1] then ") 244 | .append("\t return 1 ") 245 | .append("else ") 246 | .append("\t return 0 ") 247 | .append("end"); 248 | return sb.toString(); 249 | } 250 | 251 | public String buildUnLockScript() { 252 | StringBuilder sb = new StringBuilder(120); 253 | sb.append("if redis.call('get', KEYS[1]) == ARGV[1] then ") 254 | .append("\t return redis.call('del', KEYS[1]) ") 255 | .append("else ") 256 | .append("\t return 0 ") 257 | .append("end"); 258 | return sb.toString(); 259 | } 260 | 261 | public String buildHeldByCurrentThreadScript() { 262 | StringBuilder sb = new StringBuilder(120); 263 | sb.append("if redis.call('get', KEYS[1]) == ARGV[1] then ") 264 | .append("\t return 1 ") 265 | .append("else ") 266 | .append("\t return 0 ") 267 | .append("end"); 268 | return sb.toString(); 269 | } 270 | 271 | } 272 | ``` 273 | 274 | ``` 275 | /** 276 | * @author Ricky Fung 277 | */ 278 | public class Panda { 279 | private final RedisTemplate redisTemplate; 280 | 281 | public Panda(RedisTemplate redisTemplate) { 282 | this.redisTemplate = redisTemplate; 283 | } 284 | 285 | public DLock getLock(String name) { 286 | return new RedisLock(UUIDUtils.getUUID(), name, redisTemplate); 287 | } 288 | } 289 | ``` 290 | 291 | ``` 292 | Panda panda = new Panda(redisTemplate); 293 | 294 | @Test 295 | public void testReentrantLock() throws InterruptedException { 296 | String res = "res1"; 297 | DLock lock = panda.getLock(res); 298 | try { 299 | boolean success = lock.tryLock(5, 30, TimeUnit.SECONDS); 300 | System.out.println("key:"+res+" value:"+redisTemplate.get(res)); 301 | if (success) { 302 | System.out.println("re-entry:"+lock.tryLock(5, 30, TimeUnit.SECONDS)); 303 | System.out.println("YES"); 304 | } else { 305 | System.out.println("NO"); 306 | } 307 | } finally { 308 | lock.unlock(); 309 | } 310 | System.out.println("key:"+res+" value:"+redisTemplate.get(res)); 311 | } 312 | ``` 313 | 314 | 315 | -------------------------------------------------------------------------------- /contents/Archtecture/Solid_Principle.md: -------------------------------------------------------------------------------- 1 | S.O.L.I.D是面向对象设计和编程(OOD&OOP)中几个重要编码原则(Programming Priciple)的首字母缩写。 2 | 3 | | 名称 | 全称 | 中文 | 4 | | --- | --- | --- | 5 | | SRP | The Single Responsibility Principle | 单一责任原则 | 6 | | OCP | The Open Closed Principle| 开放封闭原则 | 7 | | LSP | The Liskov Substitution Principle | 里氏替换原则 | 8 | | ISP | The Interface Segregation Principle | 接口分离原则 | 9 | | DIP | The Dependency Inversion Principle | 依赖倒置原则 | 10 | 11 | ## 单一责任原则(Single Responsibility Principle) 12 | 当需要修改某个类的时候原因有且只有一个(THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE)。换句话说就是让一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类。 13 | 14 | ## 开闭原则(Open Close Principle) 15 | 开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 16 | 17 | ## 里氏代换原则(Liskov Substitution Principle) 18 | 里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 19 | 20 | ## 接口隔离原则(Interface Segregation Principle) 21 | 这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。 22 | 23 | ## 依赖倒转原则(Dependence Inversion Principle) 24 | 这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。 25 | 26 | ## 迪米特法则,又称最少知道原则(Demeter Principle) 27 | 最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。 28 | 29 | -------------------------------------------------------------------------------- /contents/Archtecture/TCC.md: -------------------------------------------------------------------------------- 1 | 2 | Atomikos公司对微服务的分布式事务所提出的[RESTful TCC](https://www.atomikos.com/Blog/TransactionManagementAPIForRESTTCC)解决方案。 3 | 4 | ## RESTful TCC 5 | RESTful TCC模式分3个阶段执行: 6 | 7 | Trying阶段: 主要针对业务系统检测及作出预留资源请求,若预留资源成功,则返回确认资源的链接与过期时间。 8 | Confirm阶段: 主要是对业务系统的预留资源作出确认,要求TCC服务的提供方要对确认预留资源的接口实现幂等性,若Confirm成功则返回204,资源超时则证明已经被回收且返回404。 9 | Cancel阶段: 主要是在业务执行错误或者预留资源超时后执行的资源释放操作,Cancel接口是一个可选操作,因为要求TCC服务的提供方实现自动回收的功能,所以即便是不认为进行Cancel,系统也会自动回收资源。 10 | -------------------------------------------------------------------------------- /contents/Archtecture/high_available_arch.md: -------------------------------------------------------------------------------- 1 | # 异地双活 2 | 3 | ## 实践案例 4 | * [在游戏运维实战中摸索前行的“异地双活”架构](https://mp.weixin.qq.com/s?__biz=MzA3MDk1MTM0MQ==&mid=2247484668&idx=1&sn=329cb7f2660b7108ac1595cb55d7981f&chksm=9f344c3ea843c5282f94784af762cc895cf84912d85f25a74dd9b45f53da3dbcd95fb8344946&scene=21#wechat_redirect) 5 | 6 | ## 参考资料 7 | * 阿里异地多活与同城双活的架构演进:http://myslide.cn/slides/733 8 | * 异地多活设计辣么难?其实是你想多了!:https://yq.aliyun.com/articles/57715 9 | * 饿了么MySQL异地多活的数据双向复制经验谈:http://dbaplus.cn/news-11-1399-1.html 10 | * 专访阿里巴巴毕玄:异地多活数据中心项目的来龙去脉:http://www.infoq.com/cn/articles/interview-alibaba-bixuan 11 | * 阿里巴巴“异地多活”技术:http://blog.csdn.net/psy1100/article/details/68483520?utm_source=itdadao&utm_medium=referral 12 | * 荔枝FM:异地多活IDC机房架构:http://lock522.b0.upaiyun.com/5-liuyaohua.pdf 13 | * 支付宝架构演进(单元化):http://blog.csdn.net/daiyudong2020/article/details/50550471,http://www.10tiao.com/html/46/201605/2650992365/1.html(视频) 14 | * 蚂蚁金服的单元化所解决的问题(http://www.infoq.com/cn/articles/technical-architecture-of-alipay-and-ant-check-later) 15 | * 微博“异地多活”部署经验谈:http://www.infoq.com/cn/articles/weibo-multi-datacenter-deployments 16 | -------------------------------------------------------------------------------- /contents/Concurrent/README.md: -------------------------------------------------------------------------------- 1 | # 多线程&并发编程 2 | 3 | ## 1、说说进程、线程、协程之间的区别 4 | 简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。 5 | 6 | 线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。同一进程中的多个线程之间可以并发执行。 7 | 8 | ## 2、什么是守护线程?它和非守护线程有什么区别 9 | 程序运行完毕,JVM会等待非守护线程完成后关闭,但是jvm不会等待守护线程。守护线程最典型的例子就是GC线程。 10 | 11 | 12 | ## 3、线程的状态有哪些 13 | 请参考我的另外一篇文章:Java 线程的状态及切换 14 | 15 | ## 4、创建两种线程的方式?他们有什么区别? 16 | * 通过实现java.lang.Runnable 17 | * 通过扩展java.lang.Thread类. 18 | 19 | 相比扩展Thread,实现Runnable接口可能更优,原因有二: 20 | * Java不支持多继承,因此扩展Thread类就代表这个子类不能扩展其他类.而实现Runnable接口的类还可能扩展另一个类。 21 | * 类可能只要求可执行即可,因此集成整个Thread类的开销过大。 22 | 23 | ## 5、Runnable和Callable的区别 24 | Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已; Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。 25 | 26 | 这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性, 某条线程是否执行了?某条线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。 而Callable+Future/FutureTask却可以获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务,真的是非常有用。 27 | 28 | ## 6、Thread yield和join 区别 29 | Thread.yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。 30 | 31 | Thread.join 把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的join()方法,那么直到线程A执行完毕后,才会继续执行线程B。 32 | 33 | ## 7、synchronized和ReentrantLock的区别 34 | synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。 既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上: 35 | * ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁 36 | * ReentrantLock可以获取各种锁的信息 37 | * ReentrantLock可以灵活地实现多路通知 38 | 39 | 另外,二者的锁机制其实也是不一样的:ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word。 40 | 41 | ## 8、AtomicInteger 内部实现 42 | 其实就是 CAS + volatile,参考:Java AtomicInteger原理分析 43 | 44 | 45 | ## 9、如何在两个线程间共享数据 46 | 通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的。 47 | 48 | 49 | ## 10、ThreadLoal 实现原理 50 | 简单说ThreadLocal就是一种以空间换时间的做法在每个Thread里面维护了一个ThreadLocal。ThreadLocalMap把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了。 51 | 52 | 详细参考:ThreadLocal源码深入分析 53 | 54 | ## 11、ThreadPoolExecutor 构造参数有哪些?各代表什么意义? 55 | 56 | ## 12、ConcurrentHashMap 实现原理 57 | 58 | ## 13、volatile关键字的作用 59 | 简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。 意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。 60 | volatile关键字可以保证 可见性和 禁止指令重排序。 61 | 62 | 在Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽各个硬件平台和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。Java语言本身对 **原子性**、**可见性**以及**有序性**。 63 | 64 | ## 14、CyclicBarrier和CountDownLatch区别 65 | 这两个类非常类似,都在java.util.concurrent下,都可以用来表示代码运行到某个点上,二者的区别在于: 66 | * CyclicBarrier的某个线程运行到某个点上之后,该线程即停止运行,直到所有的线程都到达了这个点,所有线程才重新运行;CountDownLatch则不是,某线程运行到某个点上之后,只是给某个数值-1而已,该线程继续运行 67 | * CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务 68 | * CyclicBarrier可重用,CountDownLatch不可重用,计数值为0该CountDownLatch就不可再用了 69 | 70 | ## 15、有哪些多线程开发良好的实践? 71 | * 给线程命名; 72 | * 最小化同步范围; 73 | * 优先使用volatile; 74 | * 尽可能使用更高层次的并发工具而非wait和notify()来实现线程通信,如BlockingQueue,Semeaphore; 75 | * 优先使用并发容器而非同步容器; 76 | * 考虑使用线程池 77 | 78 | 79 | -------------------------------------------------------------------------------- /contents/Concurrent/Thread_Lifecycle.md: -------------------------------------------------------------------------------- 1 | ## 线程的生命周期 2 | Java语言中定义了5种线程状态,在任意一个时间点,一个线程只能有且只有其中一种状态,这5种状态是: 3 | 4 | - 新建(New):创建后尚未启动的线程处于这种状态。 5 | - 运行(Runable):包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。 6 | - 无限期等待(Waiting):处于这种状态的线程不会被分配CPU执行时间,它们要等待被其他线程显式地唤醒。以下方法会让线程陷入无限期的等待状态: 7 | - 没有设置timeout参数的Object.wait()方法; 8 | - 没有设置timeout参数的Thread.join()方法; 9 | - LockSupport.park()方法; 10 | - 限期等待(Timed Waiting):处于这种状态的线程也不会被分配CPU执行时间,不过无须等待被其他线程显式地唤醒,在一定时间之后它们会由操作系统自动唤醒。以下方法会让线程进入限期等待状态: 11 | - Thread.sleep()方法; 12 | - 设置了timeout参数的Object.wait()方法; 13 | - 设置了timeout参数的Thread.join()方法; 14 | - LockSupport.parkNanos()方法; 15 | - LockSupport.parkUntil()方法; 16 | - 阻塞(Blocked):线程被阻塞了,“阻塞状态”与“等待状态”的区别是:“阻塞状态”在等待着获取到一个排它锁,这个事件将在另外一个线程放弃这个锁的时候发生;而“等待状态”则是在等待一段时间,或者唤醒动作的发生。在程序等待进入同步区域(synchronized)的时候,线程将进入这种状态。 17 | - 结束(Terminated):已终止的线程状态,线程已经结束执行。 18 | 19 | ## 线程间的状态转换 20 | ### 1、新建(New) 21 | 新创建了一个线程对象,还未调用start()方法。 22 | ``` 23 | Thread thread = new Thread(); 24 | ``` 25 | ### 2、就绪(Ready) 26 | 线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中 获取cpu 的使用权 。 27 | 28 | ### 3、运行中(Running) 29 | 可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。 30 | 31 | ### 4、限期等待(Timed Waiting) 32 | 也可以称作 TIMED_WAITING(有等待时间的等待状态)。 33 | 34 | 线程主动调用以下方法: 35 | * Thread.sleep方法; 36 | * Object的wait方法,带有时间; 37 | * Thread.join方法,带有时间; 38 | * LockSupport的parkNanos方法,带有时间。 39 | 40 | ### 5、无限期等待(Waiting) 41 | 运行中(Running)的线程执行了以下方法: 42 | * Object的wait方法,并且没有使用timeout参数; 43 | * Thread的join方法,没有使用timeout参数; 44 | * LockSupport的park方法; 45 | * Conditon的await方法。 46 | 47 | ### 6、阻塞(Blocked) 48 | 阻塞状态是指线程因为某种原因放弃了cpu 使用权,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分两种: 49 | 50 | * 同步阻塞:运行(running)的线程进入了一个synchronized方法,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。 51 | * 其他阻塞:运行(running)的线程发出了I/O请求时,JVM会把该线程置为阻塞状态。当I/O处理完毕时,线程重新转入可运行(runnable)状态。 52 | 53 | ### 7、结束(Terminated) 54 | 线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。 55 | 56 | 57 | ----- 58 | 结束 59 | 60 | -------------------------------------------------------------------------------- /contents/Design-Patterns/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/Elasticsearch/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/Git/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/Golang/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/Gradle/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/HBase/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/Interview/Advanced_Topics.md: -------------------------------------------------------------------------------- 1 | ## JDK各个版本的新特性 2 | [JDK各个版本的新特性(JDK1.5 - JDK9)](https://github.com/TFdream/blog/blob/master/contents/Java/JDK_New_Features.md) 3 | 4 | 5 | ## 秒杀系统的设计 6 | * [淘宝大秒系统设计详解-许令波](https://mp.weixin.qq.com/s?__biz=MzI3MzEzMDI1OQ==&mid=2651815451&idx=1&sn=141001dbadf3efc5f791735496d4a329&chksm=f0dc2867c7aba1714878dbc2ab1a33eac7fc5933f072b31e37a93071af485d06cb24774a2e25&mpshare=1&scene=1&srcid=11220PBqTM2PrSZL09WmzT1r) 7 | * [秒杀系统架构优化思路](https://mp.weixin.qq.com/s/5aMN9SqaWa57rYGgtdAF_A) 8 | 9 | 10 | ## CPU 使用率100%排查 11 | 某服务器上部署了若干tomcat实例,即若干垂直切分的Java站点服务,以及若干Java微服务,突然收到运维的CPU异常告警。 12 | 13 | 问:如何定位是哪个服务进程导致CPU过载,哪个线程导致CPU过载,哪段代码导致CPU过载? 14 | 15 | [Linux CPU使用率100% 异常排查实践](https://github.com/TFdream/blog/blob/master/contents/Java/CPU_100_Analysis.md) 16 | 17 | ## OOM异常排查 18 | [Java OOM异常排查](https://github.com/TFdream/blog/blob/master/contents/Java/Java_OOM_Analysis.md) 19 | 20 | ## JIT 21 | * [JVM内联函数](https://blog.csdn.net/ke_weiquan/article/details/51946174) 22 | * [逃逸分析]() 23 | 24 | ## JVM性能调优 25 | * [Java性能优化指南1.8版,及唯品会的实战](http://calvin1978.blogcn.com/articles/javatuning.html) 26 | * [一份平民化的应用性能优化检查列表(完整篇)](http://calvin1978.blogcn.com/articles/checklist.html) 27 | 28 | ## 领域驱动设计(DDD) 29 | 30 | 31 | ## 关注的技术领域 32 | 33 | -------------------------------------------------------------------------------- /contents/Interview/Algorithm_Advanced.md: -------------------------------------------------------------------------------- 1 | # 高级篇 2 | 3 | ## 斐波那契数列 4 | 又称兔子数列,或者黄金分割数列。指的是这样一个数列: 5 | 0、1、1、2、3、5、8、13、21……从第三项起,它的每一项都等于前两项的和。 6 | 即:F(0)=1,F(1)=1; F(n) = F(n-1) + F(n-2) (n>=2); 7 | 8 | ### 解法1 9 | ``` 10 | public class FibonacciDemo { 11 | 12 | public static void main(String[] args) { 13 | 14 | FibonacciDemo fibonacciDemo = new FibonacciDemo(); 15 | System.out.println(fibonacciDemo.fib(9)); 16 | } 17 | 18 | public int fib(int n) { 19 | int a=0, b=1, sum = 0; 20 | for (int i=1; inext指向前一个空间 90 | newHead = p; //新链表的头移动到p,扩长一步链表 91 | p = tmp; //p指向原始链表p指向的下一个空间 92 | } 93 | return newHead; 94 | } 95 | 96 | ``` 97 | 98 | 测试代码: 99 | ``` 100 | package juice.redis; 101 | 102 | /** 103 | * @author Ricky Fung 104 | */ 105 | public class LinkListReverse { 106 | 107 | public static void main(String[] args) { 108 | 109 | LinkListReverse reverse = new LinkListReverse(); 110 | Node head = reverse.buildLinkList(); 111 | reverse.printLinkList(head); 112 | 113 | Node newHead = reverse.reverse(head); 114 | 115 | reverse.printLinkList(newHead); 116 | } 117 | 118 | public Node reverse(Node head) { 119 | if (head==null || head.next==null) { ////链表为空或者仅1个数直接返回 120 | return head; 121 | } 122 | Node newHead = null; 123 | Node p = head; 124 | while (p!=null) { 125 | Node tmp = p.next; //暂存p下一个地址,防止变化指针指向后找不到后续的数 126 | p.next = newHead; //p->next指向前一个空间 127 | newHead = p; //新链表的头移动到p,扩长一步链表 128 | p = tmp; //p指向原始链表p指向的下一个空间 129 | } 130 | return newHead; 131 | } 132 | 133 | static class Node { 134 | private int value; 135 | private Node next; 136 | } 137 | 138 | private void printLinkList(Node head) { 139 | Node temp = head; 140 | while (temp !=null) { 141 | System.out.print(" " + temp.value); 142 | temp = temp.next; 143 | } 144 | System.out.println(""); 145 | } 146 | 147 | private Node buildLinkList() { 148 | Node head = new Node(); 149 | Node prev = head; 150 | for (int i=0; i<10; i++) { 151 | Node node = new Node(); 152 | node.value = i+1; 153 | prev.next = node; 154 | 155 | prev = node; 156 | } 157 | return head; 158 | } 159 | } 160 | 161 | ``` 162 | ## 有一个先递减后递增的数组,写一个算法找出数组中的最小值(美团二面) 163 | 解题思路: 164 | 二分查找 165 | 166 | ## 有一个2亿个整数的文件,求最大(最小)的5%个数(阿里云二面) 167 | 168 | ## 实现一个延迟队列(阿里云三面) 169 | 实现一个延迟队列,要求任务到执行时间后才能取出。 170 | 171 | ## n个整数中找出连续m个数加和是最大 172 | 173 | ## 找出一个数组中的两个数字,让这两个数字之和等于一个给定的值 174 | 175 | ## 从1亿个数中找出最大的N个数 176 | 问题: 177 | 从一亿个数中找出最大的一万个数 178 | 179 | 解题思路: 180 | 假设前一万个数是我们需要找的一万个数,但是假设不一定成立,那么,我们只需要讲后续元素与前一万个数中的最小元素比较,如果最小元素比后续元素小,则交换数据,这样只需要遍历一遍大数组就能得到正确解。时间复杂度为O(n). 181 | 182 | ## 2亿个数的文件中,统计当中出现次数最多的N个数据 183 | 184 | ## 有一个包含20亿个32位整数的大文件,在其中找到出现次数最多的数,内存限制为2GB 185 | 186 | ## 从10亿个ip找出出现次数最多的N个IP 187 | IP可以转换为32位int,等价为 ```从10亿个整数中找出出现次数最多的N个数```。 188 | 189 | 通常比较好的方案是分治+Trie树/hash+小顶堆(就是上面提到的最小堆),即先将数据集按照Hash方法分解成多个小数据集,然后使用Trie树或者Hash统计每个小数据集中的query词频,之后用小顶堆求出每个数据集中出现频率最高的前K个数,最后在所有top K中求出最终的top K。 190 | 191 | -------------------------------------------------------------------------------- /contents/Interview/Algorithms/README.md: -------------------------------------------------------------------------------- 1 | # 算法篇 2 | 3 | ## 1.归并排序 4 | 归并排序算法伪代码如下: 5 | ``` 6 | // 归并排序算法, A是数组,n表示数组大小 7 | merge_sort(A, n) { 8 | merge_sort_c(A, 0, n-1) 9 | } 10 | 11 | // 递归调用函数 12 | merge_sort_c(A, p, r) { 13 | // 递归终止条件 14 | if p >= r then return 15 | 16 | // 取p到r之间的中间位置q 17 | q = (p+r) / 2 18 | // 分治递归 19 | merge_sort_c(A, p, q) 20 | merge_sort_c(A, q+1, r) 21 | // 将A[p...q]和A[q+1...r]合并为A[p...r] 22 | merge(A[p...r], A[p...q], A[q+1...r]) 23 | } 24 | ``` 25 | 26 | java实现代码如下: 27 | ``` 28 | /** 29 | * @author Ricky Fung 30 | */ 31 | public class MergeSort { 32 | 33 | // 归并排序算法, A是数组,n表示数组大小 34 | public void mergeSort(int[] arr, int n) { 35 | merge_sort_c(arr, 0, n-1); 36 | } 37 | 38 | // 递归调用函数 39 | private void merge_sort_c(int[] arr, int p, int r) { 40 | // 递归终止条件 41 | if (p >= r) 42 | return; 43 | 44 | // 取p到r之间的中间位置q 45 | int q = (p+r) / 2; 46 | // 分治递归 47 | merge_sort_c(arr, p, q); 48 | merge_sort_c(arr, q+1, r); 49 | 50 | // 将A[p...q]和A[q+1...r]合并为A[p...r] 51 | merge(arr, p, q, r); 52 | } 53 | 54 | /** 55 | * merge函数的作用就是,将已经有序的 A[p…q]和 A[q+1…r]合并成一个有序的数组,并且放入 A[p…r]。 56 | * @param arr 57 | * @param p 58 | * @param q 59 | * @param r 60 | */ 61 | private void merge(int[] arr, int p, int q, int r) { 62 | int i = p, j = q+1, k = 0; // 初始化变量i, j, k 63 | int[] tmp = new int[r-p+1]; // 申请一个大小跟A[p...r]一样的临时数组 64 | while (i<=q && j<=r) { 65 | if (arr[i] <= arr[j]) { 66 | tmp[k++] = arr[i++]; // i++等于i = i+1 67 | } else { 68 | tmp[k++] = arr[j++]; 69 | } 70 | } 71 | // 判断哪个子数组中有剩余的数据 72 | int start = i, end = q; 73 | if (j <= r ) { //A[q+1…r] 有剩余 74 | start = j; 75 | end = r; 76 | } 77 | 78 | // 将剩余的数据拷贝到临时数组tmp 79 | while (start <= end) { 80 | tmp[k++] = arr[start++]; 81 | } 82 | 83 | // 将tmp中的数组拷贝回A[p...r] 84 | for (i=0; i= r then return 101 | 102 | q = partition(A, p, r) // 获取分区点 103 | quick_sort_c(A, p, q-1) 104 | quick_sort_c(A, q+1, r) 105 | } 106 | ``` 107 | 108 | 1.递归实现 109 | ``` 110 | /** 111 | * @author Ricky Fung 112 | */ 113 | public class QuickSort { 114 | 115 | // 快速排序,A是数组,n表示数组的大小 116 | public void quickSort(int[] A, int n) { 117 | quick_sort_c(A, 0, n-1); 118 | } 119 | 120 | // 快速排序递归函数,p,r为下标 121 | private void quick_sort_c(int[] A, int p, int r) { 122 | if (p >= r) 123 | return; 124 | 125 | int q = partition(A, p, r); // 获取分区点 126 | quick_sort_c(A, p, q-1); 127 | quick_sort_c(A, q+1, r); 128 | } 129 | 130 | private int partition(int[] A, int p, int r) { 131 | //随机选择一个元素作为 pivot(一般情况下,可以选择 p 到 r 区间的最后一个元素) 132 | int pivot = A[r]; 133 | //然后对 A[p…r]分区,函数返回 pivot 的下标。 134 | int i = p; 135 | for (int j=p; j<=r-1; j++) { 136 | if (A[j] < pivot) { 137 | ArrayUtils.swap(A, i, j); 138 | i = i+1; 139 | } 140 | } 141 | ArrayUtils.swap(A, i, r); 142 | return i; 143 | } 144 | } 145 | ``` 146 | 147 | 2.非递归 148 | ``` 149 | 150 | ``` 151 | 152 | ## 3.堆排序 153 | ``` 154 | /** 155 | * @author Ricky Fung 156 | */ 157 | public class HeapSort { 158 | 159 | public static void main(String[] args) { 160 | int[] arr = {6, 2, 9, 5, 4, 7, 12, 8, 10}; 161 | 162 | //排序 163 | sort(arr, arr.length); 164 | 165 | PrintUtils.printArray(arr); 166 | } 167 | 168 | // n表示数据的个数,数组a中的数据从下标1到n的位置。 169 | public static void sort(int[] arr, int n) { 170 | int[] a = new int[n+1]; 171 | System.arraycopy(arr, 0, a, 1, n); 172 | 173 | buildHeap(a, n); 174 | int k = n; 175 | while (k > 1) { 176 | ArrayUtils.swap(a, 1, k); 177 | --k; 178 | heapify(a, k, 1); 179 | } 180 | 181 | // 182 | System.arraycopy(a, 1, arr, 0, n); 183 | } 184 | 185 | //建堆 186 | private static void buildHeap(int[] a, int n) { 187 | for (int i = n/2; i >= 1; --i) { 188 | heapify(a, n, i); 189 | } 190 | } 191 | 192 | //堆化 193 | private static void heapify(int[] a, int n, int i) { 194 | while (true) { 195 | int maxPos = i; 196 | if (i*2 <= n && a[i] < a[i*2]) { 197 | maxPos = i * 2; 198 | } 199 | if (i*2+1 <= n && a[maxPos] < a[i*2+1]) { 200 | maxPos = i * 2 + 1; 201 | } 202 | if (maxPos == i) { 203 | break; 204 | } 205 | ArrayUtils.swap(a, i, maxPos); 206 | i = maxPos; 207 | } 208 | } 209 | } 210 | ``` 211 | -------------------------------------------------------------------------------- /contents/Interview/Concurrent_Programming/README.md: -------------------------------------------------------------------------------- 1 | # 并发编程 2 | 3 | ## 线程 4 | ### 线程的生命周期 5 | Java语言中定义了5种线程状态,在任意一个时间点,一个线程只能有且只有其中一种状态,这5种状态是: 6 | 7 | - 新建(New):创建后尚未启动的线程处于这种状态。 8 | - 运行(Runable):包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。 9 | - 无限期等待(Waiting):处于这种状态的线程不会被分配CPU执行时间,它们要等待被其他线程显式地唤醒。以下方法会让线程陷入无限期的等待状态: 10 | - 没有设置timeout参数的Object.wait()方法; 11 | - 没有设置timeout参数的Thread.join()方法; 12 | - LockSupport.park()方法; 13 | - 限期等待(Timed Waiting):处于这种状态的线程也不会被分配CPU执行时间,不过无须等待被其他线程显式地唤醒,在一定时间之后它们会由操作系统自动唤醒。以下方法会让线程进入限期等待状态: 14 | - Thread.sleep()方法; 15 | - 设置了timeout参数的Object.wait()方法; 16 | - 设置了timeout参数的Thread.join()方法; 17 | - LockSupport.parkNanos()方法; 18 | - LockSupport.parkUntil()方法; 19 | - 阻塞(Blocked):线程被阻塞了,“阻塞状态”与“等待状态”的区别是:“阻塞状态”在等待着获取到一个排它锁,这个事件将在另外一个线程放弃这个锁的时候发生;而“等待状态”则是在等待一段时间,或者唤醒动作的发生。在程序等待进入同步区域(synchronized)的时候,线程将进入这种状态。 20 | - 结束(Terminated):已终止的线程状态,线程已经结束执行。 21 | 22 | #### 线程间的状态转换 23 | #### 1、新建(New) 24 | 新创建了一个线程对象,还未调用start()方法。 25 | ``` 26 | Thread thread = new Thread(); 27 | ``` 28 | #### 2、就绪(Ready) 29 | 线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中 获取cpu 的使用权 。 30 | 31 | #### 3、运行中(Running) 32 | 可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。 33 | 34 | #### 4、限期等待(Timed Waiting) 35 | 也可以称作 TIMED_WAITING(有等待时间的等待状态)。 36 | 37 | 线程主动调用以下方法: 38 | * Thread.sleep方法; 39 | * Object的wait方法,带有时间; 40 | * Thread.join方法,带有时间; 41 | * LockSupport的parkNanos方法,带有时间。 42 | 43 | #### 5、无限期等待(Waiting) 44 | 运行中(Running)的线程执行了以下方法: 45 | * Object的wait方法,并且没有使用timeout参数; 46 | * Thread的join方法,没有使用timeout参数; 47 | * LockSupport的park方法; 48 | * Conditon的await方法。 49 | 50 | #### 6、阻塞(Blocked) 51 | 阻塞状态是指线程因为某种原因放弃了cpu 使用权,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分两种: 52 | 53 | * 同步阻塞:运行(running)的线程进入了一个synchronized方法,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。 54 | * 其他阻塞:运行(running)的线程发出了I/O请求时,JVM会把该线程置为阻塞状态。当I/O处理完毕时,线程重新转入可运行(runnable)状态。 55 | 56 | #### 7、结束(Terminated) 57 | 线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。 58 | 59 | 具体参考:[Java线程的状态](https://www.jianshu.com/p/dbbcceb6bc2a) 60 | 61 | ### 创建线程的方式 62 | 两种方式: 63 | * 通过实现java.lang.Runnable 64 | * 通过扩展java.lang.Thread类. 65 | 66 | 相比扩展Thread,实现Runnable接口可能更优,原因有二: 67 | * Java不支持多继承,因此扩展Thread类就代表这个子类不能扩展其他类,而实现Runnable接口的类还可能扩展另一个类; 68 | * 类可能只要求可执行即可,因此继承自Thread类的开销过大。 69 | 70 | ### Runnable和Callable的区别 71 | Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已; Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。 72 | 73 | ### Thread的 sleep() 、join()、yield() 有什么区别 74 | 75 | Thread.sleep() 使当前线程(即调用sleep方法的线程暂停一段时间),给其它的线程运行的机会,不考虑其它线程的优先级的,并且不释放资源锁,也就是说如果有synchronized同步块,其它线程仍然是不能访问共享数据的; 76 | 77 | Thread.yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程转入到就绪状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。 78 | 79 | Thread.join 把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的join()方法,那么直到线程A执行完毕后,才会继续执行线程B。 80 | 81 | ### 线程池的实现原理 82 | 83 | 具体参考:[ThreadPoolExecutor 线程池源码分析](https://juejin.im/post/5a7abe75f265da4e7e10a19e) 84 | 85 | ### 如何合理配置核心线程数? 86 | 分析下线程池处理的程序是CPU密集型,还是IO密集型。 87 | 88 | 《java并发编程实战》,里面有讲,要正确地设置线程池的大小,你必须估算出任务的等待时间和计算时间的比值,这种估算不会很精确。也可以使用另一种方式,就是:在某一个基准负载下,分别设置不同大小的线程池来运行应用程序,并观察cpu利用率的水平。 89 | 90 | **线程数=cpu可用核心数/(1-阻塞系数)**,其中阻塞系数的取值在[0,1]之间。计算密集型任务的阻塞系数为0,而IO密集型任务的阻塞系数则接近1。一般,我们让线程执行的任务是比较复杂的,不会是单一的计算密集型任务,或者单一的IO密集型任务,通常会夹杂着。那么就需要我们去计算阻塞系数了。**阻塞系数的定义就是执行该任务阻塞的时间与(阻塞时间+计算时间)的比值,也就是w/(w+c)**。 91 | 92 | CPU密集型:核心线程数 = CPU核数 + 1 93 | 94 | IO密集型:核心线程数 = CPU核数 * 2 95 | 96 | ### ThreadLocal实现原理 97 | 简单说ThreadLocal就是一种以空间换时间的做法在每个Thread里面维护了一个ThreadLocal。ThreadLocalMap把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了。 98 | 99 | 具体参考:[ThreadLocal源码分析](http://blog.csdn.net/top_code/article/details/51397397) 100 | 101 | ## 锁机制 102 | ### Java中的有哪些锁 103 | 比如Synchronized和ReentrantLock。 104 | 105 | ### synchronized的实现原理 106 | Synchronized是JVM实现的一种锁,其中锁的获取和释放分别是monitorenter和monitorexit指令,该锁在实现上分为了偏向锁、轻量级锁和重量级锁,其中偏向锁在1.6是默认开启的,轻量级锁在多线程竞争的情况下会膨胀成重量级锁,有关锁的数据都保存在对象头中。 107 | 108 | ### ReentrantLock的实现原理 109 | ReentrantLock是基于AQS实现的。 110 | 111 | ### 什么是AQS 112 | 在AQS内部会保存一个状态变量state,通过CAS修改该变量的值,修改成功的线程表示获取到该锁,没有修改成功,或者发现状态state已经是加锁状态,则通过一个Waiter对象封装线程,添加到等待队列中,并挂起等待被唤醒。 113 | 114 | ### CAS的实现原理 115 | CAS(Compare and Swap)是通过Unsafe类的compareAndSwap方法实现的,第一个参数是要修改的对象,第二个参数是对象中要修改变量的偏移量,第三个参数是修改之前的值,第四个参数是预想修改后的值。 116 | 117 | ### CAS缺点 118 | CAS存在一个很明显的问题,即ABA问题。 119 | 问题:如果变量V初次读取的时候是A,并且在准备赋值的时候检查到它仍然是A,那能说明它的值没有被其他线程修改过了吗? 120 | 如果在这段期间曾经被改成B,然后又改回A,那CAS操作就会误认为它从来没有被修改过。针对这种情况,java并发包中提供了一个带有标记的原子引用类AtomicStampedReference,它可以通过控制变量值的版本来保证CAS的正确性。 121 | 122 | ### AtomicInteger 内部实现 123 | 其实就是 CAS + volatile 124 | 125 | ### synchronized 与 lock 的区别 126 | ReentrantLock 在加锁和内存上提供的语义上与内置锁相同,此外它还提供了一些其他功能,包括定时的锁等待、可中断的锁等待、公平性,以及实现非块结构的加锁。 127 | 128 | ### 乐观锁&悲观锁 129 | 130 | 131 | ## CountDownLatch 132 | 在多线程协作完成业务功能时,有时候需要等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能,在这种的业务场景下,通常可以使用Thread类的join方法,让主线程等待被join的线程执行完之后,主线程才能继续往下执行。当然,使用线程间消息通信机制也可以完成。其实,java并发工具类中为我们提供了类似“倒计时”这样的工具类,可以十分方便的完成所说的这种业务场景。 133 | 134 | 下面来看些CountDownLatch的一些重要方法: 135 | ``` 136 | //调用该方法的线程等到构造方法传入的N减到0的时候,才能继续往下执行; 137 | await() throws InterruptedException; 138 | 139 | //与上面的await方法功能一致,只不过这里有了时间限制,调用该方法的线程等到指定的timeout时间后,不管N是否减至为0,都会继续往下执行; 140 | await(long timeout, TimeUnit unit); 141 | 142 | //使CountDownLatch初始值N减1; 143 | countDown(); 144 | 145 | //获取当前CountDownLatch维护的值; 146 | long getCount(); 147 | 148 | ``` 149 | 150 | CountDownLatch的构造方法看起: 151 | ``` 152 | public CountDownLatch(int count) 153 | ``` 154 | 构造方法会传入一个整型数N,之后调用CountDownLatch的countDown方法会对N减一,知道N减到0的时候,当前调用await方法的线程继续执行。 155 | 156 | 157 | **另外,需要注意的是,当调用CountDownLatch的countDown方法时,当前线程是不会被阻塞,会继续往下执行**。 158 | 159 | ## CyclicBarrier 160 | CyclicBarrier也是一种多线程并发控制的实用工具,和CountDownLatch一样具有等待计数的功能,但是相比于CountDownLatch功能更加强大。 161 | 162 | 为了理解CyclicBarrier,这里举一个通俗的例子。开运动会时,会有跑步这一项运动,我们来模拟下运动员入场时的情况,假设有6条跑道,在比赛开始时,就需要6个运动员在比赛开始的时候都站在起点了,裁判员吹哨后才能开始跑步。跑道起点就相当于“barrier”,是临界点,而这6个运动员就类比成线程的话,就是这6个线程都必须到达指定点了,意味着凑齐了一波,然后才能继续执行,否则每个线程都得阻塞等待,直至凑齐一波即可。cyclic是循环的意思,也就是说CyclicBarrier当多个线程凑齐了一波之后,仍然有效,可以继续凑齐下一波。 163 | 164 | 下面来看下CyclicBarrier的主要方法: 165 | ``` 166 | //等到所有的线程都到达指定的临界点 167 | await() throws InterruptedException, BrokenBarrierException 168 | 169 | //与上面的await方法功能基本一致,只不过这里有超时限制,阻塞等待直至到达超时时间为止 170 | await(long timeout, TimeUnit unit) throws InterruptedException, 171 | BrokenBarrierException, TimeoutException 172 | 173 | //获取当前有多少个线程阻塞等待在临界点上 174 | int getNumberWaiting() 175 | 176 | //用于查询阻塞等待的线程是否被中断 177 | boolean isBroken() 178 | 179 | 180 | //将屏障重置为初始状态。如果当前有线程正在临界点等待的话,将抛出BrokenBarrierException。 181 | void reset() 182 | 183 | ``` 184 | 185 | 另外需要注意的是,CyclicBarrier提供了这样的构造方法: 186 | ``` 187 | public CyclicBarrier(int parties, Runnable barrierAction) 188 | ``` 189 | 可以用来,当指定的线程都到达了指定的临界点的时,接下来执行的操作可以由barrierAction传入即可。 190 | 191 | ## CountDownLatch与CyclicBarrier的比较 192 | CountDownLatch与CyclicBarrier都是用于控制并发的工具类,都可以理解成维护的就是一个计数器,但是这两者还是各有不同侧重点的: 193 | * CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;CountDownLatch强调一个线程等多个线程完成某件事情。CyclicBarrier是多个线程互等,等大家都完成,再携手共进。 194 | * 调用CountDownLatch的countDown方法后,当前线程并不会阻塞,会继续往下执行;而调用CyclicBarrier的await方法,会阻塞当前线程,直到CyclicBarrier指定的线程全部都到达了指定点的时候,才能继续往下执行; 195 | * CountDownLatch方法比较少,操作比较简单,而CyclicBarrier提供的方法更多,比如能够通过getNumberWaiting(),isBroken()这些方法获取当前多个线程的状态,并且CyclicBarrier的构造方法可以传入barrierAction,指定当所有线程都到达时执行的业务功能; 196 | * CountDownLatch是不能复用的,而CyclicLatch是可以复用的。 197 | 198 | ## Semaphore 199 | Semaphore可以理解为信号量,用于控制资源能够被并发访问的线程数量,以保证多个线程能够合理的使用特定资源。Semaphore就相当于一个许可证,线程需要先通过acquire方法获取该许可证,该线程才能继续往下执行,否则只能在该方法出阻塞等待。当执行完业务功能后,需要通过release()方法将许可证归还,以便其他线程能够获得许可证继续执行。 200 | Semaphore可以用于做流量控制,特别是公共资源有限的应用场景,比如数据库连接。假如有多个线程读取数据后,需要将数据保存在数据库中,而可用的最大数据库连接只有10个,这时候就需要使用Semaphore来控制能够并发访问到数据库连接资源的线程个数最多只有10个。在限制资源使用的应用场景下,Semaphore是特别合适的。 201 | 202 | 下面来看下Semaphore的主要方法: 203 | ``` 204 | //获取许可,如果无法获取到,则阻塞等待直至能够获取为止 205 | void acquire() throws InterruptedException 206 | 207 | //同acquire方法功能基本一样,只不过该方法可以一次获取多个许可 208 | void acquire(int permits) throws InterruptedException 209 | 210 | //释放许可 211 | void release() 212 | 213 | //释放指定个数的许可 214 | void release(int permits) 215 | 216 | //尝试获取许可,如果能够获取成功则立即返回true,否则,则返回false 217 | boolean tryAcquire() 218 | 219 | //与tryAcquire方法一致,只不过这里可以指定获取多个许可 220 | boolean tryAcquire(int permits) 221 | 222 | //尝试获取许可,如果能够立即获取到或者在指定时间内能够获取到,则返回true,否则返回false 223 | boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException 224 | 225 | //与上一个方法一致,只不过这里能够获取多个许可 226 | boolean tryAcquire(int permits, long timeout, TimeUnit unit) 227 | 228 | //返回当前可用的许可证个数 229 | int availablePermits() 230 | 231 | ``` 232 | 233 | ## ConcurrentHashMap实现原理 234 | [谈谈ConcurrentHashMap1.7和1.8的不同实现](https://www.jianshu.com/p/e694f1e868ec) 235 | 236 | ## 生产者 — 消费者问题 237 | 生产者-消费者模式是一个十分经典的多线程并发协作的模式,弄懂生产者-消费者问题能够让我们对并发编程的理解加深。所谓生产者-消费者问题,实际上主要是包含了两类线程,一种是生产者线程用于生产数据,另一种是消费者线程用于消费数据,为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为;而消费者只需要从共享数据区中去获取数据,就不再需要关心生产者的行为。但是,这个共享数据区域中应该具备这样的线程间并发协作的功能: 238 | * 如果共享数据区已满的话,阻塞生产者继续生产数据放置入内; 239 | * 如果共享数据区为空的话,阻塞消费者继续消费数据; 240 | 241 | 在实现生产者消费者问题时,可以采用三种方式: 242 | * 使用Object的wait/notify的消息通知机制; 243 | * 使用Lock的Condition的await/signal的消息通知机制; 244 | * 使用BlockingQueue实现。 245 | 246 | 本文主要将这三种实现方式进行总结归纳。 247 | 248 | [Java实现生产者-消费者模型的几种方法](https://www.jianshu.com/p/9f6b7fb891dd) 249 | 250 | 251 | ## volatile关键字 252 | volatile关键字提供了内存可见性和禁止内存重排序。 253 | 因为在虚拟机内存中有主内存和工作内存的概念,每个cpu都有自己的工作内存,当读取一个普通变量时,优先读取工作内存的变量,如果工作内存中没有对应的变量,则从主内存中加载到工作内存,对工作内存的普通变量进行修改,不会立马同步到主内存,内存可见性保证了在多线程的场景下,保证了线程A对变量的修改,其它线程可以读到最新值&&%%…… 254 | 255 | 当对volatile修饰的变量进行写操作时,直接把最新值写到主内存中,并清空其它cpu工作内存中该变量所在的内存行数据,当对volatile修饰的变量进行读操作时,会读取主内存的数据 256 | 257 | ## 有哪些多线程开发良好的实践? 258 | * 给线程命名; 259 | * 最小化同步范围; 260 | * 优先使用volatile; 261 | * 尽可能使用更高层次的并发工具而非wait和notify()来实现线程通信,如BlockingQueue,Semeaphore; 262 | * 优先使用并发容器而非同步容器; 263 | * 考虑使用线程池 264 | 265 | ## happen-before原则 266 | 先行发生(happen-before)原则: 267 | 268 | 1. **程序次序规则**:在一个线程内,书写在前面的操作happen-before书写在后面的操作。这条规则是说,在单线程 中操作间happen-before关系完全是由源代码的顺序决定的,这里的前提“在同一个线程中”是很重要的,这条规则也称为单线程规则 。这个规则多少说得有些简单了,考虑到控制结构和循环结构,书写在后面的操作可能happen-before书写在前面的操作,不过我想读者应该明白我的意思。 269 | 2. **管程锁定规则**:对锁的unlock操作happen-before后续的对同一个锁的lock操作。这里的“后续”指的是时间上的先后关系,unlock操作发生在退出同步块之后,lock操作发生在进入同步块之前。这是条最关键性的规则,线程安全性主要依赖于这条规则。但是仅仅是这条规则仍然不起任何作用,它必须和下面这条规则联合起来使用才显得意义重大。这里关键条件是必须对“同一个锁”的lock和unlock。 如果操作A happen-before操作B,操作B happen-before操作C,那么操作A happen-before操作C。这条规则也称为传递规。 270 | 3. **volatile变量规则**:对volatile字段的写操作happen-before后续的对同一个字段的读操作.(Java5 新增)。 271 | 4. **线程启动规则**:Thread对象的start()方法先行发生于此线程的每一个动作。 272 | 5. **线程终止规则**:线程中的所有操作都先行发生于此线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()方法的返回值等手段检测到线程已经终止执行。 273 | 6. **线程中断规则**:对线程的interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件发生,可以通过Thread.interrupted()方法检测到是否有中断发生。 274 | 7. **对象终结规则**:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。 275 | 8. **传递性**:如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。 276 | 277 | Java语言无须任何同步手段保障就能成立的先行发生规则就只有上面这些了。 278 | 279 | 280 | 281 | 本文将会不定期更新,欢迎大家持续关注! 282 | 283 | 284 | -------------------------------------------------------------------------------- /contents/Interview/Distributed_Transactions.md: -------------------------------------------------------------------------------- 1 | ## 分布式算法 2 | ### Paxos 3 | 4 | ### Raft 5 | 6 | ### Zookeeper 原理 7 | Zookeeper的角色 8 | * 领导者(leader),负责进行投票的发起和决议,更新系统状态 9 | * 学习者(learner),包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并想客户端返回结果,在选主过程中参与投票 10 | * Observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度 11 | * 客户端(client),请求发起方 12 | 13 | Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。 14 | 15 | 为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。 16 | 17 | ## 分布式事务一致性 18 | 19 | ### 2PC 20 | * [分布式事务2PC && 3PC](http://int64.me/2016/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A12PC%20&&%203PC.html) 21 | 22 | ### TCC 23 | * [分布式事务解决方案与适用场景分析](https://juejin.im/post/5a9cb1bf6fb9a028c06a4f6b) 24 | 25 | ### 解决方案 26 | [微服务架构下分布式事务解决方案 —— 阿里GTS](https://url.cn/5q6IveA) 27 | -------------------------------------------------------------------------------- /contents/Interview/Elasticsearch/README.md: -------------------------------------------------------------------------------- 1 | # Elasticsearch面试专题 2 | Elasticsearch面试专题。 3 | 4 | ## Elasticsearch的核心概念 5 | near realTime(NRT):近实时,从写入数据到数据可以搜索到有一个小延迟(一秒),基于es的搜索和分析可以达到秒级 6 | 7 | 集群:包含多个节点,每个节点是属于哪个集群是通过一个配置(集群名称,默认是elasticsearch)来决定的。 8 | 9 | node节点:集群中的一个节点,节点的名称是默认随机分配的,在执行运维管理操作的时候节点名称很重要,默认节点会加入一个叫elasticsearch的集群。 10 | 11 | index索引:包含一堆有相似结构的document文档数据,一个index代表了一类类似的或相同的document。 12 | 13 | type类型:每个index里面可以有多个或一个type,6.x版本默认为_doc。 14 | 15 | document和field:document是es中最小的数据单元,一个document可以是一条数据,通常用json数据结构表示,每个index下的type中可以存储多个document,一个document里面有多个field,每个field就是一个数据字段。 16 | 17 | shard:单台机器无法存储大量的数据,es可以将一个index中的数据切分为多个shard,分布在多个机器上进行存储,有了shard可以进行横向扩展,进而可以存储更多的数据,让搜索和分析等操作分布到多台机器上去执行,提高吞吐量和性能,每个shard都是一个Lucene index。 18 | 19 | replica:任何一个服务都可能出现故障或者宕机,此时shard的数据就有可能丢失,因此可以为每个shard建立replica副本,replica可以在shard发生故障时提供备用服务保证数据不丢失,多个replica还可以提供搜索和分析等操作的性能。建立index时一次性设置primary shard,不能进行修改,默认是5个,replica可以随便修改,默认是1个(这里的1个的意思是每个primary shard拥有一个replica shard),默认每个索引有10个shard,5个primary shard,5个replica shard,最小的高可用配置,默认是2台服务器。 20 | 21 | 22 | ## 什么是倒排索引? 23 | 倒排索引是搜索引擎的核心。搜索引擎的主要目标是在查找发生搜索条件的文档时提供快速搜索。 24 | 通俗解释一下就可以。 25 | 传统的我们的检索是通过文章,逐个遍历找到对应关键词的位置。 26 | 而倒排索引,是通过分词策略,形成了词和文章的映射关系表,这种词典+映射表即为倒排索引。 27 | 28 | 有了倒排索引,就能实现o(1)时间复杂度的效率检索文章了,极大的提高了检索效率。 29 | 30 | ## ElasticSearch中的集群、节点、索引、文档、类型是什么? 31 | 32 | ## Elasticsearch 索引文档的过程 33 | 协调节点默认使用文档ID参与计算(也支持通过routing),以便为路由提供合适的分片。 34 | shard = hash(document_id) % (num_of_primary_shards)当分片所在的节点接收到来自协调节点的请求后,会将请求写入到Memory Buffer,然后定时(默认是每隔1秒)写入到Filesystem Cache,这个从Momery Buffer到Filesystem   Cache的过程就叫做refresh; 35 | 36 | 当然在某些情况下,存在Momery Buffer和Filesystem Cache的数据可能会丢失,ES是通过translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到translog中,当Filesystem cache中的数据写入到磁盘中时,才会清除掉,这个过程叫做flush; 37 | 在flush过程中,内存中的缓冲将被清除,内容被写入一个新段,段的fsync将创建一个新的提交点,并将内容刷新到磁盘,旧的translog将被删除并开始一个新的translog。 38 | flush触发的时机是定时触发(默认30分钟)或者translog变得太大(默认为512M)时; 39 | 40 | ## Elasticsearch 读取文档的过程 41 | 可以通过 doc id 来查询,会根据 doc id 进行 hash,判断出来当时把 doc id 分配到了哪个 shard 上面去,从那个 shard 去查询。 42 | 43 | * 客户端发送请求到任意一个 node,称为协调节点(coordinate node)。 44 | * coordinate node 对 doc id 进行哈希路由,将请求转发到对应的 node,此时会使用 round-robin随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡。 45 | * 接收请求的 node 返回 document 给 coordinate node。 46 | * coordinate node 返回 document 给客户端。 47 | 48 | 写请求是写入 primary shard,然后同步给所有的 replica shard;读请求可以从 primary shard或replica shard 读取,采用的是随机轮询算法。 49 | 50 | ## Elasticsearch更新和删除文档的过程 51 | 删除和更新也都是写操作,但是Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更; 52 | 53 | 磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段。 54 | 55 | 在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。 56 | 57 | ## Elasticsearch搜索的过程 58 | 搜索拆解为“query then fetch” 两个阶段。 59 | 60 | query阶段的目的:定位到位置,但不取。 61 | 62 | 步骤拆解如下: 63 | 1. 假设一个索引数据有5主+1副本 共10分片,一次请求会命中(主或者副本分片中)的一个。 64 | 2. 每个分片在本地进行查询,结果返回到本地有序的优先队列中。 65 | 3. 第2)步骤的结果发送到协调节点,协调节点产生一个全局的排序列表。 66 | 67 | 查询阶段会计算文档的相关性得分用于排序。 68 | 69 | fetch阶段的目的:取数据。 70 | 路由节点获取所有文档,返回给客户端。 71 | 72 | ## 在并发情况下Elasticsearch如果保证读写一致? 73 | 可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突; 74 | 另外对于写操作,一致性级别支持quorum/one/all,默认为quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。 75 | 对于读操作,可以设置replication为sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置replication为async时,也可以通过设置搜索请求参数_preference为primary来查询主分片,确保文档是最新版本。 76 | 77 | ## Elasticsearch 海量数据如何调优? 78 | ### 1.设计阶段调优: 79 | 1. 根据业务增量需求,采取基于日期模板创建索引,通过roll over API滚动索引; 80 | 2. 使用别名进行索引管理; 81 | 3. 每天凌晨定时对索引做force_merge操作,以释放空间; 82 | 4. 采取冷热分离机制,热数据存储到SSD,提高检索效率;冷数据定期进行shrink操作,以缩减存储; 83 | 5. 采取curator进行索引的生命周期管理; 84 | 6. 仅针对需要分词的字段,合理的设置分词器; 85 | 7. Mapping阶段充分结合各个字段的属性,是否需要检索、是否需要存储等。 86 | 87 | ### 2.写入调优 88 | 1. 写入前副本数设置为0; 89 | 2. 写入前关闭refresh_interval设置为-1,禁用刷新机制; 90 | 3. 写入过程中:采取bulk批量写入; 91 | 4. 写入后恢复副本数和刷新间隔; 92 | 5. 尽量使用自动生成的id。 93 | 94 | ### 3.查询调优 95 | 1. 禁用wildcard; 96 | 2. 禁用批量terms(成百上千的场景); 97 | 3. 充分利用倒排索引机制,能keyword类型尽量keyword; 98 | 4. 数据量大时候,可以先基于时间敲定索引再检索; 99 | 5. 设置合理的路由机制。 100 | 101 | ### 4.部署 102 | 103 | 1. 关闭缓存swap; 104 | 2. 堆内存设置为:Min(节点内存/2, 32GB); 105 | 3. 设置最大文件句柄数; 106 | 4. 线程池+队列大小根据业务需要做调整; 107 | 5. 磁盘存储raid方式——存储有条件使用RAID10,增加单节点性能以及避免单节点存储故障。 108 | 109 | 110 | -------------------------------------------------------------------------------- /contents/Interview/HR_Interview/README.md: -------------------------------------------------------------------------------- 1 | # HR面 2 | 3 | ## 4 | [史上最强HR面试问题汇总,拿走不谢~](https://zhuanlan.zhihu.com/p/49752483) 5 | 6 | ## 请您先用3-5分钟介绍一下自己。 7 | 8 | ## 请说说您最近服务的这家公司的基本情况 9 | 最近服务的这家公司的基本情况(规模、产品、市场)。 10 | 11 | ## 您为何要离开目前服务的这家公司? 12 | 答案可能是待遇或成长空间或人际氛围或其它,待回答完毕后继续发问; 13 | 14 | 您跟您的主管或直接上司有没有针对以上问题沟通过?(如果没有,问其原因;如果有,问其过程和结果) 15 | 16 | ## 您在选择工作中更看重的是什么? 17 | 可能是成长空间、培训机会、发挥平台、薪酬等答案 18 | ① (若薪酬不排在第一,可继续发问)您可不可以说说您在薪酬方面的心理预期? 19 | ② (待回答完毕后)那您刚才的意思也可以这样理解:薪酬方面可以适当低于您的心理预期,对吗? 20 | -------------------------------------------------------------------------------- /contents/Interview/Interview_ Highlights.md: -------------------------------------------------------------------------------- 1 | 2 | ## java基础篇 3 | ### 集合 4 | * Arraylist 与 LinkedList 区别 5 | * ArrayList 与 Vector 区别 6 | * HashMap 的工作原理及代码实现 7 | * HashMap 和 Hashtable 的区别 8 | * HashSet 和 HashMap 区别 9 | * ConcurrentHashMap 的工作原理及代码实现 10 | * COW集合 11 | 12 | ### 线程 13 | * 创建线程的方式及实现 14 | * sleep() 、join()、yield()有什么区别 15 | * 说说 CountDownLatch 原理 16 | * 说说 CyclicBarrier 原理 17 | * 说说 Semaphore 原理 18 | * 说说 Exchanger 原理 19 | * 说说 CountDownLatch 与 CyclicBarrier 区别 20 | * ThreadLocal 原理分析 21 | * 讲讲线程池的实现原理 22 | * 线程池的几种方式 23 | * 线程的生命周期 24 | 25 | ### 锁机制 26 | * 说说线程安全问题 27 | * volatile 实现原理 28 | * synchronize 实现原理 29 | * synchronized 与 lock 的区别 30 | * CAS 乐观锁 31 | * ABA 问题 32 | * 乐观锁的业务场景及实现方式 33 | 34 | ## HTTP/TCP 35 | * HTTP 请求的 GET 与 POST 方式的区别 36 | * session 与 cookie 区别 37 | 38 | ## 框架篇 39 | ### Spring 40 | * BeanFactory 和 ApplicationContext 有什么区别 41 | * Spring Bean 的生命周期 42 | * Spring IOC 如何实现 43 | * 说说 Spring AOP 44 | * Spring AOP 实现原理 45 | * 动态代理(cglib 与 JDK) 46 | * Spring 事务实现方式 47 | * Spring 事务底层原理 48 | * 如何自定义注解实现功能 49 | * Spring MVC 运行流程 50 | * Spring MVC 启动流程 51 | * Spring 的单例实现原理 52 | * Spring 框架中用到了哪些设计模式 53 | 54 | ### Mybatis 55 | * #{}和${}的区别是什么? 56 | * 通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么? 57 | * Dao接口里的方法,参数不同时,方法能重载吗? 58 | * Mybatis 是如何进行分页的?分页插件的原理是什么? 59 | * Mybatis的一级、二级缓存 60 | 61 | 62 | ### Dubbo 63 | * dubbo都支持什么协议,推荐用哪种? 64 | * Dubbo推荐使用什么序列化框架,你知道的还有哪些?推荐使用Hessian序列化,还有Duddo、FastJson、Java自带序列化。 65 | * Dubbo有哪几种集群容错方案,默认是哪种? 66 | * Dubbo有哪几种负载均衡策略,默认是哪种? 67 | * 当一个服务接口有多种实现时怎么做? 68 | * 服务上线怎么兼容旧版本? 69 | 70 | 71 | ### Netty 72 | 73 | ### Spring Boot 74 | 75 | ## 核心篇 76 | ### 缓存 77 | * Redis 有哪些类型 78 | * Redis 内部结构 79 | * 聊聊 Redis 使用场景 80 | * Redis 持久化机制 81 | * Redis 如何实现持久化 82 | * Redis 集群方案与实现 83 | * Redis 为什么是单线程的 84 | * 缓存雪崩 85 | * 缓存降级 86 | * 使用缓存的合理性问题 87 | 88 | ### MQ 89 | * 消息队列的使用场景 90 | * 消息的重发补偿解决思路 91 | * 消息的幂等性解决思路 92 | * 消息的堆积解决思路 93 | * 自己如何实现消息队列 94 | * 如何保证消息的有序性 95 | 96 | ### 数据库 97 | * MySQL 索引使用的注意事项 98 | * 说说反范式设计 99 | * 说说分库与分表设计 100 | * 分库与分表带来的分布式困境与应对之策 101 | * 说说 SQL 优化之道 102 | * MySQL 遇到的死锁问题 103 | * 存储引擎的 InnoDB 与 MyISAM 104 | * 数据库索引的原理 105 | * 为什么要用 B-tree 106 | * 聚集索引与非聚集索引的区别 107 | * 什么叫做覆盖索引? 108 | * 聚集索引与非聚集索引的区别 109 | * 大数据量分页优化 110 | * 选择合适的数据存储方案 111 | 112 | ### 搜索引擎 113 | * 倒排索引 114 | * 聊聊 ElasticSearch 使用场景 115 | 116 | ## 微服务篇 117 | ### 微服务 118 | * 前后端分离是如何做的 119 | * 微服务哪些框架 120 | * 你怎么理解 RPC 框架 121 | * 说说 RPC 的实现原理 122 | * 说说 Dubbo 的实现原理 123 | * 你怎么理解 RESTful 124 | * 说说如何设计一个良好的 API 125 | * 如何理解 RESTful API 的幂等性 126 | * 如何保证接口的幂等性 127 | * 说说 CAP 定理、 BASE 理论 128 | * 怎么考虑数据一致性问题 129 | * 说说最终一致性的实现方案 130 | * 你怎么看待微服务 131 | * 微服务与 SOA 的区别 132 | * 如何拆分服务 133 | * 如何应对微服务的链式调用异常 134 | * 对于快速追踪与定位问题 135 | * 微服务的安全 136 | 137 | ### 分布式 138 | * 谈谈业务中使用分布式的场景 139 | * Session 分布式方案 140 | * 分布式锁的场景 141 | * 分布是锁的实现方案 142 | * 分布式事务 143 | * 集群与负载均衡的算法与实现 144 | * 说说分库与分表设计 145 | * 分库与分表带来的分布式困境与应对之策 146 | 147 | ### 性能优化 148 | * 性能指标有哪些 149 | * 如何发现性能瓶颈 150 | * 性能调优的常见手段 151 | * 说说你在项目中如何进行性能调优 152 | 153 | ## 工程篇 154 | ### 需求分析 155 | * 你如何对需求原型进行理解和拆分 156 | * 说说你对功能性需求的理解 157 | * 说说你对非功能性需求的理解 158 | * 你针对产品提出哪些交互和改进意见 159 | * 你如何理解用户痛点 160 | 161 | ### 设计能力 162 | * 说说你在项目中使用过的 UML 图 163 | * 你如何考虑组件化 164 | * 你如何考虑服务化 165 | * 你如何进行领域建模 166 | * 你如何划分领域边界 167 | * 说说你项目中的领域建模 168 | * 说说概要设计 169 | 170 | ### 设计模式 171 | * 你项目中有使用哪些设计模式 172 | * 说说常用开源框架中设计模式使用分析 173 | * 说说你对设计原则的理解 174 | * 23种设计模式的设计理念 175 | * 设计模式之间的异同,例如策略模式与状态模式的区别 176 | * 设计模式之间的结合,例如策略模式+简单工厂模式的实践 177 | * 设计模式的性能,例如单例模式哪种性能更好。 178 | 179 | ### 软实力 180 | * 说说你的亮点 181 | * 说说你最近在看什么书 182 | * 说说你觉得最有意义的技术书籍 183 | * 工作之余做什么事情 184 | * 说说个人发展方向方面的思考 185 | * 说说你认为的服务端开发工程师应该具备哪些能力 186 | -------------------------------------------------------------------------------- /contents/Interview/Interview_Final.md: -------------------------------------------------------------------------------- 1 | ## 个人介绍 2 | 我是冯兵兵,2012年大学本科毕业,目前任职于宜人贷创新产品部,负责小咖贷借款App 后端系统需求分析、功能开发、技术方案设计和优化工作。小咖贷2017年8月份立项,从0开始搭建小咖贷App 后端系统。 3 | 4 | ## 系统整体架构 5 | 6 | ![System_Archtecture](https://github.com/TFdream/blog/blob/master/docs/image/Interview/System_Archtecture.png) 7 | 8 | ### 分层架构 9 | | 名称 | 功能 | 10 | | --- | --- | 11 | | 端 | 包括:Android、iOS、Wap(H5) | 12 | | 接入层 | 主要功能:身份鉴权、限流、feature开关,轻量级业务处理 | 13 | | 服务层 | 分为:三方服务、业务服务、通用服务 | 14 | | 数据存储 | 包括:MySQL 和 Redis cluster | 15 | 16 | ## 一、系统亮点 17 | ### 1. 高可用 18 | 基于RPC框架(dubbo)做集群化,避免单点故障 19 | #### 服务降级策略 20 | 1. 限流策略 21 | 2. 熔断策略 22 | 3. feature开关,关键业务节点可以即开即停; 23 | 24 | ### 2. 可扩展 25 | 单个服务都是无状态的,可以按需扩容。 26 | 27 | ### 3. 数据最终一致性 28 | 采用eBay BASE (basically available, soft state, eventually consistent)方案保证业务数据的最终一致性。 29 | 30 | 此方案的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。人工重试更多的是应用于支付场景,通过对账系统对事后问题的处理。 31 | 32 | 消息日志方案的核心是保证服务接口的幂等性。考虑到网络通讯失败、数据丢包等原因,如果接口不能保证**幂等性**,数据的唯一性将很难保证。 33 | 34 | 文章中描述了一个最常见的场景,如果产生了一笔交易,需要在交易表增加记录,同时还要修改用户表的金额。这两个表属于不同的远程服务,所以就涉及到分布式事务一致性的问题。 35 | 36 | 文中提出了一个经典的解决方法,将主要**修改操作**以及更新**用户表的消息**放在一个本地事务来完成。同时为了避免重复消费用户表消息带来的问题,达到多次重试的幂等性,增加一个更新记录表 updates_applied 来记录已经处理过的消息。 37 | 38 | #### 涉及到业务点 39 | 1. 提交借款申请 40 | 2. 设置借款单默认还款银行卡 41 | 42 | ## 二、系统待优化点 43 | 1. 应用指标实时监控,PV、UV,方便产品决策,目前是产品经理查SQL[技术]; 44 | 2. 分布式Trace,方便开发排查线上问题[技术]; 45 | 3. 统一的分布式事务解决方案,调研阶段,TCC[技术] 46 | 47 | ## 三、技术满足产品需求 48 | 1. feature开关实时生效,风控要求,方便风控进行规则正确性验证; 49 | 2. 借款单批量设置还款卡操作, 涉及到分布式事物; 50 | 3. 风控规则引擎出参拆分,导出任意指定节点字段,方便风控同学验证 51 | 4. 借款单还款银行卡同步,人工处理工单 到 定时job同步,解放生产力 52 | 53 | ## 四、延伸想法 54 | ### 1. 业务的优势 55 | 1. 门槛低(身份证+淘宝+运营商),无须征信报告; 56 | 2. 速度快(最快30分钟到账) 57 | 3. 更灵活(一次授信,循环使用) 58 | 4. 安全有保障,大平台; 59 | 60 | ### 2. 业务的劣势 61 | 1. 授信资料项 不够丰富,数据抓取成功率不够高,一直在跟蜂巢团队在讨论解决方案; 62 | 2. 63 | 64 | ### 3. 业务发展方向 65 | 1. 循环授信 66 | 2. 提额 67 | 68 | ## 五、个人职业规划 69 | ### 1. 离职原因 70 | 1. 公司技术架构演进 缺乏统一、清晰的规划; 71 |  - 没人主导新技术预研、落地,例如Docker 72 | - 项目组各自为战,项目组之间配合不给力 73 | 2. 个人成长不够,平台流量不大,技术领域期待能更进一步; 74 | - 架构设计方面没人一起交流、探讨、指导,感觉很孤独,希望找到志同道合的团队。 75 | 3. 调薪结果没有达到自己的预期; 76 | 77 | ### 2. 个人性格缺点 78 | 要点以下: 79 | 1. 避免避重就轻,不要谈一个算不得缺点的缺点。比如熬夜会困,或者(待人接物)太客气,(投资思路)太保守。 80 | 2. 避免谈非职业缺点,比如有感情洁癖,挑食,不擅长陪女友逛街,做饭经常糊。 81 | 3. 避免谈到无法改善的弱点,比如我算数必须用计算器,我脑子不好用看书不理解。 82 | 4. 避免谈到致命弱点,比如脾气怪异不喜欢合作迟到早退等。 83 | 84 | 那谈什么最好呢?我认为要点有三: 85 | 1. 谈已经在改正的缺点/有明确计划来改正的缺点。尤其是你能够充分论证在近期就可以解决的缺点。 86 | 2. 谈一个利用你的优点改正的缺点,顺便带出一个优点。 87 | 3. 很关键的是,谈一个真实的缺点。如果你申请的投行资管咨询等工作,要记得你的面试官就是靠跟人打交道为生的。该条适用于所有面试问题,一定不要在简历上作假,你的赢面不大。正确的做法是选择性介绍你的经历,突出学到/用到关联技能,忽略无关部分。 88 | 89 | 我听到的比较好的几个包括: 90 | 1. 喜欢追求细节导致项目/作业未能按期完成。通过时间管理能力改变工作方式,先完成框架再改善细节得以解决; 91 | 2. 不知如何拒绝,同事要求帮忙一概揽下,影响自身工作进度。通过多任务处理能力设定优先顺序,以该优先顺序表向求助同事展示自己手上工作,并给其一个自己在何时可以给予帮助的时间估计,让求助人自行决定是否求助,问题解决。 92 | 93 | ### 2. 职业规划 94 | 明确方向: 95 | 1. 从技术向业务过渡 96 | - 要成为一个成功PM最重视的是管理能力,但对技术也应该有足够的了解,因为这是与团队成员沟通的桥梁,只有这样才能与整个团队的成员有着紧密的结合,让团队成员感觉到他们自己存在的意义,从而调动团队的积极性 97 | 2. 从程序员向技术管理发展 98 | - Team Leader的职责与Project Manager相像,但Team Leader更着重于技术开发方面,通常一个大型项目都会有一两个开发团队由Team Leader带领,负责开发核心部分 99 | - 无论是PM与TL,对业务与技术都要有深入的了解,只是PM更侧重于业务的管理,盈利的多少,风险的大小等等,而TL则侧重于项目的成本,开发的难度,软件的架构等技术方面的问题。 100 | 3. 技术专家路线 101 | - Base某个领域 102 | 103 | ## 对求职公司的了解 104 | 105 | ### 技术实力 106 | 107 | ### 开源贡献 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /contents/Interview/Java_Interview_Summary.md: -------------------------------------------------------------------------------- 1 | # Java面试知识点集锦 2 | 3 | ## 基础篇 4 | ### 基础知识 5 | * 面向对象的特征 6 | * final, finally, finalize 的区别 7 | * int 和 Integer 有什么区别 8 | * 重载和重写的区别 9 | * 抽象类和接口有什么区别 10 | * 说说反射的用途及实现 11 | * 说说自定义注解的场景及实现 12 | * HTTP 请求的 GET 与 POST 方式的区别 13 | * session 与 cookie 区别 14 | * session 分布式处理 15 | * JDBC 流程 16 | * MVC 设计思想 17 | * equals 与 == 的区别 18 | 19 | ### 集合 20 | * List 和 Set 区别 21 | * List 和 Map 区别 22 | * Arraylist 与 LinkedList 区别 23 | * ArrayList 与 Vector 区别 24 | * HashMap 和 Hashtable 的区别 25 | * HashSet 和 HashMap 区别 26 | * HashMap 和 ConcurrentHashMap 的区别 27 | * HashMap 的工作原理及代码实现 28 | * ConcurrentHashMap 的工作原理及代码实现 29 | 30 | ### 线程 31 | * 创建线程的方式及实现 32 | * sleep() 、join()、yield()有什么区别 33 | * 说说 CountDownLatch 原理 34 | * 说说 CyclicBarrier 原理 35 | * 说说 Semaphore 原理 36 | * 说说 Exchanger 原理 37 | * 说说 CountDownLatch 与 CyclicBarrier 区别 38 | * ThreadLocal 原理分析 39 | * 讲讲线程池的实现原理 40 | * 线程池的几种方式 41 | * 线程的生命周期 42 | 43 | ### 锁机制 44 | * 说说线程安全问题 45 | * volatile 实现原理 46 | * synchronize 实现原理 47 | * synchronized 与 lock 的区别 48 | * CAS 乐观锁 49 | * ABA 问题 50 | * 乐观锁的业务场景及实现方式 51 | 52 | ## 核心篇 53 | ### 缓存使用 54 | * Redis 有哪些类型 55 | * Redis 内部结构 56 | * 聊聊 Redis 使用场景 57 | * Redis 持久化机制 58 | * Redis 如何实现持久化 59 | * Redis 集群方案与实现 60 | * Redis 为什么是单线程的 61 | * 缓存雪崩 62 | * 缓存降级 63 | * 使用缓存的合理性问题 64 | 65 | ### 消息队列 66 | * 消息队列的使用场景 67 | * 消息的重发补偿解决思路 68 | * 消息的幂等性解决思路 69 | * 消息的堆积解决思路 70 | * 自己如何实现消息队列 71 | * 如何保证消息的有序性 72 | 73 | ### 数据存储 74 | * MySQL 索引使用的注意事项 75 | * 说说反范式设计 76 | * 说说分库与分表设计 77 | * 分库与分表带来的分布式困境与应对之策 78 | * 说说 SQL 优化之道 79 | * MySQL 遇到的死锁问题 80 | * 存储引擎的 InnoDB 与 MyISAM 81 | * 数据库索引的原理 82 | * 为什么要用 B-tree 83 | * 聚集索引与非聚集索引的区别 84 | * limit 20000 加载很慢怎么解决 85 | * 选择合适的分布式主键方案 86 | * 选择合适的数据存储方案 87 | * ObjectId 规则 88 | * 聊聊 MongoDB 使用场景 89 | * 倒排索引 90 | * 聊聊 ElasticSearch 使用场景 91 | 92 | ## 框架篇 93 | ### Spring 94 | * BeanFactory 和 ApplicationContext 有什么区别 95 | * Spring Bean 的生命周期 96 | * Spring IOC 如何实现 97 | * 说说 Spring AOP 98 | * Spring AOP 实现原理 99 | * 动态代理(cglib 与 JDK) 100 | * Spring 事务实现方式 101 | * Spring 事务底层原理 102 | * 如何自定义注解实现功能 103 | * Spring MVC 运行流程 104 | * Spring MVC 启动流程 105 | * Spring 的单例实现原理 106 | * Spring 框架中用到了哪些设计模式 107 | * Spring 其他产品(Srping Boot、Spring Cloud、Spring Secuirity、Spring Data、Spring AMQP 等) 108 | 109 | ### Netty 110 | * 为什么选择 Netty 111 | * 说说业务中,Netty 的使用场景 112 | * 原生的 NIO 在 JDK 1.7 版本存在 epoll bug 113 | * 什么是TCP 粘包/拆包 114 | * TCP粘包/拆包的解决办法 115 | * Netty 线程模型 116 | * 说说 Netty 的零拷贝 117 | * Netty 内部执行流程 118 | * Netty 重连实现 119 | 120 | ## 微服务篇 121 | ### 微服务 122 | * 前后端分离是如何做的 123 | * 微服务哪些框架 124 | * 你怎么理解 RPC 框架 125 | * 说说 RPC 的实现原理 126 | * 说说 Dubbo 的实现原理 127 | * 你怎么理解 RESTful 128 | * 说说如何设计一个良好的 API 129 | * 如何理解 RESTful API 的幂等性 130 | * 如何保证接口的幂等性 131 | * 说说 CAP 定理、 BASE 理论 132 | * 怎么考虑数据一致性问题 133 | * 说说最终一致性的实现方案 134 | * 你怎么看待微服务 135 | * 微服务与 SOA 的区别 136 | * 如何拆分服务 137 | * 微服务如何进行数据库管理 138 | * 如何应对微服务的链式调用异常 139 | * 对于快速追踪与定位问题 140 | * 微服务的安全 141 | 142 | ### 分布式 143 | * 谈谈业务中使用分布式的场景 144 | * Session 分布式方案 145 | * 分布式锁的场景 146 | * 分布是锁的实现方案 147 | * 分布式事务 148 | * 集群与负载均衡的算法与实现 149 | * 说说分库与分表设计 150 | * 分库与分表带来的分布式困境与应对之策 151 | 152 | ### 安全问题 153 | * 安全要素与 STRIDE 威胁 154 | * 防范常见的 Web 攻击 155 | * 服务端通信安全攻防 156 | * HTTPS 原理剖析 157 | * HTTPS 降级攻击 158 | * 授权与认证 159 | * 基于角色的访问控制 160 | * 基于数据的访问控制 161 | 162 | ### 性能优化 163 | * 性能指标有哪些 164 | * 如何发现性能瓶颈 165 | * 性能调优的常见手段 166 | * 说说你在项目中如何进行性能调优 167 | 168 | ## 工程篇 169 | ### 需求分析 170 | * 你如何对需求原型进行理解和拆分 171 | * 说说你对功能性需求的理解 172 | * 说说你对非功能性需求的理解 173 | * 你针对产品提出哪些交互和改进意见 174 | * 你如何理解用户痛点 175 | 176 | ### 设计能力 177 | * 说说你在项目中使用过的 UML 图 178 | * 你如何考虑组件化 179 | * 你如何考虑服务化 180 | * 你如何进行领域建模 181 | * 你如何划分领域边界 182 | * 说说你项目中的领域建模 183 | * 说说概要设计 184 | 185 | ### 设计模式 186 | * 你项目中有使用哪些设计模式 187 | * 说说常用开源框架中设计模式使用分析 188 | * 说说你对设计原则的理解 189 | * 23种设计模式的设计理念 190 | * 设计模式之间的异同,例如策略模式与状态模式的区别 191 | * 设计模式之间的结合,例如策略模式+简单工厂模式的实践 192 | * 设计模式的性能,例如单例模式哪种性能更好。 193 | 194 | 195 | ### 软实力 196 | * 说说你的亮点 197 | * 说说你最近在看什么书 198 | * 说说你觉得最有意义的技术书籍 199 | * 工作之余做什么事情 200 | * 说说个人发展方向方面的思考 201 | * 说说你认为的服务端开发工程师应该具备哪些能力 202 | * 说说你认为的架构师是什么样的,架构师主要做什么 203 | * 说说你所理解的技术专家 204 | 205 | -------------------------------------------------------------------------------- /contents/Interview/Microservices/README.md: -------------------------------------------------------------------------------- 1 | # 微服务面试专题 2 | 3 | ## 单体式应用的不足 4 | 开发、部署、扩展困难; 5 | 6 | ## 对微服务有何了解? 7 | 微服务(Microservices),又称微服务架构(MSA),是一种架构风格,它将应用程序构建为以业务领域为模型的小型自治服务集合 。 8 | 9 | ## 微服务架构有哪些优势? 10 | * 独立开发 – 这种架构使得每个服务都可以有专门开发团队来开发。开发者可以自由选择开发技术,提供API服务。 11 | * 独立部署 – 微服务架构模式是每个微服务独立的部署。开发者不再需要协调其它服务部署对本服务的影响。这种改变可以加快部署速度。 12 | * 独立扩展 – 微服务架构模式使得每个服务独立扩展。你可以根据每个服务的规模来部署满足需求的规模。甚至于,你可以使用更适合于服务资源需求的硬件。 13 | * 混合技术堆栈 – 可以使用不同的语言和技术来构建同一应用程序的不同服务 14 | 15 | ## 微服务架构的优缺点是什么? 16 | 17 | ## 微服务拆分的原则 18 | 19 | 原则一,做到单一服务内部功能的高内聚和低耦合。也就是说每个服务只完成自己职责之内的任务,对于不是自己职责的功能交给其它服务来完成。说起来你可能觉得理所当然对这一点不屑一顾,但很多人在实际开发中,经常会出现一些问题。 20 | 21 | 原则二,你需要关注服务拆分的粒度,先粗略拆分再逐渐细化。在服务拆分的初期,你其实很难确定服务究竟要拆分成什么样。 22 | 所以我推荐的做法是:拆分初期可以把服务粒度拆的粗一些,后面随着团队对于业务和微服务理解的加深,再考虑把服务粒度细化。 23 | 24 | 原则三,拆分的过程,要尽量避免影响产品的日常功能迭代。也就是说,要一边做产品功能迭代,一边完成服务化拆分。 25 | 26 | 我们的拆分只能在现有一体化系统的基础上不断剥离业务独立部署,剥离的顺序你可以参考以下几点:1. 优先剥离比较独立的边界服务(比如短信服务、地理位置服务),从非核心的服务出发减少拆分对现有业务的影响,也给团队一个练习、试错的机会;2. 当两个服务存在依赖关系时优先拆分被依赖的服务。比如内容服务依赖于用户服务获取用户的基本信息,那么如果先把内容服务拆分出来,内容服务就会依赖于一体化架构中的用户模块,这样还是无法保证内容服务的快速部署能力。 27 | 28 | 原则四,服务接口的定义要具备可扩展性。服务拆分之后,由于服务是以独立进程的方式部署,所以服务之间通信就不再是进程内部的方法调用而是跨进程的网络通信了。在这种通信模型下服务接口的定义要具备可扩展性,否则在服务变更时会造成意想不到的错误。 29 | 30 | ## 微服务化带来的问题和解决思路 31 | 1. 服务接口的调用不再是同一进程内的方法调用而是跨进程的网络调用,这会增加接口响应时间的增加。此时我们就要选择高效的服务调用框架,同时接口调用方需要知道服务部署在哪些机器的哪个端口上,这些信息需要存储在一个分布式一致性的存储中,于是就需要引入服务注册中心。 32 | 33 | 2. 多个服务之间有着错综复杂的依赖关系。一个服务会依赖多个其它服务也会被多个服务所依赖,那么一旦被依赖的服务的性能出现问题产生大量的慢请求,就会导致依赖服务的工作线程池中的线程被占满,依赖的服务也会出现性能问题。接下来问题就会沿着依赖网逐步向上蔓延,直到整个系统出现故障为止。为了避免发生这种情况,我们需要引入服务治理体系针对出问题的服务**采用熔断、降级、限流、超时控制的方法**,使问题被限制在单一服务中,保护服务网络中的其它服务不受影响。 34 | 35 | 3. 服务拆分到多个进程后,一条请求的调用链路上涉及多个服务,那么一旦这个请求的响应时间增长或者是出现错误,我们就很难知道是哪一个服务出现的问题。另外,整体系统一旦出现故障,很可能外在的表现是所有服务在同一时间都出现了问题,你在问题定位时很难确认哪一个服务是源头,这就需要引入**分布式追踪工具**,以及更细致的服务端监控报表。 36 | 37 | 4.分布式事务 38 | 39 | ## 什么是Spring Cloud? 40 | 在微服务中,SpringCloud是一个提供与外部系统集成的系统。它是一个敏捷的框架,可以短平快构建应用程序。与有限数量的数据处理相关联,它在微服务体系结构中起着非常重要的作用。 41 | 42 | 以下为 Spring Cloud 的核心特性: 43 | 1. 版本化/分布式配置。 44 | 2. 服务注册和发现。 45 | 3. 服务和服务之间的调用。 46 | 3. 路由。 47 | 4. 断路器和负载平衡。 48 | 5. 分布式消息传递。 49 | 50 | ## SpringCloud和dubbo 有哪些区别? 51 | 52 | ## Eureka和Consul的区别? 53 | 最大的区别是Eureka保证AP, Consul为CP。 54 | 55 | Consul强一致性(C)带来的是: 56 | 1. 服务注册相比Eureka会稍慢一些。因为Consul的raft协议要求必须过半数的节点都写入成功才认为注册成功 57 | 2. Leader挂掉时,重新选举期间整个consul不可用。保证了强一致性但牺牲了可用性。 58 | 59 | Eureka保证高可用(A)和最终一致性: 60 | 1. 服务注册相对要快,因为不需要等注册信息replicate到其他节点,也不保证注册信息是否replicate成功 61 | 2. 当数据出现不一致时,虽然A, B上的注册信息不完全相同,但每个Eureka节点依然能够正常对外提供服务,这会出现查询服务信息时如果请求A查不到,但请求B就能查到。如此保证了可用性但牺牲了一致性。 62 | 63 | 其他方面,eureka就是个servlet程序,跑在servlet容器中; Consul则是go编写而成。 64 | 65 | -------------------------------------------------------------------------------- /contents/Interview/MySQL/README.md: -------------------------------------------------------------------------------- 1 | # MySQL面试专题 2 | 3 | ## 索引 4 | ### 什么是索引吗? 5 | 索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据 6 | 7 | ### 索引具体采用的哪种数据结构呢? 8 | 常见的MySQL主要有两种结构:Hash索引和B+ Tree索引,我们使用的是InnoDB引擎,默认的是B+树 9 | 10 | ### B+ Tree索引和Hash索引区别? 11 | 区别如下: 12 | * 哈希索引适合等值查询,但是无法进行范围查询 13 | * 哈希索引没办法利用索引完成排序 14 | * 哈希索引不支持多列联合索引的最左匹配规则 15 | * 如果有大量重复键值的情况下,哈希索引的效率会很低,因为存在哈希碰撞问题 16 | 17 | ### 聚簇索引、非聚簇索引 18 | 聚簇索引(有的地方叫 聚集索引)叶子节点存的是整行数据,直接通过这个聚集索引的键值找到某行;数据的物理存放顺序与索引顺序是一致的,即:只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上的。 19 | 20 | 聚集索引,数据行和相邻的键值紧凑地存储在一起,因为无法同时把数据行存放在两个不同的地方,所以**一个表只能有一个聚集索引**。 21 | 22 | 非聚集索引,叶子节点存的是字段的值,通过这个非聚集索引的键值找到对应的聚集索引字段的值,再通过聚集索引键值找到表的某行,类似oracle通过键值找到rowid,再通过rowid找到行 23 | 24 | MySQL使用innodb引擎的表,其聚集索引相当于整张表,而整张表也是聚集索引。默认通过主键聚集数据,如果没有定义主键,则选择第一个非空的唯一索引,如果没有非空唯一索引,则选择rowid来作为聚集索引。 25 | 26 | ### 聚簇索引和非聚簇索引的区别? 27 | 在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引;而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引。 28 | 29 | 聚簇索引查询会更快,因为主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次查询。 30 | 31 | Q:刚刚你提到主键索引查询只会查一次,而非主键索引需要回表查询多次。(后来我才知道,原来这个过程叫做回表)是所有情况都是这样的吗?非主键索引一定会查询多次吗? 32 | 33 | A:(额、这个问题我回答的不好,后来我自己查资料才知道,通过覆盖索引也可以只查询一次) 34 | 35 | ### 覆盖索引? 36 | 覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。 37 | 38 | 当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。 39 | 40 | 如,表t_sample中有一个普通索引 idx_key1_key2(key1,key2),当我们通过SQL语句:select key2 from t_sample where key1 = 'keytest'的时候,就可以通过覆盖索引查询,无需回表。 41 | 42 | ### 联合索引、最左前缀匹配 43 | 44 | 45 | ### 事务隔离级 46 | 47 | 48 | -------------------------------------------------------------------------------- /contents/Interview/Mybatis/README.md: -------------------------------------------------------------------------------- 1 | # Mybatis 2 | 3 | ## Mybatis之工作原理 4 | Mybatis之工作原理:https://blog.csdn.net/u014297148/article/details/78696096 5 | 6 | 7 | ## MyBatis缓存机制 8 | 聊聊MyBatis缓存机制:https://tech.meituan.com/mybatis_cache.html 9 | -------------------------------------------------------------------------------- /contents/Interview/README.md: -------------------------------------------------------------------------------- 1 | # Java面试专题 2 | Java面试专题 3 | 4 | 5 | ### 活动页面展示 6 | 大转盘抽奖:http://m.iqianjin.com/aop/820/index.html 7 | 砸宝箱活动:http://m.iqianjin.com/aop/821/index.html 8 | 擂台赛:http://m.iqianjin.com/aop/530/index.html 9 | 全面造富活动(开奖码):https://m.iqianjin.com/aop/558/index.html 10 | 会员砸宝箱:https://m.iqianjin.com/aop/582/index.html 11 | 10亿-会员砸蛋:https://m.iqianjin.com/aop/592/index.html 12 | 阶梯满返:https://m.iqianjin.com/aop/609/index.html 13 | 818财神节:https://m.iqianjin.com/aop/631/index.html 14 | 财神节返场活动:https://m.iqianjin.com/aop/661/index.html 15 | 淘金节:https://m.iqianjin.com/aop/672/index.html 16 | 国庆节感恩活动:https://m.iqianjin.com/aop/704/index.html 17 | -------------------------------------------------------------------------------- /contents/Interview/Redis/README.md: -------------------------------------------------------------------------------- 1 | # Redis面试专题 2 | 3 | ## Redis持久化机制 4 | Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。 5 | 6 | RDB是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。 7 | 8 | AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。 9 | 10 | ## Redis的数据类型,以及每种数据类型的使用场景,Redis 内部结构 11 | ### list 12 | 一种是压缩列表(ziplist),另一种是双向循环链表。 13 | ### hash 14 | 字典类型用来存储一组数据对。每个数据对又包含键值两部分。字典类型也有两种实现方式。一种是我们刚刚讲到的压缩列表,另一种是散列表。 15 | ### set 16 | 集合这种数据类型用来存储一组不重复的数据。这种数据类型也有两种实现方法,一种是基于有序数组,另一种是基于散列表。 17 | 18 | ### sortedset 19 | 有序集合这种数据类型,我们在跳表里已经详细讲过了。它用来存储一组数据,并且每个数据会附带一个得分。通过得分的大小,我们将数据组织成跳表这样的数据结构,以支持快速地按照得分值、得分区间获取数据。 20 | 21 | ## 单线程的redis为什么这么快 22 | 原因如下: 23 | 1. 绝大部分请求是纯粹的内存操作(非常快速) 24 | 2. 单线程操作,避免了频繁的上下文切换 25 | 3. 采用了非阻塞I/O多路复用机制 26 | 27 | ## Redis的过期策略以及内存淘汰机制 28 | Redis采用的是定期删除+惰性删除策略。 29 | ### 为什么不用定时删除策略? 30 | 定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略. 31 | ### 定期删除+惰性删除是如何工作的呢? 32 | 定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。 33 | 34 | 内存淘汰策略: 35 | * volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 36 | * volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 37 | * volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 38 | * allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰 39 | * allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 40 | * no-enviction(驱逐):禁止驱逐数据,新写入操作会报错 41 | 42 | ## 缓存穿透、缓存击穿和缓存雪崩 43 | ### 缓存穿透 44 | 在高并发下,查询一个不存在的值时,缓存不会被命中,导致大量请求直接落到数据库上,如活动系统里面查询一个不存在的活动。 45 | 46 | ### 缓存击穿 47 | 在高并发下,对一个特定的值进行查询,但是这个时候缓存正好过期了,缓存没有命中,导致大量请求直接落到数据库上,如活动系统里面查询活动信息,但是在活动进行过程中活动缓存突然过期了。 48 | ### 缓存雪崩 49 | 在高并发下,大量的缓存key在同一时间失效,导致大量的请求落到数据库上,如活动系统里面同时进行着非常多的活动,但是在某个时间点所有的活动缓存全部过期。 50 | 51 | ## Redis热点数据 52 | 53 | ## redis集群选举机制 54 | 55 | ## Redis实现分布式锁 56 | -------------------------------------------------------------------------------- /contents/Interview/Spring/README.md: -------------------------------------------------------------------------------- 1 | # Spring以及SpringMVC面试题 2 | 3 | ## 一、Spring 4 | ### 1.Spring IOC原理 5 | [Spring IOC底层原理](https://github.com/TFdream/blog/issues/220) 6 | 7 | ### 2.Spring AOP原理 8 | 【Spring源码分析】AOP源码解析(上篇):http://www.cnblogs.com/xrq730/p/6753160.html 9 | 10 | 【Spring源码分析】AOP源码解析(下篇):http://www.cnblogs.com/xrq730/p/6757608.html 11 | 12 | ### 3.SpringBean生命周期 13 | SpringBean生命周期详解:https://blog.csdn.net/lisongjia123/article/details/52091013 14 | 15 | ### 4.Spring事务管理&原理 16 | Spring 事务隔离级别:http://www.cnblogs.com/yangy608/archive/2011/06/29/2093478.html 17 | 18 | Spring事务源码阅读笔记:https://www.cnblogs.com/micrari/p/7612962.html 19 | 20 | Spring Transaction源码分析:https://blog.csdn.net/dalinsi/article/details/53215041 21 | 22 | ## 二、SpringMVC 23 | ### 1.Spring MVC原理 24 | Spring MVC原理:https://www.cnblogs.com/xiaoxi/p/6164383.html 25 | 26 | 27 | ## 三、Spring Boot 28 | ### 1.Spring Boot 原理 29 | Spring Boot干货系列:(三)启动原理解析:https://www.cnblogs.com/zheting/p/6707035.html 30 | 31 | It's a Kind of Magic: Under the Covers of Spring Boot: https://content.pivotal.io/springone-platform-2017/its-a-kind-of-magic-under-the-covers-of-spring-boot-brian-clozel-st%C3%A9phane-nicoll 32 | 33 | ### 2.Spring Boot除了自动配置,相比传统的 Spring 有什么其他的区别? 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /contents/Interview/SpringCloud/Eureka.md: -------------------------------------------------------------------------------- 1 | # Eureka 工作原理 2 | [Eureka](https://github.com/Netflix/eureka) 作为 Spring Cloud 体系中最核心、默认的注册中心组件,研究它的运行机制,有助于我们在工作中更好地使用它。 3 | 4 | ## Eureka 核心概念 5 | Eureka包含两个组件:Eureka Server和Eureka Client。 6 | ### Eureka Server 7 | 注册中心服务端主要对外提供了三个功能: 8 | * 服务注册:服务提供者启动时,会通过 Eureka Client 向 Eureka Server 注册信息,Eureka Server 会存储该服务的信息,Eureka Server 内部有二层缓存机制来维护整个注册表 9 | * 提供注册表: 服务消费者在调用服务时,如果 Eureka Client 没有缓存注册表的话,会从 Eureka Server 获取最新的注册表 10 | * 同步状态:Eureka Client 通过注册、心跳机制和 Eureka Server 同步当前客户端的状态。 11 | 12 | ### Eureka Client 13 | Eureka Client 是一个 Java 客户端,用于简化与 Eureka Server 的交互。Eureka Client 会拉取、更新和缓存 Eureka Server 中的信息。因此当所有的 Eureka Server 节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者,但是当服务有更改的时候会出现信息不一致。 14 | 15 | ### Register: 服务注册 16 | 服务的提供者,将自身注册到注册中心,服务提供者也是一个 Eureka Client。当 Eureka Client 向 Eureka Server 注册时,它提供自身的元数据,比如 IP 地址、端口,运行状况指示符 URL,主页等。 17 | 18 | ### Renew: 服务续约 19 | Eureka Client 会每隔 30 秒发送一次心跳来续约。 通过续约来告知 Eureka Server 该 Eureka Client 运行正常,没有出现问题。 默认情况下,如果 Eureka Server 在 90 秒内没有收到 Eureka Client 的续约,Server 端会将实例从其注册表中删除,此时间可配置,一般情况不建议更改。 20 | 21 | 服务续约的两个重要属性 22 | ``` 23 | 服务续约任务的调用间隔时间,默认为30秒 24 | eureka.instance.lease-renewal-interval-in-seconds=30 25 | 26 | 服务失效的时间,默认为90秒。 27 | eureka.instance.lease-expiration-duration-in-seconds=90 28 | ``` 29 | 30 | ### Eviction 服务剔除 31 | 当 Eureka Client 和 Eureka Server 不再有心跳时,Eureka Server 会将该服务实例从服务注册列表中删除,即服务剔除。 32 | 33 | ### Cancel: 服务下线 34 | Eureka Client 在程序关闭时向 Eureka Server 发送取消请求。 发送请求后,该客户端实例信息将从 Eureka Server 的实例注册表中删除。该下线请求不会自动完成,它需要调用以下内容: 35 | 36 | ### GetRegisty: 获取注册列表信息 37 | Eureka Client 从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与 Eureka Client 的缓存信息不同,Eureka Client 自动处理。 38 | 39 | 如果由于某种原因导致注册列表信息不能及时匹配,Eureka Client 则会重新获取整个注册表信息。 Eureka Server 缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka Client 和 Eureka Server 可以使用 JSON/XML 格式进行通讯。在默认情况下 Eureka Client 使用压缩 JSON 格式来获取注册列表的信息。 40 | 41 | 获取服务是服务消费者的基础,所以必有两个重要参数需要注意: 42 | ``` 43 | # 启用服务消费者从注册中心拉取服务列表的功能 44 | eureka.client.fetch-registry=true 45 | 46 | # 设置服务消费者从注册中心拉取服务列表的间隔 47 | eureka.client.registry-fetch-interval-seconds=30 48 | ``` 49 | 50 | ### 自我保护机制 51 | 默认情况下,如果 Eureka Server 在一定的 90s 内没有接收到某个微服务实例的心跳,会注销该实例。但是在微服务架构下服务之间通常都是跨进程调用,网络通信往往会面临着各种问题,比如微服务状态正常,网络分区故障,导致此实例被注销。 52 | 53 | 固定时间内大量实例被注销,可能会严重威胁整个微服务架构的可用性。为了解决这个问题,Eureka 开发了自我保护机制,那么什么是自我保护机制呢? 54 | 55 | Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 即会进入自我保护机制。 56 | 57 | 58 | ## Eureka 集群原理 59 | 再来看看 Eureka 集群的工作原理。我们假设有三台 Eureka Server 组成的集群,第一台 Eureka Server 在北京机房,另外两台 Eureka Server 在深圳和西安机房。这样三台 Eureka Server 就组建成了一个跨区域的高可用集群,只要三个地方的任意一个机房不出现问题,都不会影响整个架构的稳定性。 60 | 61 | ![](https://raw.githubusercontent.com/Netflix/eureka/master/images/eureka_architecture.png) 62 | 63 | 从图中可以看出 Eureka Server 集群相互之间通过 Replicate 来同步数据,相互之间不区分主节点和从节点,所有的节点都是平等的。在这种架构中,节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。 64 | 65 | 如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点。当宕机的服务器重新恢复后,Eureka 会再次将其纳入到服务器集群管理之中。当节点开始接受客户端请求时,所有的操作都会进行节点间复制,将请求复制到其它 Eureka Server 当前所知的所有节点中。 66 | 67 | 另外 Eureka Server 的同步遵循着一个非常简单的原则:只要有一条边将节点连接,就可以进行信息传播与同步。所以,如果存在多个节点,只需要将节点之间两两连接起来形成通路,那么其它注册中心都可以共享信息。每个 Eureka Server 同时也是 Eureka Client,多个 Eureka Server 之间通过 P2P 的方式完成服务注册表的同步。 68 | 69 | Eureka Server 集群之间的状态是采用异步方式同步的,所以不保证节点间的状态一定是一致的,不过基本能保证最终状态是一致的。 70 | 71 | ### Eurka 保证 AP 72 | 73 | Eureka Server 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而 Eureka Client 在向某个 Eureka 注册时,如果发现连接失败,则会自动切换至其它节点。只要有一台 Eureka Server 还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。 74 | 75 | ### Eurka 工作流程 76 | 了解完 Eureka 核心概念,自我保护机制,以及集群内的工作原理后,我们来整体梳理一下 Eureka 的工作流程: 77 | 78 | 1、Eureka Server 启动成功,等待服务端注册。在启动过程中如果配置了集群,集群之间定时通过 Replicate 同步注册表,每个 Eureka Server 都存在独立完整的服务注册表信息 79 | 80 | 2、Eureka Client 启动时根据配置的 Eureka Server 地址去注册中心注册服务 81 | 82 | 3、Eureka Client 会每 30s 向 Eureka Server 发送一次心跳请求,证明客户端服务正常 83 | 84 | 4、当 Eureka Server 90s 内没有收到 Eureka Client 的心跳,注册中心则认为该节点失效,会注销该实例 85 | 86 | 5、单位时间内 Eureka Server 统计到有大量的 Eureka Client 没有上送心跳,则认为可能为网络异常,进入自我保护机制,不再剔除没有上送心跳的客户端 87 | 88 | 6、当 Eureka Client 心跳请求恢复正常之后,Eureka Server 自动退出自我保护模式 89 | 90 | 7、Eureka Client 定时全量或者增量从注册中心获取服务注册表,并且将获取到的信息缓存到本地 91 | 92 | 8、服务调用时,Eureka Client 会先从本地缓存找寻调取的服务。如果获取不到,先从注册中心刷新注册表,再同步到本地缓存 93 | 94 | 9、Eureka Client 获取到目标服务器信息,发起服务调用 95 | 96 | 10、Eureka Client 程序关闭时向 Eureka Server 发送取消请求,Eureka Server 将实例从注册表中删除 97 | 98 | 这就是Eurka基本工作流程。 99 | 100 | ### Eureka Server之间的注册表信息同步 101 | 链接:https://cloud.tencent.com/developer/article/1083131 102 | -------------------------------------------------------------------------------- /contents/Interview/SpringCloud/Feign.md: -------------------------------------------------------------------------------- 1 | # Feign 2 | [Feign](https://github.com/OpenFeign/feign)是受到Retrofit,JAXRS-2.0和WebSocket启发的java客户端联编程序。Feign的第一个目标是将约束分母的复杂性统一到http apis,而不考虑其稳定性。 3 | -------------------------------------------------------------------------------- /contents/Interview/SpringCloud/Hystrix.md: -------------------------------------------------------------------------------- 1 | # Hystrix 2 | [Hystrix](https://github.com/Netflix/Hystrix)是Netflix开源的一款容错框架,旨在隔离远程系统,服务和第三方库的访问点,当出现故障是不可避免的故障时,停止级联故障并在复杂的分布式系统中实现弹性。 3 | 4 | Hystrix通过命令模式对发送请求的对象和执行请求的对象进行解耦,将不同类型的业务请求封装为对应的命令请求。 5 | 6 | ## Hystrix处理流程 7 | Hystrix处理流程如下图: 8 | ![](https://github.com/Netflix/Hystrix/wiki/images/hystrix-command-flow-chart.png) 9 | 10 | Hystrix整个工作流如下: 11 | 1. 构造一个 HystrixCommand或HystrixObservableCommand对象,用于封装请求,并在构造方法配置请求被执行需要的参数; 12 | 2. 执行命令,Hystrix提供了4种执行命令的方法,后面详述; 13 | 3. 判断是否使用缓存响应请求,若启用了缓存,且缓存可用,直接使用缓存响应请求。Hystrix支持请求缓存,但需要用户自定义启动; 14 | 4. 判断熔断器是否打开,如果打开,跳到第8步; 15 | 5. 判断线程池/队列/信号量是否已满,已满则跳到第8步; 16 | 6. 执行HystrixObservableCommand.construct()或HystrixCommand.run(),如果执行失败或者超时,跳到第8步;否则,跳到第9步; 17 | 7. 统计熔断器监控指标; 18 | 8. 走Fallback备用逻辑 19 | 9. 返回请求响应 20 | 21 | 从流程图上可知道,第5步线程池/队列/信号量已满时,还会执行第7步逻辑,更新熔断器统计信息,而第6步无论成功与否,都会更新熔断器统计信息。 22 | 23 | ## 资源隔离 24 | 资源隔离主要指对线程的隔离。Hystrix提供了两种线程隔离方式:线程池和信号量。 25 | 26 | ### 线程隔离-线程池 27 | Hystrix通过命令模式对发送请求的对象和执行请求的对象进行解耦,将不同类型的业务请求封装为对应的命令请求。如订单服务查询商品,查询商品请求->商品Command;商品服务查询库存,查询库存请求->库存Command。并且为每个类型的Command配置一个线程池,当第一次创建Command时,根据配置创建一个线程池,并放入ConcurrentHashMap,如商品Command: 28 | ``` 29 | 30 | final static ConcurrentHashMap threadPools = new ConcurrentHashMap(); 31 | ... 32 | if (!threadPools.containsKey(key)) { 33 | threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder)); 34 | 35 | ``` 36 | 后续查询商品的请求创建Command时,将会重用已创建的线程池。线程池隔离之后的服务依赖关系: 37 | 38 | 通过将发送请求线程与执行请求的线程分离,可有效防止发生级联故障。当线程池或请求队列饱和时,Hystrix将拒绝服务,使得请求线程可以快速失败,从而避免依赖问题扩散。 39 | 40 | #### 线程池隔离优缺点 41 | 优点: 42 | * 保护应用程序以免受来自依赖故障的影响,指定依赖线程池饱和不会影响应用程序的其余部分。 43 | * 当引入新客户端lib时,即使发生问题,也是在本lib中,并不会影响到其他内容。 44 | * 当依赖从故障恢复正常时,应用程序会立即恢复正常的性能。 45 | * 当应用程序一些配置参数错误时,线程池的运行状况会很快检测到这一点(通过增加错误,延迟,超时,拒绝等),同时可以通过动态属性进行实时纠正错误的参数配置。 46 | * 如果服务的性能有变化,需要实时调整,比如增加或者减少超时时间,更改重试次数,可以通过线程池指标动态属性修改,而且不会影响到其他调用请求。 47 | * 除了隔离优势外,hystrix拥有专门的线程池可提供内置的并发功能,使得可以在同步调用之上构建异步门面(外观模式),为异步编程提供了支持(Hystrix引入了Rxjava异步框架)。 48 | 49 | 注意:尽管线程池提供了线程隔离,我们的客户端底层代码也必须要有超时设置或响应线程中断,不能无限制的阻塞以致线程池一直饱和。 50 | 51 | 缺点: 52 | 线程池的主要缺点是增加了计算开销。每个命令的执行都在单独的线程完成,增加了排队、调度和上下文切换的开销。因此,要使用Hystrix,就必须接受它带来的开销,以换取它所提供的好处。 53 | 54 | 通常情况下,线程池引入的开销足够小,不会有重大的成本或性能影响。但对于一些访问延迟极低的服务,如只依赖内存缓存,线程池引入的开销就比较明显了,这时候使用线程池隔离技术就不适合了,我们需要考虑更轻量级的方式,如信号量隔离。 55 | 56 | ### 线程隔离-信号量 57 | 上面提到了线程池隔离的缺点,当依赖延迟极低的服务时,线程池隔离技术引入的开销超过了它所带来的好处。这时候可以使用信号量隔离技术来代替,通过设置信号量来限制对任何给定依赖的并发调用量。下图说明了线程池隔离和信号量隔离的主要区别: 58 | 59 | ![](https://static.oschina.net/uploads/space/2018/0206/111924_txyW_2663573.png) 60 | 61 | 使用线程池时,发送请求的线程和执行依赖服务的线程不是同一个,而使用信号量时,发送请求的线程和执行依赖服务的线程是同一个,都是发起请求的线程。 62 | 63 | 先看一个使用信号量隔离线程的示例: 64 | ``` 65 | 66 | public class QueryByOrderIdCommandSemaphore extends HystrixCommand { 67 | private final static Logger logger = LoggerFactory.getLogger(QueryByOrderIdCommandSemaphore.class); 68 | private OrderServiceProvider orderServiceProvider; 69 | 70 | public QueryByOrderIdCommandSemaphore(OrderServiceProvider orderServiceProvider) { 71 | super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("orderService")) 72 | .andCommandKey(HystrixCommandKey.Factory.asKey("queryByOrderId")) 73 | .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() 74 | .withCircuitBreakerRequestVolumeThreshold(10)////至少有10个请求,熔断器才进行错误率的计算 75 | .withCircuitBreakerSleepWindowInMilliseconds(5000)//熔断器中断请求5秒后会进入半打开状态,放部分流量过去重试 76 | .withCircuitBreakerErrorThresholdPercentage(50)//错误率达到50开启熔断保护 77 | .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE) 78 | .withExecutionIsolationSemaphoreMaxConcurrentRequests(10)));//最大并发请求量 79 | this.orderServiceProvider = orderServiceProvider; 80 | } 81 | 82 | @Override 83 | protected Integer run() { 84 | return orderServiceProvider.queryByOrderId(); 85 | } 86 | 87 | @Override 88 | protected Integer getFallback() { 89 | return -1; 90 | } 91 | } 92 | ``` 93 | 94 | **由于Hystrix默认使用线程池做线程隔离**,使用信号量隔离需要显示地将属性execution.isolation.strategy设置为ExecutionIsolationStrategy.SEMAPHORE,同时配置信号量个数,默认为10。客户端需向依赖服务发起请求时,首先要获取一个信号量才能真正发起调用,由于信号量的数量有限,当并发请求量超过信号量个数时,后续的请求都会直接拒绝,进入fallback流程。 95 | 96 | 信号量隔离主要是通过控制并发请求量,防止请求线程大面积阻塞,从而达到限流和防止雪崩的目的。 97 | 98 | ### 线程隔离总结 99 | 线程池和信号量都可以做线程隔离,但各有各的优缺点和支持的场景,对比如下: 100 | | | 线程切换 | 支持异步 | 支持超时 | 支持熔断 | 限流 | 开销 | 101 | | --- |--- |--- |--- |--- |--- | --- | 102 | | 信号量 | 否 | 否 | 否| 是 | 是 | 小 | 103 | | 线程池 | 是 | 是 | 是 |是 | 是 | 大 | 104 | 105 | 线程池和信号量都支持熔断和限流。相比线程池,信号量不需要线程切换,因此避免了不必要的开销。但是信号量不支持异步,也不支持超时,也就是说当所请求的服务不可用时,信号量会控制超过限制的请求立即返回,但是已经持有信号量的线程只能等待服务响应或从超时中返回,即可能出现长时间等待。线程池模式下,当超过指定时间未响应的服务,Hystrix会通过响应中断的方式通知线程立即结束并返回。 106 | 107 | ## 熔断 108 | ### 熔断器简介 109 | 现实生活中,可能大家都有注意到家庭电路中通常会安装一个保险盒,当负载过载时,保险盒中的保险丝会自动熔断,以保护电路及家里的各种电器,这就是熔断器的一个常见例子。Hystrix中的熔断器(Circuit Breaker)也是起类似作用,Hystrix在运行过程中会向每个commandKey对应的熔断器报告成功、失败、超时和拒绝的状态,熔断器维护并统计这些数据,并根据这些统计信息来决策熔断开关是否打开。如果打开,熔断后续请求,快速返回。隔一段时间(默认是5s)之后熔断器尝试半开,放入一部分流量请求进来,相当于对依赖服务进行一次健康检查,如果请求成功,熔断器关闭。 110 | 111 | ### 熔断器配置 112 | Circuit Breaker主要包括如下6个参数: 113 | 114 | 1、circuitBreaker.enabled 115 | 是否启用熔断器,默认是TRUE。 116 | 117 | 2 、circuitBreaker.forceOpen 118 | 熔断器强制打开,始终保持打开状态,不关注熔断开关的实际状态。默认值FLASE。 119 | 120 | 3、circuitBreaker.forceClosed 121 | 熔断器强制关闭,始终保持关闭状态,不关注熔断开关的实际状态。默认值FLASE。 122 | 123 | 4、circuitBreaker.errorThresholdPercentage 124 | 错误率,默认值50%,例如一段时间(10s)内有100个请求,其中有54个超时或者异常,那么这段时间内的错误率是54%,大于了默认值50%,这种情况下会触发熔断器打开。 125 | 126 | 5、circuitBreaker.requestVolumeThreshold 127 | 默认值20。含义是一段时间内至少有20个请求才进行errorThresholdPercentage计算。比如一段时间了有19个请求,且这些请求全部失败了,错误率是100%,但熔断器不会打开,总请求数不满足20。 128 | 129 | 6、circuitBreaker.sleepWindowInMilliseconds 130 | 半开状态试探睡眠时间,默认值5000ms。如:当熔断器开启5000ms之后,会尝试放过去一部分流量进行试探,确定依赖服务是否恢复。 131 | 132 | -------------------------------------------------------------------------------- /contents/Interview/SpringCloud/README.md: -------------------------------------------------------------------------------- 1 | # Spring Cloud面试专题 2 | [Spring Cloud](https://spring.io/projects/spring-cloud/) focuses on providing good out of box experience for typical use cases and extensibility mechanism to cover others. 3 | * Distributed/versioned configuration 4 | * Service registration and discovery 5 | * Routing 6 | * Service-to-service calls 7 | * Load balancing 8 | * Circuit Breakers 9 | * Global locks 10 | * Leadership election and cluster state 11 | * Distributed messaging 12 | 13 | ## Spring Cloud体系 14 | 15 | ### Eureka 16 | [Eureka专题](Eureka.md) 17 | 18 | ### Hystrix 19 | [Hystrix专题](Hystrix.md) 20 | 21 | ### Ribbon 22 | [Ribbon专题](Ribbon.md) 23 | 24 | ### Feign 25 | [Feign专题](Feign.md) 26 | 27 | ### Zuul路由网关 28 | 29 | ### Spring Boot和 Spring Cloud 联系与区别? 30 | Spring Boot是一个快速整合第三方框架,关注的是**微观**,具体关注快速方便的开发单个个体的服务; 31 | Spring Cloud 关注的是**宏观**,具体关注全局的微服务协调整理治理框架,将spring boot开发的一个个单体服务整合并管理起来;它为各个微服务之间提供 配置管理 服务发现 断路器路由 微代理 全局锁 分布式会话 等 集成服务。 32 | 33 | ### Spring Cloud和 Dubbo有哪些区別? 34 | 本质区别: 35 | * dubbo 是 基于 RPC 远程 过程调用 36 | * Spring Cloud 是基于 http rest api调用; 37 | 38 | ### 请说说eureka、consul和 zookeeper 的区別? 39 | 这里不得不提一下,我们知道分布式里一个重要的理论,那就是CAP原则。指的是在一个分布式系统中,Consistency(一致性)、Availability(可用性)、Partition Tolerance(分区容错性),不能同时成立。 40 | * 一致性:它要求在同一时刻点,分布式系统中的所有数据备份都处于同一状态。 41 | * 可用性:在系统集群的一部分节点宕机后,系统依然能够响应用户的请求。 42 | * 分区容错性:在网络区间通信出现失败,系统能够容忍。 43 | 44 | 一般来讲,基于网络的不稳定性,分布容错是不可避免的,所以我们默认CAP中的P总是成立的。一致性的强制数据统一要求,必然会导致在更新数据时部分节点处于被锁定状态,此时不可对外提供服务,影响了服务的可用性,反之亦然。因此一致性和可用性不能同时满足。在注册中心的发展上面,一直有两个分支:一个就是 CP 系统,追求数据的强一致性。还有一个是 AP 系统,追求高可用与最终一致。 45 | 46 | 我们接下来介绍的服务注册和发现组件中,Eureka满足了其中的AP,Consul和Zookeeper满足了其中的CP。 47 | 48 | 下面是这三个服务注册中心的特性对比, 没有最好的服务中心, 只有最合适的,我们可以根据项目的实际情况来进行选择。 49 | | | 开发语言 | CAP | 一致性算法 | 服务健康检查 | 对外接口 | kv存储 | 多数据中心 | 50 | | --- |--- |--- |--- |--- |--- |--- |--- | 51 | | Eureka | java | AP| - | 可配置 | HTTP | - | - | 52 | | Zookeeper | java | CP | Zab | 弱,长链接 |client | 支持 | - | 53 | | Consul | go | CP | Raft | 服务状态 | http和https | 支持 |支持 | 54 | -------------------------------------------------------------------------------- /contents/Interview/SpringCloud/Ribbon.md: -------------------------------------------------------------------------------- 1 | # Ribbon 2 | Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要介绍的Feign,它也是基于Ribbon实现的工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要。 3 | 4 | ## Ribbon负载均衡策略 5 | 6 | ## Ribbon底层原理 7 | 通过Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用非常简单,只需要如下两步: 8 | * 服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心。 9 | * 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用。 10 | 11 | 这样,我们就可以将服务提供者的高可用以及服务消费者的负载均衡调用一起实现了。 12 | ``` 13 | @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | @Inherited 17 | @Qualifier 18 | public @interface LoadBalanced { 19 | 20 | } 21 | ``` 22 | 从@LoadBalanced注解码的注释中,可以知道该注解用来给RestTemplate标记,以使用负载均衡的客户端(LoadBalancerClient)来配置它。 23 | 24 | 从LoadBalancerAutoConfiguration类上的注解可知,Ribbon实现负载均衡自动化配置需要满足下面两个条件: 25 | * @ConditionalOnClass(RestTemplate.class):RestTemplate必须存在于当前工程的环境中。 26 | * @ConditionalOnBean(LoadBalancerClient.class):在Spring的Bean工程中必须有LoadBalancerClient的实现bean。 27 | 28 | 该自动配置类,主要做了下面三件事: 29 | 1. 创建了一个LoadBalancerInterceptor的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。 30 | 2. 创建了一个RestTemplateCustomizer的Bean,用于给RestTemplate增加LoadbalancerInterceptor 31 | 3. 维护了一个被@LoadBalanced注解修饰的RestTemplate对象列表,并在这里进行初始化,通过调用RestTemplateCustomizer的实例来给需要客户端负载均衡的RestTemplate增加LoadBalancerInterceptor拦截器。 32 | 33 | 34 | -------------------------------------------------------------------------------- /contents/Interview/TCP_HTTP/Cookie_Session.md: -------------------------------------------------------------------------------- 1 | ## Cookie 2 | ### 什么是cookie? 3 | http是一个无状态协议,什么是无状态呢?就是说这一次请求和上一次请求是没有任何关系的,互不认识的,没有关联的。这种无状态的的好处是快速。坏处是假如我们想要把www.zhihu.com/login.html和www.zhihu.com/index.html关联起来,必须使用某些手段和工具。 4 | 5 | 由于http的无状态性,为了使某个域名下的所有网页能够共享某些数据,session和cookie出现了。客户端访问服务器的流程如下: 6 | * 首先,客户端会发送一个http请求到服务器端。 7 | * 服务器端接受客户端请求后,建立一个session,并发送一个http响应到客户端,这个响应头,其中就包含Set-Cookie头部。该头部包含了sessionId。Set-Cookie格式如下,具体请看Cookie详解 8 | Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure] 9 | * 在客户端发起的第二次请求,假如服务器给了set-Cookie,浏览器会自动在请求头中添加cookie 10 | * 服务器接收请求,分解cookie,验证信息,核对成功后返回response给客户端 11 | 12 | ### cookie 常用属性项 13 | | 属性项 | 属性项介绍 | 14 | | --- | --- | 15 | | Name | 键值对,可以设置要保存的 Key/Value,注意这里的 NAME 不能和其他属性项的名字一样| 16 | | Expires | 过期时间,在设置的某个时间点后该 Cookie 就会失效 | 17 | | Domain | 生成该 Cookie 的域名,如 domain="www.baidu.com" | 18 | | Path | 该 Cookie 是在当前的哪个路径下生成的,如 path=/wp-admin/ | 19 | | httpOnly | 这个选项用来设置cookie是否能通过js去访问。默认情况下,cookie不会带httpOnly选项(即为空)| 20 | |Secure | 如果设置了这个属性,那么只会在 SSH 连接时才会回传该 Cookie | 21 | 22 | ### Expires 23 | 该属性用来设置Cookie的有效期。Cookie中的maxAge用来表示该属性,单位为秒。Cookie中通过getMaxAge()和setMaxAge(int maxAge)来读写该属性。maxAge有3种值,分别为正数,负数和0。 24 | 25 | 如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中(每个浏览器存储的位置不一致)。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信息将永远有效。 26 | 27 | 当maxAge属性为负数,则表示该Cookie只是一个临时Cookie,不会被持久化,仅在本浏览器窗口或者本窗口打开的子窗口中有效,关闭浏览器后该Cookie立即失效。 28 | 29 | 当maxAge为0时,表示立即删除Cookie。 30 | 31 | ### Domain 32 | Cookie是不可以跨域名的,隐私安全机制禁止网站非法获取其他网站的Cookie。 33 | 34 | 正常情况下,同一个一级域名下的两个二级域名也不能交互使用Cookie,比如book.jd.com和shouji.jd.com,因为二者的域名不完全相同。如果想要jd.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数为.jd.com,这样使用xinfang.jd.com和diannao.jd.com 就能访问同一个cookie了。 35 | 36 | ### Path 37 | path属性决定允许访问Cookie的路径。比如,设置为"/"表示允许所有路径都可以使用Cookie。 38 | 39 | ### httpOnly 40 | 这个选项用来设置cookie是否能通过 js 去访问。默认情况下,cookie不会带httpOnly选项(即为空),所以默认情况下,客户端是可以通过js代码去访问(包括读取、修改、删除等)这个cookie的。当cookie带httpOnly选项时,客户端则无法通过js代码去访问(包括读取、修改、删除等)这个cookie。 41 | 42 | 在客户端是不能通过js代码去设置一个httpOnly类型的cookie的,这种类型的cookie只能通过服务端来设置。 43 | 44 | ## Session 45 | 46 | 47 | ## cookie与session的区别 48 | 主要有以下几点: 49 | * 存在的位置:cookie以文本格式存储在浏览器上,存储量有限;而session存储在服务端; 50 | * 安全性:cookie是以明文的方式存放在客户端的,安全性低,可以通过一个加密算法进行加密后存放; session存放于服务器的内存中,所以安全性好 51 | * 网络传输限制:cookie会传递消息给服务器; session本身存放于服务器,不会有传送流量; 52 | * 生命周期:cookie的生命周期是累计的,从创建时,就开始计时,20分钟后,cookie生命周期结束;session的生命周期是间隔的,从创建时,开始计时如在20分钟,没有访问session,那么session生命周期被销毁。 53 | 54 | -------------------------------------------------------------------------------- /contents/Interview/TCP_HTTP/HTTP.md: -------------------------------------------------------------------------------- 1 | # HTTP协议 2 | ## HTTP请求消息 3 | 客户端发送一个HTTP请求到服务器的请求消息包括以下格式: 4 | 请求行(request line)、请求头部(header)、空行和请求数据四个部分组成。 5 | 6 | ![](https://github.com/TFdream/blog/blob/master/docs/image/Http/http_request.png) 7 | 8 | ### GET请求 9 | Get请求例子,使用Charles抓取的request: 10 | ``` 11 | GET /logo.jpg HTTP/1.1 12 | Host yirendai.com 13 | User-Agent Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 14 | Accept image/webp,image/*,*/*;q=0.8 15 | Referer http://www.imooc.com/ 16 | Accept-Encoding gzip, deflate, sdch 17 | Accept-Language zh-CN,zh;q=0.8 18 | ``` 19 | 20 | 第一部分:请求行,用来说明请求类型,要访问的资源以及所使用的HTTP版本. 21 | GET说明请求类型为GET,[/logo.jpg]为要访问的资源,该行的最后一部分说明使用的是HTTP1.1版本。 22 | 23 | 第二部分:请求头部,紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息,从第二行起为请求头部,HOST将指出请求的目的地.User-Agent,服务器端和客户端脚本都能访问它,它是浏览器类型检测逻辑的重要基础.该信息由你的浏览器来定义,并且在每个请求中自动发送等等 24 | 25 | 第三部分:空行,请求头部后面的空行是必须的,即使第四部分的请求数据为空,也必须有空行。 26 | 27 | 第四部分:请求数据也叫主体,可以添加任意的其他数据。这个例子的请求数据为空。 28 | 29 | ### POST请求 30 | POST请求例子,使用Charles抓取的request: 31 | ``` 32 | POST /login HTTP1.1 33 | Host:127.0.0.1 34 | User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022) 35 | Content-Type:application/x-www-form-urlencoded 36 | Content-Length:40 37 | Connection: Keep-Alive 38 | 39 | name=Professional%20Ajax&publisher=Wiley 40 | ``` 41 | 第一部分:请求行,第一行明了是post请求,以及http1.1版本。 42 | 43 | 第二部分:请求头部,第二行至第六行。 44 | 45 | 第三部分:空行,第七行的空行。 46 | 47 | 第四部分:请求数据,第八行。 48 | 49 | ## HTTP响应消息 50 | 一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。 51 | 52 | HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。 53 | ![](https://github.com/TFdream/blog/blob/master/docs/image/Http/http_response.png) 54 | 55 | 第一部分:状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。 56 | 第一行为状态行,(HTTP/1.1)表明HTTP版本为1.1版本,状态码为200,状态消息为(ok) 57 | 58 | 第二部分:消息报头,用来说明客户端要使用的一些附加信息 59 | 第二行和第三行为消息报头, 60 | Date:生成响应的日期和时间;Content-Type:指定了MIME类型的HTML(text/html),编码类型是UTF-8 61 | 62 | 第三部分:空行,消息报头后面的空行是必须的。 63 | 64 | 第四部分:响应正文,服务器返回给客户端的文本信息。 65 | 66 | 67 | ## HTTPS 68 | https, 全称Hyper Text Transfer Protocol Secure,相比http,多了一个secure,这一个secure是怎么来的呢?这是由TLS(SSL)提供的。 69 | 70 | http是HTTP协议运行在TCP之上。所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。 71 | https是HTTP运行在SSL/TLS之上,SSL/TLS运行在TCP之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。此外客户端可以验证服务器端的身份,如果配置了客户端验证,服务器方也可以验证客户端的身份。 72 | 73 | HTTP缺省工作在TCP协议80端口,用户访问网站以http:\/\/打头的都是标准HTTP服务,HTTP所封装的信息都是明文的,通过抓包工具可以分析其信息内容,如果这些信息包含你的银行卡账号、密码,你肯定无法接受这种服务,那有没有可以加密这些敏感信息的服务呢?那就是HTTPS。 74 | 75 | HTTPS缺省工作在TCP协议443端口,它的工作流程一般如以下方式: 76 | 1. 完成TCP三次同步握手 77 | 2. 客户端验证服务器数字证书,通过,进入步骤3 78 | 3. DH算法协商对称加密算法的密钥、hash算法的密钥; 79 | 4. SSL安全加密隧道协商完成 80 | 5. 网页以加密的方式传输,用协商的对称加密算法和密钥加密,保证数据机密性;用协商的hash算法进行数据完整性保护,保证数据不被篡改 81 | 82 | 如果HTTPS是网银服务,以上SSL安全隧道成功建立才会要求用户输入账户信息,账户信息是在安全隧道里传输,所以不会泄密! 83 | 84 | ## HTTP 2.0 85 | HTTP 2.0 的出现,相比于 HTTP 1.x ,大幅度的提升了 web 性能。在与 HTTP/1.1 完全语义兼容的基础上,进一步减少了网络延迟。而对于前端开发人员来说,无疑减少了在前端方面的优化工作。本文将对 HTTP 2.0 协议 个基本技术点进行总结,联系相关知识,探索 HTTP 2.0 是如何提高性能的。 86 | 87 | ### 1. 多路复用 (Multiplexing) 88 | 众所周知 ,在 HTTP/1.1 协议中 「浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制。超过限制数目的请求会被阻塞」。 89 | 90 | 而 HTTP/2 的多路复用(Multiplexing) 则允许同时通过单一的 HTTP/2 连接发起多重的**请求-响应**消息。 91 | ![http2_multiplexing](https://github.com/TFdream/blog/blob/master/docs/image/Http/http2_multiplexing.jpg) 92 | 93 | 因此 HTTP/2 可以很容易的去实现多流并行而不用依赖建立多个 TCP 连接,HTTP/2 把 HTTP 协议通信的基本单位缩小为一个一个的帧,这些帧对应着逻辑流中的消息。并行地在同一个 TCP 连接上**双向交换**信息。 94 | 95 | ### 2. 二进制分帧 96 | 在不改动 HTTP/1.x 的语义、方法、状态码、URI 以及首部字段….. 的情况下, HTTP/2 是如何做到「突破 HTTP1.1 的性能限制,改进传输性能,实现低延迟和高吞吐量」的 ? 97 | 98 | 关键之一就是在 应用层(HTTP/2)和传输层(TCP or UDP)之间增加一个二进制分帧层。 99 | ![](https://github.com/TFdream/blog/blob/master/docs/image/Http/htt2_binary_frame.jpg) 100 | 101 | 在二进制分帧层中, HTTP/2 会将所有传输的信息分割为更小的消息和帧(frame),并对它们采用二进制格式的编码 ,其中 HTTP1.x 的首部信息会被封装到 HEADER frame,而相应的 Request Body 则封装到 DATA frame 里面。 102 | 103 | HTTP/2 通信都在一个连接上完成,这个连接可以承载任意数量的双向数据流。 104 | 105 | 在过去, HTTP 性能优化的关键并不在于高带宽,而是低延迟。。TCP 连接会随着时间进行自我「调谐」,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐则被称为 TCP 慢启动。由于这种原因,让原本就具有突发性和短时性的 HTTP 连接变的十分低效。 106 | 107 | HTTP/2 通过让所有数据流共用同一个连接,可以更有效地使用 TCP 连接,让高带宽也能真正的服务于 HTTP 的性能提升。 108 | 109 | ### 总结 110 | * 单连接多资源的方式,减少服务端的链接压力,内存占用更少,连接吞吐量更大 111 | * 由于 TCP 连接的减少而使网络拥塞状况得以改善,同时慢启动时间的减少,使拥塞和丢包恢复速度更快 112 | 113 | 114 | ## 参考资料 115 | [HTTP/2.0 相比1.0有哪些重大改进?](https://www.zhihu.com/question/34074946) 116 | 117 | -------------------------------------------------------------------------------- /contents/Interview/TCP_HTTP/README.md: -------------------------------------------------------------------------------- 1 | # 网络传输协议 2 | 3 | ## TCP协议 4 | [TCP协议专题](TCP.md) 5 | 6 | ## UDP协议 7 | [UDP协议专题](UDP.md) 8 | 9 | ## HTTP协议 10 | [HTTP协议专题](HTTP.md) 11 | 12 | ## WebSocket协议 13 | [WebSocket协议专题](WebSocket.md) 14 | 15 | 16 | ## TCP与UDP的区别 17 | 两者区别如下: 18 | 1. TCP提供面向连接的传输,通信前要先建立连接(三次握手机制); UDP提供无连接的传输,通信前不需要建立连接。 19 | 2. TCP提供可靠的传输(有序,无差错,不丢失,不重复); UDP提供不可靠的传输。 20 | 3. TCP面向字节流的传输,因此它能将信息分割成组,并在接收端将其重组; UDP是面向数据报的传输,没有分组开销。 21 | 4. TCP提供拥塞控制和流量控制机制; UDP不提供拥塞控制和流量控制机制。 22 | 23 | ## TCP 为什么是三次握手,而不是两次或四次? 24 | TCP作为一种可靠传输控制协议,其核心思想:既要保证数据可靠传输,又要提高传输的效率,而用三次恰恰可以满足以上两方面的需求。 25 | 26 | 那么为什么需要第三次握手呢?我们来看一下,假设一下如果没有第三次握手,而是两次握手后我们就认为连接成功了,那么会发生什么?第三次握手是为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误。 27 | 28 | 譬如发起请求遇到类似这样的情况:客户端发出去的第一个连接请求由于某些原因在网络节点中滞留了导致延迟,直到连接释放的某个时间点才到达服务端,这是一个早已失效的报文,但是此时服务端仍然认为这是客户端的建立连接请求第一次握手,于是服务端回应了客户端,第二次握手。 29 | 30 | 如果只有两次握手,那么到这里,连接就建立了,但是此时客户端并没有任何数据要发送,而服务端还在傻傻的等候佳音,造成很大的资源浪费。所以需要第三次握手,只有客户端再次回应一下,就可以避免这种情况。 31 | 32 | 链接:https://www.zhihu.com/question/24853633 33 | 34 | ## 长连接和短连接 35 | HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议。 IP协议主要解决网络路由和寻址问题,TCP协议主要解决如何在IP层之上可靠地传递数据包,使得网络上接收端收到发送端所发出的所有包,并且顺序与发送顺序一致。TCP协议是可靠的、面向连接的。 36 | 37 | 在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。 38 | 39 | 而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码: 40 | ``` 41 | Connection:keep-alive 42 | ``` 43 | 在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。 44 | 45 | HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。 46 | 47 | -------------------------------------------------------------------------------- /contents/Interview/TCP_HTTP/TCP.md: -------------------------------------------------------------------------------- 1 | # TCP协议 2 | 位于传输层, 提供可靠的字节流服务。所谓的字节流服务(Byte Stream Service) 是指, 为了方便传输, 将大块数据分割成以报文段(segment) 为单位的数据包进行管理。 而可靠的传输服务是指, 能够把数据准确可靠地传给对方。 即TCP 协议为了更容易传送大数据才把数据分割, 而且 TCP 协议能够确认数据最终是否送达到对方。所以,TCP连接相当于两根管道(一个用于服务器到客户端,一个用于客户端到服务器),管道里面数据传输是通过字节码传输,传输是有序的,每个字节都是一个一个来传输。 3 | 4 | ## 三次握手 5 | 所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。握手过程中使用了 TCP 的标志(flag):SYN(synchronize) 和ACK(acknowledgement)。 6 | 7 | 在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示: 8 | ![](https://github.com/TFdream/blog/blob/master/docs/image/Http/tcp_3_handshake.png) 9 | 10 | 1. 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。 11 | 2. 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。 12 | 3. 第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。 13 | 14 | ## 四次挥手 15 | 所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示: 16 | ![](https://github.com/TFdream/blog/blob/master/docs/image/Http/tcp_4_bye.png) 17 | 18 | 由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。 19 | 20 | 1. 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。 21 | 2. 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。 22 | 3. 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。 23 | 4. 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。 24 | 25 | ## 为什么建立连接是三次握手,而关闭连接却是四次挥手呢? 26 | 这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。 27 | 28 | 而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。 29 | 30 | ## TCP是如何保证可靠数据传输的? 31 | TCP为应用程序提供可靠的通信连接,因为他采用了三次握手协议,三次握手协议指的是在发送数据的准备阶段,服务器端和客户端之间需要进行三次交互。 32 | 33 | 连接建立后,客户端和服务器就开始进行安全可靠的数据传输了。 34 | 35 | 什么是可靠,所谓的可靠就是说发送方发送的数据到达接收方的时候无差错,不丢失,不重复,且按序到达;。 36 | 37 | 为了保证接受的报文段是没有比特差错的,TCP协议主要通过检验和、序列号、确认应答(ACK)、重发控制、连接管理、窗口控制等实现可靠性连接。 38 | 39 | -------------------------------------------------------------------------------- /contents/Interview/TCP_HTTP/UDP.md: -------------------------------------------------------------------------------- 1 | # UDP协议 2 | 无连接协议,也称透明协议,也位于传输层。 3 | 4 | -------------------------------------------------------------------------------- /contents/Interview/Writing_Code.md: -------------------------------------------------------------------------------- 1 | 2 | ## 编程实现一个简单的HashMap 3 | ``` 4 | package io.mindflow.elasticjob; 5 | 6 | /** 7 | * @author Ricky Fung 8 | */ 9 | public class HashMap { 10 | 11 | //默认初始化化容量,即16 12 | static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 13 | 14 | //最大容量,即2的30次方 15 | static final int MAXIMUM_CAPACITY = 1 << 30; 16 | 17 | //默认装载因子 18 | static final float DEFAULT_LOAD_FACTOR = 0.75f; 19 | 20 | //HashMap内部的存储结构是一个数组,此处数组为空,即没有初始化之前的状态 21 | static final Entry[] EMPTY_TABLE = {}; 22 | 23 | //空的存储实体 24 | transient Entry[] table = (Entry[]) EMPTY_TABLE; 25 | 26 | //实际存储的key-value键值对的个数 27 | transient int size; 28 | 29 | //阈值,当table == {}时,该值为初始容量(初始容量默认为16);当table被填充了,也就是为table分配内存空间后,threshold一般为 capacity*loadFactory。HashMap在进行扩容时需要参考threshold 30 | int threshold; 31 | 32 | //负载因子,代表了table的填充度有多少,默认是0.75 33 | final float loadFactor; 34 | 35 | //用于快速失败,由于HashMap非线程安全,在对HashMap进行迭代时,如果期间其他线程的参与导致HashMap的结构发生变化了(比如put,remove等操作),需要抛出异常ConcurrentModificationException 36 | transient int modCount; 37 | 38 | //通过初始容量和状态因子构造HashMap 39 | public HashMap(int initialCapacity, float loadFactor) { 40 | if (initialCapacity < 0)//参数有效性检查 41 | throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); 42 | if (initialCapacity > MAXIMUM_CAPACITY)//参数有效性检查 43 | initialCapacity = MAXIMUM_CAPACITY; 44 | if (loadFactor <= 0 || Float.isNaN(loadFactor))//参数有效性检查 45 | throw new IllegalArgumentException("Illegal load factor: " + 46 | loadFactor); 47 | 48 | this.loadFactor = loadFactor; 49 | threshold = initialCapacity; 50 | } 51 | 52 | //通过扩容因子构造HashMap,容量去默认值,即16 53 | public HashMap(int initialCapacity) { 54 | this(initialCapacity, DEFAULT_LOAD_FACTOR); 55 | } 56 | 57 | //装载因子取0.75,容量取16,构造HashMap 58 | public HashMap() { 59 | this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); 60 | } 61 | 62 | static class Entry { 63 | final K key; 64 | V value; 65 | Entry next; 66 | final int hash; 67 | 68 | Entry(int h, K k, V v, Entry n) { 69 | value = v; 70 | next = n; 71 | key = k; 72 | hash = h; 73 | } 74 | 75 | public K getKey() { 76 | return key; 77 | } 78 | 79 | public V setValue(V newValue) { 80 | V oldValue = value; 81 | value = newValue; 82 | return oldValue; 83 | } 84 | 85 | public V getValue() { 86 | return value; 87 | } 88 | } 89 | 90 | public V put(K key, V value) { 91 | //如果table数组为空数组{},进行数组填充(为table分配实际内存空间),入参为threshold,此时threshold为initialCapacity 默认是1<<4(=16) 92 | if (table == EMPTY_TABLE) { 93 | inflateTable(threshold);//分配数组空间 94 | } 95 | //如果key为null,存储位置为table[0]或table[0]的冲突链上 96 | if (key == null) 97 | return putForNullKey(value); 98 | int hash = hash(key);//对key的hashcode进一步计算,确保散列均匀 99 | int i = indexFor(hash, table.length);//获取在table中的实际位置 100 | for (Entry e = table[i]; e != null; e = e.next) { 101 | //如果该对应数据已存在,执行覆盖操作。用新value替换旧value,并返回旧value 102 | Object k; 103 | if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 104 | V oldValue = e.value; 105 | e.value = value; 106 | e.recordAccess(this);//调用value的回调函数,其实这个函数也为空实现 107 | return oldValue; 108 | } 109 | } 110 | modCount++;//保证并发访问时,若HashMap内部结构发生变化,快速响应失败 111 | addEntry(hash, key, value, i);//新增一个entry 112 | return null; 113 | } 114 | 115 | void addEntry(int hash, K key, V value, int bucketIndex) { 116 | if ((size >= threshold) && (null != table[bucketIndex])) { 117 | resize(2 * table.length);//当size超过临界阈值threshold,并且即将发生哈希冲突时进行扩容,新容量为旧容量的2倍 118 | hash = (null != key) ? hash(key) : 0; 119 | bucketIndex = indexFor(hash, table.length);//扩容后重新计算插入的位置下标 120 | } 121 | 122 | //把元素放入HashMap的桶的对应位置 123 | createEntry(hash, key, value, bucketIndex); 124 | } 125 | 126 | //创建元素 127 | void createEntry(int hash, K key, V value, int bucketIndex) { 128 | Entry e = table[bucketIndex]; //获取待插入位置元素 129 | table[bucketIndex] = new Entry<>(hash, key, value, e);//这里执行链接操作,使得新插入的元素指向原有元素。 130 | //这保证了新插入的元素总是在链表的头 131 | size++;//元素个数+1 132 | } 133 | 134 | //按新的容量扩容Hash表 135 | void resize(int newCapacity) { 136 | Entry[] oldTable = table;//老的数据 137 | int oldCapacity = oldTable.length;//获取老的容量值 138 | if (oldCapacity == MAXIMUM_CAPACITY) {//老的容量值已经到了最大容量值 139 | threshold = Integer.MAX_VALUE;//修改扩容阀值 140 | return; 141 | } 142 | //新的结构 143 | Entry[] newTable = new Entry[newCapacity]; 144 | transfer(newTable, initHashSeedAsNeeded(newCapacity));//将老的表中的数据拷贝到新的结构中 145 | table = newTable;//修改HashMap的底层数组 146 | threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);//修改阀值 147 | } 148 | //返回数组下标 149 | static int indexFor(int h, int length) { 150 | return h & (length-1); 151 | } 152 | 153 | //用了很多的异或,移位等运算,对key的hashcode进一步进行计算以及二进制位的调整等来保证最终获取的存储位置尽量分布均匀 154 | final int hash(Object k) { 155 | int h = hashSeed; 156 | if (0 != h && k instanceof String) {//这里针对String优化了Hash函数,是否使用新的Hash函数和Hash因子有关 157 | return sun.misc.Hashing.stringHash32((String) k); 158 | } 159 | 160 | h ^= k.hashCode(); 161 | 162 | h ^= (h >>> 20) ^ (h >>> 12); 163 | return h ^ (h >>> 7) ^ (h >>> 4); 164 | } 165 | 166 | private void inflateTable(int toSize) { 167 | int capacity = roundUpToPowerOf2(toSize);//capacity一定是2的次幂 168 | threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);//此处为threshold赋值,取capacity*loadFactor和MAXIMUM_CAPACITY+1的最小值,capaticy一定不会超过MAXIMUM_CAPACITY,除非loadFactor大于1 169 | table = new Entry[capacity];//分配空间 170 | initHashSeedAsNeeded(capacity);//选择合适的Hash因子 171 | } 172 | 173 | private static int roundUpToPowerOf2(int number) { 174 | // assert number >= 0 : "number must be non-negative"; 175 | return number >= MAXIMUM_CAPACITY 176 | ? MAXIMUM_CAPACITY 177 | : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1; 178 | } 179 | } 180 | 181 | ``` 182 | 183 | 参考:[HashMap源码解析(基于JDK1.7)](https://blog.csdn.net/xiaokang123456kao/article/details/77503784) 184 | 185 | ## 编程实现一个延时队列 186 | 时间轮(TimingWheel) 187 | 188 | Kafka时间轮的实现是TimingWheel,他是一个存储定时任务的环形队列(桶),底层使用数组实现,数组中每一个元素可以存放一个TimerTaskList对象 189 | TimerTaskList是环形双向链表,在其中链表项TimeTaskEntry封装了真正的定时任务TimerTask。TimerTaskList使用expiration字段记录了整个TimerTaskList的超时时间。TimeTaskEntry中的expirationMs字段记录了超时时间戳,timerTask字段指向了对应的TimerTask任务. 190 | -------------------------------------------------------------------------------- /contents/JDK Source/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/JVM/README.md: -------------------------------------------------------------------------------- 1 | # blog 2 | My blog. 3 | -------------------------------------------------------------------------------- /contents/JVM/jmm_model.md: -------------------------------------------------------------------------------- 1 | # JMM内存模型 2 | 3 | ## JMM简介 4 | Java内存模型(即Java Memory Model,简称JMM)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝,前面说过,工作内存是每个线程的私有数据区域,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图 5 | 6 | ## 1. CPU缓存 7 | CPU 缓存(Cache Memory)是位于 CPU 与内存之间的临时存储器,它的容量比内存小的多但是交换速度却比内存要快得多。 8 | 9 | 高速缓存的出现主要是为了解决 CPU 运算速度与内存读写速度不匹配的矛盾,因为 CPU 运算速度要比内存读写速度快很多,这样会使 CPU 花费很长时间等待数据到来或把数据写入内存。 10 | 11 | CPU 缓存可以分为一级缓存、二级缓存(L2),部分高端 CPU 还具有三级缓存。每一级缓存中所储存的全部数据都是下一级缓存的一部分,越靠近 CPU 的缓存越快也越小。所以 L1 缓存很小但很快(译注:L1 表示一级缓存),并且紧靠着在使用它的 CPU 内核。L2 大一些,也慢一些,并且仍然只能被一个单独的 CPU 核使用。L3 在现代多核机器中更普遍,仍然更大更慢,并且被单个插槽上的所有 CPU 核共享。主内存由全部插槽上的所有 CPU 核共享。 12 | 关系如下图: 13 | 14 | ![image](https://user-images.githubusercontent.com/13992911/115152914-d647a680-a0a5-11eb-8fc7-9977629858e7.png) 15 | 16 | 当 CPU 执行运算的时候,它先去 L1 查找所需的数据,再去 L2,然后是 L3,最后如果这些缓存中都没有,所需的数据就要去主内存拿。走得越远,运算耗费的时间就越长。所以如果你在做一些很频繁的事,你要确保数据在 L1 缓存中。 17 | 18 | 19 | ## 2. CPU缓存一致性协议 20 | 缓存一致性:在多核CPU中,内存中的数据会在多个核心中存在数据副本,某一个核心发生修改操作,就产生了数据不一致的问题。而一致性协议正是用于保证多个CPU cache之间缓存共享数据的一致。 21 | 22 | 为了达到数据访问的一致,需要各个处理器在访问缓存时遵循一些协议,在读写时根据协议来操作,常见的协议有MSI,MESI,MOSI等。其中最经典的MESI协议 23 | 24 | ![image](https://user-images.githubusercontent.com/13992911/115152995-45bd9600-a0a6-11eb-8775-2ed3ed2ee182.png) 25 | 26 | 27 | ## Java内存模型和硬件内存架构之间的桥接 28 | Java内存模型与硬件内存架构之间存在差异。硬件内存架构没有区分线程栈和堆。对于硬件,所有的线程栈和堆都分布在主内存中。部分线程栈和堆可能有时候会出现在CPU缓存中和CPU内部的寄存器中。如下图所示: 29 | 30 | ![image](https://user-images.githubusercontent.com/13992911/115153025-5c63ed00-a0a6-11eb-98e1-3a291088c9b6.png) 31 | 32 | 从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系: 33 | * 线程之间的共享变量存储在主内存(Main Memory)中 34 | * 每个线程都有一个私有的本地内存(Local Memory),本地内存是JMM的一个抽象概念,并不真实存在,它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。本地内存中存储了该线程以读/写共享变量的拷贝副本。 35 | * 从更低的层次来说,主内存就是硬件的内存,而为了获取更好的运行速度,虚拟机及硬件系统可能会让工作内存优先存储于寄存器和高速缓存中。 36 | * Java内存模型中的线程的工作内存(working memory)是cpu的寄存器和高速缓存的抽象描述。而JVM的静态内存储模型(JVM内存模型)只是一种对内存的物理划分而已,它只局限在内存,而且只局限在JVM的内存。 37 | 38 | ![image](https://user-images.githubusercontent.com/13992911/115153049-7e5d6f80-a0a6-11eb-8e89-062f8405e4a6.png) 39 | 40 | 41 | ## 相关资料 42 | * [Java内存模型(JMM)总结](https://zhuanlan.zhihu.com/p/29881777) 43 | * [杂谈伪共享(false sharing)及解决方案](https://github.com/TFdream/workshop/issues/37) 44 | -------------------------------------------------------------------------------- /contents/JVM/jvm_classloader.md: -------------------------------------------------------------------------------- 1 | # JVM类加载机制 2 | -------------------------------------------------------------------------------- /contents/JVM/jvm_space.md: -------------------------------------------------------------------------------- 1 | # 基础篇:JVM运行时内存布局 2 | 3 | JVM运行时内存布局如下: 4 | ![image](https://user-images.githubusercontent.com/13992911/115152364-5ddfe600-a0a3-11eb-9c90-a805dfa14cb0.png) 5 | 6 | 其中,Java虚拟机栈、程序计数器、Heap、本地方法栈、Metaspace属于JVM运行时的内存; 7 | 8 | JAVA堆和MetasSpace元空间属于线程共享的(黄色部分);虚拟机栈和本地方法栈、程序计数器是线程私有的 9 | 10 | **虚拟机栈和本地方法栈、程序计数器 3个区域,生命周期与Thread相同,即:线程创建时,相应的内存区创建,线程销毁时,释放相应内存。** 11 | 12 | ## 1、JVM的内存区域布局 13 | * java代码的执行步骤有三点 14 | * java源码文件->编译器->字节码文件 15 | * 字节码文件->JVM->机器码 16 | * 机器码->系统CPU执行 17 | * JVM执行的字节码需要用类加载来载入;字节码文件可以来自本地文件,可以在网络上获取,也可以实时生成。就是说你可以跳过写java代码阶段,直接生成字节码交由JVM执行 18 | * 其中Java虚拟机栈、程序计数器、Heap、本地方法栈、Metaspace属于JVM运行时的内存; 19 | 20 | ### 2.1 程序计数器(Progarm Counter Register) 21 | * 一块较小的内存空间, 是当前线程所执行的字节码的行号指示器。线程有一个独属的程序计数器,字节码解析工作时需要程序计数器来选取下一指令,分支、循环、跳转等依赖它 22 | * 正在执行java方法线程的计数器记录的是虚拟机字节码指令的地址;如果还是Native方法,则为空 23 | * 程序计数器内存区域是唯一一个在虚拟机中没有规定任何OutOfMemoryError错误的区域 24 | 25 | ### 2.2 虚拟机栈(Virtual Machine Stack) 26 | ![image](https://user-images.githubusercontent.com/13992911/115152702-b368c280-a0a4-11eb-9f1f-89158bc4a593.png) 27 | 28 | * Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息 29 | * 每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程 30 | * 栈帧是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、 方法返回值和异常分派(Dispatch Exception)。栈帧随着方法调用而创建,随着方法结束而销毁(无论方法是正常完成还是异常完成) 31 | * 如果线程请求的栈深度大于虚拟机允许深度,则抛出StackOverflowError;扩展时无法申请到足够内存,则抛出OutOfMemeryError 32 | 33 | ### 2.3 本地方法栈(Native Method Stack) 34 | 35 | 本地方法栈和虚拟机栈作用类似,区别是虚拟机栈为执行Java方法服务,而本地方法栈则为Native方法服务。(HopShot的实现 直接把本地方法栈和虚拟机栈合二为一) 36 | 37 | **上述3类区域,生命周期与Thread相同,即:线程创建时,相应的内存区创建,线程销毁时,释放相应内存。** 38 | 39 | ### 2.4 堆(Heap) 40 | 线程共享的一块内存区域,几乎所有的对象实例在这里分配内存,也是垃圾收集器进行垃圾收集的最重要的内存区域。因此很多时候也叫GC堆 41 | 线程私有的分配缓存区(Thread Local Alloaction Buffer)也是在堆划分出来的 42 | 43 | JDK8的版本,因使用元空间代替永久代,字符串常量池和类的静态变量也放入java堆中。 44 | 45 | ![image](https://user-images.githubusercontent.com/13992911/115152555-27ef3180-a0a4-11eb-8f50-ea6551a18beb.png) 46 | 47 | ### 2.5 元空间(MetaSpace) 48 | 49 | 主要存储类的元数据,比如类的各种描述信息,类名、方法、字段、访问限制等,既编译器编译后的代码等数据 50 | 运行时常量池:Class文件中除了有类的版本、字段、方法等描述等信息外;还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分将在类加载后存放到元空间的运行时常量池中 51 | 52 | **使用元空间代替永久代原因** 53 | * 永久代的大小是在启动时固定好的,很难进行调优;太大则容易导致永久代溢出;太小在运行时,容易抛出OutOfMemeryError 54 | * 字符串存在永久代中,使用时易出问题,由于永久代内存经常不够用,爆出异常OutOfMemoryError: PermGen 55 | 56 | 57 | ### 直接内存 58 | 59 | 它并不是虚拟机运行时数据区的一般分,也不在规范定义。JDK1.4,引入了Channel(通道)与Buffer(缓存区)的I/O方式,它可以使用Native函数分配堆外内存,可通过DirectByteBuffer操作。 60 | -------------------------------------------------------------------------------- /contents/Java/Alibaba_Java_Coding_Guidelines.md: -------------------------------------------------------------------------------- 1 | ## 阿里巴巴Java开发规约插件全球首发 2 | 2017年10月14日杭州云栖大会,Java代码规约扫描插件全球首发仪式正式启动,规范正式以插件形式公开走向业界,引领Java语言的规范之路。 3 | 4 | ## 传送门 5 | 链接:https://github.com/alibaba/p3c 6 | -------------------------------------------------------------------------------- /contents/Java/Apache_Tika_In_Action.md: -------------------------------------------------------------------------------- 1 | 2 | ## 概览 3 | [Apache Tika](http://tika.apache.org/) 是一个内容分析工具,自带全面的parser工具类,能解析基本所有常见格式的文件,得到文件的metadata,content等内容,返回格式化信息。总的来说可以作为一个通用的解析工具。特别对于搜索引擎的数据抓去和处理步骤有重要意义。 4 | 官方介绍如下: 5 | > The Apache Tika™ toolkit detects and extracts metadata and text from over a thousand different file types (such as PPT, XLS, and PDF). 6 | > All of these file types can be parsed through a single interface, making Tika useful for search engine indexing, content analysis, translation, and much more. 7 | 8 | ## 3分钟入门 9 | 10 | ### 一、 检测文件类型(The Detector Interface) 11 | 12 | 如果仅仅是使用Tika 检测文档的类型,只需要添加 ``` tika-core ```依赖即可: 13 | ``` 14 | 15 | org.apache.tika 16 | tika-core 17 | 1.16 18 | 19 | ``` 20 | 21 | Apache Tika 官方提供的 [Detection](http://tika.apache.org/1.16/detection.html) demo如下: 22 | ``` 23 | package com.mindflow; 24 | 25 | import org.apache.tika.config.TikaConfig; 26 | import org.apache.tika.exception.TikaException; 27 | import org.apache.tika.io.TikaInputStream; 28 | import org.apache.tika.metadata.Metadata; 29 | import org.apache.tika.mime.MediaType; 30 | 31 | import java.io.File; 32 | import java.io.IOException; 33 | import java.io.InputStream; 34 | 35 | /** 36 | * @author Ricky Fung 37 | * 38 | */ 39 | public class App { 40 | 41 | public static void main( String[] args ) throws TikaException, IOException { 42 | 43 | TikaConfig tika = new TikaConfig(); 44 | 45 | //1. file 46 | for (File f : myListOfFiles) { 47 | Metadata metadata = new Metadata(); 48 | metadata.set(Metadata.RESOURCE_NAME_KEY, f.toString()); 49 | MediaType mimetype = tika.getDetector().detect( 50 | TikaInputStream.get(f), metadata); 51 | System.out.println("File " + f + " is " + mimetype); 52 | } 53 | 54 | //2. stream 55 | for (InputStream is : myListOfStreams) { 56 | MediaType mimetype = tika.getDetector().detect( 57 | TikaInputStream.get(is), new Metadata()); 58 | System.out.println("Stream " + is + " is " + mimetype); 59 | } 60 | } 61 | } 62 | 63 | ``` 64 | 65 | 接下来,我们列出指定的目录下的文件的类型,代码如下: 66 | ``` 67 | package com.mindflow; 68 | 69 | import org.apache.tika.config.TikaConfig; 70 | import org.apache.tika.exception.TikaException; 71 | import org.apache.tika.io.TikaInputStream; 72 | import org.apache.tika.metadata.Metadata; 73 | import org.apache.tika.mime.MediaType; 74 | import java.io.File; 75 | import java.io.IOException; 76 | 77 | /** 78 | * @author Ricky Fung 79 | */ 80 | public class TikaDemo { 81 | 82 | public static void main(String[] args) throws TikaException, IOException { 83 | 84 | File dir = new File("F:\\backup"); 85 | File[] files = dir.listFiles(); 86 | 87 | TikaConfig tika = new TikaConfig(); 88 | for (File f : files) { 89 | if (f.isDirectory()) { 90 | continue; 91 | } 92 | Metadata metadata = new Metadata(); 93 | metadata.set(Metadata.RESOURCE_NAME_KEY, f.toString()); 94 | MediaType mimetype = tika.getDetector().detect(TikaInputStream.get(f), metadata); 95 | System.out.println("File " + f + " is " + mimetype); 96 | } 97 | 98 | } 99 | } 100 | 101 | ``` 102 | 103 | 输出结果如下: 104 | ``` 105 | File F:\backup\40144.jpg is image/jpeg 106 | File F:\backup\40144.png is image/jpeg 107 | File F:\backup\abc.txt is text/plain 108 | File F:\backup\Java面试题.txt is text/plain 109 | File F:\backup\Vegas.rar is application/x-rar-compressed 110 | File F:\backup\架构文档.docx is application/vnd.openxmlformats-officedocument.wordprocessingml.document 111 | File F:\backup\PRD.doc is application/msword 112 | File F:\backup\蔡学镛架构设计方法-2014-8-17.pdf is application/pdf 113 | File F:\backup\demo.txt is text/plain 114 | ``` 115 | 116 | ### 二、 解析文件内容(The Parser interface) 117 | 118 | 如果你打算使用Tika 解析文档的内容,你需要添加 ```tika-parsers``` 依赖: 119 | ``` 120 | 121 | org.apache.tika 122 | tika-parsers 123 | 1.16 124 | 125 | ```` 126 | 127 | Apache Tika 文件内容解析的核心API类是 ```org.apache.tika.parser.Parser```,它通过``` Java SPI ```机制来扩展,在[tika-parsers/src/main/resources/META-INF/services/org.apache.tika.parser.Parser](https://gitbox.apache.org/repos/asf?p=tika.git;a=blob;f=tika-parsers/src/main/resources/META-INF/services/org.apache.tika.parser.Parser;hb=refs/heads/master) 中列举了Tika内置的所有Parser实现类,当然如果没有适合我们需求的Parser,可以通过 Java SPI 挂载我们自己的实现类。 128 | 129 | Tika Parser API的使用比较简单,固定套路如下: 130 | ``` 131 | Parser parser = ...; 132 | InputStream stream = ...; // open the stream 133 | try { 134 | parser.parse(stream, ...); // parse the stream 135 | } finally { 136 | stream.close(); // close the stream 137 | } 138 | ``` 139 | 140 | -------------------------------------------------------------------------------- /contents/Java/CPU_100_Analysis.md: -------------------------------------------------------------------------------- 1 | ## 背景 2 | 某服务器上部署了若干tomcat实例,即若干垂直切分的Java站点服务,以及若干Java微服务,突然收到运维的CPU异常告警。 3 | 4 | 问:如何定位是哪个服务进程导致CPU过载,哪个线程导致CPU过载,哪段代码导致CPU过载? 5 | 6 | ## 业务指标 7 | 查看业务各项指标是否正常,例如业务系统本身就是CPU密集型的 那CPU使用率 100% 可能是正常的,但如果业务系统并不是一个高并发或者CPU密集型的应用,那这个利用率有点太夸张,一定是哪里的业务代码逻辑有问题。 8 | 9 | ## 定位异常线程及具体代码行 10 | ### 步骤一、找到最耗CPU的进程 11 | 工具:top 12 | 13 | 方法: 14 | * 执行top -c ,显示进程运行信息列表 15 | * 键入P (大写p),进程按照CPU使用率排序 16 | 17 | 图示: 18 | 19 | 如上图,最耗CPU的进程PID为10765 20 | 21 | ### 步骤二:找到最耗CPU的线程 22 | 工具:top 23 | 24 | 方法: 25 | * top -Hp 10765 ,显示一个进程的线程运行信息列表 26 | * 键入P (大写p),线程按照CPU使用率排序 27 | 28 | 图示: 29 | 30 | 如上图,进程10765内,最耗CPU的线程PID为10804 31 | 32 | ### 步骤三:将线程PID转化为16进制 33 | 34 | 工具:printf 35 | 36 | 方法:printf “%x” 10804 37 | 38 | 图示: 39 | 40 | 如上图,10804对应的16进制是0x2a34,当然,这一步可以用计算器。 41 | 42 | 之所以要转化为16进制,是因为堆栈里,线程id是用16进制表示的。 43 | 44 | ### 步骤四:查看堆栈,找到线程在干嘛 45 | 46 | 工具:pstack/jstack/grep 47 | 48 | 方法:jstack 10765 | grep ‘0x2a34’ -C5 --color 49 | 50 | * 打印进程堆栈 51 | * 通过线程id,过滤得到线程堆栈 52 | 53 | 图示: 54 | 55 | 如上图,找到了耗CPU高的线程对应的线程名称“AsyncLogger-1”,以及看到了该线程正在执行代码的堆栈。 56 | 57 | ## 总结 58 | 传统的方案一般是4步: 59 | * top oder by with P // 首先按进程负载排序找到 maxLoad(pid) 60 | * top -Hp // 找到相关负载 线程PID 61 | * printf “0x%x\n”线程PID // 将线程PID转换为 16进制,为后面查找 jstack 日志做准备 62 | * jstack 进程PID | grep 十六进制线程PID // 例如:jstack 1040 | grep '0x431' 63 | 64 | 但是对于线上问题定位来说,分秒必争,上面的 4 步还是太繁琐耗时了,之前介绍过淘宝的[oldratlee]() 同学就将上面的流程封装为了一个工具:[show-busy-java-threads.sh](https://github.com/oldratlee/useful-scripts),可以很方便的定位线上的这类问题。 65 | 66 | ## 参考资料 67 | * [useful-scripts - 淘宝李鼎](https://github.com/oldratlee/useful-scripts) 68 | * [awesome-scripts](https://github.com/Suishenyun/awesome-scripts) 69 | * [jvm-tools](https://github.com/aragozin/jvm-tools) 70 | 71 | -------------------------------------------------------------------------------- /contents/Java/ConcurrentHashMap_Theory.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 以前写过介绍HashMap的文章,文中提到过HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。 3 | 4 | 我们来了解另一个键值存储集合HashTable,它是线程安全的,它在所有涉及到多线程操作的都加上了synchronized关键字来锁住整个table,这就意味着所有的线程都在竞争一把锁,在多线程的环境下,它是安全的,但是无疑是效率低下的。 5 | 6 | 其实HashTable有很多的优化空间,锁住整个table这么粗暴的方法可以变相的柔和点,比如在多线程的环境下,对不同的数据集进行操作时其实根本就不需要去竞争一个锁,因为他们不同hash值,不会因为rehash造成线程不安全,所以互不影响,这就是锁分离技术,将锁的粒度降低,利用多个锁来控制多个小的table,这就是这篇文章的主角ConcurrentHashMap JDK1.7版本的核心思想。 7 | 8 | ## JDK 1.7实现 9 | 在JDK1.7版本中,ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成,如下图所示: 10 | ![](https://github.com/TFdream/blog/blob/master/docs/image/JDK_Source/concurrenthashmap_jdk7.png) 11 | 12 | Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是上面的提到的锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样。 13 | 14 | ### 初始化 15 | ConcurrentHashMap的初始化是会通过位与运算来初始化Segment的大小,用ssize来表示,如下所示: 16 | ``` 17 | 18 | ``` 19 | 20 | ### put操作 21 | 22 | ### size实现 23 | 24 | 25 | ## JDK 1.8实现 26 | JDK1.8的实现已经摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,结构如下: 27 | 28 | ![](https://github.com/TFdream/blog/blob/master/docs/image/JDK_Source/concurrenthashmap_jdk8.png) 29 | 30 | ### 初始化 31 | 32 | ### put操作 33 | 34 | ### size实现 35 | 36 | ## 参考资料 37 | [谈谈ConcurrentHashMap1.7和1.8的不同实现](http://www.importnew.com/23610.html) 38 | 39 | -------------------------------------------------------------------------------- /contents/Java/Filter_Invisible_ASCII_Character.md: -------------------------------------------------------------------------------- 1 | 2 | 今天兄弟团队同事报告了一个BUG,经过调试发现,由于用户输入的字符串中,包含字符0x7F,导致后端服务接口解析出错。于是就想在字符串中过滤掉这些没多大用途的字符,同时又要保留部分常用的字符,例如换行,回车和水平制表符。 3 | 4 | 上面所说的“不可见字符”,其实属于[ASCII码](https://baike.baidu.com/item/ASCII)中的控制字符,它们是0到31、以及127。 5 | 6 | Java代码如下: 7 | ``` 8 | 9 | public static String filter(String content){ 10 | if (content==null || content.length()==0) { 11 | return content; 12 | } 13 | StringBuilder sb = new StringBuilder(content.length()); 14 | char[] contentCharArr = content.toCharArray(); 15 | for (int i = 0; i < contentCharArr.length; i++) { 16 | if (contentCharArr[i] < 0x20 || contentCharArr[i] == 0x7F) { 17 | continue; 18 | } 19 | sb.append(contentCharArr[i]); 20 | } 21 | return sb.toString(); 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /contents/Java/Guava_Join_Split_Usage.md: -------------------------------------------------------------------------------- 1 | ## 概述 2 | 本文主要讲解Guava中的Joiner和Splitter 的用法,通过使用Joiner将集合转换为String,使用Splitter将字符串转换为集合。 3 | 4 | ## 实战 5 | ``` 6 | 7 | com.google.guava 8 | guava 9 | 19.0 10 | 11 | ``` 12 | 13 | ### 1.Joiner 14 | ``` 15 | List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); 16 | String result = Joiner.on(",").join(names); 17 | ``` 18 | 19 | #### MapJoinner 20 | 对于MapSplitter的最好案例就是url的param编码。 21 | 生产一个查询id: 123,name: green的学生信息的url。 22 | ``` 23 | Joiner.on("&").withKeyValueSeparator("=").join(ImmutableMap.of("id", "123", "name", "green")); 24 | ``` 25 | 26 | 27 | ### 2. Splitter 28 | #### 1. 基本用法 29 | ``` 30 | String str = "a,b, c,,d"; 31 | Iterable result = Splitter.on(',')//设置分隔符 32 | .split(str); //要分割的字符串 33 | System.out.println("--start--"); 34 | for (String s : result) { 35 | System.out.println(s); 36 | } 37 | ``` 38 | 39 | #### 2. 去除分隔结果为空格的数据 40 | ``` 41 | String str = "a,b, c,,d"; 42 | Iterable result = Splitter.on(',')//设置分隔符 43 | .omitEmptyStrings() //用于去除为空格的分割结果 44 | .split(str); //要分割的字符串 45 | System.out.println("--start--"); 46 | for (String s : result) { 47 | System.out.println(s); 48 | } 49 | ``` 50 | 51 | #### 3. 去除分隔结果的前后空格 52 | ``` 53 | String str = "a, b , c,,d"; 54 | Iterable result = Splitter.on(',')//设置分隔符 55 | .trimResults() //去除前后空格 56 | .omitEmptyStrings() //用于去除为空格的分割结果 57 | .split(str); //要分割的字符串 58 | System.out.println("--start--"); 59 | for (String s : result) { 60 | System.out.println(s); 61 | } 62 | ``` 63 | #### 4. Splitter方法把string转换为list 64 | ``` 65 | String input = "apple - banana - orange"; 66 | List result = Splitter.on("-") 67 | .trimResults() 68 | .splitToList(input); 69 | for (String string : result) { 70 | System.out.println(string); 71 | } 72 | ``` 73 | 74 | #### 5. MapSplitter 75 | ``` 76 | final Map join = Splitter.on("&").withKeyValueSeparator("=").split("id=123&name=green"); 77 | ``` 78 | 79 | ## 参考资料 80 | [Guava StringsExplained](https://github.com/google/guava/wiki/StringsExplained) 81 | -------------------------------------------------------------------------------- /contents/Java/JDK_New_Features.md: -------------------------------------------------------------------------------- 1 | 2 | ## JDK 1.5 新特性 3 | ### 1. 自动装箱与拆箱 4 | 5 | ### 2. 枚举 6 | 7 | ### 3. 静态导入 8 | 9 | ### 4. 可变参数(Varargs) 10 | 11 | ### 5. 内省(Introspector) 12 | 13 | ### 6. 泛型(Generic) 14 | 15 | ### 7. For-Each循环 16 | 17 | 18 | ## JDK 1.6 新特性 19 | ### 1. 脚本语言支持 20 | 21 | ### 2. 更简单,更强大的JAX-WS 22 | 23 | ### 3.轻量级Http Server 24 | 25 | ### 4.插入式注解处理API(Pluggable Annotation Processing API) 26 | 插入式注解处理API(JSR 269)提供一套标准API来处理Annotations(JSR 175) 27 | 28 | 29 | ## JDK 1.7 新特性 30 | ### 1. switch中可以使用字串 31 | 32 | ### 2. 泛型实例化类型自动推断 33 | 34 | ### 3. 自动关闭 35 | 36 | ### 4. 对Java集合(Collections)的增强支持 37 | 38 | 在JDK1.7之前的版本中,Java集合容器中存取元素的形式如下: 39 | 40 | 以List、Set、Map集合容器为例: 41 | ``` 42 | //创建List接口对象 43 | List list=new ArrayList(); 44 | list.add("item"); //用add()方法获取对象 45 | String Item=list.get(0); //用get()方法获取对象 46 | 47 | //创建Set接口对象 48 | Set set=new HashSet(); 49 | set.add("item"); //用add()方法添加对象 50 | 51 | //创建Map接口对象 52 | Map map=new HashMap(); 53 | map.put("key",1); //用put()方法添加对象 54 | int value=map.get("key"); 55 | ``` 56 | 57 | 在JDK1.7中,摒弃了Java集合接口的实现类,如:ArrayList、HashSet和HashMap。而是直接采用[]、{}的形式存入对象,采用[]的形式按照索引、键值来获取集合中的对象,如下: 58 | ``` 59 | List list=["item"]; //向List集合中添加元素 60 | String item=list[0]; //从List集合中获取元素 61 | 62 | Set set={"item"}; //向Set集合对象中添加元素 63 | 64 | Map map={"key":1}; //向Map集合中添加对象 65 | int value=map["key"]; //从Map集合中获取对象 66 | ``` 67 | 68 | 69 | ## JDK 8 新特性 70 | ### 1.允许在接口中有默认方法实现 71 | 72 | ### 2. Lambda 表达式 73 | 74 | ### 3. 函数式接口 75 | 76 | ### 4. 方法和构造函数引用 77 | 78 | ### 5. Lambda的范围 79 | 80 | ### 6. 内置函数式接口 81 | 82 | ### 7. Streams 83 | 84 | ### 8. Parallel Streams 85 | 86 | ### 9. Map 87 | 88 | ### 10. 时间日期API 89 | 90 | 91 | ## JDK 9 新特性 92 | ### 1. Jigsaw 项目 93 | 模块化源码 94 | 95 | ### 2. 简化进程API 96 | 97 | ### 3. 轻量级 JSON API 98 | 99 | ### 4. 钱和货币的API 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /contents/Java/Java_Env_Config.md: -------------------------------------------------------------------------------- 1 | 2 | ## JDK Install 3 | ### 1. 下载 4 | 去Oracle官网下载最新版的安装包:http://www.oracle.com/technetwork/java/javase/downloads/index.html 5 | 6 | ### 2. 配置Java环境变量 7 | 8 | #### 2.1 Windows 9 | ![](https://github.com/TFdream/blog/blob/master/docs/image/JAVA_HOME.png) 10 | 11 | 并在 Path 末尾加上: 12 | ``` 13 | ;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; 14 | ``` 15 | #### 2.2 Linux 16 | 在/etc/profile文件中添加Maven环境变量,如下所示: 17 | ``` 18 | export JAVA_HOME=/usr/share/jdk1.8.0_121 19 | export PATH=$PATH:$JAVA_HOME/bin 20 | ``` 21 | 22 | ### 3. 验证 23 | 打开命令行窗口,执行``` java -version ```命令: 24 | 结果如下: 25 | ``` 26 | C:\Users\Ricky>java -version 27 | java version "1.8.0_121" 28 | Java(TM) SE Runtime Environment (build 1.8.0_121-b13) 29 | Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode) 30 | ``` 31 | 32 | ## Maven Install 33 | ### 1. 下载安装包 34 | 在Maven官网下载最新版的安装包:http://maven.apache.org/download.cgi 35 | 36 | ### 2. 配置Maven环境变量 37 | 配置M2_HOME环境变量,指向maven的安装目录,并将bin目录追加到PATH路径中,方便在命令行调用。 38 | 39 | #### 2.1 Windows 40 | ![](https://github.com/TFdream/blog/blob/master/docs/image/maven_home.png) 41 | 42 | 并在 Path 末尾加上: 43 | ``` 44 | ;%M2_HOME%\bin; 45 | ``` 46 | 47 | #### 2.2 Linux 48 | 在/etc/profile文件中添加Maven环境变量,如下所示: 49 | ``` 50 | export M2_HOME=/opt/maven 51 | export PATH=$PATH:$M2_HOME/bin 52 | ``` 53 | 54 | ### 3. 验证 55 | 在命令行执行mvn –v,如果显示下图所示信息,说明maven已经安装成功 56 | ``` 57 | C:\Users\bingbingfeng>mvn -v 58 | Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d; 2017-10-18T15:58:13+08:00) 59 | Maven home: F:\Maven\apache-maven-3.5.2\bin\.. 60 | Java version: 1.8.0_121, vendor: Oracle Corporation 61 | Java home: C:\Program Files\Java\jdk1.8.0_121\jre 62 | Default locale: zh_CN, platform encoding: GBK 63 | OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows" 64 | ``` 65 | 66 | ## 注意事项 67 | Linux下用冒号“:”来分隔路径,Windows下使用分号“;”来分隔路径。 68 | 69 | -------------------------------------------------------------------------------- /contents/Java/Java_OOM_Analysis.md: -------------------------------------------------------------------------------- 1 | ## 背景 2 | 前段时间系统经常出现OOM,每次出现之后系统会出现各种问题,临时解决方案只能是重启,然后等找到问题后再发布解决。 3 | 4 | ## 问题定位 5 | 线上问题日志如下: 6 | ``` 7 | Exception in thread "msgWorkTP-811568603-1-thread-6" java.lang.OutOfMemoryError: Java heap space 8 | 9 | Exception in thread "schedulerFactory_QuartzSchedulerThread" java.lang.OutOfMemoryError: Java heap space 10 | 11 | Exception in thread "server-timer" java.lang.OutOfMemoryError: Java heap space 12 | 13 | Exception in thread "Tracer-AsyncAppender-Thread-CommonAppender" java.lang.OutOfMemoryError: Java heap space 14 | ``` 15 | 16 | 线上遇到OOM需要做两件事情第一个是dump内存,第二个是看下GC日志。 17 | 18 | ### 1. dump内存 19 | 使用jmap命令dump内存,需要注意的是,在linux JDK1.6某个版本里使用jmap可能会让系统挂掉,可以通过-d64来解决。 20 | ``` 21 | jmap -J-d64 -dump:format=b,file=dump.bin PID 22 | ``` 23 | 一般dump下来的内存有几个G,而我这次在线上dump下来只有200M,说明jmap有问题,这个时候可以用gcore 把整个内存dump出来,然后再使用jmap把core dump转换成heap dump。命令如下: 24 | 25 | ``` 26 | $ jmap -permstat /opt/taobao/java/bin/java core.17024 27 | ``` 28 | 因为我们没有线上权限,又担心dump的时候系统容易挂掉,所以我们在JVM里加了个参数,在OOM的时候自动dump内存, 29 | ``` 30 | -XX:+HeapDumpOnOutOfMemeryError 31 | ``` 32 | 33 | ### 2. 查看gc日志 34 | 35 | 如果没有任何JVM参数设置,gc日志默认打印在stdout.log文件里,里面可能会打其他的日志,而且GC日志也不会输出时间,所以在JVM启动参数里最好加以下命令,规范下GC日志输出到/home/admin/logs/gc.log,并且打印GC时间。 36 | ``` 37 | -XX:HeapDumpPath=/home/admin/logs -Xloggc:/home/admin/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps 38 | ``` 39 | 40 | 在查看GC日志里发现concurrent mode failure问题,日志如下: 41 | 42 | (concurrent mode failure): 1146697K->1146697K(1146880K), 1.0353630 secs] 1773385K->1697954K(1773568K), 43 | 44 | 这个问题是因为CMS(Concurrent Mark-Sweep)垃圾回收器在做fullgc,还没到达回收的阶段,但是年轻代又有新对象晋升到年老代,而年老代又没有足够空间了,只能全停机,进行fullgc。但是从日志上看没有释放多少空间,要么是堆不够大,要么是泄露,先调大堆,如果是泄露,还是会重现,如果不是泄露,就稳定了。于是我们进行了JVM参数调整,系统有8G内存,我们把JVM堆内存升到3.8G,修改参数XX:CMSInitiatingOccupancyFraction=60,让年老代在达到60%的时候进行CMS回收,这样在进行回收时候年老贷至少还有3800x0.4=1520m空间,就算把整个年轻代塞进去也没有问题。 45 | ``` 46 | -server -Xms3800m -Xmx3800m -Xmn1500m -Xss256k -XX:PermSize=340m -XX:MaxPermSize=340m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -XX:+CMSClassUnloadingEnabled -XX:+DisableExplicitGC -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/logs -Xloggc:/home/admin/logs/gc.log -Dcom.sun.management.jmxremote.port=9981 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dfile.encoding=UTF-8 47 | ``` 48 | 49 | 参数调整后,过了几天还是出现OOM,可以断定是内存溢出导致的,通过自动dump下来的内存文件很快发现有一个对象占用内存非常大,解决后系统恢复正常。 50 | 51 | ### 3. 使用VisualVM分析 52 | 53 | 54 | ### 4. 使用MAT分析 55 | 56 | 57 | ## 参考资料 58 | * [又一次线上OOM排查经过 - ImportNew](http://www.importnew.com/24393.html) 59 | * [一次应用OOM排查 - 并发编程网](http://ifeve.com/one-java-oom/) 60 | -------------------------------------------------------------------------------- /contents/Java/LinkedList_Analysis.md: -------------------------------------------------------------------------------- 1 | ## 数据结构 2 | LinkedList底层的数据结构是基于双向循环链表的,如果对双向链表这个数据结构很熟悉的话,学习 LinkedList 就没什么难度了。下面是双向链表的结构: 3 | 4 | ![](https://github.com/TFdream/blog/blob/master/docs/image/JDK_Source/LinkedList_jdk8.png) 5 | 6 | ## 源码分析 7 | 本文基于 **JDK 1.8** 源码进行分析。 8 | 9 | ### 构造方法 10 | LinkedList 构造方法如下: 11 | ``` 12 | public class LinkedList 13 | extends AbstractSequentialList 14 | implements List, Deque, Cloneable, java.io.Serializable 15 | { 16 | transient int size = 0; 17 | 18 | /** 19 | * Pointer to first node. 20 | * Invariant: (first == null && last == null) || 21 | * (first.prev == null && first.item != null) 22 | */ 23 | transient Node first; 24 | 25 | /** 26 | * Pointer to last node. 27 | * Invariant: (first == null && last == null) || 28 | * (last.next == null && last.item != null) 29 | */ 30 | transient Node last; 31 | 32 | /** 33 | * Constructs an empty list. 34 | */ 35 | public LinkedList() { 36 | } 37 | } 38 | ``` 39 | 特性如下: 40 | * LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。 41 | * LinkedList 实现 List 接口,能对它进行队列操作。 42 | * LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。 43 | * LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。 44 | * LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。 45 | 46 | ### add操作 47 | add()方法如下: 48 | ``` 49 | public boolean add(E e) { 50 | linkLast(e); 51 | return true; 52 | } 53 | 54 | /** 55 | * Links e as last element. 56 | */ 57 | void linkLast(E e) { 58 | final Node l = last; 59 | final Node newNode = new Node<>(l, e, null); 60 | last = newNode; 61 | if (l == null) 62 | first = newNode; 63 | else 64 | l.next = newNode; 65 | size++; 66 | modCount++; 67 | } 68 | 69 | ``` 70 | 71 | 因为是链表结构,所以添加时无须扩容,只修改size。 72 | 73 | ### get操作 74 | ``` 75 | public E get(int index) { 76 | checkElementIndex(index); 77 | return node(index).item; 78 | } 79 | 80 | /** 81 | * Returns the (non-null) Node at the specified element index. 82 | */ 83 | Node node(int index) { 84 | // assert isElementIndex(index); 85 | 86 | if (index < (size >> 1)) { 87 | Node x = first; 88 | for (int i = 0; i < index; i++) 89 | x = x.next; 90 | return x; 91 | } else { 92 | Node x = last; 93 | for (int i = size - 1; i > index; i--) 94 | x = x.prev; 95 | return x; 96 | } 97 | } 98 | 99 | private void checkElementIndex(int index) { 100 | if (!isElementIndex(index)) 101 | throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 102 | } 103 | /** 104 | * Tells if the argument is the index of an existing element. 105 | */ 106 | private boolean isElementIndex(int index) { 107 | return index >= 0 && index < size; 108 | } 109 | ``` 110 | 首先调用checkElementIndex判断index是否合法(index >= 0 && index < size),然后根据 index是否大于当前size的一半决定是从头结点开始往后查询,还是从尾节点开始往前查询。 111 | 112 | ### 删除操作 113 | ``` 114 | public E remove(int index) { 115 | checkElementIndex(index); 116 | return unlink(node(index)); 117 | } 118 | 119 | /** 120 | * Unlinks non-null node x. 121 | */ 122 | E unlink(Node x) { 123 | // assert x != null; 124 | final E element = x.item; 125 | final Node next = x.next; 126 | final Node prev = x.prev; 127 | 128 | if (prev == null) { 129 | first = next; 130 | } else { 131 | prev.next = next; 132 | x.prev = null; 133 | } 134 | 135 | if (next == null) { 136 | last = prev; 137 | } else { 138 | next.prev = prev; 139 | x.next = null; 140 | } 141 | 142 | x.item = null; 143 | size--; 144 | modCount++; 145 | return element; 146 | } 147 | ``` 148 | 主要逻辑如下: 149 | 1. 判断index是否合法。 150 | 2. 根据index定位到Node(node(index)方法)。 151 | 3. 修改节点指针(unlink()方法)。 152 | 153 | ----------- 154 | Ending! 155 | 156 | -------------------------------------------------------------------------------- /contents/Java/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/Java/ThreadLocal_Analysis.md: -------------------------------------------------------------------------------- 1 | ## ThreadLocal概述 2 | ThreadLocal提供了线程本地变量,它可以保证访问到的变量属于当前线程,每个线程都保存有一个变量副本,每个线程的变量都不同。ThreadLocal相当于提供了一种线程隔离,将变量与线程相绑定。 3 | 4 | > This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). 5 | > 6 | > Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist). 7 | 8 | ## ThreadLocal源码分析 9 | 10 | ### ThreadLocal 构造方法 11 | 构造方法如下: 12 | ``` 13 | public class ThreadLocal { 14 | 15 | private final int threadLocalHashCode = nextHashCode(); 16 | 17 | private static AtomicInteger nextHashCode = 18 | new AtomicInteger(); 19 | 20 | private static final int HASH_INCREMENT = 0x61c88647; 21 | 22 | private static int nextHashCode() { 23 | return nextHashCode.getAndAdd(HASH_INCREMENT); 24 | } 25 | 26 | public ThreadLocal() { 27 | } 28 | } 29 | ``` 30 | ThreadLocal通过threadLocalHashCode来标识每一个ThreadLocal的唯一性。threadLocalHashCode通过CAS操作进行更新,每次hash操作的增量为 0x61c88647(不知为何)。 31 | 32 | ### set方法 33 | 接下来,看看它的set方法: 34 | ``` 35 | public void set(T value) { 36 | Thread t = Thread.currentThread(); 37 | ThreadLocalMap map = getMap(t); 38 | if (map != null) 39 | map.set(this, value); 40 | else 41 | createMap(t, value); 42 | } 43 | ``` 44 | 45 | 通过Thread.currentThread()方法获取了当前的线程引用,并传给了getMap(Thread)方法获取一个ThreadLocalMap的实例。我们继续跟进getMap(Thread)方法: 46 | ``` 47 | ThreadLocalMap getMap(Thread t) { 48 | return t.threadLocals; 49 | } 50 | ``` 51 | 52 | 可以看到getMap(Thread)方法直接返回Thread实例的成员变量threadLocals。它的定义在Thread内部,访问级别为package级别: 53 | ``` 54 | 55 | public class Thread implements Runnable { 56 | /* Make sure registerNatives is the first thing does. */ 57 | private static native void registerNatives(); 58 | static { 59 | registerNatives(); 60 | } 61 | 62 | /* ThreadLocal values pertaining to this thread. This map is maintained 63 | * by the ThreadLocal class. */ 64 | ThreadLocal.ThreadLocalMap threadLocals = null; 65 | 66 | } 67 | ``` 68 | 69 | 到了这里,我们可以看出,每个Thread里面都有一个ThreadLocal.ThreadLocalMap成员变量,也就是说每个线程通过ThreadLocal.ThreadLocalMap与ThreadLocal相绑定,这样可以确保每个线程访问到的thread-local variable都是本线程的。 70 | 71 | 我们往下继续分析。获取了ThreadLocalMap实例以后,如果它不为空则调用ThreadLocalMap.ThreadLocalMap 的set方法设值;若为空则调用ThreadLocal 的createMap方法new一个ThreadLocalMap实例并赋给Thread.threadLocals。 72 | 73 | ThreadLocal 的 createMap方法的源码如下: 74 | ``` 75 | void createMap(Thread t, T firstValue) { 76 | t.threadLocals = new ThreadLocalMap(this, firstValue); 77 | } 78 | ``` 79 | 80 | ### get方法 81 | ThreadLocal 的 get 方法,源码如下: 82 | ``` 83 | public T get() { 84 | Thread t = Thread.currentThread(); 85 | ThreadLocalMap map = getMap(t); 86 | if (map != null) { 87 | ThreadLocalMap.Entry e = map.getEntry(this); 88 | if (e != null) 89 | return (T)e.value; 90 | } 91 | return setInitialValue(); 92 | } 93 | ``` 94 | 95 | 通过Thread.currentThread()方法获取了当前的线程引用,并传给了getMap(Thread)方法获取一个ThreadLocalMap的实例。继续跟进setInitialValue()方法: 96 | ``` 97 | private T setInitialValue() { 98 | T value = initialValue(); 99 | Thread t = Thread.currentThread(); 100 | ThreadLocalMap map = getMap(t); 101 | if (map != null) 102 | map.set(this, value); 103 | else 104 | createMap(t, value); 105 | return value; 106 | } 107 | ``` 108 | 109 | 首先调用 initialValue()方法来初始化,然后 通过Thread.currentThread()方法获取了当前的线程引用,并传给了getMap(Thread)方法获取一个ThreadLocalMap的实例,并将 初始化值存到ThreadLocalMap 中。 110 | 111 | initialValue() 源码如下: 112 | ``` 113 | protected T initialValue() { 114 | return null; 115 | } 116 | ``` 117 | 118 | 下面我们探究一下ThreadLocalMap的实现。 119 | 120 | ### ThreadLocalMap 121 | ThreadLocalMap是ThreadLocal的静态内部类,源码如下: 122 | ``` 123 | public class ThreadLocal { 124 | 125 | static class ThreadLocalMap { 126 | 127 | static class Entry extends WeakReference { 128 | /** The value associated with this ThreadLocal. */ 129 | Object value; 130 | 131 | Entry(ThreadLocal k, Object v) { 132 | super(k); 133 | value = v; 134 | } 135 | } 136 | 137 | /** 138 | * The initial capacity -- MUST be a power of two. 139 | */ 140 | private static final int INITIAL_CAPACITY = 16; 141 | 142 | /** 143 | * The table, resized as necessary. 144 | * table.length MUST always be a power of two. 145 | */ 146 | private Entry[] table; 147 | 148 | /** 149 | * The number of entries in the table. 150 | */ 151 | private int size = 0; 152 | 153 | /** 154 | * The next size value at which to resize. 155 | */ 156 | private int threshold; // Default to 0 157 | 158 | ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { 159 | table = new Entry[INITIAL_CAPACITY]; 160 | int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 161 | table[i] = new Entry(firstKey, firstValue); 162 | size = 1; 163 | setThreshold(INITIAL_CAPACITY); 164 | } 165 | } 166 | 167 | } 168 | ``` 169 | 170 | 其中INITIAL_CAPACITY代表这个Map的初始容量;1是一个Entry类型的数组,用于存储数据;size代表表中的存储数目;threshold代表需要扩容时对应size的阈值。 171 | 172 | **ThreadLocalMap key为当前ThreadLocal对象,value为我们要设置的对象**。 173 | 174 | Entry类是ThreadLocalMap的静态内部类,用于存储数据。 175 | 176 | Entry类继承了WeakReference>,即每个Entry对象都有一个ThreadLocal的弱引用(作为key),这是为了防止内存泄露。一旦线程结束,key变为一个不可达的对象,这个Entry就可以被GC了。 177 | 178 | 接下来我们来看ThreadLocalMap 的set方法的实现: 179 | ``` 180 | private void set(ThreadLocal key, Object value) { 181 | 182 | // We don't use a fast path as with get() because it is at 183 | // least as common to use set() to create new entries as 184 | // it is to replace existing ones, in which case, a fast 185 | // path would fail more often than not. 186 | 187 | Entry[] tab = table; 188 | int len = tab.length; 189 | int i = key.threadLocalHashCode & (len-1); 190 | 191 | for (Entry e = tab[i]; 192 | e != null; 193 | e = tab[i = nextIndex(i, len)]) { 194 | ThreadLocal k = e.get(); 195 | 196 | if (k == key) { 197 | e.value = value; 198 | return; 199 | } 200 | 201 | if (k == null) { 202 | replaceStaleEntry(key, value, i); 203 | return; 204 | } 205 | } 206 | 207 | tab[i] = new Entry(key, value); 208 | int sz = ++size; 209 | if (!cleanSomeSlots(i, sz) && sz >= threshold) 210 | rehash(); 211 | } 212 | ``` 213 | 214 | ThreadLocal 的get方法会调用 ThreadLocalMap 的 getEntry(ThreadLocal key) ,其源码如下: 215 | ``` 216 | private Entry getEntry(ThreadLocal key) { 217 | int i = key.threadLocalHashCode & (table.length - 1); 218 | Entry e = table[i]; 219 | if (e != null && e.get() == key) 220 | return e; 221 | else 222 | return getEntryAfterMiss(key, i, e); 223 | } 224 | 225 | private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { 226 | Entry[] tab = table; 227 | int len = tab.length; 228 | 229 | while (e != null) { 230 | ThreadLocal k = e.get(); 231 | if (k == key) 232 | return e; 233 | if (k == null) 234 | expungeStaleEntry(i); 235 | else 236 | i = nextIndex(i, len); 237 | e = tab[i]; 238 | } 239 | return null; 240 | } 241 | ``` 242 | 243 | ### 参考资料 244 | [并发编程 | ThreadLocal源码深入分析](http://www.sczyh30.com/posts/Java/java-concurrent-threadlocal/) 245 | -------------------------------------------------------------------------------- /contents/Linux/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/MQ/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/Microservices/Hystrix_Isolation.md: -------------------------------------------------------------------------------- 1 | Hystrix熔断器支持的隔离策略目前有两种方式:信号量模式和线程池模式。 2 | 但信号量并不支持超时,当被调服务发生问题时,有少部分用户会长时间无法得到响应。 3 | 另外,使用线程池模式无法传递Header,我估计是由于线程切换,参数传递过程中被去掉了。 4 | 5 | ### 信号量和线程池对比 6 | | 策略 | 是否有线程切换 | 是否支持异步 | 是否支持超时 | 是否支持熔断 | 开销大小 | 是否支持限流| 7 | | --- | --- | --- |--- | --- |--- | --- | 8 | | 信号量 | 否 | 否 | 否 | 是 | 小 | 是 | 9 | | 线程池 | 是 | 是 | 是 | 是 | 大 | 是 | 10 | 11 | 12 | ### 信号量 13 | 信号量的使用示意图如下图所示,当n个并发请求去调用一个目标服务接口时,都要获取一个信号量才能真正去调用目标服务接口,但信号量有限,默认是10个,可以使用maxConcurrentRequests参数配置,如果并发请求数多于信号量个数,就有线程需要进入队列排队,但排队队列也有上限,默认是 5,如果排队队列也满,则必定有请求线程会走fallback流程,从而达到限流和防止雪崩的目的。 14 | 15 | 信号量模式从始至终都只有请求线程自身,是同步调用模式,不支持超时调用,不支持直接熔断,由于没有线程的切换,开销非常小。 16 | 17 | ### 线程池 18 | 线程池的使用示意图如下图所示,当n个请求线程并发对某个接口请求调用时,会先从hystrix管理的线程池里面获得一个线程,然后将参数传递给这个线程去执行真正调用。线程池的大小有限,默认是10个线程,可以使用maxConcurrentRequests参数配置,如果并发请求数多于线程池线程个数,就有线程需要进入队列排队,但排队队列也有上限,默认是 5,如果排队队列也满,则必定有请求线程会走fallback流程。 19 | 20 | 线程池模式可以支持异步调用,支持超时调用,支持直接熔断,存在线程切换,开销大。 21 | -------------------------------------------------------------------------------- /contents/Microservices/MSA.md: -------------------------------------------------------------------------------- 1 | ## 微服务架构 2 | 3 | ### 什么是微服务? 4 | 微服务就是一些协同工作的小而自治的服务。具有两个特点:1:很小,专注于做好一件事;2.自治性,独立部署,服务会暴露出API。 5 | 6 | ### 微服务架构的好处 7 | 首先,通过分解巨大单体式应用为多个服务方法解决了复杂性问题。在功能不变的情况下,应用被分解为多个可管理的分支或服务。每个服务都有一个用RPC-或者消息驱动API定义清楚的边界。微服务架构模式给采用单体式编码方式很难实现的功能提供了模块化的解决方案,由此,单个服务很容易开发、理解和维护。 8 | 9 | 第二,这种架构使得每个服务都可以有专门开发团队来开发。开发者可以自由选择开发技术,提供API服务。当然,许多公司试图避免混乱,只提供某些技术选择。然后,这种自由意味着开发者不需要被迫使用某项目开始时采用的过时技术,他们可以选择现在的技术。甚至于,因为服务都是相对简单,即使用现在技术重写以前代码也不是很困难的事情。 10 | 11 | 第三,微服务架构模式是每个微服务独立的部署。开发者不再需要协调其它服务部署对本服务的影响。这种改变可以加快部署速度。UI团队可以采用AB测试,快速的部署变化。微服务架构模式使得持续化部署成为可能。 12 | 13 | 最后,微服务架构模式使得每个服务独立扩展。你可以根据每个服务的规模来部署满足需求的规模。甚至于,你可以使用更适合于服务资源需求的硬件。比如,你可以在EC2 Compute Optimized instances上部署CPU敏感的服务,而在EC2 memory-optimized instances上部署内存数据库。 14 | 15 | ### 微服务架构的不足 16 | Fred Brooks在30年前写道,“there are no silver bullets”,像任何其它科技一样,微服务架构也有不足。其中一个跟他的名字类似,『微服务』强调了服务大小,实际上,有一些开发者鼓吹建立稍微大一些的,10-100 LOC服务组。尽管小服务更乐于被采用,但是不要忘了这只是终端的选择而不是最终的目的。微服务的目的是有效的拆分应用,实现敏捷开发和部署。 17 | 18 | 主要不足体现在 部署、测试、监控。 19 | 20 | ### 参考 21 | [微服务实战(一):微服务架构的优势与不足](http://www.dockone.io/article/394) 22 | -------------------------------------------------------------------------------- /contents/Microservices/Short_url.md: -------------------------------------------------------------------------------- 1 | ## short_url/shorten 2 | 将一个或多个长链接转换成短链接 3 | 4 | ## 实现 5 | [短网址(short URL)系统的原理及其实现](https://segmentfault.com/a/1190000012088345) 6 | [短址(short URL)原理及其实现](https://blog.csdn.net/beiyeqingteng/article/details/7706010) 7 | -------------------------------------------------------------------------------- /contents/MyBatis/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/MySQL/README.md: -------------------------------------------------------------------------------- 1 | # MySQL 2 | 3 | ## 列 4 | ### 1. 增加列 5 | ``` 6 | ALTER TABLE `tb_user` ADD COLUMN `address` VARCHAR(40) NOT NULL DEFAULT '' COMMENT '用户住址'; 7 | ``` 8 | 9 | ### 2. 删除列 10 | ``` 11 | ALTER TABLE 表名 drop column 列名; 12 | ``` 13 | 14 | ### 3. 修改字段长度 15 | ``` 16 | ALTER TABLE `tb_user` MODIFY COLUMN `single_code` VARCHAR(40); 17 | ``` 18 | 19 | ## 索引 20 | ### 1.普通索引 21 | ``` 22 | ALTER TABLE `t_user` ADD KEY INX_biz_id(`loan_id`, `lender_id`, `sub_account_id`, `buy_type`, `phase`); 23 | ``` 24 | 25 | ### 2.唯一索引 26 | ``` 27 | ALTER TABLE `t_user` ADD UNIQUE KEY INX_biz_id(`loan_id`, `lender_id`, `sub_account_id`, `buy_type`, `phase`); 28 | ``` 29 | 30 | ### 2.删除索引 31 | ``` 32 | alter table 表名 drop index 索引名 ; 33 | ``` 34 | 35 | 36 | ## 有用的脚本 37 | ### 1.根据多个字段查找重复数据 38 | ``` 39 | select * from (select lender_id, loan_id,sub_account_id, buy_type, phase, concat(lender_id, loan_id, sub_account_id, buy_type, phase) as col_t from calendar_repay_1 order by id desc) tmp group by col_t having count(col_t)>1; 40 | ``` 41 | -------------------------------------------------------------------------------- /contents/Netty/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | ## Netty实战 3 | * [蚂蚁通信框架实践](https://mp.weixin.qq.com/s?__biz=MzI3MzEzMDI1OQ==&mid=2651819829&idx=1&sn=5c6dbee399fe564fd73a59e1dc6cf1af&chksm=f0dcd949c7ab505fd08a330167e10b87926cbf729b507ca666c9509e52e81e670f7dfd25ce10&mpshare=1&scene=1&srcid=0404YUfqF8az2kYBYfvo0Pl0#rd) 4 | * [京东到家基于netty与websocket的实践](https://mp.weixin.qq.com/s?__biz=MzAwMzg1ODMwNw==&mid=2653791831&idx=1&sn=77b436914cf21f09e3bfea193fa25b3b&chksm=80edccbbb79a45ad2aaf85a0f8a88b18415185884bf7f335e00b3e80e68d9198ae31958c9399&scene=0#rd) 5 | -------------------------------------------------------------------------------- /contents/Others/README.md: -------------------------------------------------------------------------------- 1 | # My blog 2 | [CSDN](http://blog.csdn.net/top_code?viewmode=list) 3 |
4 | [简书](http://www.jianshu.com/u/215e5a237558) 5 | -------------------------------------------------------------------------------- /contents/Redis/README.md: -------------------------------------------------------------------------------- 1 | # Netty 2 | Netty. 3 | -------------------------------------------------------------------------------- /contents/Redis/Redis_Hash_Datastructure.md: -------------------------------------------------------------------------------- 1 | ## 介绍 2 | Redis hash是一个string类型的field和value的映射表.一个key可对应多个field,一个field对应一个value。 3 | 将一个对象存储为hash类型,较于每个字段都存储成string类型更能节省内存。新建一个hash对象时开始是用zipmap(又称为small hash)来存储的。 4 | 这个zipmap其实并不是hash table,但是zipmap相比正常的hash实现可以节省不少hash本身需要的一些元数据存储开销。 5 | 尽管zipmap的添加,删除,查找都是O(n),但是由于一般对象的field数量都不太多。 6 | 所以使用zipmap也是很快的,也就是说添加删除平均还是O(1)。如果field或者value的大小超出一定限制后,Redis会在内部自动将zipmap替换成正常的hash实现。 7 | 8 | ## 操作 9 | ### 1. hset 10 | ``` 11 | HSET key field value 12 | ``` 13 | 将哈希表key中的域field的值设为value。如果key不存在,一个新的哈希表被创建并进行hset操作。如果域field已经存在于哈希表中,旧值将被覆盖。 14 | 15 | ### 2. hget 16 | ``` 17 | HGET key field 18 | ``` 19 | 返回哈希表key中指定的field的值。 20 | 21 | ### 3. hsetnx 22 | ``` 23 | HSETNX key field value 24 | ``` 25 | 将哈希表key中的域field的值设置为value,当且仅当域field不存在。若域field已经存在,该操作无效。如果key不存在,一个新哈希表被创建并执行hsetnx命令。 26 | 27 | ### 4. hmset 28 | ``` 29 | HMSET key field value [field value ...] 30 | ``` 31 | 同时将多个field - value(域-值)对设置到哈希表key中。此命令会覆盖哈希表中已存在的域。如果key不存在,一个空哈希表被创建并执行hmset操作。 32 | 33 | ``` 34 | redis> MSET key1 "Hello" key2 "World" 35 | "OK" 36 | redis> GET key1 37 | "Hello" 38 | redis> GET key2 39 | "World" 40 | redis> 41 | ``` 42 | 43 | ### 5. hmget 44 | ``` 45 | HMGET key field [field ...] 46 | ``` 47 | 返回哈希表key中,一个或多个给定域的值。如果给定的域不存在于哈希表,那么返回一个nil值。因为不存在的key被当作一个空哈希表来处理,所以对一个不存在的key进行hmget操作将返回一个只带有nil值的表。 48 | 49 | ### 6. hgetall 50 | ``` 51 | HGETALL key 52 | ``` 53 | 54 | 返回哈希表key中,所有的域和值。在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。 55 | 56 | ### 7. hdel 57 | ``` 58 | HDEL key field [field ...] 59 | ``` 60 | 删除哈希表key中的一个或多个指定域,不存在的域将被忽略。 61 | 62 | ### 8. hlen 63 | ``` 64 | HLEN key 65 | ``` 66 | 返回哈希表key对应的field的数量。 67 | 68 | ### 9. hexists 69 | ``` 70 | HEXISTS key field 71 | ``` 72 | 查看哈希表key中,给定域field是否存在。 73 | 74 | ### 10. hkeys 75 | ``` 76 | HKEYS key 77 | ``` 78 | 获得哈希表中key对应的所有field。 79 | 80 | ### 11. hvals 81 | ``` 82 | HVALS key 83 | ``` 84 | 获得哈希表中key对应的所有values。 85 | 86 | ### 12. hincrby 87 | ``` 88 | redis> HSET myhash field 5 89 | (integer) 1 90 | redis> HINCRBY myhash field 1 91 | (integer) 6 92 | redis> HINCRBY myhash field -1 93 | (integer) 5 94 | redis> HINCRBY myhash field -10 95 | (integer) -5 96 | redis> 97 | ``` 98 | 为哈希表key中的域field的值加上增量increment。增量也可以为负数,相当于对给定域进行减法操作。如果key不存在,一个新的哈希表被创建并执行hincrby命令。如果域field不存在,那么在执行命令前,域的值被初始化为0。对一个储存字符串值的域field执行hincrby命令将造成一个错误。本操作的值限制在64位(bit)有符号数字表示之内。 99 | 100 | -------------------------------------------------------------------------------- /contents/Redis/Redis_List_Datastructure.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边) 3 | 一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。 4 | 5 | ## 操作 6 | -------------------------------------------------------------------------------- /contents/Redis/Redis_Set_Datastructure.md: -------------------------------------------------------------------------------- 1 | ## Redis 集合(Set) 2 | Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。 3 | Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 4 | 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。 5 | 6 | ## 操作 7 | -------------------------------------------------------------------------------- /contents/Redis/Redis_Sorted_Set_Datastructure.md: -------------------------------------------------------------------------------- 1 | ## Redis 有序集合(sorted set) 2 | Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。 3 | 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。 4 | 有序集合的成员是唯一的,但分数(score)却可以重复。 5 | 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。 6 | 7 | ## 操作 8 | -------------------------------------------------------------------------------- /contents/Security/README.md: -------------------------------------------------------------------------------- 1 | # blog 2 | My blog. 3 | -------------------------------------------------------------------------------- /contents/Spring Boot/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot 2 | Spring Boot Tech Blog. 3 | -------------------------------------------------------------------------------- /contents/Spring Boot/Spring-Boot-Custom-Your-Starter.md: -------------------------------------------------------------------------------- 1 | SpringBoot最大的特点就是提供了很多默认的配置,Spring4.x提供了基于条件来配置Bean的能力,SpringBoot就是通过这一原理来实现的。 2 | 3 | ## 创建自己的Spring Boot Starter 4 | 如果你想要自己创建一个starter,那么基本上包含以下几步: 5 | 1. 创建一个starter项目,关于项目的命名你可以参考[这里](https://docs.spring.io/spring-boot/docs/1.5.12.RELEASE/reference/htmlsingle/#boot-features-custom-starter-naming) 6 | 2. 创建一个ConfigurationProperties用于保存你的配置信息(如果你的项目不使用配置信息则可以跳过这一步,不过这种情况非常少见) 7 | 3. 创建一个AutoConfiguration,引用定义好的配置信息;在AutoConfiguration中实现所有starter应该完成的操作,并且把这个类加入spring.factories配置文件中进行声明 8 | 4. 打包项目,之后在一个SpringBoot项目中引入该项目依赖,然后就可以使用该starter了 9 | 10 | Spring 官方 Starter通常命名为spring-boot-starter-{name}如 spring-boot-starter-web, Spring官方建议非官方Starter命名应遵循{name}-spring-boot-starter的格式。 11 | 12 | ``` 13 | package com.alibaba.druid.spring.boot.autoconfigure; 14 | 15 | import javax.sql.DataSource; 16 | 17 | import com.alibaba.druid.pool.DruidDataSource; 18 | import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; 19 | import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidFilterConfiguration; 20 | import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidSpringAopConfiguration; 21 | import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidStatViewServletConfiguration; 22 | import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidWebStatFilterConfiguration; 23 | 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 27 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 28 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 29 | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 30 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; 31 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 32 | import org.springframework.context.annotation.Bean; 33 | import org.springframework.context.annotation.Configuration; 34 | import org.springframework.context.annotation.Import; 35 | /** 36 | * @author lihengming [89921218@qq.com] 37 | */ 38 | @Configuration 39 | @ConditionalOnClass(DruidDataSource.class) 40 | @AutoConfigureBefore(DataSourceAutoConfiguration.class) 41 | @EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class}) 42 | @Import({DruidSpringAopConfiguration.class, 43 | DruidStatViewServletConfiguration.class, 44 | DruidWebStatFilterConfiguration.class, 45 | DruidFilterConfiguration.class}) 46 | public class DruidDataSourceAutoConfigure { 47 | 48 | private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class); 49 | 50 | @Bean(initMethod = "init") 51 | @ConditionalOnMissingBean 52 | public DataSource dataSource() { 53 | LOGGER.info("Init DruidDataSource"); 54 | return new DruidDataSourceWrapper(); 55 | } 56 | } 57 | ``` 58 | 我们首先讲一下源码中注解的作用: 59 | * @Configuration,被该注解注释的类会提供一个或则多个@Bean修饰的方法并且会被spring容器处理来生成bean definitions。 60 | * @Bean注解是必须修饰函数的,该函数可以提供一个bean。而且该函数的函数名必须和bean的名称一致,除了首字母不需要大写。 61 | * @ConditionalOnClass注解是条件判断的注解,表示对应的类在classpath目录下存在时,才会去解析对应的配置文件。 62 | * @EnableConfigurationProperties注解给出了该配置类所需要的配置信息类,也就是StorageServiceProperties类,这样spring容器才会去读取配置信息到StorageServiceProperties对象中。 63 | * @ConditionalOnMissingBean注解也是条件判断的注解,表示如果不存在对应的bean条件才成立,这里就表示如果已经有StorageService的bean了,那么就不再进行该bean的生成。这个注解十分重要,涉及到默认配置和用户自定义配置的原理。也就是说用户可以自定义一个StorageService的bean,这样的话,spring容器就不需要再初始化这个默认的bean了。 64 | * ConditionalOnProperty注解是条件判断的注解,表示如果配置文件中的响应配置项数值为true,才会对该bean进行初始化。 65 | 66 | 67 | ## 解决Spring Boot Parent依赖 68 | 69 | 70 | ## 参考资料 71 | * [Spring Boot custom-starter](https://docs.spring.io/spring-boot/docs/1.5.12.RELEASE/reference/htmlsingle/#boot-features-custom-starter-naming) 72 | * [dubbo-spring-boot-starter](https://github.com/alibaba/dubbo-spring-boot-starter) 73 | * [druid-spring-boot-starter](https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter) 74 | * [Condition annotations](https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-bean-conditions) 75 | [Spring Boot - parent pom when you already have a parent pom 76 | ](https://stackoverflow.com/questions/21317006/spring-boot-parent-pom-when-you-already-have-a-parent-pom/21318359#21318359) 77 | -------------------------------------------------------------------------------- /contents/Spring Boot/SpringBoot-Theory-AutoConfigure.md: -------------------------------------------------------------------------------- 1 | ## Spring Boot核心原理-自动配置 2 | Spring Boot出现之后,得益于COC(“习惯优于配置”)这个理念,再也没有繁琐的配置(大多数流行第三方技术都被集成在内)。 3 | 那么背后实现的核心原理到底是什么呢? 其实是spring 4.x提供的基于条件配置bean的能力。 4 | Spring boot关于自动配置的源码在spring-boot-autoconfigure-x.x.x.x.jar中,主要包含了如下图所示的配置(并未截全): 5 | -------------------------------------------------------------------------------- /contents/Spring Boot/Spring_Boot_Profile_Usage.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 在Spring Boot的开发中,应用程序在不同的环境可能会有不同的配置,例如数据库连接、日志级别等,开发,测试,生产每个环境可能配置都不一致。 3 | 4 | 使用Spring Boot的Profile可以实现多场景下的配置切换,方便开发中进行测试和部署生产环境。 5 | 下面就大致介绍一下yml配置文件跟properties配置文件怎么使用profile配置不同环境的配置文件。 6 | 7 | ## 开发环境 8 | 开发环境如下: 9 | * JDK 1.8 10 | * Maven 3.x 11 | * Spring Boot 1.5.8 12 | * Intellij Idea 2017 13 | 14 | ## 实战 15 | 16 | ### 1. 使用yml文件 17 | 1.首先,我们先创建一个名为 application.yml的属性文件,如下: 18 | ``` 19 | server: 20 | port: 8080 21 | 22 | my: 23 | name: demo 24 | 25 | spring: 26 | profiles: 27 | active: dev 28 | 29 | --- 30 | #development environment 31 | spring: 32 | profiles: dev 33 | 34 | server: 35 | port: 8160 36 | 37 | my: 38 | name: ricky 39 | 40 | --- 41 | #test environment 42 | spring: 43 | profiles: test 44 | 45 | server: 46 | port: 8180 47 | 48 | my: 49 | name: test 50 | 51 | --- 52 | #production environment 53 | spring: 54 | profiles: prod 55 | 56 | server: 57 | port: 8190 58 | 59 | my: 60 | name: prod 61 | 62 | ``` 63 | 64 | application.yml文件分为四部分,使用```---``` 来作为分隔符,第一部分通用配置部分,表示三个环境都通用的属性, 65 | 后面三段分别为:开发,测试,生产,用spring.profiles指定了一个值(开发为dev,测试为test,生产为prod),这个值表示该段配置应该用在哪个profile里面。 66 | 67 | 如果我们是本地启动,在通用配置里面可以设置调用哪个环境的profil,也就是第一段的```spring.profiles.active=XXX```, 68 | 其中XXX是后面3段中spring.profiles对应的value,通过这个就可以控制本地启动调用哪个环境的配置文件,例如: 69 | ``` 70 | spring: 71 | profiles: 72 | active: dev 73 | ``` 74 | 75 | 加载的就是开发环境的属性,如果dev换成test,则会加载测试环境的属性,以此类推。 76 | 77 | **注意**
78 | 如果spring.profiles.active没有指定值,那么只会使用没有指定spring.profiles文件的值,也就是只会加载通用的配置。 79 | 80 | #### 打包发布 81 | 如果是部署到服务器的话,我们正常打成jar包,启动时通过 82 | ```--spring.profiles.active=xxx```来控制加载哪个环境的配置,完整命令如下: 83 | ``` 84 | java -jar xxx.jar --spring.profiles.active=test 表示加载测试环境的配置 85 | 86 | java -jar xxx.jar --spring.profiles.active=prod 表示加载生产环境的配置 87 | ``` 88 | 89 | #### 命令行参数 90 | 通过 ```java -jar app.jar --name="Spring" --server.port=9090``` 方式来传递参数。 91 | 92 | 参数用 ```--xxx=xxx```的形式传递。 93 | 94 | 可以使用的参数可以是我们自己定义的,也可以是Spring Boot中默认的参数。 95 | 96 | **注意:命令行参数在app.jar的后面** 97 | 98 | #### 使用多个yml配置文件进行配置属性文件 99 | 如果是使用多个yml来配置属性,通过与配置文件相同的明明规范,创建application-{profile}.yml文件,将于环境无关的属性,放置到application.yml文件里面,可以通过这种形式来配置多个环境的属性文件,在application.yml文件里面指定```spring.profiles.active=xxx```,来加载不同环境的配置,如果不指定,则默认只使用application.yml属性文件,不会加载其他的profiles的配置 100 | 101 | 102 | ### 2. 使用properties文件 103 | 如果使用application.properties进行多个环境的配置,原理跟使用多个yml配置文件一致,也是通过application-{profile}.properties来控制加载哪个环境的配置,将于环境无关的属性,放置到application.properties文件里面,通过```spring.profiles.active=xxx```,加载不同环境的配置,如果不指定,则默认加载application.properties的配置,不会加载带有profile的配置 104 | 105 | 106 | ### 3. Maven中的场景配置 107 | ``` 108 | 109 | 110 | 111 | dev 112 | 113 | dev 114 | 115 | 116 | true 117 | 118 | 119 | 120 | 121 | test 122 | 123 | test 124 | 125 | 126 | 127 | 128 | prod 129 | 130 | prod 131 | 132 | 133 | 134 | 135 | 136 | ${project.artifactId} 137 | 138 | 139 | src/main/resources 140 | false 141 | 142 | 143 | src/main/resources.${build.profile.id} 144 | false 145 | 146 | 147 | 148 | 149 | org.springframework.boot 150 | spring-boot-maven-plugin 151 | 152 | exec 153 | 154 | 155 | 156 | 157 | ``` 158 | 159 | ## 源码 160 | [spring-boot-profiles](https://github.com/TFrise/spring-boot-tutorials/tree/master/spring-boot-profiles) 161 | 162 | ## 参考文档 163 | [Spring Boot Reference Guide - Profiles](https://docs.spring.io/spring-boot/docs/1.5.8.RELEASE/reference/htmlsingle/#boot-features-profiles) 164 | 165 | -------------------------------------------------------------------------------- /contents/Spring MVC/README.md: -------------------------------------------------------------------------------- 1 | # blog 2 | My blog. 3 | -------------------------------------------------------------------------------- /contents/Spring/README.md: -------------------------------------------------------------------------------- 1 | # Spring 2 | Spring Tech Blog. 3 | -------------------------------------------------------------------------------- /contents/Spring/Spring Framework-Introduction.md: -------------------------------------------------------------------------------- 1 | 2 | # Overview 3 | Spring Framework 的功能被组织成了 20 来个模块。这些模块分成 Core Container, Data Access/Integration, Web, AOP (Aspect Oriented Programming), Instrumentation, Messaging, 和 Test,如下图: 4 | 5 | Figure 2.1. Overview of the Spring Framework: 6 |
7 | ![Spring-Framework-Overview](/docs/image/Spring-Framework-overview.png) 8 | -------------------------------------------------------------------------------- /contents/Spring/Spring4.x-Conditional-Annotation.md: -------------------------------------------------------------------------------- 1 | # Spring4.x新特性: 条件注解@Conditional 2 | @Conditional根据满足某一个特定条件创建一个特定的Bean。比如说,当某一个jar包在某一个类路径下的时候,自动配置一个或者多个Bean;或者只有某个Bean被创建才会创建另外一个Bean。总的来说,就是根据特定条件来控制Bean的创建行为,这样我们可以利用这个特性进行一些自动的配置。 3 | 在Spring Boot中大量的应用到条件注解,后面有机会我们会说到。 4 | -------------------------------------------------------------------------------- /contents/Spring/Spring4.x-Java-Based-Configuration.md: -------------------------------------------------------------------------------- 1 | # Spring4.x新特性-基于Java配置 2 | 基于 Java 的配置选项,可以使你在不用配置 XML 的情况下编写大多数的 Spring。 3 | 4 | ## @Configuration 和 @Bean 注解 5 | 带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean。 6 | 7 | 最简单可行的 @Configuration 类如下所示: 8 | ``` 9 | package com.mindflow.spring4; 10 | 11 | import org.springframework.context.annotation.*; 12 | 13 | @Configuration 14 | public class AppConfig { 15 | @Bean 16 | public Foo foo() { 17 | return new Foo(bar()); 18 | } 19 | @Bean 20 | public Bar bar() { 21 | return new Bar(); 22 | } 23 | } 24 | ``` 25 | 26 | 上面的代码将等同于下面的 XML 配置: 27 | ``` 28 | 29 | 30 | 31 | 32 | ``` 33 | 34 | 在这里,带有 @Bean 注解的方法名称作为 bean 的 ID,它创建并返回实际的 bean。你的配置类可以声明多个 @Bean。一旦定义了配置类,你就可以使用 AnnotationConfigApplicationContext 来加载并把他们提供给 Spring 容器,如下所示: 35 | ``` 36 | package com.mindflow.spring4; 37 | 38 | import org.springframework.context.ApplicationContext; 39 | import org.springframework.context.annotation.*; 40 | 41 | public class MainApp { 42 | public static void main(String[] args) { 43 | ApplicationContext ctx = 44 | new AnnotationConfigApplicationContext(AppConfig.class); 45 | 46 | Foo foo = ctx.getBean(Foo.class); 47 | 48 | foo.say("Hello World!"); 49 | } 50 | } 51 | ``` 52 | 53 | ## @Import 注解 54 | @Import 注解允许从另一个配置类中加载 @Bean 定义。就像 Spring 的 XML 文件中使用的元素帮助模块化配置一样,@Import注解允许从其它配置类中加载@Bean的配置。 55 | 56 | 考虑 ConfigA 类,如下所示: 57 | ``` 58 | @Configuration 59 | public class ConfigA { 60 | @Bean 61 | public A a() { 62 | return new A(); 63 | } 64 | } 65 | ``` 66 | 你可以在另一个 Bean 声明中导入上述 Bean 声明,如下所示: 67 | ``` 68 | @Configuration 69 | @Import(ConfigA.class) 70 | public class ConfigB { 71 | @Bean 72 | public B a() { 73 | return new A(); 74 | } 75 | } 76 | ``` 77 | 78 | 现在,当实例化上下文时,不需要同时指定 ConfigA.class 和 ConfigB.class,只有 ConfigB 类需要提供,如下所示: 79 | ``` 80 | public static void main(String[] args) { 81 | ApplicationContext ctx = 82 | new AnnotationConfigApplicationContext(ConfigB.class); 83 | // now both beans A and B will be available... 84 | A a = ctx.getBean(A.class); 85 | B b = ctx.getBean(B.class); 86 | } 87 | ``` 88 | 89 | ## AnnotationConfigApplicationContext 90 | AnnotationConfigApplicationContext可以使用无参构造方法来实例化,之后使用register()方法来配置。这个方法当编程式地构建AnnotationConfigApplicationContext时尤其有用: 91 | ``` 92 | public static void main(String[] args) { 93 | AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); 94 | ctx.register(AppConfig.class, OtherConfig.class); 95 | ctx.register(AdditionalConfig.class); 96 | ctx.refresh(); 97 | MyService myService = ctx.getBean(MyService.class); 98 | myService.doStuff(); 99 | } 100 | ``` 101 | 102 | 要开启组件扫描,仅仅需要在你的@Configuration类上加上如下注解: 103 | ``` 104 | @Configuration 105 | @ComponentScan(basePackages = "com.acme") 106 | public class AppConfig { 107 | ... 108 | } 109 | ``` 110 | 111 | 有经验的 Spring 用户肯定会熟悉下面这个 Spring 的 context:命名空间中的常用 XML声明: 112 | ``` 113 | 114 | 115 | 116 | ``` 117 | 在上面的示例中,com.acme包会被扫描到,去查找任意@Component注解的类,那些类就会被注册为 Spring 容器中的 bean。AnnotationConfigApplicationContext 暴露出 scan(String ...)方法,允许相同的组件扫描功能: 118 | ``` 119 | public static void main(String[] args) { 120 | AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); 121 | ctx.scan("com.acme"); 122 | ctx.refresh(); 123 | MyService myService = ctx.getBean(MyService.class); 124 | } 125 | ``` 126 | 127 | 128 | ## @Bean注解 129 | ### 生命周期回调 130 | @Bean 注解支持指定任意的初始化和销毁的回调方法,就像在 bean 元素中 Spring 的 XML 的初始化方法和销毁方法的属性: 131 | ``` 132 | public class Foo { 133 | public void init() { 134 | // initialization logic 135 | } 136 | public void cleanup() { 137 | // destruction logic 138 | } 139 | } 140 | 141 | @Configuration 142 | public class AppConfig { 143 | @Bean(initMethod = "init", destroyMethod = "cleanup" ) 144 | public Foo foo() { 145 | return new Foo(); 146 | } 147 | } 148 | ``` 149 | 150 | ### 指定 Bean 的范围 151 | 152 | 默认范围是单实例,但是你可以重写带有 @Scope 注解的该方法,如下所示: 153 | ``` 154 | @Configuration 155 | public class AppConfig { 156 | @Bean 157 | @Scope("prototype") 158 | public Foo foo() { 159 | return new Foo(); 160 | } 161 | } 162 | ``` 163 | 164 | ### 指定bean的名称 165 | 默认地,配置类使用@Bean方法的名称来作为注册bean的名称。这个方法可以被重写,当然,使用的是name属性。 166 | ``` 167 | @Configuration 168 | public class AppConfig { 169 | 170 | @Bean(name = "myFoo") 171 | public Foo foo() { 172 | return new Foo(); 173 | } 174 | } 175 | ``` 176 | 177 | 178 | -------------------------------------------------------------------------------- /contents/Spring/Spring_Annotation_Scan.md: -------------------------------------------------------------------------------- 1 | 2 | 在使用Spring时,有时候有会有一些自定义Annotation的需求,比如一些Listener的回调函数。 3 | 4 | 例如,在配置中心的设计上,我们期望在配置发生变更时,client能收到通知,如下: 5 | ``` 6 | @Service 7 | public class DemoService { 8 | 9 | @Value("${batch:100}") 10 | private int batch; 11 | 12 | @ConfigChangeListener 13 | private void onChange(ConfigChangeEvent changeEvent) { 14 | //update injected value of batch if it is changed in Apollo 15 | if (changeEvent.isChanged("batch")) { 16 | batch = config.getIntProperty("batch", 100); 17 | } 18 | } 19 | } 20 | ``` 21 | 22 | 一开始的时候,我是在Spring的ContextRefreshedEvent事件里,通过context.getBeansWithAnnotation(Component.class) 来获取到所有的bean,然后再检查method是否有@MyListener的annotation。 23 | 24 | 后来发现这个方法有缺陷,当有一些spring bean的@Scope设置为session/request时,创建bean会失败。 25 | 26 | 参考:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-scopes 27 | 28 | ## BeanPostProcessor接口 29 | 后来看了下spring jms里的@JmsListener的实现,发现实现BeanPostProcessor接口才是最合理的办法。 30 | ``` 31 | public interface BeanPostProcessor { 32 | 33 | /** 34 | * Apply this BeanPostProcessor to the given new bean instance before any bean 35 | * initialization callbacks (like InitializingBean's {@code afterPropertiesSet} 36 | * or a custom init-method). The bean will already be populated with property values. 37 | * The returned bean instance may be a wrapper around the original. 38 | * @param bean the new bean instance 39 | * @param beanName the name of the bean 40 | * @return the bean instance to use, either the original or a wrapped one; 41 | * if {@code null}, no subsequent BeanPostProcessors will be invoked 42 | * @throws org.springframework.beans.BeansException in case of errors 43 | * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet 44 | */ 45 | Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; 46 | 47 | /** 48 | * Apply this BeanPostProcessor to the given new bean instance after any bean 49 | * initialization callbacks (like InitializingBean's {@code afterPropertiesSet} 50 | * or a custom init-method). The bean will already be populated with property values. 51 | * The returned bean instance may be a wrapper around the original. 52 | *

In case of a FactoryBean, this callback will be invoked for both the FactoryBean 53 | * instance and the objects created by the FactoryBean (as of Spring 2.0). The 54 | * post-processor can decide whether to apply to either the FactoryBean or created 55 | * objects or both through corresponding {@code bean instanceof FactoryBean} checks. 56 | *

This callback will also be invoked after a short-circuiting triggered by a 57 | * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method, 58 | * in contrast to all other BeanPostProcessor callbacks. 59 | * @param bean the new bean instance 60 | * @param beanName the name of the bean 61 | * @return the bean instance to use, either the original or a wrapped one; 62 | * if {@code null}, no subsequent BeanPostProcessors will be invoked 63 | * @throws org.springframework.beans.BeansException in case of errors 64 | * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet 65 | * @see org.springframework.beans.factory.FactoryBean 66 | */ 67 | Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; 68 | 69 | } 70 | ``` 71 | 所有的bean在创建完之后,都会回调postProcessAfterInitialization函数,这时就可以确定bean是已经创建好的了。 72 | 73 | 所以扫描自定义的annotation的代码大概是这个样子的: 74 | ``` 75 | public class ConfigChangeListenerProcessor implements BeanPostProcessor { 76 | 77 | @Override 78 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 79 | return bean; 80 | } 81 | 82 | @Override 83 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 84 | Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass()); 85 | if (methods != null) { 86 | for (Method method : methods) { 87 |                ConfigChangeListener listener = AnnotationUtils.findAnnotation(method, ConfigChangeListener.class); 88 | // process 89 | } 90 | } 91 | return bean; 92 | } 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /contents/Spring/Spring_Bean_Lifecycle.md: -------------------------------------------------------------------------------- 1 | ## Spring Bean生命周期详解 2 | 在Spring中 Bean 可谓是一个核心的元素,当我们结合Spring进行编程的时候也离不开Bean,面对这样重要的一个角色,了解其生命周期和该生命周期所涉及的环节对我们更加熟练灵活地使用Bean是很有Bean必要的,下面我们就来详细分析下Bean的生命周期吧。 3 | 此处,可以借鉴Java EE中Servlet的生命周期,实例化、初始init、接收请求service、销毁destroy。 4 | 5 | ### Spring Bean的生命周期概述 6 | 1. 实例化BeanFactoryPostProcessor接口实现类; 7 | 2. 执行BeanFactoryPostProcessor的postProcessBeanFactory方法; 8 | 3. 实例化BeanPostProcessor接口实现类; 9 | 4. 实例化InstantiationAwareBeanPostProcessor接口实现类; 10 | 5. 执行InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法; 11 | 6. 执行Bean的构造方法; 12 | 7. 执行InstantiationAwareBeanPostProcessor的postProcessPropertyValues方法; 13 | 8. 为Bean注入属性; 14 | 9. 调用BeanNameAware接口的setBeanName方法; 15 | 10. 调用BeanFactoryAware接口的setBeanFactory()方法; 16 | 11. 调用ApplicationContextAware接口的setApplicationContext(ApplicationContext)方法; 17 | 12. 执行BeanPostProcessor接口的postProcessBeforeInitialization方法; 18 | 13. 执行InitializingBean接口的afterPropertiesSet方法; 19 | 14. 调用的init-method属性指定的初始化方法; 20 | 15. 执行BeanPostProcessor接口的postProcessAfterInitialization方法; 21 | 16. 执行InstantiationAwareBeanPostProcessor接口的postProcessAfterInstantiation方法; 22 | 17. 容器初始化成功,执行业务代码后,下面开始销毁容器; 23 | 24 | 18. 调用DisposableBean接口的destroy方法; 25 | 19. 调用中的destroy-method指定的销毁方法。 26 | 27 | ### Spring Bean生命周期流程图 28 | 29 | -------------------------------------------------------------------------------- /contents/Spring/Spring_IOC_Analysis_Part1.md: -------------------------------------------------------------------------------- 1 | ## Spring IOC源码分析 2 | 3 | 首先,看一个简单示例: 4 | ``` 5 | import org.springframework.context.support.ClassPathXmlApplicationContext; 6 | 7 | /** 8 | * ${DESCRIPTION} 9 | * 10 | * @author Ricky Fung 11 | * @create 2018-04-15 12:07 12 | */ 13 | public class SpringApp { 14 | 15 | public static void main(String[] args) throws InterruptedException { 16 | 17 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); 18 | 19 | ExampleBean exampleBean = (ExampleBean) context.getBean("exampleBean"); 20 | System.out.println(exampleBean); 21 | } 22 | } 23 | ``` 24 | 25 | applicationContext.xml 26 | ``` 27 | 28 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ``` 41 | 42 | 接下来,我们分析一下```org.springframework.context.support.ClassPathXmlApplicationContext```。 43 | 44 | ### ClassPathXmlApplicationContext 45 | ClassPathXmlApplicationContext继承层次关系如下: 46 | ![](https://github.com/TFdream/blog/blob/master/docs/image/Spring/ClassPathXmlApplicationContext.png) 47 | 48 | 1. 构造方法 49 | ``` 50 | public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { 51 | 52 | /** 53 | * Create a new ClassPathXmlApplicationContext, loading the definitions 54 | * from the given XML file and automatically refreshing the context. 55 | * @param configLocation resource location 56 | * @throws BeansException if context creation failed 57 | */ 58 | public ClassPathXmlApplicationContext(String configLocation) throws BeansException { 59 | this(new String[] {configLocation}, true, null); 60 | } 61 | 62 | /** 63 | * Create a new ClassPathXmlApplicationContext with the given parent, 64 | * loading the definitions from the given XML files. 65 | * @param configLocations array of resource locations 66 | * @param refresh whether to automatically refresh the context, 67 | * loading all bean definitions and creating all singletons. 68 | * Alternatively, call refresh manually after further configuring the context. 69 | * @param parent the parent context 70 | * @throws BeansException if context creation failed 71 | * @see #refresh() 72 | */ 73 | public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) 74 | throws BeansException { 75 | 76 | super(parent); 77 | setConfigLocations(configLocations); 78 | if (refresh) { 79 | refresh(); 80 | } 81 | } 82 | } 83 | ``` 84 | 85 | refresh方法继承自```org.springframework.context.support.AbstractApplicationContext```类,如下: 86 | ``` 87 | @Override 88 | public void refresh() throws BeansException, IllegalStateException { 89 | synchronized (this.startupShutdownMonitor) { 90 | // Prepare this context for refreshing. 91 | prepareRefresh(); 92 | 93 | // Tell the subclass to refresh the internal bean factory. 94 | ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 95 | 96 | // Prepare the bean factory for use in this context. 97 | prepareBeanFactory(beanFactory); 98 | 99 | try { 100 | // Allows post-processing of the bean factory in context subclasses. 101 | postProcessBeanFactory(beanFactory); 102 | 103 | // Invoke factory processors registered as beans in the context. 104 | invokeBeanFactoryPostProcessors(beanFactory); 105 | 106 | // Register bean processors that intercept bean creation. 107 | registerBeanPostProcessors(beanFactory); 108 | 109 | // Initialize message source for this context. 110 | initMessageSource(); 111 | 112 | // Initialize event multicaster for this context. 113 | initApplicationEventMulticaster(); 114 | 115 | // Initialize other special beans in specific context subclasses. 116 | onRefresh(); 117 | 118 | // Check for listener beans and register them. 119 | registerListeners(); 120 | 121 | // Instantiate all remaining (non-lazy-init) singletons. 122 | finishBeanFactoryInitialization(beanFactory); 123 | 124 | // Last step: publish corresponding event. 125 | finishRefresh(); 126 | } 127 | 128 | catch (BeansException ex) { 129 | if (logger.isWarnEnabled()) { 130 | logger.warn("Exception encountered during context initialization - " + 131 | "cancelling refresh attempt: " + ex); 132 | } 133 | 134 | // Destroy already created singletons to avoid dangling resources. 135 | destroyBeans(); 136 | 137 | // Reset 'active' flag. 138 | cancelRefresh(ex); 139 | 140 | // Propagate exception to caller. 141 | throw ex; 142 | } 143 | 144 | finally { 145 | // Reset common introspection caches in Spring's core, since we 146 | // might not ever need metadata for singleton beans anymore... 147 | resetCommonCaches(); 148 | } 149 | } 150 | } 151 | ``` 152 | 153 | obtainFreshBeanFactory()方法如下: 154 | ``` 155 | /** 156 | * Tell the subclass to refresh the internal bean factory. 157 | * @return the fresh BeanFactory instance 158 | * @see #refreshBeanFactory() 159 | * @see #getBeanFactory() 160 | */ 161 | protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { 162 | refreshBeanFactory(); 163 | ConfigurableListableBeanFactory beanFactory = getBeanFactory(); 164 | if (logger.isDebugEnabled()) { 165 | logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); 166 | } 167 | return beanFactory; 168 | } 169 | ``` 170 | 这里的 refreshBeanFactory()和getBeanFactory()方法实现在```org.springframework.context.support.AbstractRefreshableApplicationContext```类中,如下: 171 | 172 | ``` 173 | /** 174 | * This implementation performs an actual refresh of this context's underlying 175 | * bean factory, shutting down the previous bean factory (if any) and 176 | * initializing a fresh bean factory for the next phase of the context's lifecycle. 177 | */ 178 | @Override 179 | protected final void refreshBeanFactory() throws BeansException { 180 | if (hasBeanFactory()) { 181 | destroyBeans(); 182 | closeBeanFactory(); 183 | } 184 | try { 185 | DefaultListableBeanFactory beanFactory = createBeanFactory(); 186 | beanFactory.setSerializationId(getId()); 187 | customizeBeanFactory(beanFactory); 188 | loadBeanDefinitions(beanFactory); 189 | synchronized (this.beanFactoryMonitor) { 190 | this.beanFactory = beanFactory; 191 | } 192 | } 193 | catch (IOException ex) { 194 | throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); 195 | } 196 | } 197 | 198 | /** 199 | * Determine whether this context currently holds a bean factory, 200 | * i.e. has been refreshed at least once and not been closed yet. 201 | */ 202 | protected final boolean hasBeanFactory() { 203 | synchronized (this.beanFactoryMonitor) { 204 | return (this.beanFactory != null); 205 | } 206 | } 207 | 208 | protected DefaultListableBeanFactory createBeanFactory() { 209 | return new DefaultListableBeanFactory(getInternalParentBeanFactory()); 210 | } 211 | 212 | @Override 213 | public final ConfigurableListableBeanFactory getBeanFactory() { 214 | synchronized (this.beanFactoryMonitor) { 215 | if (this.beanFactory == null) { 216 | throw new IllegalStateException("BeanFactory not initialized or already closed - " + 217 | "call 'refresh' before accessing beans via the ApplicationContext"); 218 | } 219 | return this.beanFactory; 220 | } 221 | } 222 | ``` 223 | 224 | 通过createBeanFactory方法,可以我们知道创建的BeanFactory实例是```org.springframework.beans.factory.support.DefaultListableBeanFactory```,DefaultListableBeanFactory类的继承层次关系如下图: 225 | ![DefaultListableBeanFactory_hierarchical_structure](https://github.com/TFdream/blog/blob/master/docs/image/Spring/DefaultListableBeanFactory.png) 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /contents/Spring/Spring_Transaction_Analysis.md: -------------------------------------------------------------------------------- 1 | Spring @Transactional 实现原理分析 2 | 3 | 4 | ## 参考 5 | [Spring @Transactional explained](https://doanduyhai.wordpress.com/2011/11/20/spring-transactional-explained/) 6 | -------------------------------------------------------------------------------- /contents/Tools/Atom_Markdown_Config.md: -------------------------------------------------------------------------------- 1 | 为了让中文显示起来跟好看,我使用微软雅黑作为中文字体,Monaco作为英文字体,具体配置如下: 2 | 3 | 下面这些是和Markdown编辑相关的包: 4 | * language-markdown: Markdown语法支持 5 | * markdown-image-paste: 使用Ctrl-V将图片插入到Markdown文档中 6 | * markdown-preview-plus:对markdown-preview的强化,更好的预览Markdown文档 7 | * markdown-scroll-sync: 同步显示Markdown源文件和预览文件 8 | * markdown-table-editor: 让Markdown编辑表格更轻松 9 | * markdown-themeable-pdf: 为Markdown生成的PDF文件使用自定义主题。 10 | * markdown-writer: 更好的使用Markdown写作 11 | * pdf-view: 基于PDF.js的PDF预览,和markdown-themeable-pdf结合起来,可以在Atom里面直接查看生成的PDF文件 12 | 13 | ## 修改页眉 14 | ~/.atom/markdown-themeable-pdf/header.js: PDF文件页眉,我的设置如下(页眉中只显示文件名称) 15 | ``` 16 | module.exports = function (info) { 17 | return { 18 | height: '2cm', 19 | contents: '

' + info.fileInfo.name + '
' 20 | }; 21 | }; 22 | ``` 23 | 24 | ## 修改页脚 25 | ~/.atom/markdown-themeable-pdf/footer.js: PDF文件页脚,我的设置如下(页脚中显示页码信息,Copyright,时间戳和作者名)。 26 | ``` 27 | module.exports = function (info) { 28 | var dateFormat = function () { 29 | var d = new Date(); 30 | var mm = d.getMonth() + 1; 31 | var dd = d.getDate(); 32 | var yy = d.getFullYear(); 33 | var myDateString = yy + '-' + mm + '-' + dd; 34 | return myDateString; 35 | }; 36 | return { 37 | height: '1cm', 38 | contents: '
Page {{page}}/{{pages}}
© Copyright ' + dateFormat() + ' by K.X
' 39 | }; 40 | }; 41 | ``` 42 | 43 | ## 导出字体 44 | ~/.atom/markdown-themeable-pdf/styles: PDF文本字体。和Atom一样,我使用微软雅黑作为中文字体,Monaco作为英文字体。 45 | ``` 46 | * { 47 | font-family: Monaco, Microsoft YaHei UI; 48 | } 49 | ``` 50 | 51 | 52 | -------------------------------------------------------------------------------- /contents/Tools/Mac_Env_Config.md: -------------------------------------------------------------------------------- 1 | # 在 macOS High Sierra 10.13 搭建Java开发环境 2 | 3 | ## 安装 iTerm2 4 | 推荐 [iTerm2](https://www.iterm2.com),iTerm2 功能强大,可以替代系统默认的命令行终端。下载解压后,将 iTerm2 直接拖入"应用程序"目录。 5 | 6 | ## 安装 Xcode 7 | [Xcode](https://itunes.apple.com/cn/app/xcode/id497799835) 是苹果出品的包含一系列工具及库的开发套件。通过 AppStore 安装最新版本的 Xcode(9.0)。我们一般不会用 Xcode 来开发后端项目。但这一步也是必须的,因为 Xcode 会附带安装一些如 Git 等必要的软件。 8 | 9 | ## 安装 Command Line Tools for Xcode 10 | 这一步会帮你安装许多常见的基于 Unix 的工具。Xcode 命令行工具作为 Xcode 的一部分,包含了 GCC 编译器。在命令行中执行以下命令即可安装: 11 | ``` 12 | # 安装 Xcode Command Line Tools 13 | $ xcode-select --install 14 | ``` 15 | 16 | 当 Xcode 和 Xcode Command Line Tools 安装完成后,你需要启动 Xcode,并点击同意接受许可协议,然后关闭 Xcode 就可以了。这一步骤也是必须的,否则 Xcode 包含的一系列开发工具都将不可用。 17 | 18 | ## 安装 Homebrew 19 | [Homebrew](https://brew.sh/index_zh-cn.html) 作为 macOS 不可或缺的套件管理器,用来安装、升级以及卸载常用的软件。在命令行中执行以下命令即可安装: 20 | ``` 21 | $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 22 | ``` 23 | 安装后可以修改 Homebrew 源,国外源一直不是很给力,这里我们将 Homebrew 的 git 远程仓库改为中国科学技术大学开源软件镜像: 24 | 25 | ## 安装一些必要的工具包 26 | ``` 27 | brew install wget 28 | brew install autoconf 29 | brew install openssl 30 | ``` 31 | 32 | ## Java环境配置 33 | ### JDK 34 | JDK在Mac系统中,其实有两个路径:一个是默认的,一个是用户自己下载安装的JDK。 35 | 36 | 想查看默认使用的是哪个JDK,只需在终端中输入whereis java就能看到路径,然后用ls -l则能看到真实路径,命令如下: 37 | ``` 38 | $ whereis java 39 | /usr/bin/java 40 | $ ls -l /usr/bin/java 41 | 42 | ``` 43 | 如果是从Oracle下载的idk,且想要更新的话,则首先需要修改jdk的环境变量。oracle下载的,默认会安装在:/Library/Java/JavaVirtualMachines/ 目录下。 44 | 45 | #### 1. 下载JDK 安装包 46 | [点此](http://www.oracle.com/technetwork/java/javase/downloads/index.html) 下载JDK 安装包。下载成功后直接点击安装包进行安装。 47 | 48 | 打开Mac自带终端Terminal,进入当前用户主目录: 49 | ``` 50 | $ cd ~ 51 | ``` 52 | 默认用户目录则不需要 53 | 54 | #### 2. 创建.bash_profile文件 55 | 如果你是第一次配置环境变量,可以使用 56 | ``` 57 | $ touch .bash_profile 58 | ``` 59 | 创建一个.bash_profile的隐藏配置文件,如果是已存在的配置文件,则使用 60 | ``` 61 | $ open -e .bash_profile 62 | ``` 63 | 64 | #### 3. 打开.bash_profile文件 65 | 输入jdk下面的命令,注意根据自己的目录进行调整JAVA_HOME的值,我安装的JDK1.8路径:/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home 66 | 67 | 完整配置如下: 68 | ``` 69 | JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home 70 | PATH=$JAVA_HOME/bin:$PATH:. 71 | CLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:. 72 | export JAVA_HOME 73 | export CLASSPATH 74 | export PATH 75 | ``` 76 | #### 4. 重新加载.bash_profile 77 | 使用"source .bash_profile"使配置生效,如下: 78 | ``` 79 | $ source .bash_profile 80 | ``` 81 | 82 | #### 5.验证 83 | 输入如下命令: 84 | ``` 85 | $ java -version 86 | ``` 87 | 或者: 88 | ``` 89 | $ echo $JAVA_HOME 90 | ``` 91 | 92 | ### Maven 93 | #### 1. 下载Maven 94 | [点此](http://maven.apache.org/download.cgi)下载Maven安装包。 95 | 96 | #### 2. 解压缩 97 | 解压上一步下载的gz文件,命令如下: 98 | ``` 99 | $ tar xzvf apache-maven-3.5.3-bin.tar.gz 100 | ``` 101 | 102 | #### 3. 添加环境变量 103 | 104 | 添加bin到环境变量中,需要编辑 ```.bash_profile```文件,命令如下: 105 | ``` 106 | $ vi .bash_profile 107 | ``` 108 | 109 | 添加内容如下: 110 | ``` 111 | MAVEN_HOME=/Users/ricky/Data/Software/Maven/apache-maven-3.5.3 112 | PATH=$PATH:$MAVEN_HOME/bin 113 | 114 | export MAVEN_HOME 115 | export PATH 116 | ``` 117 | 保存并退出。 118 | 119 | #### 4. 重新加载.bash_profile 120 | 使用"source .bash_profile"使配置生效,如下: 121 | ``` 122 | $ source .bash_profile 123 | ``` 124 | 125 | #### 5.验证 126 | 输入如下命令: 127 | ``` 128 | $ mvn -v 129 | ``` 130 | 或者: 131 | ``` 132 | $ echo $MAVEN_HOME 133 | ``` 134 | 135 | #### setting.xml 136 | Maven的配置文件settings.xml存在于两个地方: 137 | * 安装的地方:${M2_HOME}/conf/settings.xml 138 | * 用户的目录:${user.home}/.m2/settings.xml 139 | 140 | 前者又被叫做全局配置,对操作系统的所有使用者生效;后者被称为用户配置,只对当前操作系统的使用者生效。如果两者都存在,它们的内容将被合并,并且用户范围的settings.xml会覆盖全局的settings.xml。 141 | 142 | Windows位置在C:\Users\{你的用户名}\.m2\settings.xml,Mac位置在/Users/{你的用户名}/.m2/settings.xml。 143 | 144 | ### IntelliJ IDEA 145 | #### 1. 下载 146 | [点此](https://www.jetbrains.com/idea/download/) 下载IntelliJ IDEA安装包。 147 | 148 | #### 2. 激活方式 149 | 激活方式如下: 150 | * 通过License Server:http://idea.iteblog.com/key.php[已被封杀] 151 | * 通过License Server:http://idea.toocruel.net[已被封杀] 152 | 153 | #### 3. 启动JVM参数调优 154 | 155 | #### 4. 模板配置 156 | ``` 157 | /** 158 | * @author Ricky Fung 159 | * @version 1.0 160 | * @since ${YEAR}-${MONTH}-${DAY} ${HOUR}:${MINUTE} 161 | */ 162 | ``` 163 | 164 | ## Git 165 | ### 1. 下载Git 166 | [点此](https://git-scm.com/)下载Git安装包。 167 | 168 | ### 2. 初次运行 Git 前的配置 169 | Git 提供了一个叫做 git config 的工具(译注:实际是 git-config 命令,只不过可以通过 git 加一个名字来呼叫此命令。),专门用来配置或读取相应的工作环境变量。而正是由这些环境变量,决定了 Git 在各个环节的具体工作方式和行为。这些变量可以存放在以下三个不同的地方: 170 | 171 | * ```/etc/gitconfig``` 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 --system 选项,读写的就是这个文件。 172 | * ```~/.gitconfig``` 文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 --global 选项,读写的就是这个文件。 173 | * 当前项目的 Git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。 174 | 175 | #### 用户信息 176 | 第一个要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录: 177 | ``` 178 | $ git config --global user.name "John Doe" 179 | $ git config --global user.email johndoe@example.com 180 | ``` 181 | 182 | #### 查看配置信息 183 | 要检查已有的配置信息,可以使用 git config --list 命令: 184 | ``` 185 | $ git config --list 186 | user.name=Scott Chacon 187 | user.email=schacon@gmail.com 188 | color.status=auto 189 | color.branch=auto 190 | color.interactive=auto 191 | color.diff=auto 192 | ... 193 | ``` 194 | 195 | ### 3. 生成 ssh 密钥 196 | #### 1.查看ssh密钥 197 | 查看是否已经有了ssh密钥,命令如下: 198 | ``` 199 | $ cd ~/.ssh 200 | ``` 201 | 如果没有密钥则不会有此文件夹,有则备份删除。 202 | 203 | #### 2.生成密钥 204 | 生成ssh密钥,命令如下: 205 | ``` 206 | $ ssh-keygen -t rsa -C “example@example.com” 207 | ``` 208 | 按3次回车,密码为空。 209 | 210 | #### 3 .添加ssh密钥 211 | 在gitlab/github个人中心```SSH keys```添加ssh密钥,这要添加的是“id_rsa.pub”里面的公钥。 212 | 213 | 214 | ## 安装MySQL 215 | 一、安装MySQL 216 | ``` 217 | $ brew install mysql 218 | ``` 219 | 二、启动mysql服务 220 | ``` 221 | $ mysql.server start 222 | ``` 223 | 三、初始化mysql配置 224 | ``` 225 | $ mysql_secure_installation 226 | ``` 227 | 228 | 其他命令: 229 | ``` 230 | # 获取 service 列表 231 | brew services list 232 | # 重启 mysql 服务 233 | brew services restart mysql 234 | # 停止 mysql 服务 235 | brew services stop mysql 236 | ``` 237 | 238 | ### 常见问题 239 | 解决 Authentication plugin ‘caching_sha2_password’ cannot be loaded 的方法,可以往你的连接工具、或者程序应用显示指定身份验证方式,或者直接改为以前的版本方式: 240 | 你可以使用如下方式: 241 | ``` 242 | ALTER USER root@localhost IDENTIFIED WITH mysql_native_password BY ‘root2018’; 243 | ``` 244 | 245 | 修改root的密码改为root2018。 246 | -------------------------------------------------------------------------------- /contents/Tools/Mac_Useful_Skills.md: -------------------------------------------------------------------------------- 1 | # Mac 使用技巧 2 | 3 | ## Mac OS X MySQL客户端工具 4 | * [Sequel Pro](http://www.sequelpro.com/) 5 | * Navicat Premium 6 | * [MySQL workbench](http://www.sequelpro.com/) 7 | 8 | ## Mac OS 画图工具 9 | * [draw.io](https://www.draw.io/) 10 | -------------------------------------------------------------------------------- /docs/image/Http/README.md: -------------------------------------------------------------------------------- 1 | ## HTTP 2 | -------------------------------------------------------------------------------- /docs/image/Http/htt2_binary_frame.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Http/htt2_binary_frame.jpg -------------------------------------------------------------------------------- /docs/image/Http/http2_multiplexing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Http/http2_multiplexing.jpg -------------------------------------------------------------------------------- /docs/image/Http/http_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Http/http_request.png -------------------------------------------------------------------------------- /docs/image/Http/http_response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Http/http_response.png -------------------------------------------------------------------------------- /docs/image/Http/tcp_3_handshake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Http/tcp_3_handshake.png -------------------------------------------------------------------------------- /docs/image/Http/tcp_4_bye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Http/tcp_4_bye.png -------------------------------------------------------------------------------- /docs/image/Hystrix/HystrixRollingNumber_bucket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Hystrix/HystrixRollingNumber_bucket.png -------------------------------------------------------------------------------- /docs/image/Hystrix/HystrixRollingNumber_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Hystrix/HystrixRollingNumber_window.png -------------------------------------------------------------------------------- /docs/image/Hystrix/README.md: -------------------------------------------------------------------------------- 1 | ## Hystrix 2 | -------------------------------------------------------------------------------- /docs/image/Interview/README.md: -------------------------------------------------------------------------------- 1 | # Interview 2 | -------------------------------------------------------------------------------- /docs/image/Interview/System_Archtecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Interview/System_Archtecture.png -------------------------------------------------------------------------------- /docs/image/JAVA_HOME.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/JAVA_HOME.png -------------------------------------------------------------------------------- /docs/image/JDK_Source/HashMap_jdk8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/JDK_Source/HashMap_jdk8.jpg -------------------------------------------------------------------------------- /docs/image/JDK_Source/LinkedList_jdk8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/JDK_Source/LinkedList_jdk8.png -------------------------------------------------------------------------------- /docs/image/JDK_Source/README.md: -------------------------------------------------------------------------------- 1 | ## JDK源码 2 | -------------------------------------------------------------------------------- /docs/image/JDK_Source/concurrenthashmap_jdk7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/JDK_Source/concurrenthashmap_jdk7.png -------------------------------------------------------------------------------- /docs/image/JDK_Source/concurrenthashmap_jdk8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/JDK_Source/concurrenthashmap_jdk8.png -------------------------------------------------------------------------------- /docs/image/JVM/Jvm_Area.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/JVM/Jvm_Area.jpg -------------------------------------------------------------------------------- /docs/image/JVM/Jvm_GC_Root.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/JVM/Jvm_GC_Root.png -------------------------------------------------------------------------------- /docs/image/JVM/Jvm_collector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/JVM/Jvm_collector.png -------------------------------------------------------------------------------- /docs/image/JVM/Jvm_heap_area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/JVM/Jvm_heap_area.png -------------------------------------------------------------------------------- /docs/image/JVM/README.md: -------------------------------------------------------------------------------- 1 | # JVM 2 | -------------------------------------------------------------------------------- /docs/image/RBAC/RBAC_extension.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/RBAC/RBAC_extension.jpg -------------------------------------------------------------------------------- /docs/image/RBAC/RBAC_group.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/RBAC/RBAC_group.jpg -------------------------------------------------------------------------------- /docs/image/RBAC/RBAC_permission_category.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/RBAC/RBAC_permission_category.jpg -------------------------------------------------------------------------------- /docs/image/RBAC/RBAC_permission_model.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/RBAC/RBAC_permission_model.jpg -------------------------------------------------------------------------------- /docs/image/Redis/README.md: -------------------------------------------------------------------------------- 1 | ## Redis 2 | -------------------------------------------------------------------------------- /docs/image/Redis/redis_ds2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Redis/redis_ds2.jpg -------------------------------------------------------------------------------- /docs/image/Redis/redis_ds_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Redis/redis_ds_1.jpg -------------------------------------------------------------------------------- /docs/image/Spring-Framework-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Spring-Framework-overview.png -------------------------------------------------------------------------------- /docs/image/Spring/AspectJAwareAdvisorAutoProxyCreator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Spring/AspectJAwareAdvisorAutoProxyCreator.png -------------------------------------------------------------------------------- /docs/image/Spring/ClassPathXmlApplicationContext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Spring/ClassPathXmlApplicationContext.png -------------------------------------------------------------------------------- /docs/image/Spring/DefaultListableBeanFactory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/Spring/DefaultListableBeanFactory.png -------------------------------------------------------------------------------- /docs/image/Spring/README.md: -------------------------------------------------------------------------------- 1 | ## Spring 2 | -------------------------------------------------------------------------------- /docs/image/alipay/README.md: -------------------------------------------------------------------------------- 1 | # 支付宝双十一 2 | -------------------------------------------------------------------------------- /docs/image/cap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/cap.png -------------------------------------------------------------------------------- /docs/image/cpu_cache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/cpu_cache.png -------------------------------------------------------------------------------- /docs/image/gbf40/README.md: -------------------------------------------------------------------------------- 1 | ## 高并发系统设计40问 2 | 高并发系统设计40问 3 | -------------------------------------------------------------------------------- /docs/image/gbf40/gbf40_danti.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/gbf40/gbf40_danti.jpg -------------------------------------------------------------------------------- /docs/image/gbf40/gbf40_msa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/gbf40/gbf40_msa.jpg -------------------------------------------------------------------------------- /docs/image/git_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/git_flow.png -------------------------------------------------------------------------------- /docs/image/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/logo.jpg -------------------------------------------------------------------------------- /docs/image/maven_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/maven_home.png -------------------------------------------------------------------------------- /docs/image/tas_list_alipay.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/tas_list_alipay.jpeg -------------------------------------------------------------------------------- /docs/image/task_list_iqj.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/task_list_iqj.jpeg -------------------------------------------------------------------------------- /docs/image/zfb_zqhb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/docs/image/zfb_zqhb.jpg -------------------------------------------------------------------------------- /font/README.md: -------------------------------------------------------------------------------- 1 | # 中文字体库 2 | 3 | 可以从 https://www.wfonts.com/ 网站下载字体。 4 | -------------------------------------------------------------------------------- /font/fangsong.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/font/fangsong.zip -------------------------------------------------------------------------------- /font/kaiti.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TFdream/blog/b127bc2533d953adcc8f366ab9fb4033fa8e20d8/font/kaiti.zip -------------------------------------------------------------------------------- /ppt/README.md: -------------------------------------------------------------------------------- 1 | #PPT 2 | PPT 3 | --------------------------------------------------------------------------------