├── docs ├── .nojekyll ├── CNAME ├── favicon.ico ├── _media │ ├── qrcode.png │ ├── fustack.png │ ├── onlinebook.svg │ └── wxbugstack.svg ├── assets │ ├── resources │ │ └── DataTree.xmind │ ├── img │ │ └── 2020 │ │ │ ├── itstack-naive-chat-01.png │ │ │ ├── itstack-naive-chat-02.png │ │ │ ├── itstack-naive-chat-03.png │ │ │ └── itstack-naive-chat-04.png │ └── js │ │ ├── prism-java.min.js │ │ ├── docsify-pagination.min.js │ │ └── prism-bash.js ├── notes │ ├── itstack-demo-middleware.md │ ├── itstack-demo-ddd.md │ ├── itstack-demo-frame.md │ ├── _sidebar.md │ ├── itstack-demo-any.md │ ├── itstack-demo-agent.md │ ├── itstack-demo-code.md │ ├── itstack-demo-jvm.md │ ├── itstack-demo-any │ │ ├── 2019-08-12-windows环境下安装elasticsearch6.2.2.md │ │ ├── 2019-08-13-elasticsearch-head插件安装.md │ │ ├── 2020-01-18-似乎你总也记不住,byte的取值范围是-127~128还是-128~127.md │ │ └── 2019-12-21-[有点干货]JDK、CGLIB动态代理使用以及源码分析.md │ ├── itstack-demo-netty │ │ ├── itstack-demo-netty-1 │ │ │ ├── 2019-08-01-netty案例,netty4.1基础入门篇一《嗨!NettyServer》.md │ │ │ ├── 2019-08-10-netty案例,netty4.1基础入门篇七《嗨!NettyClient》.md │ │ │ ├── 2019-08-05-netty案例,netty4.1基础入门篇二《NettyServer接收数据》.md │ │ │ ├── 2019-08-06-netty案例,netty4.1基础入门篇三《NettyServer字符串解码器》.md │ │ │ ├── 2019-08-07-netty案例,netty4.1基础入门篇四《NettyServer收发数据》.md │ │ │ ├── 2019-08-08-netty案例,netty4.1基础入门篇五《NettyServer字符串编码器》.md │ │ │ ├── 2019-08-11-netty案例,netty4.1基础入门篇八《NettyClient半包粘包处理、编码解码处理、收发数据方式》.md │ │ │ ├── 2019-08-09-netty案例,netty4.1基础入门篇六《NettyServer群发消息》.md │ │ │ ├── 2019-08-12-netty案例,netty4.1基础入门篇九《自定义编码解码器,处理半包、粘包数据》.md │ │ │ ├── 2019-08-15-netty案例,netty4.1基础入门篇十二《简单实现一个基于Netty搭建的Http服务》.md │ │ │ ├── 2019-08-14-netty案例,netty4.1基础入门篇十一《nettyudp通信方式案例Demo》.md │ │ │ └── 2019-08-13-netty案例,netty4.1基础入门篇十《关于ChannelOutboundHandlerAdapter简单使用》.md │ │ ├── itstack-demo-netty-2 │ │ │ └── 2019-08-26-netty案例,netty4.1中级拓展篇十一《Netty基于ChunkedStream数据流切块传输》.md │ │ └── itstack-demo-netty-4 │ │ │ ├── 2019-09-13-netty案例,netty4.1源码分析篇四《ByteBuf的数据结构在使用方式中的剖析》.md │ │ │ └── 2019-09-12-netty案例,netty4.1源码分析篇三《Netty服务端初始化过程以及反射工厂的作用》.md │ ├── itstack-demo-jvm │ │ ├── 2019-05-01-用Java实现JVM第一章《命令行工具》.md │ │ ├── 2019-05-04-用Java实现JVM第三章《解析class文件》附[classReader拆解].md │ │ └── 2019-05-05-用Java实现JVM第四章《运行时数据区》.md │ ├── itstack-demo-agent │ │ ├── 2019-07-10-基于JavaAgent的全链路监控一《嗨!JavaAgent》.md │ │ ├── 2019-07-13-基于JavaAgent的全链路监控四《JVM内存与GC信息》.md │ │ ├── 2019-07-11-基于JavaAgent的全链路监控二《通过字节码增加监控执行耗时》.md │ │ ├── 2019-07-12-基于JavaAgent的全链路监控三《ByteBuddy操作监控方法字节码》.md │ │ └── 2019-07-14-基于JavaAgent的全链路监控五《ThreadLocal链路追踪》.md │ ├── itstack-demo-netty.md │ └── itstack-demo-ddd │ │ ├── 2019-10-15-DDD专题案例一《初识领域驱动设计DDD落地》.md │ │ └── 2019-10-17-DDD专题案例三《领域驱动设计架构基于SpringCloud搭建微服务》.md ├── _sidebar.md ├── _coverpage.md ├── README.md └── index.html └── .gitignore /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | xx.itstack.org -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyGitBooks/itstack/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /docs/_media/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyGitBooks/itstack/HEAD/docs/_media/qrcode.png -------------------------------------------------------------------------------- /docs/_media/fustack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyGitBooks/itstack/HEAD/docs/_media/fustack.png -------------------------------------------------------------------------------- /docs/assets/resources/DataTree.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyGitBooks/itstack/HEAD/docs/assets/resources/DataTree.xmind -------------------------------------------------------------------------------- /docs/assets/img/2020/itstack-naive-chat-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyGitBooks/itstack/HEAD/docs/assets/img/2020/itstack-naive-chat-01.png -------------------------------------------------------------------------------- /docs/assets/img/2020/itstack-naive-chat-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyGitBooks/itstack/HEAD/docs/assets/img/2020/itstack-naive-chat-02.png -------------------------------------------------------------------------------- /docs/assets/img/2020/itstack-naive-chat-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyGitBooks/itstack/HEAD/docs/assets/img/2020/itstack-naive-chat-03.png -------------------------------------------------------------------------------- /docs/assets/img/2020/itstack-naive-chat-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyGitBooks/itstack/HEAD/docs/assets/img/2020/itstack-naive-chat-04.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-middleware.md: -------------------------------------------------------------------------------- 1 | ## 文章目录 2 | * [`Spring Boot 中间件开发(一)《服务治理中间件之统一白名单验证》`](/notes/itstack-demo-middleware/2019-12-02-SpringBoot中间件开发(一)《服务治理中间件之统一白名单验证》.md) 3 | * [`开发基于SpringBoot的分布式任务中间件DcsSchedule(为开源贡献力量)`](/notes/itstack-demo-middleware/2019-12-08-开发基于SpringBoot的分布式任务中间件DcsSchedule(为开源贡献力量).md) -------------------------------------------------------------------------------- /docs/notes/itstack-demo-ddd.md: -------------------------------------------------------------------------------- 1 | ## 文章目录 2 | * [`DDD专题案例一《初识领域驱动设计DDD落地》`](/notes/itstack-demo-ddd/2019-10-15-DDD专题案例一《初识领域驱动设计DDD落地》.md) 3 | * [`DDD专题案例二《领域层决策规则树服务设计》`](/notes/itstack-demo-ddd/2019-10-16-DDD专题案例二《领域层决策规则树服务设计》.md) 4 | * [`DDD专题案例三《领域驱动设计架构基于SpringCloud搭建微服务》`](/notes/itstack-demo-ddd/2019-10-17-DDD专题案例三《领域驱动设计架构基于SpringCloud搭建微服务》.md) -------------------------------------------------------------------------------- /docs/notes/itstack-demo-frame.md: -------------------------------------------------------------------------------- 1 | ## 文章目录 2 | * [`发布Jar包到Maven中央仓库(为开发开源中间件做准备)`](/notes/itstack-demo-frame/2019-12-07-发布Jar包到Maven中央仓库(为开发开源中间件做准备).md) 3 | * [`架构框架搭建(一)《单体应用服务之SSM整合:Spring4 + SpringMvc + Mybatis》`](/notes/itstack-demo-frame/2019-12-22-架构框架搭建(一)《单体应用服务之SSM整合:Spring4+SpringMvc+Mybatis》.md) 4 | * [`架构框架搭建(二)《Dubbo分布式领域驱动设计架构框体》`](/notes/itstack-demo-frame/2019-12-31-架构框架搭建(二)《Dubbo分布式领域驱动设计架构框体》.md) 5 | -------------------------------------------------------------------------------- /docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | * [:octocat: 首页](/README) 2 | * [:coffee: Java基础编程](/notes/itstack-demo-any.md) 3 | * [:computer: 用Java实现jvm虚拟机](/notes/itstack-demo-jvm.md) 4 | * [:pencil2: Spring系列源码解读](/notes/itstack-demo-code.md) 5 | * [:sound: Netty4.x专题](/notes/itstack-demo-netty.md) 6 | * [:triangular_ruler: DDD领域驱动设计](/notes/itstack-demo-ddd.md) 7 | * [:electric_plug: 中间件开发](/notes/itstack-demo-middleware.md) 8 | * [:ghost: JavaAgent全链路监控](/notes/itstack-demo-agent.md) 9 | * [:art: 架构框架搭建](/notes/itstack-demo-frame.md) -------------------------------------------------------------------------------- /docs/notes/_sidebar.md: -------------------------------------------------------------------------------- 1 | * [:octocat: 首页](/README) 2 | * [:coffee: Java基础编程](/notes/itstack-demo-any.md) 3 | * [:computer: 用Java实现jvm虚拟机](/notes/itstack-demo-jvm.md) 4 | * [:pencil2: Spring系列源码解读](/notes/itstack-demo-code.md) 5 | * [:sound: Netty4.x专题](/notes/itstack-demo-netty.md) 6 | * [:triangular_ruler: DDD领域驱动设计](/notes/itstack-demo-ddd.md) 7 | * [:electric_plug: 中间件开发](/notes/itstack-demo-middleware.md) 8 | * [:ghost: JavaAgent全链路监控](/notes/itstack-demo-agent.md) 9 | * [:art: 架构框架搭建](/notes/itstack-demo-frame.md) -------------------------------------------------------------------------------- /docs/notes/itstack-demo-any.md: -------------------------------------------------------------------------------- 1 | ## 文章目录 2 | * [`在windows环境下安装Elasticsearch 6.2.2`](/notes/itstack-demo-any/2019-08-12-windows环境下安装elasticsearch6.2.2.md) 3 | * [`elasticsearch-head插件安装`](/notes/itstack-demo-any/2019-08-13-elasticsearch-head插件安装.md) 4 | * [`并不想吹牛皮,但!为了把Github博客粉丝转移到公众号,我干了!`](/notes/itstack-demo-any/2019-11-23-并不想吹牛皮,但!为了把Github博客粉丝转移到公众号,我干了!.md) 5 | * [`有点干货 | Jdk1.8新特性实战篇(41个案例)`](/notes/itstack-demo-any/2019-12-10-[有点干货]Jdk1.8新特性实战篇(41个案例).md) 6 | * [`有点干货 | JDK、CGLIB动态代理使用以及源码分析`](/notes/itstack-demo-any/2019-12-21-[有点干货]JDK、CGLIB动态代理使用以及源码分析.md) 7 | * [`似乎你总也记不住,byte取值范围是 -127~128 还是 -128~127`](/notes/itstack-demo-any/2020-01-18-似乎你总也记不住,byte的取值范围是-127~128还是-128~127.md) -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | ![logo](_media/icon.svg) 2 | 3 | # 虫洞 · 技术栈 4 | 5 | - 本文档是作者小傅哥多年从事一线互联网Java开发的学习历程技术汇总,旨在为大家提供一个较清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本文能为您提供帮助,请给予支持(关注、点赞、分享)! 6 | 7 | [![stars](https://badgen.net/github/stars/MyGitBooks/itstack.github.io?icon=github&color=4ab8a1)](https://github.com/MyGitBooks/itstack.github.io) [![forks](https://badgen.net/github/forks/MyGitBooks/itstack.github.io?icon=github&color=4ab8a1)](https://github.com/MyGitBooks/itstack.github.io) [](https://itstack.org/_media/qrcode.png?x-oss-process=style/may) 8 | 9 | [GitHub]() 10 | [开始阅读](README.md) 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-agent.md: -------------------------------------------------------------------------------- 1 | ## 文章目录 2 | * [`基于JavaAgent的全链路监控一《嗨!JavaAgent》`](/notes/itstack-demo-agent/2019-07-10-基于JavaAgent的全链路监控一《嗨!JavaAgent》.md) 3 | * [`基于JavaAgent的全链路监控二《通过字节码增加监控执行耗时》`](/notes/itstack-demo-agent/2019-07-11-基于JavaAgent的全链路监控二《通过字节码增加监控执行耗时》.md) 4 | * [`基于JavaAgent的全链路监控三《ByteBuddy操作监控方法字节码》`](/notes/itstack-demo-agent/2019-07-12-基于JavaAgent的全链路监控三《ByteBuddy操作监控方法字节码》.md) 5 | * [`基于JavaAgent的全链路监控四《JVM内存与GC信息》`](/notes/itstack-demo-agent/2019-07-13-基于JavaAgent的全链路监控四《JVM内存与GC信息》.md) 6 | * [`基于JavaAgent的全链路监控五《ThreadLocal链路追踪》`](/notes/itstack-demo-agent/2019-07-14-基于JavaAgent的全链路监控五《ThreadLocal链路追踪》.md) 7 | * [`基于JavaAgent的全链路监控六《开发应用级监控》`](/notes/itstack-demo-agent/2019-07-15-基于JavaAgent的全链路监控六《开发应用级监控》.md) 8 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-code.md: -------------------------------------------------------------------------------- 1 | ## 文章目录 2 | * [`源码分析 | Mybatis接口没有实现类为什么可以执行增删改查`](/notes/itstack-demo-code/2019-12-25-[源码分析]Mybatis接口没有实现类为什么可以执行增删改查.md) 3 | * [`源码分析 | Spring定时任务Quartz执行全过程源码解读`](/notes/itstack-demo-code/2020-01-01-[源码解析]Spring定时任务Quartz执行全过程源码解读.md) 4 | * [`源码分析 | 咋嘞?你的IDEA过期了吧!加个Jar包就破解了,为什么?`](/notes/itstack-demo-code/2020-01-06-[源码分析]咋嘞?你的IDEA过期了吧!加个Jar包就破解了,为什么?.md) 5 | * [`源码分析 | 像盗墓一样分析Spring是怎么初始化xml并注册bean的`](/notes/itstack-demo-code/2020-01-08-[源码分析]像盗墓一样分析Spring是怎么初始化xml并注册bean的.md) 6 | * [`源码分析 | 基于jdbc实现一个Demo版的Mybatis`](/notes/itstack-demo-code/2020-01-13-[源码分析]基于jdbc实现一个Demo版的Mybatis.md) 7 | * [`源码分析 | 手写mybait-spring核心功能(干货好文一次学会工厂bean、类代理、bean注册的使用)`](/notes/itstack-demo-code/2020-01-20-[源码分析]手写mybait-spring核心功能(干货好文一次学会工厂bean、类代理、bean注册的使用).md) -------------------------------------------------------------------------------- /docs/notes/itstack-demo-jvm.md: -------------------------------------------------------------------------------- 1 | ## 文章目录 2 | * [`用Java实现JVM第一章《命令行工具》`](/notes/itstack-demo-jvm/2019-05-01-用Java实现JVM第一章《命令行工具》.md) 3 | * [`用Java实现JVM第二章《搜索class文件》`](/notes/itstack-demo-jvm/2019-05-02-用Java实现JVM第二章《搜索class文件》.md) 4 | * [`用Java实现JVM第三章《解析class文件》`](/notes/itstack-demo-jvm/2019-05-03-用Java实现JVM第三章《解析class文件》.md) 5 | * [`用Java实现JVM第三章《解析class文件》附[classReader拆解]`](/notes/itstack-demo-jvm/2019-05-04-用Java实现JVM第三章《解析class文件》附[classReader拆解].md) 6 | * [`用Java实现JVM第四章《运行时数据区》`](/notes/itstack-demo-jvm/2019-05-05-用Java实现JVM第四章《运行时数据区》.md) 7 | * [`用Java实现JVM第五章《指令集和解释器》`](/notes/itstack-demo-jvm/2019-05-06-用Java实现JVM第五章《指令集和解释器》.md) 8 | * [`用Java实现JVM第六章《类和对象》`](/notes/itstack-demo-jvm/2019-05-07-用Java实现JVM第六章《类和对象》.md) 9 | * [`用Java实现JVM第七章《方法调用和返回》`](/notes/itstack-demo-jvm/2019-05-08-用Java实现JVM第七章《方法调用和返回》.md) 10 | * [`用Java实现JVM第八章《数组和字符串》`](/notes/itstack-demo-jvm/2019-05-09-用Java实现JVM第八章《数组和字符串》.md) 11 | * [`用Java实现JVM第九章《本地方法调用》`](/notes/itstack-demo-jvm/2019-05-10-用Java实现JVM第九章《本地方法调用》.md) 12 | * [`用Java实现JVM第十章《异常处理》`](/notes/itstack-demo-jvm/2019-05-11-用Java实现JVM第十章《异常处理》.md) -------------------------------------------------------------------------------- /docs/notes/itstack-demo-any/2019-08-12-windows环境下安装elasticsearch6.2.2.md: -------------------------------------------------------------------------------- 1 | # windows环境下安装elasticsearch6.2.2 2 | 3 | --- 4 | 5 | ## 前言介绍 6 | 在windows环境下安装Elasticsearch 6.2.2 7 | 8 | >ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。ElasticSearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。 9 | 10 | ## 安装环境 11 | **1、Elasticsearch 6.2.2 下载** [https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2](https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2) 12 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/elasticsearch6.6.2.png) 13 | 14 | **2、JDK 1.8 (jdk1.7及以下不能支持Elasticsearch)下载** [https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) 15 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/es-3.png) 16 | 17 | **3、安装cmd进入elasticsearch-6.2.2\bin,执行elasticsearch.bat** 18 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/es-1.png) 19 | 20 | **4、启动完成后打开http://localhost:9200 {默认端口9200}** 21 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/es-2.png) 22 | 23 | ------------ -------------------------------------------------------------------------------- /docs/assets/js/prism-java.min.js: -------------------------------------------------------------------------------- 1 | !function(e){var t=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|null|open|opens|package|private|protected|provides|public|requires|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,a=/\b[A-Z](?:\w*[a-z]\w*)?\b/;e.languages.java=e.languages.extend("clike",{"class-name":[a,/\b[A-Z]\w*(?=\s+\w+\s*[;,=())])/],keyword:t,function:[e.languages.clike.function,{pattern:/(\:\:)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x[\da-f_]*\.?[\da-f_p+-]+\b|(?:\b\d[\d_]*\.?[\d_]*|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0}}),e.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"}}),e.languages.insertBefore("java","class-name",{annotation:{alias:"punctuation",pattern:/(^|[^.])@\w+/,lookbehind:!0},namespace:{pattern:/(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)[a-z]\w*(?:\.[a-z]\w*)+/,lookbehind:!0,inside:{punctuation:/\./}},generics:{pattern:/<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/,inside:{"class-name":a,keyword:t,punctuation:/[<>(),.:]/,operator:/[?&|]/}}})}(Prism); -------------------------------------------------------------------------------- /docs/notes/itstack-demo-any/2019-08-13-elasticsearch-head插件安装.md: -------------------------------------------------------------------------------- 1 | # elasticsearch-head插件安装 2 | 3 | --- 4 | 5 | ## 前言介绍 6 | 安装Elasticsearch的head插件,用于方便操作Elasticsearch 7 | 8 | >elasticsearch-head 是用于监控 Elasticsearch 状态的客户端插件,包括数据可视化、执行增删改查操作等。elasticsearch-head 插件的安装在 Linux 和 Windows 没什么区别,安装之前确保当前系统已经安装 nodejs 即可。 9 | 10 | ## 安装环境 11 | 1. 安装[node.js](https://nodejs.org/en/download/)并配置环境变量PATH{path:D:\Program Files\nodejs\} 12 | 13 | - [nodejs下载](https://nodejs.org/en/download/) 14 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/nodejs.png) 15 | - 执行安装,配置环境变量{path:D:\Program Files\nodejs\} 16 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/nodejspath.png) 17 | - 查看nodejs版本;node -v 18 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/nodejsversion.png) 19 | 20 | 2. 安装elasticsearch-head 21 | - [下载elasticsearch-head](https://github.com/mobz/elasticsearch-head) 22 | - 将elasticsearch-head放到与elasticsearch同层级文件夹下 23 | - 修改elasticsearch-head/Gruntfile.js 24 | ```xml 25 | connect: { 26 | server: { 27 | options: { 28 | port: 9100, 29 | base: '.', 30 | keepalive: true 31 | } 32 | } 33 | } 34 | ``` 35 | - 修改elasticsearch-6.2.2/config/elasticsearch.yml *添加配置信息 36 | ```xml 37 | http.cors.enabled: true 38 | http.cors.allow-origin: "*" 39 | ``` 40 | 41 | 3、启动elasticsearch-head 42 | ```java 43 | Microsoft Windows [版本 6.1.7601] 44 | 版权所有 (c) 2009 Microsoft Corporation。保留所有权利。 45 | 46 | C:\Users\user>node -v 47 | v10.16.0 48 | 49 | C:\Users\user>D: 50 | 51 | D:\>cd D:\Program Files\elasticsearch\head 52 | 53 | D:\Program Files\elasticsearch\head>npm run start 54 | 55 | > elasticsearch-head@0.0.0 start D:\Program Files\elasticsearch\head 56 | > grunt server 57 | 58 | Running "connect:server" (connect) task 59 | Waiting forever... 60 | Started connect web server on http://localhost:9100 61 | 62 | ``` 63 | **运行结果** 64 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/eshead.png) 65 | 66 | ------------ 67 | -------------------------------------------------------------------------------- /docs/_media/onlinebook.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Book 17 | Book 18 | 在线阅读 19 | 在线阅读 20 | 21 | 23 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-01-netty案例,netty4.1基础入门篇一《嗨!NettyServer》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 凡是新知识都需要有个入门的案例,一个简单的输入输出就能解除你当前遇到的所有疑惑。不要总想着先学理论后学实战。【X东方还135学理论,246学实战,800个床位不锈钢】,本案例专题主要介绍netty4.1的使用。 3 | 4 | ## 开发环境 5 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 6 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 7 | 3. telnet 测试【可以现在你的win7机器上测试这个命令,用于链接到服务端的测试命令】 8 | 9 | ## 代码示例 10 | ```java 11 | itstack-demo-netty-1-01 12 | └── src 13 | ├── main 14 | │ └── java 15 | │ └── org.itstack.demo.netty.server 16 | │ ├── MyChannelInitializer.java 17 | │ └── NettyServer.java 18 | └── test 19 | └── java 20 | └── org.itstack.demo.netty.test 21 | └── ApiTest.java 22 | ``` 23 | 24 | >NettyServer.java 25 | 26 | ```java 27 | package org.itstack.demo.netty.server; 28 | 29 | import io.netty.bootstrap.ServerBootstrap; 30 | import io.netty.channel.ChannelFuture; 31 | import io.netty.channel.ChannelOption; 32 | import io.netty.channel.EventLoopGroup; 33 | import io.netty.channel.nio.NioEventLoopGroup; 34 | import io.netty.channel.socket.nio.NioServerSocketChannel; 35 | 36 | /** 37 | * 公众号:bugstack虫洞栈 {获取学习源码} 38 | * 博 客:http://itstack.org 39 | * 论 坛:http://bugstack.cn 40 | * Create by fuzhengwei on 2019/7/20 41 | * 一个简单的Netty服务端示例 42 | */ 43 | public class NettyServer { 44 | 45 | public static void main(String[] args) { 46 | new NettyServer().bing(7397); 47 | } 48 | 49 | private void bing(int port) { 50 | //配置服务端NIO线程组 51 | EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); 52 | EventLoopGroup childGroup = new NioEventLoopGroup(); 53 | try { 54 | ServerBootstrap b = new ServerBootstrap(); 55 | b.group(parentGroup, childGroup) 56 | .channel(NioServerSocketChannel.class) //非阻塞模式 57 | .option(ChannelOption.SO_BACKLOG, 128) 58 | .childHandler(new MyChannelInitializer()); 59 | ChannelFuture f = b.bind(port).sync(); 60 | f.channel().closeFuture().sync(); 61 | } catch (InterruptedException e) { 62 | e.printStackTrace(); 63 | } finally { 64 | childGroup.shutdownGracefully(); 65 | parentGroup.shutdownGracefully(); 66 | } 67 | 68 | } 69 | 70 | } 71 | 72 | ``` 73 | ## 运行演示 74 | 1、启动server服务端 75 | 2、连接server服务端 76 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty-01-2.jpg) -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-10-netty案例,netty4.1基础入门篇七《嗨!NettyClient》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 在前六章的案例中使用socket模拟器链接我们的NettyServer,进行通信测试。本章节我们写一个helloworld版的NettyClient客户端,与我们的socket模拟器进行通信。在netty中客户端与服务端的写法基本类似,注意一些细节即可,这也是netty的强大之处,它把nio流与sokcet封装的相当简单易用。 3 | ## 开发环境 4 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 5 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 6 | 3. telnet 测试【可以现在你的win7机器上测试这个命令,用于链接到服务端的测试命令】 7 | ## 代码示例 8 | ```java 9 | itstack-demo-netty-1-07 10 | └── src 11 | ├── main 12 | │ └── java 13 | │ └── org.itstack.demo.netty.client 14 | │ ├── MyChannelInitializer.java 15 | │ └── NettyClient.java 16 | └── test 17 | └── java 18 | └── org.itstack.demo.netty.test 19 | └── ApiTest.java 20 | ``` 21 | >MyChannelInitializer.java 22 | 23 | ```java 24 | /** 25 | * 虫洞栈:https://bugstack.cn 26 | * 公众号:bugstack虫洞栈 {获取学习源码} 27 | * Create by fuzhengwei on 2019 28 | */ 29 | public class MyChannelInitializer extends ChannelInitializer { 30 | 31 | @Override 32 | protected void initChannel(SocketChannel channel) throws Exception { 33 | System.out.println("链接报告开始"); 34 | System.out.println("链接报告信息:本客户端链接到服务端。channelId:" + channel.id()); 35 | System.out.println("链接报告完毕"); 36 | } 37 | 38 | } 39 | ``` 40 | >NettyClient.java 41 | 42 | ```java 43 | /** 44 | * 虫洞栈:https://bugstack.cn 45 | * 公众号:bugstack虫洞栈 {获取学习源码} 46 | * Create by fuzhengwei on 2019 47 | */ 48 | public class NettyClient { 49 | 50 | public static void main(String[] args) { 51 | new NettyClient().connect("127.0.0.1", 7397); 52 | } 53 | 54 | private void connect(String inetHost, int inetPort) { 55 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 56 | try { 57 | Bootstrap b = new Bootstrap(); 58 | b.group(workerGroup); 59 | b.channel(NioSocketChannel.class); 60 | b.option(ChannelOption.AUTO_READ, true); 61 | b.handler(new MyChannelInitializer()); 62 | ChannelFuture f = b.connect(inetHost, inetPort).sync(); 63 | System.out.println("itstack-demo-netty client start done. {关注公众号:bugstack虫洞栈,获取源码}"); 64 | f.channel().closeFuture().sync(); 65 | } catch (InterruptedException e) { 66 | e.printStackTrace(); 67 | } finally { 68 | workerGroup.shutdownGracefully(); 69 | } 70 | } 71 | 72 | } 73 | ``` 74 | ## 测试结果 75 | >启动模拟器NetAssist 设置TCP Server 76 | 77 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty-1-07-1.png) 78 | 79 | >启动客户端NettyClient 80 | 81 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty-1-07-2.png) 82 | 83 | >执行结果 84 | 85 | ```java 86 | 链接报告开始 87 | 链接报告信息:本客户端链接到服务端。channelId:ea1df0b3 88 | 链接报告完毕 89 | itstack-demo-netty client start done. {关注公众号:bugstack虫洞栈,获取源码} 90 | 91 | Process finished with exit code -1 92 | ``` -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # 《虫洞 · 技术栈》 2 | 3 | [![stars](https://badgen.net/github/stars/MyGitBooks/itstack.github.io?icon=github&color=4ab8a1)](https://github.com/MyGitBooks/itstack.github.io) [![forks](https://badgen.net/github/forks/MyGitBooks/itstack.github.io?icon=github&color=4ab8a1)](https://github.com/MyGitBooks/itstack.github.io) [](https://itstack.org/_media/qrcode.png?x-oss-process=style/may) 4 | 5 | > 本文档是作者小傅哥多年从事一线互联网```Java```开发的学习历程技术汇总,旨在为大家提供一个较清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本文能为您提供帮助,请给予支持(关注、点赞、分享)! 6 | 7 | > 为 Java 高级编程以及技能拓展打造的一个博客站点 [:memo: bugstack虫洞栈](https://bugstack.cn) 8 | 9 | **如何支持:** 10 | - 关注公众号 [bugstack虫洞栈](https://itstack.org/_media/qrcode.png?x-oss-process=style/may) 11 | - 点击右上角Star :star: 给予关注 12 | - 分享给您身边更多的小伙伴 13 | 14 | > **作者:** 小傅哥,Java Developer,[CSDN 博客专家](https://bugstack.blog.csdn.net) 15 | 16 | ## 技术栈目录 17 | 18 | * [`Java基础编程`](/notes/itstack-demo-any.md) 19 | * [`用Java实现jvm虚拟机`](/notes/itstack-demo-jvm.md) 20 | * [`Spring系列源码解读`](/notes/itstack-demo-code.md) 21 | * [`Netty4.x专题`](/notes/itstack-demo-netty.md) 22 | * [`DDD领域驱动设计`](/notes/itstack-demo-ddd.md) 23 | * [`中间件开发`](/notes/itstack-demo-middleware.md) 24 | * [`JavaAgent全链路监控`](/notes/itstack-demo-agent.md) 25 | * [`架构框架搭建`](/notes/itstack-demo-frame.md) 26 | 27 | ## 转载分享 28 | 29 | 建立本开源项目的初衷是基于个人学习与工作中对 Java 相关技术栈的总结记录,在这里也希望能帮助一些在学习 Java 过程中遇到问题的小伙伴,如果您需要转载本仓库的一些文章到自己的博客,请按照以下格式注明出处,谢谢合作。 30 | 31 | ``` 32 | 作者:小傅哥 33 | 链接:https://bugstack.cn 34 | 来源:bugstack虫洞栈 35 | ``` 36 | 37 | ## 与我联系 38 | 39 | - **加群交流** 40 | 本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!由于微信群人满 100 之后无法加入,请扫描下方二维码先添加作者 “小傅哥” 微信,备注:加群。 41 | 42 | 43 | - **公众号** 44 | 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、DDD专题案例、源码分析等。 45 | 46 | 47 | ## 参与贡献 48 | 49 | 1. 如果您对本项目有任何建议或发现文中内容有误的,欢迎提交 issues 进行指正。 50 | 2. 对于文中我没有涉及到知识点,欢迎提交 PR。 51 | 52 | ## 致谢 53 | 54 | 感谢以下人员对本仓库做出的贡献,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与我联系。 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 虫洞 · 技术栈 | 沉淀、分享、成长,让自己和他人都能有所收获 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 |
23 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-jvm/2019-05-01-用Java实现JVM第一章《命令行工具》.md: -------------------------------------------------------------------------------- 1 | ## 背景描述 2 | 为了更好的学习jvm阅读过《Java虚拟机规范》、《自己动手写Java虚拟机》,尤其是《自动动手写java虚拟机》可以更加清晰的看到全貌。对于程序开发者来说学习一个新东西最好事必躬亲,亲力亲为的做出一些demo,只有输出了结果心里才踏实。 3 | 4 | ## 案例简述 5 | 本章节主要是通过编写java代码,从main方法入口进行获取指令。例如;-version 6 | 7 | ## 环境准备 8 | 1. jdk 1.8.0 9 | 2. IntelliJ IDEA Community Edition 2018.3.1 x64 10 | 11 | ## 配置信息 12 | 1. 调试配置 13 | 1. 配置位置:Run/Debug Configurations -> program arguments 14 | 2. 配置内容:-version 15 | 16 | ## 代码示例 17 | ```java 18 | itstack-demo-jvm-01 19 | ├── pom.xml 20 | └── src 21 | └── main 22 | │ └── java 23 | │ └── org.itstack.demo.jvm 24 | │ ├── Cmd.java 25 | │ └── Main.java 26 | └── test 27 | └── java 28 | └── org.itstack.demo.test 29 | └── HelloWorld.java 30 | 31 | ``` 32 | >pom.xml 33 | 34 | ```xml 35 | 36 | 37 | com.beust 38 | jcommander 39 | 1.72 40 | 41 | ``` 42 | >Cmd.java 43 | 44 | ```java 45 | package org.itstack.demo.jvm; 46 | 47 | import com.beust.jcommander.JCommander; 48 | import com.beust.jcommander.Parameter; 49 | 50 | import java.util.List; 51 | 52 | /** 53 | * http://www.itstack.org 54 | * create by fuzhengwei on 2019/4/24 55 | */ 56 | public class Cmd { 57 | 58 | @Parameter(names = {"-?", "-help"}, description = "print help message", order = 3, help = true) 59 | boolean helpFlag = false; 60 | 61 | @Parameter(names = "-version", description = "print version and exit", order = 2) 62 | boolean versionFlag = false; 63 | 64 | @Parameter(names = {"-cp", "-classpath"}, description = "classpath", order = 1) 65 | String classpath; 66 | 67 | @Parameter(description = "main class and args") 68 | List mainClassAndArgs; 69 | 70 | boolean ok; 71 | 72 | String getMainClass() { 73 | return mainClassAndArgs != null && !mainClassAndArgs.isEmpty() 74 | ? mainClassAndArgs.get(0) 75 | : null; 76 | } 77 | 78 | List getAppArgs() { 79 | return mainClassAndArgs != null && mainClassAndArgs.size() > 1 80 | ? mainClassAndArgs.subList(1, mainClassAndArgs.size()) 81 | : null; 82 | } 83 | 84 | static Cmd parse(String[] argv) { 85 | Cmd args = new Cmd(); 86 | JCommander cmd = JCommander.newBuilder().addObject(args).build(); 87 | cmd.parse(argv); 88 | args.ok = true; 89 | return args; 90 | } 91 | 92 | } 93 | ``` 94 | 95 | >Main.java 96 | 97 | ```java 98 | package org.itstack.demo.jvm; 99 | 100 | /** 101 | * http://www.itstack.org 102 | * create by fuzhengwei on 2019/4/24 103 | * program arguments:-version 104 | */ 105 | public class Main { 106 | 107 | public static void main(String[] args) { 108 | Cmd cmd = Cmd.parse(args); 109 | if (!cmd.ok || cmd.helpFlag) { 110 | System.out.println("Usage:
[-options] class [args...]"); 111 | return; 112 | } 113 | if (cmd.versionFlag) { 114 | System.out.println("java version \"1.8.0\""); 115 | return; 116 | } 117 | startJVM(cmd); 118 | } 119 | 120 | private static void startJVM(Cmd cmd) { 121 | System.out.printf("classpath:%s class:%s args:%s\n", cmd.classpath, cmd.getMainClass(), cmd.getAppArgs()); 122 | } 123 | 124 | } 125 | ``` 126 | ## 测试结果 127 | ```java 128 | java version "1.8.0" 129 | ``` -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-05-netty案例,netty4.1基础入门篇二《NettyServer接收数据》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 繁事都需要一个简单的入门的点,尤其学习程序员行业的知识最快的方式是先运行期helloworld,往往这样一个简单能运行的例子,就能解除你当前遇到的所有疑惑。切记,对于一个初学者,不建议上来就研究理论,实操往往更重要。本章节介绍使用netty端写一个能接收数据的socketServer服务端,通过实现通道适配器ChannelInboundHandlerAdapter.channelRead获取并并解析接收数据。 3 | 4 | ## 开发环境 5 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 6 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 7 | 3. telnet 测试【可以现在你的win7机器上测试这个命令,用于链接到服务端的测试命令】 8 | 9 | ## 代码示例 10 | ```java 11 | itstack-demo-netty-1-02 12 | └── src 13 | ├── main 14 | │ └── java 15 | │ └── org.itstack.demo.netty.server 16 | │ ├── MyChannelInitializer.java 17 | │ ├── MyServerHandler.java 18 | │ └── NettyServer.java 19 | └── test 20 | └── java 21 | └── org.itstack.demo.netty.test 22 | └── ApiTest.java 23 | ``` 24 | >MyChannelInitializer.java 25 | 26 | ```java 27 | /** 28 | * 虫洞栈:https://bugstack.cn 29 | * 公众号:bugstack虫洞栈 {获取学习源码} 30 | * Create by fuzhengwei on 2019 31 | */ 32 | public class MyChannelInitializer extends ChannelInitializer { 33 | 34 | @Override 35 | protected void initChannel(SocketChannel channel) { 36 | 37 | System.out.println("链接报告开始"); 38 | System.out.println("链接报告信息:有一客户端链接到本服务端"); 39 | System.out.println("链接报告IP:" + channel.localAddress().getHostString()); 40 | System.out.println("链接报告Port:" + channel.localAddress().getPort()); 41 | System.out.println("链接报告完毕"); 42 | 43 | //在管道中添加我们自己的接收数据实现方法 44 | channel.pipeline().addLast(new MyServerHandler()); 45 | 46 | } 47 | 48 | } 49 | ``` 50 | 51 | >MyServerHandler.java 52 | 53 | ```java 54 | /** 55 | * 虫洞栈:https://bugstack.cn 56 | * 公众号:bugstack虫洞栈 {获取学习源码} 57 | * Create by fuzhengwei on 2019 58 | */ 59 | public class MyServerHandler extends ChannelInboundHandlerAdapter { 60 | 61 | @Override 62 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 63 | //接收msg消息 64 | ByteBuf buf = (ByteBuf) msg; 65 | byte[] msgByte = new byte[buf.readableBytes()]; 66 | buf.readBytes(msgByte); 67 | System.out.print(new Date() + "接收到消息:"); 68 | System.out.println(new String(msgByte, Charset.forName("GBK"))); 69 | } 70 | 71 | } 72 | ``` 73 | 74 | >NettyServer.java 75 | 76 | ```java 77 | /** 78 | * 虫洞栈:https://bugstack.cn 79 | * 公众号:bugstack虫洞栈 {获取学习源码} 80 | * Create by fuzhengwei on 2019 81 | */ 82 | public class NettyServer { 83 | 84 | public static void main(String[] args) { 85 | new NettyServer().bing(7397); 86 | } 87 | 88 | private void bing(int port) { 89 | //配置服务端NIO线程组 90 | EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); 91 | EventLoopGroup childGroup = new NioEventLoopGroup(); 92 | try { 93 | ServerBootstrap b = new ServerBootstrap(); 94 | b.group(parentGroup, childGroup) 95 | .channel(NioServerSocketChannel.class) //非阻塞模式 96 | .option(ChannelOption.SO_BACKLOG, 128) 97 | .childHandler(new MyChannelInitializer()); 98 | ChannelFuture f = b.bind(port).sync(); 99 | System.out.println("itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}"); 100 | f.channel().closeFuture().sync(); 101 | } catch (InterruptedException e) { 102 | e.printStackTrace(); 103 | } finally { 104 | childGroup.shutdownGracefully(); 105 | parentGroup.shutdownGracefully(); 106 | } 107 | 108 | } 109 | 110 | } 111 | ``` 112 | 113 | ## 测试结果 114 | >启动服务端NettyServer 115 | 116 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/nettyserver02.png) 117 | 118 | >启动模拟器NetAssist 119 | 120 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/1.png) 121 | 122 | >执行结果 123 | 124 | ```java 125 | itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码} 126 | 链接报告开始 127 | 链接报告信息:有一客户端链接到本服务端 128 | 链接报告IP:127.0.0.1 129 | 链接报告Port:7397 130 | 链接报告完毕 131 | Mon Aug 05 14:15:32 CST 2019接收到消息:你好,服务端。我是公众号,关注我获取源码。“点发送数据,不需要回车换行” 132 | Mon Aug 05 14:15:34 CST 2019接收到消息:你好,服务端。我是公众号,关注我获取源码。“点发送数据,不需要回车换行” 133 | Mon Aug 05 14:15:34 CST 2019接收到消息:你好,服务端。我是公众号,关注我获取源码。“点发送数据,不需要回车换行” 134 | Mon Aug 05 14:15:35 CST 2019接收到消息:你好,服务端。我是公众号,关注我获取源码。“点发送数据,不需要回车换行” 135 | 136 | Process finished with exit code -1 137 | ``` -------------------------------------------------------------------------------- /docs/notes/itstack-demo-agent/2019-07-10-基于JavaAgent的全链路监控一《嗨!JavaAgent》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | >全链路监控又名分布式监控系统全链路追踪,目前市面的全链路监控系统基本都是参考Google的[Dapper](https://mp.weixin.qq.com/s?__biz=MzIxMDAwMDAxMw==&mid=2650724660&idx=1&sn=0f33d3386c7652bf536cb071e9f79921&chksm=8f6138d6b816b1c0d92fb75257da4fc8ddefb7ec53dfcad98dffec87740df455cc75aa7b4a5c&token=144816615&lang=zh_CN#rd)(大规模分布式系统的跟踪系统)来做的。例如;蚂蚁金服分布式链路跟踪组件SOFATracer、Gokit微服务-服务链路追踪 、Pinpoint、Prometheus(普罗米修斯)等等。 3 | 4 | ## 章节列表 5 | - [基于JavaAgent的全链路监控一《嗨!JavaAgent》](https://bugstack.cn/itstack-demo-agent/2019/07/10/%E5%9F%BA%E4%BA%8EJavaAgent%E7%9A%84%E5%85%A8%E9%93%BE%E8%B7%AF%E7%9B%91%E6%8E%A7%E4%B8%80-%E5%97%A8-JavaAgent.html) 6 | - [基于JavaAgent的全链路监控二《通过字节码增加监控执行耗时》](https://bugstack.cn/itstack-demo-agent/2019/07/11/%E5%9F%BA%E4%BA%8EJavaAgent%E7%9A%84%E5%85%A8%E9%93%BE%E8%B7%AF%E7%9B%91%E6%8E%A7%E4%BA%8C-%E9%80%9A%E8%BF%87%E5%AD%97%E8%8A%82%E7%A0%81%E5%A2%9E%E5%8A%A0%E7%9B%91%E6%8E%A7%E6%89%A7%E8%A1%8C%E8%80%97%E6%97%B6.html) 7 | - [基于JavaAgent的全链路监控三《ByteBuddy操作监控方法字节码》](https://bugstack.cn/itstack-demo-agent/2019/07/12/%E5%9F%BA%E4%BA%8EJavaAgent%E7%9A%84%E5%85%A8%E9%93%BE%E8%B7%AF%E7%9B%91%E6%8E%A7%E4%B8%89-ByteBuddy%E6%93%8D%E4%BD%9C%E7%9B%91%E6%8E%A7%E6%96%B9%E6%B3%95%E5%AD%97%E8%8A%82%E7%A0%81.html) 8 | - [基于JavaAgent的全链路监控四《JVM内存与GC信息》](https://bugstack.cn/itstack-demo-agent/2019/07/13/%E5%9F%BA%E4%BA%8EJavaAgent%E7%9A%84%E5%85%A8%E9%93%BE%E8%B7%AF%E7%9B%91%E6%8E%A7%E5%9B%9B-JVM%E5%86%85%E5%AD%98%E4%B8%8EGC%E4%BF%A1%E6%81%AF.html) 9 | - [基于JavaAgent的全链路监控五《ThreadLocal链路追踪》](https://bugstack.cn/itstack-demo-agent/2019/07/14/%E5%9F%BA%E4%BA%8EJavaAgent%E7%9A%84%E5%85%A8%E9%93%BE%E8%B7%AF%E7%9B%91%E6%8E%A7%E4%BA%94-ThreadLocal%E9%93%BE%E8%B7%AF%E8%BF%BD%E8%B8%AA.html) 10 | - [基于JavaAgent的全链路监控六《开发应用级监控》](https://bugstack.cn/itstack-demo-agent/2019/07/15/%E5%9F%BA%E4%BA%8EJavaAgent%E7%9A%84%E5%85%A8%E9%93%BE%E8%B7%AF%E7%9B%91%E6%8E%A7%E5%85%AD-%E5%BC%80%E5%8F%91%E5%BA%94%E7%94%A8%E7%BA%A7%E7%9B%91%E6%8E%A7.html) 11 | 12 | ## 案例简述 13 | JavaAgent是在JDK5之后提供的新特性,也可以叫java代理。开发者通过这种机制(Instrumentation)可以在加载class文件之前修改方法的字节码(此时字节码尚未加入JVM),动态更改类方法实现AOP,提供监控服务如;方法调用时长、可用率、内存等。本章节初步怎么让java代码执行时可以进入我们的agent方法。 14 | 15 | ## 环境准备 16 | 1. IntelliJ IDEA Community Edition 17 | 2. jdk1.8.0_45 64位 18 | 19 | ## 配置信息 (路径相关修改为自己的) 20 | 1. 配置位置:Run/Debug Configurations -> VM options 21 | 2. 配置内容:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-01\target\itstack-demo-agent-01-1.0.0-SNAPSHOT.jar=testargs 22 | 23 | ## 代码示例 24 | ```java 25 | itstack-demo-agent-01 26 | ├── pom.xml 27 | └── src 28 | ├── main 29 | │ ├── java 30 | │ │ └── org.itstack.demo.agent 31 | │ │ └── MyAgent.java 32 | │ └── resources 33 | │ └── META-INF 34 | │ └── MANIFEST.MF 35 | └── test 36 | └── java 37 | └── org.itstack.demo.test 38 | └── ApiTest.java 39 | ``` 40 | >pom.xml 41 | 42 | ```xml 43 | 44 | 45 | -Xms512m -Xmx512m 46 | false 47 | true 48 | utf-8 49 | true 50 | 51 | src/main/resources/META-INF/MANIFEST.MF 52 | 53 | ``` 54 | 55 | >MyAgent.java 56 | 57 | ```java 58 | /** 59 | * 博客:http://itstack.org 60 | * 论坛:http://bugstack.cn 61 | * 公众号:bugstack虫洞栈 {获取学习源码} 62 | */ 63 | public class MyAgent { 64 | 65 | //JVM 首先尝试在代理类上调用以下方法 66 | public static void premain(String agentArgs, Instrumentation inst) { 67 | System.out.println("嗨!JavaAgent " + agentArgs); 68 | } 69 | 70 | //如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法 71 | public static void premain(String agentArgs) { 72 | } 73 | 74 | } 75 | ``` 76 | >MANIFEST.MF 77 | 78 | ``` 79 | Manifest-Version: 1.0 80 | Premain-Class: org.itstack.demo.agent.MyAgent 81 | Can-Redefine-Classes: true 82 | 83 | ``` 84 | >ApiTest.java 85 | 86 | ```java 87 | /** 88 | * 89 | * http://bigbully.github.io/Dapper-translation/ 90 | * 91 | * 配置监控 92 | * VM options: 93 | * -javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-01\target\itstack-demo-agent-01-1.0.0-SNAPSHOT.jar=testargs 94 | * 95 | * 博客:http://itstack.org 96 | * 论坛:http://bugstack.cn 97 | * 公众号:bugstack虫洞栈 {获取学习源码} 98 | * create by fuzhengwei on 2019 99 | */ 100 | public class ApiTest { 101 | 102 | public static void main(String[] args) { 103 | System.out.println("hi itstack-demo-agent-01"); 104 | } 105 | 106 | } 107 | ``` 108 | 109 | ## 测试结果 110 | 111 | ```java 112 | this is my agent:testargs 113 | 嗨!JavaAgent 114 | 115 | Process finished with exit code 0 116 | ``` 117 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-26-netty案例,netty4.1中级拓展篇十一《Netty基于ChunkedStream数据流切块传输》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 在Netty这种异步NIO框架的结构下,服务端与客户端通信过程中,高效、频繁、大量的写入大块数据时,因网络传输饱和的可能性就会造成数据处理拥堵、GC频繁、用户掉线的可能性。那么由于写操作是非阻塞的,所以即使没有写出所有的数据,写操作也会在完成时返回并通知ChannelFuture。当这种情况发生时,如果仍然不停地写入,就有内存耗尽的风险。所以在写大块数据时,需要对大块数据进行切割发送处理。 3 | 4 | >https://netty.io/4.0/api/io/netty/handler/stream/ChunkedStream.html 5 | >ChunkedInput 的实现 6 | ChunkedFile 从文件中逐块获取数据,当你的平台不支持零拷贝或者你需要转换数据时使用 7 | ChunkedNioFile 和ChunkedFile 类似,只是它使用了FileChannel 8 | ChunkedStream 从InputStream 中逐块传输内容 9 | ChunkedNioStream 从ReadableByteChannel 中逐块传输内容 10 | 11 | ## 开发环境 12 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 13 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 14 | 3. NetAssist 网络调试助手[获取:关注公众号:bugstack虫洞栈 | 回复;NetAssist+邮箱] 15 | 16 | ## 代码示例 17 | ```java 18 | itstack-demo-netty-2-11 19 | └── src 20 | ├── main 21 | │ └── java 22 | │ └── org.itstack.demo.netty.server 23 | │ ├── MyChannelInitializer.java 24 | │ ├── MyServerChunkHandler.java 25 | │ ├── MyServerHandler.java 26 | │ └── NettyServer.java 27 | └── test 28 | └── java 29 | └── org.itstack.demo.test 30 | └── ApiTest.java 31 | ``` 32 | 33 | ** 重点代码块讲解,完整代码,关注公众号:bugstack虫洞栈 | 回复Netty源码获取 ** 34 | 35 | >MyChannelInitializer.java | 添加流量分块功能 36 | 37 | ```java 38 | /** 39 | * 虫洞栈:https://bugstack.cn 40 | * 公众号:bugstack虫洞栈 {获取学习源码} 41 | * Create by fuzhengwei on 2019 42 | */ 43 | public class MyChannelInitializer extends ChannelInitializer { 44 | 45 | @Override 46 | protected void initChannel(SocketChannel channel) { 47 | // 基于换行符号 48 | channel.pipeline().addLast(new LineBasedFrameDecoder(1024)); 49 | // 流量分块 50 | channel.pipeline().addLast(new ChunkedWriteHandler()); 51 | channel.pipeline().addLast(new MyServerChunkHandler()); 52 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 53 | channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK"))); 54 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 55 | channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK"))); 56 | // 在管道中添加我们自己的接收数据实现方法 57 | channel.pipeline().addLast(new MyServerHandler()); 58 | } 59 | 60 | } 61 | ``` 62 | 63 | >MyServerChunkHandler.java | 流量分块实现ChunkedStream(in, 10) 64 | 65 | ```java 66 | /** 67 | * 虫洞栈:https://bugstack.cn 68 | * 公众号:bugstack虫洞栈 | 欢迎关注并获取专题&源码 69 | * Create by fuzhengwei on 2019 70 | */ 71 | public class MyServerChunkHandler extends ChannelOutboundHandlerAdapter { 72 | 73 | @Override 74 | public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { 75 | //内容验证 76 | if (!(msg instanceof ByteBuf)) { 77 | super.write(ctx, msg, promise); 78 | return; 79 | } 80 | //获取Byte 81 | ByteBuf buf = (ByteBuf) msg; 82 | byte[] data = this.getData(buf); 83 | //写入流中 84 | ByteInputStream in = new ByteInputStream(); 85 | in.setBuf(data); 86 | //消息分块;10个字节,测试过程中可以调整 87 | ChunkedStream stream = new ChunkedStream(in, 10); 88 | //管道消息传输承诺 89 | ChannelProgressivePromise progressivePromise = ctx.channel().newProgressivePromise(); 90 | progressivePromise.addListener(new ChannelProgressiveFutureListener() { 91 | @Override 92 | public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) throws Exception { 93 | } 94 | @Override 95 | public void operationComplete(ChannelProgressiveFuture future) throws Exception { 96 | if (future.isSuccess()) { 97 | System.out.println("消息发送成功 success"); 98 | promise.setSuccess(); 99 | } else { 100 | System.out.println("消息发送失败 failure:" + future.cause()); 101 | promise.setFailure(future.cause()); 102 | } 103 | } 104 | }); 105 | ReferenceCountUtil.release(msg); 106 | ctx.write(stream, progressivePromise); 107 | } 108 | 109 | //获取Byte 110 | private byte[] getData(ByteBuf buf) { 111 | if (buf.hasArray()) { 112 | return buf.array().clone(); 113 | } 114 | byte[] data = new byte[buf.readableBytes() - 1]; 115 | buf.readBytes(data); 116 | return data; 117 | } 118 | 119 | } 120 | ``` 121 | 122 | ## 测试结果 123 | 124 | >启动服务端NettyServer 125 | 126 | ```java 127 | itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码} 128 | 链接报告开始 129 | 链接报告信息:有一客户端链接到本服务端 130 | 链接报告IP:127.0.0.1 131 | 链接报告Port:7397 132 | 链接报告完毕 133 | 消息发送成功 success 134 | 2019-09-15 16:36:04 接收到消息:hi 微信公众号:bugstack虫洞栈 | 欢迎关注并获取专题文章和源码 135 | 消息发送成功 success 136 | 2019-09-15 16:36:04 接收到消息: 137 | 消息发送成功 success 138 | 139 | Process finished with exit code -1 140 | 141 | ``` 142 | 143 | >启动NetAssist网络调试助手 | 发送测试消息[结尾加换行] 144 | 145 | ```java 146 | hi 微信公众号:bugstack虫洞栈 | 欢迎关注并获取专题文章和源码 147 | ``` 148 | 149 | ![微信公众号:bugstack虫洞栈](https://fuzhengwei.github.io/assets/images/pic-content/2019/09/netty-2-11-1.png) -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-06-netty案例,netty4.1基础入门篇三《NettyServer字符串解码器》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 在实际开发中,server端接收数据后我们希望他是一个字符串或者是一个对象类型,而不是字节码,那么; 3 | 1. 在netty中是否可以自动的把接收的Bytebuf数据转String,不需要我手动处理? 4 | 答;有,可以在管道中添加一个StringDecoder。 5 | 2. 在网络传输过程中有半包粘包的问题,netty能解决吗? 6 | 答:能,netty提供了很丰富的解码器,在正确合理的使用下就能解决半包粘包问题。 7 | 3. 常用的String字符串下有什么样的解码器呢? 8 | 答:不仅在String下有处理半包粘包的解码器在处理其他的数据格式也有,其中谷歌的protobuf数据格式就是其中一个。对于String的有一下常用的三种: 9 | 1. LineBasedFrameDecoder 基于换行 10 | 2. DelimiterBasedFrameDecoder 基于指定字符串 11 | 3. FixedLengthFrameDecoder 基于字符串长度 12 | ## 开发环境 13 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 14 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 15 | 3. telnet 测试【可以现在你的win7机器上测试这个命令,用于链接到服务端的测试命令】 16 | ## 代码示例 17 | ```java 18 | itstack-demo-netty-1-03 19 | └── src 20 | ├── main 21 | │ └── java 22 | │ └── org.itstack.demo.netty.server 23 | │ ├── MyChannelInitializer.java 24 | │ ├── MyServerHandler.java 25 | │ └── NettyServer.java 26 | └── test 27 | └── java 28 | └── org.itstack.demo.netty.test 29 | └── ApiTest.java 30 | ``` 31 | >MyChannelInitializer.java 32 | 33 | ```java 34 | /** 35 | * 虫洞栈:https://bugstack.cn 36 | * 公众号:bugstack虫洞栈 {获取学习源码} 37 | * Create by fuzhengwei on 2019 38 | */ 39 | public class MyChannelInitializer extends ChannelInitializer { 40 | 41 | @Override 42 | protected void initChannel(SocketChannel channel) { 43 | /* 解码器 */ 44 | // 基于换行符号 45 | channel.pipeline().addLast(new LineBasedFrameDecoder(1024)); 46 | // 基于指定字符串【换行符,这样功能等同于LineBasedFrameDecoder】 47 | // e.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, false, Delimiters.lineDelimiter())); 48 | // 基于最大长度 49 | // e.pipeline().addLast(new FixedLengthFrameDecoder(4)); 50 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 51 | channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK"))); 52 | //在管道中添加我们自己的接收数据实现方法 53 | channel.pipeline().addLast(new MyServerHandler()); 54 | } 55 | 56 | } 57 | ``` 58 | >MyServerHandler.java 59 | 60 | ```java 61 | /** 62 | * 虫洞栈:https://bugstack.cn 63 | * 公众号:bugstack虫洞栈 {获取学习源码} 64 | * Create by fuzhengwei on 2019 65 | */ 66 | public class MyServerHandler extends ChannelInboundHandlerAdapter { 67 | 68 | @Override 69 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 70 | SocketChannel channel = (SocketChannel) ctx.channel(); 71 | System.out.println("链接报告开始 {公众号:bugstack虫洞栈 >获取学习源码}"); 72 | System.out.println("链接报告信息:有一客户端链接到本服务端"); 73 | System.out.println("链接报告IP:" + channel.localAddress().getHostString()); 74 | System.out.println("链接报告Port:" + channel.localAddress().getPort()); 75 | System.out.println("链接报告完毕"); 76 | } 77 | 78 | @Override 79 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 80 | //接收msg消息{与上一章节相比,此处已经不需要自己进行解码} 81 | System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg); 82 | } 83 | 84 | } 85 | ``` 86 | >NettyServer.java 87 | 88 | ```java 89 | /** 90 | * 虫洞栈:https://bugstack.cn 91 | * 公众号:bugstack虫洞栈 {获取学习源码} 92 | * Create by fuzhengwei on 2019 93 | */ 94 | public class NettyServer { 95 | 96 | public static void main(String[] args) { 97 | new NettyServer().bing(7397); 98 | } 99 | 100 | private void bing(int port) { 101 | //配置服务端NIO线程组 102 | EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); 103 | EventLoopGroup childGroup = new NioEventLoopGroup(); 104 | try { 105 | ServerBootstrap b = new ServerBootstrap(); 106 | b.group(parentGroup, childGroup) 107 | .channel(NioServerSocketChannel.class) //非阻塞模式 108 | .option(ChannelOption.SO_BACKLOG, 128) 109 | .childHandler(new MyChannelInitializer()); 110 | ChannelFuture f = b.bind(port).sync(); 111 | System.out.println("itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}"); 112 | f.channel().closeFuture().sync(); 113 | } catch (InterruptedException e) { 114 | e.printStackTrace(); 115 | } finally { 116 | childGroup.shutdownGracefully(); 117 | parentGroup.shutdownGracefully(); 118 | } 119 | 120 | } 121 | 122 | } 123 | ``` 124 | ## 测试结果 125 | >启动服务端NettyServer 126 | 127 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/nettyserver02.png) 128 | 129 | >启动模拟器NetAssist 130 | 131 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/nettyserver03.png) 132 | 133 | >执行结果 134 | 135 | ```java 136 | itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码} 137 | 链接报告开始 {公众号:bugstack虫洞栈 >获取学习源码} 138 | 链接报告信息:有一客户端链接到本服务端 139 | 链接报告IP: 127.0.0.1 140 | 链接报告Port:7397 141 | 链接报告完毕 142 | 2019-08-05 15:23:13 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 143 | 2019-08-05 15:23:15 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 144 | 2019-08-05 15:23:15 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 145 | 2019-08-05 15:23:16 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 146 | 2019-08-05 15:23:16 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 147 | 2019-08-05 15:23:17 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 148 | 149 | Process finished with exit code -1 150 | ``` -------------------------------------------------------------------------------- /docs/notes/itstack-demo-agent/2019-07-13-基于JavaAgent的全链路监控四《JVM内存与GC信息》.md: -------------------------------------------------------------------------------- 1 | ## 案例简述 2 | 除了监控java方法的执行耗时,我们还需要获取应用实例的jvm内存与gc信息,以实时把控我们的服务器性能是否在安全范围。监控jvm内存与gc信息是非常重要的,尤其是在大促以及微博火热爆点的时候,我们需要根据监控信息进行扩容,以保证系统稳定。 3 | 4 | ## 环境准备 5 | 1. IntelliJ IDEA Community Edition 6 | 2. jdk1.8.0_45 64位 7 | 8 | ## 配置信息(路径相关修改为自己的) 9 | 1. 配置位置:Run/Debug Configurations -> VM options 10 | 2. 配置内容:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-04\target\itstack-demo-agent-04-1.0.0-SNAPSHOT.jar=testargs 11 | 12 | ## 代码示例 13 | ```java 14 | itstack-demo-agent-04 15 | ├── pom.xml 16 | └── src 17 | ├── main 18 | │ ├── java 19 | │ │ └── org.itstack.demo.agent 20 | │ │ ├── JvmStack.java 21 | │ │ └── MyAgent.java 22 | │ └── resources 23 | │ └── META-INF 24 | │ └── MANIFEST.MF 25 | └── test 26 | └── java 27 | └── org.itstack.demo.test 28 | └── ApiTest.java 29 | ``` 30 | >JvmStack.java 31 | 32 | ```java 33 | /** 34 | * 博客:http://itstack.org 35 | * 论坛:http://bugstack.cn 36 | * 公众号:bugstack虫洞栈 {获取学习源码} 37 | * create by fuzhengwei on 2019 38 | */ 39 | class JvmStack { 40 | 41 | private static final long MB = 1048576L; 42 | 43 | static void printMemoryInfo() { 44 | MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); 45 | MemoryUsage headMemory = memory.getHeapMemoryUsage(); 46 | 47 | String info = String.format("\ninit: %s\t max: %s\t used: %s\t committed: %s\t use rate: %s\n", 48 | headMemory.getInit() / MB + "MB", 49 | headMemory.getMax() / MB + "MB", headMemory.getUsed() / MB + "MB", 50 | headMemory.getCommitted() / MB + "MB", 51 | headMemory.getUsed() * 100 / headMemory.getCommitted() + "%" 52 | 53 | ); 54 | 55 | System.out.print(info); 56 | 57 | MemoryUsage nonheadMemory = memory.getNonHeapMemoryUsage(); 58 | 59 | info = String.format("init: %s\t max: %s\t used: %s\t committed: %s\t use rate: %s\n", 60 | nonheadMemory.getInit() / MB + "MB", 61 | nonheadMemory.getMax() / MB + "MB", nonheadMemory.getUsed() / MB + "MB", 62 | nonheadMemory.getCommitted() / MB + "MB", 63 | nonheadMemory.getUsed() * 100 / nonheadMemory.getCommitted() + "%" 64 | 65 | ); 66 | System.out.println(info); 67 | 68 | } 69 | 70 | static void printGCInfo() { 71 | List garbages = ManagementFactory.getGarbageCollectorMXBeans(); 72 | for (GarbageCollectorMXBean garbage : garbages) { 73 | String info = String.format("name: %s\t count:%s\t took:%s\t pool name:%s", 74 | garbage.getName(), 75 | garbage.getCollectionCount(), 76 | garbage.getCollectionTime(), 77 | Arrays.deepToString(garbage.getMemoryPoolNames())); 78 | System.out.println(info); 79 | } 80 | } 81 | 82 | } 83 | ``` 84 | >MyAgent.java 85 | 86 | ```java 87 | /** 88 | * 博客:http://itstack.org 89 | * 论坛:http://bugstack.cn 90 | * 公众号:bugstack虫洞栈 {获取学习源码} 91 | * create by fuzhengwei on 2019 92 | */ 93 | public class MyAgent { 94 | 95 | public static void premain(String agentArgs, Instrumentation inst) { 96 | System.out.println("this is my agent:" + agentArgs); 97 | 98 | Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() { 99 | public void run() { 100 | JvmStack.printMemoryInfo(); 101 | JvmStack.printGCInfo(); 102 | System.out.println("==================================================================================================="); 103 | } 104 | }, 0, 5000, TimeUnit.MILLISECONDS); 105 | } 106 | 107 | } 108 | ``` 109 | >MANIFEST.MF 110 | 111 | ```其他语言 112 | Manifest-Version: 1.0 113 | Premain-Class: org.itstack.demo.agent.MyAgent 114 | Can-Redefine-Classes: true 115 | 116 | ``` 117 | >ApiTest.java 118 | 119 | ```java 120 | /** 121 | * 博客:http://itstack.org 122 | * 论坛:http://bugstack.cn 123 | * 公众号:bugstack虫洞栈 {获取学习源码} 124 | * create by fuzhengwei on 2019 125 | * -javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-04\target\itstack-demo-agent-04-1.0.0-SNAPSHOT.jar=testargs 126 | */ 127 | public class ApiTest { 128 | 129 | public static void main(String[] args) { 130 | while (true) { 131 | List list = new LinkedList<>(); 132 | list.add("嗨!JavaAgent"); 133 | list.add("嗨!JavaAgent"); 134 | list.add("嗨!JavaAgent"); 135 | } 136 | } 137 | 138 | } 139 | ``` 140 | 141 | ## 测试结果 142 | 143 | ```其他语言 144 | this is my agent:testargs 145 | 146 | init: 192MB max: 2708MB used: 5MB committed: 184MB use rate: 3% 147 | init: 2MB max: 0MB used: 5MB committed: 7MB use rate: 75% 148 | 149 | name: PS Scavenge count:1 took:2 pool name:[PS Eden Space, PS Survivor Space] 150 | name: PS MarkSweep count:0 took:0 pool name:[PS Eden Space, PS Survivor Space, PS Old Gen] 151 | =================================================================================================== 152 | 153 | init: 192MB max: 2708MB used: 249MB committed: 624MB use rate: 39% 154 | init: 2MB max: 0MB used: 6MB committed: 7MB use rate: 78% 155 | 156 | name: PS Scavenge count:32 took:98 pool name:[PS Eden Space, PS Survivor Space] 157 | name: PS MarkSweep count:0 took:0 pool name:[PS Eden Space, PS Survivor Space, PS Old Gen] 158 | =================================================================================================== 159 | 160 | Process finished with exit code -1 161 | ``` 162 | 163 | 164 | -------------------------------------------------------------------------------- /docs/assets/js/docsify-pagination.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e():"function"==typeof define&&define.amd?define(e):e()}(0,function(){"use strict";var t,c=(function(t,e){function n(t,e){return e.querySelector(t)}(e=t.exports=function(t,e){return n(t,e=e||document)}).all=function(t,e){return(e=e||document).querySelectorAll(t)},e.engine=function(t){if(!t.one)throw new Error(".one callback required");if(!t.all)throw new Error(".all callback required");return n=t.one,e.all=t.all,e}}(t={exports:{}},t.exports),t.exports);c.all,c.engine;try{var a=c}catch(t){a=c}var e=Element.prototype,r=e.matches||e.webkitMatchesSelector||e.mozMatchesSelector||e.msMatchesSelector||e.oMatchesSelector,s=function(t,e){if(!t||1!==t.nodeType)return!1;if(r)return r.call(t,e);for(var n=a.all(e,t.parentNode),i=0;i*{line-height:1;vertical-align:middle}.pagination-item-label svg{height:.8em;width:auto;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1px}.pagination-item--next{margin-left:auto;text-align:right}.pagination-item--next svg{margin-left:.5em}.pagination-item--previous svg{margin-right:.5em}.pagination-item-title{font-size:1.6em}.pagination-item-subtitle{text-transform:uppercase;opacity:.3}");var o=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},l=function(){function i(t,e){for(var n=0;n ul > li"),c("p",e)),this.hyperlink=m(t))}return l(n,[{key:"toJSON",value:function(){if(this.hyperlink)return{name:this.hyperlink.innerText,href:this.hyperlink.getAttribute("href"),chapterName:this.chapter&&this.chapter.innerText||""}}}]),n}();var x={container:function(){return'
'},inner:function(t,e){return[t.prev&&'\n \n ",t.next&&'\n \n "].filter(Boolean).join("")}};window.$docsify=window.$docsify||{},window.$docsify.plugins=[function(t,e){var n=u({},f,e.config.pagination||{});function i(){var t=c("."+d);t&&(t.innerHTML=x.inner(function(t,e){try{var n=t.route.path,i=h(c.all(".sidebar li a")).filter(function(t){return!s(t,".section-link")}),a=i.find(g(n)),r=h((p(a,"ul")||{}).children).filter(function(t){return"LI"===t.tagName.toUpperCase()}),o=e?i.findIndex(g(n)):r.findIndex(function(t){var e=m(t);return e&&g(n,e)}),l=e?i:r;return{prev:new v(l[o-1]).toJSON(),next:new v(l[o+1]).toJSON()}}catch(t){return{}}}(e,n.crossChapter),n))}t.afterEach(function(t){return t+x.container()}),t.doneEach(function(){return i()})}].concat(window.$docsify.plugins||[])}); -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty.md: -------------------------------------------------------------------------------- 1 | ## 文章目录 2 | 3 | - 基础入门篇 4 | * [`netty案例,netty4.1基础入门篇零《初入JavaIO之门BIO、NIO、AIO实战练习》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-07-30-netty案例,netty4.1基础入门篇零《初入JavaIO之门BIO、NIO、AIO实战练习》.md) 5 | * [`netty案例,netty4.1基础入门篇一《嗨!NettyServer》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-01-netty案例,netty4.1基础入门篇一《嗨!NettyServer》.md) 6 | * [`netty案例,netty4.1基础入门篇二《NettyServer接收数据》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-05-netty案例,netty4.1基础入门篇二《NettyServer接收数据》.md) 7 | * [`netty案例,netty4.1基础入门篇三《NettyServer字符串解码器》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-06-netty案例,netty4.1基础入门篇三《NettyServer字符串解码器》.md) 8 | * [`netty案例,netty4.1基础入门篇四《NettyServer收发数据》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-07-netty案例,netty4.1基础入门篇四《NettyServer收发数据》.md) 9 | * [`netty案例,netty4.1基础入门篇五《NettyServer字符串编码器》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-08-netty案例,netty4.1基础入门篇五《NettyServer字符串编码器》.md) 10 | * [`netty案例,netty4.1基础入门篇六《NettyServer群发消息》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-09-netty案例,netty4.1基础入门篇六《NettyServer群发消息》.md) 11 | * [`netty案例,netty4.1基础入门篇七《嗨!NettyClient》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-10-netty案例,netty4.1基础入门篇七《嗨!NettyClient》.md) 12 | * [`netty案例,netty4.1基础入门篇八《NettyClient半包粘包处理、编码解码处理、收发数据方式》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-11-netty案例,netty4.1基础入门篇八《NettyClient半包粘包处理、编码解码处理、收发数据方式》.md) 13 | * [`netty案例,netty4.1基础入门篇九《自定义编码解码器,处理半包、粘包数据》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-12-netty案例,netty4.1基础入门篇九《自定义编码解码器,处理半包、粘包数据》.md) 14 | * [`netty案例,netty4.1基础入门篇十《关于ChannelOutboundHandlerAdapter简单使用》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-13-netty案例,netty4.1基础入门篇十《关于ChannelOutboundHandlerAdapter简单使用》.md) 15 | * [`netty案例,netty4.1基础入门篇十一《netty udp通信方式案例Demo》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-14-netty案例,netty4.1基础入门篇十一《nettyudp通信方式案例Demo》.md) 16 | * [`netty案例,netty4.1基础入门篇十二《简单实现一个基于Netty搭建的Http服务》`](/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-15-netty案例,netty4.1基础入门篇十二《简单实现一个基于Netty搭建的Http服务》.md) 17 | 18 | - 中级拓展篇 19 | * [`netty案例,netty4.1中级拓展篇一《Netty与SpringBoot整合》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-16-netty案例,netty4.1中级拓展篇一《Netty与SpringBoot整合》.md) 20 | * [`netty案例,netty4.1中级拓展篇二《Netty使用Protobuf传输数据》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-17-netty案例,netty4.1中级拓展篇二《Netty使用Protobuf传输数据》.md) 21 | * [`netty案例,netty4.1中级拓展篇三《Netty传输Java对象》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-18-netty案例,netty4.1中级拓展篇三《Netty传输Java对象》.md) 22 | * [`netty案例,netty4.1中级拓展篇四《Netty传输文件、分片发送、断点续传》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-19-netty案例,netty4.1中级拓展篇四《Netty传输文件、分片发送、断点续传》.md) 23 | * [`netty案例,netty4.1中级拓展篇五《基于Netty搭建WebSocket,模仿微信聊天页面》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-20-netty案例,netty4.1中级拓展篇五《基于Netty搭建WebSocket,模仿微信聊天页面》.md) 24 | * [`netty案例,netty4.1中级拓展篇六《SpringBoot+Netty+Elasticsearch收集日志信息数据存储》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-21-netty案例,netty4.1中级拓展篇六《SpringBoot+Netty+Elasticsearch收集日志信息数据存储》.md) 25 | * [`netty案例,netty4.1中级拓展篇七《Netty请求响应同步通信》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-22-netty案例,netty4.1中级拓展篇七《Netty请求响应同步通信》.md) 26 | * [`netty案例,netty4.1中级拓展篇八《Netty心跳服务与断线重连》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-23-netty案例,netty4.1中级拓展篇八《Netty心跳服务与断线重连》.md) 27 | * [`netty案例,netty4.1中级拓展篇九《Netty集群部署实现跨服务端通信的落地方案》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-24-netty案例,netty4.1中级拓展篇九《Netty集群部署实现跨服务端通信的落地方案》.md) 28 | * [`netty案例,netty4.1中级拓展篇十《Netty接收发送多种协议消息类型的通信处理方案》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-25-netty案例,netty4.1中级拓展篇十《Netty接收发送多种协议消息类型的通信处理方案》.md) 29 | * [`netty案例,netty4.1中级拓展篇十一《Netty基于ChunkedStream数据流切块传输》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-26-netty案例,netty4.1中级拓展篇十一《Netty基于ChunkedStream数据流切块传输》.md) 30 | * [`netty案例,netty4.1中级拓展篇十二《Netty流量整形数据流速率控制分析与实战》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-27-netty案例,netty4.1中级拓展篇十二《Netty流量整形数据流速率控制分析与实战》.md) 31 | * [`netty案例,netty4.1中级拓展篇十三《Netty基于SSL实现信息传输过程中双向加密验证》`](/notes/itstack-demo-netty/itstack-demo-netty-2/2019-08-28-netty案例,netty4.1中级拓展篇十三《Netty基于SSL实现信息传输过程中双向加密验证》.md) 32 | 33 | - 高级应用篇 34 | * [`手写RPC框架第一章《自定义配置xml》`](/notes/itstack-demo-netty/itstack-demo-netty-3/2019-09-01-手写RPC框架第一章《自定义配置xml》.md) 35 | * [`手写RPC框架第二章《netty通信》`](/notes/itstack-demo-netty/itstack-demo-netty-3/2019-09-02-手写RPC框架第二章《netty通信》.md) 36 | * [`手写RPC框架第三章《RPC中间件》`](/notes/itstack-demo-netty/itstack-demo-netty-3/2019-09-03-手写RPC框架第三章《RPC中间件》.md) 37 | * [`websocket与下位机通过netty方式通信传输行为信息`](/notes/itstack-demo-netty/itstack-demo-netty-3/2019-12-01-websocket与下位机通过netty方式通信传输行为信息.md) 38 | 39 | - 源码分析篇 40 | * [`netty案例,netty4.1源码分析篇一《NioEventLoopGroup源码分析》`](/notes/itstack-demo-netty/itstack-demo-netty-4/2019-09-10-netty案例,netty4.1源码分析篇一《NioEventLoopGroup源码分析》.md) 41 | * [`netty案例,netty4.1源码分析篇二《ServerBootstrap配置与绑定启动》`](/notes/itstack-demo-netty/itstack-demo-netty-4/2019-09-11-netty案例,netty4.1源码分析篇二《ServerBootstrap配置与绑定启动》.md) 42 | * [`netty案例,netty4.1源码分析篇三《Netty服务端初始化过程以及反射工厂的作用》`](/notes/itstack-demo-netty/itstack-demo-netty-4/2019-09-12-netty案例,netty4.1源码分析篇三《Netty服务端初始化过程以及反射工厂的作用》.md) 43 | * [`netty案例,netty4.1源码分析篇四《ByteBuf的数据结构在使用方式中的剖析》`](/notes/itstack-demo-netty/itstack-demo-netty-4/2019-09-13-netty案例,netty4.1源码分析篇四《ByteBuf的数据结构在使用方式中的剖析》.md) 44 | * [`netty案例,netty4.1源码分析篇五《一行简单的writeAndFlush都做了哪些事》`](/notes/itstack-demo-netty/itstack-demo-netty-4/2019-09-14-netty案例,netty4.1源码分析篇五《一行简单的writeAndFlush都做了哪些事》.md) 45 | * [`netty案例,netty4.1源码分析篇六《Netty异步架构监听类Promise源码分析》`](/notes/itstack-demo-netty/itstack-demo-netty-4/2019-09-15-netty案例,netty4.1源码分析篇六《Netty异步架构监听类Promise源码分析》.md) -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-07-netty案例,netty4.1基础入门篇四《NettyServer收发数据》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 本章节主要介绍服务端在收到数据后,通过writeAndFlush发送ByteBuf字节码向客户端传输信息。因为我们使用客户端模拟器的编码是GBK格式,所以代码中也需要将字节码转换为GBK,否则会乱码。 3 | ## 开发环境 4 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 5 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 6 | 3. telnet 测试【可以现在你的win7机器上测试这个命令,用于链接到服务端的测试命令】 7 | ## 代码示例 8 | ```java 9 | itstack-demo-netty-1-04 10 | └── src 11 | ├── main 12 | │ └── java 13 | │ └── org.itstack.demo.netty.server 14 | │ ├── MyChannelInitializer.java 15 | │ ├── MyServerHandler.java 16 | │ └── NettyServer.java 17 | └── test 18 | └── java 19 | └── org.itstack.demo.netty.test 20 | └── ApiTest.java 21 | ``` 22 | >MyChannelInitializer.java 23 | 24 | ```java 25 | /** 26 | * 虫洞栈:https://bugstack.cn 27 | * 公众号:bugstack虫洞栈 {获取学习源码} 28 | * Create by fuzhengwei on 2019 29 | */ 30 | public class MyChannelInitializer extends ChannelInitializer { 31 | 32 | @Override 33 | protected void initChannel(SocketChannel channel) { 34 | // 基于换行符号 35 | channel.pipeline().addLast(new LineBasedFrameDecoder(1024)); 36 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 37 | channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK"))); 38 | // 在管道中添加我们自己的接收数据实现方法 39 | channel.pipeline().addLast(new MyServerHandler()); 40 | } 41 | 42 | } 43 | ``` 44 | >MyServerHandler.java 45 | 46 | ```java 47 | /** 48 | * 虫洞栈:https://bugstack.cn 49 | * 公众号:bugstack虫洞栈 {获取学习源码} 50 | * Create by fuzhengwei on 2019 51 | */ 52 | public class MyServerHandler extends ChannelInboundHandlerAdapter { 53 | 54 | /** 55 | * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据 56 | */ 57 | @Override 58 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 59 | SocketChannel channel = (SocketChannel) ctx.channel(); 60 | System.out.println("链接报告开始"); 61 | System.out.println("链接报告信息:有一客户端链接到本服务端"); 62 | System.out.println("链接报告IP:" + channel.localAddress().getHostString()); 63 | System.out.println("链接报告Port:" + channel.localAddress().getPort()); 64 | System.out.println("链接报告完毕"); 65 | //通知客户端链接建立成功 66 | String str = "通知客户端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n"; 67 | ByteBuf buf = Unpooled.buffer(str.getBytes().length); 68 | buf.writeBytes(str.getBytes("GBK")); 69 | ctx.writeAndFlush(buf); 70 | } 71 | 72 | /** 73 | * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据 74 | */ 75 | @Override 76 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 77 | System.out.println("客户端断开链接" + ctx.channel().localAddress().toString()); 78 | } 79 | 80 | @Override 81 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 82 | //接收msg消息{与上一章节相比,此处已经不需要自己进行解码} 83 | System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg); 84 | //通知客户端链消息发送成功 85 | String str = "服务端收到:" + new Date() + " " + msg + "\r\n"; 86 | ByteBuf buf = Unpooled.buffer(str.getBytes().length); 87 | buf.writeBytes(str.getBytes("GBK")); 88 | ctx.writeAndFlush(buf); 89 | } 90 | 91 | /** 92 | * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接 93 | */ 94 | @Override 95 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 96 | ctx.close(); 97 | System.out.println("异常信息:\r\n" + cause.getMessage()); 98 | } 99 | 100 | } 101 | ``` 102 | >NettyServer.java 103 | 104 | ```java 105 | /** 106 | * 虫洞栈:https://bugstack.cn 107 | * 公众号:bugstack虫洞栈 {获取学习源码} 108 | * Create by fuzhengwei on 2019 109 | */ 110 | public class NettyServer { 111 | 112 | public static void main(String[] args) { 113 | new NettyServer().bing(7397); 114 | } 115 | 116 | private void bing(int port) { 117 | //配置服务端NIO线程组 118 | EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); 119 | EventLoopGroup childGroup = new NioEventLoopGroup(); 120 | try { 121 | ServerBootstrap b = new ServerBootstrap(); 122 | b.group(parentGroup, childGroup) 123 | .channel(NioServerSocketChannel.class) //非阻塞模式 124 | .option(ChannelOption.SO_BACKLOG, 128) 125 | .childHandler(new MyChannelInitializer()); 126 | ChannelFuture f = b.bind(port).sync(); 127 | System.out.println("itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}"); 128 | f.channel().closeFuture().sync(); 129 | } catch (InterruptedException e) { 130 | e.printStackTrace(); 131 | } finally { 132 | childGroup.shutdownGracefully(); 133 | parentGroup.shutdownGracefully(); 134 | } 135 | 136 | } 137 | 138 | } 139 | ``` 140 | ## 测试结果 141 | >启动服务端NettyServer 142 | 143 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty04-1.png) 144 | 145 | >启动模拟器NetAssist 发送数据测试 146 | 147 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty04-2.png) 148 | 149 | >执行结果 150 | 151 | ```java 152 | itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码} 153 | 链接报告开始 154 | 链接报告信息:有一客户端链接到本服务端 155 | 链接报告IP:10.13.28.13 156 | 链接报告Port:7397 157 | 链接报告完毕 158 | 2019-08-05 16:03:31 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 159 | 2019-08-05 16:03:32 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 160 | 2019-08-05 16:03:33 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 161 | 2019-08-05 16:03:33 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 162 | 客户端断开链接/10.13.28.13:7397 163 | 164 | Process finished with exit code -1 165 | ``` -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-08-netty案例,netty4.1基础入门篇五《NettyServer字符串编码器》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | netty通信就向一个流水channel管道,我们可以在管道的中间插入一些‘挡板’为我们服务。比如字符串的编码解码,在前面我们使用new StringDecoder(Charset.forName("GBK"))进行字符串解码,这样我们在收取数据就不需要手动处理字节码。那么本章节我们使用与之对应的new StringEncoder(Charset.forName("GBK"))进行进行字符串编码,用以实现服务端在发送数据的时候只需要传输字符串内容即可。 3 | ## 开发环境 4 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 5 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 6 | 3. telnet 测试【可以现在你的win7机器上测试这个命令,用于链接到服务端的测试命令】 7 | ## 代码示例 8 | ```java 9 | itstack-demo-netty-1-05 10 | └── src 11 | ├── main 12 | │ └── java 13 | │ └── org.itstack.demo.netty.server 14 | │ ├── MyChannelInitializer.java 15 | │ ├── MyServerHandler.java 16 | │ └── NettyServer.java 17 | └── test 18 | └── java 19 | └── org.itstack.demo.netty.test 20 | └── ApiTest.java 21 | ``` 22 | >MyChannelInitializer.java 编码器StringEncoder 23 | 24 | ```java 25 | /** 26 | * 虫洞栈:https://bugstack.cn 27 | * 公众号:bugstack虫洞栈 {获取学习源码} 28 | * Create by fuzhengwei on 2019 29 | */ 30 | public class MyChannelInitializer extends ChannelInitializer { 31 | 32 | @Override 33 | protected void initChannel(SocketChannel channel) { 34 | // 基于换行符号 35 | channel.pipeline().addLast(new LineBasedFrameDecoder(1024)); 36 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 37 | channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK"))); 38 | // 编码转String,注意调整自己的编码格式GBK、UTF-8 39 | channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK"))); 40 | // 在管道中添加我们自己的接收数据实现方法 41 | channel.pipeline().addLast(new MyServerHandler()); 42 | } 43 | 44 | } 45 | ``` 46 | >MyServerHandler.java 47 | 48 | ```java 49 | /** 50 | * 虫洞栈:https://bugstack.cn 51 | * 公众号:bugstack虫洞栈 {获取学习源码} 52 | * Create by fuzhengwei on 2019 53 | */ 54 | public class MyServerHandler extends ChannelInboundHandlerAdapter { 55 | 56 | /** 57 | * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据 58 | */ 59 | @Override 60 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 61 | SocketChannel channel = (SocketChannel) ctx.channel(); 62 | System.out.println("链接报告开始"); 63 | System.out.println("链接报告信息:有一客户端链接到本服务端"); 64 | System.out.println("链接报告IP:" + channel.localAddress().getHostString()); 65 | System.out.println("链接报告Port:" + channel.localAddress().getPort()); 66 | System.out.println("链接报告完毕"); 67 | //通知客户端链接建立成功 68 | String str = "通知客户端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n"; 69 | ctx.writeAndFlush(str); 70 | } 71 | 72 | /** 73 | * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据 74 | */ 75 | @Override 76 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 77 | System.out.println("客户端断开链接" + ctx.channel().localAddress().toString()); 78 | } 79 | 80 | @Override 81 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 82 | //接收msg消息{与上一章节相比,此处已经不需要自己进行解码} 83 | System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg); 84 | //通知客户端链消息发送成功{不需要通过ByteBuf,可以直接发送字符串} 85 | String str = "服务端收到:" + new Date() + " " + msg + "\r\n"; 86 | ctx.writeAndFlush(str); 87 | } 88 | 89 | /** 90 | * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接 91 | */ 92 | @Override 93 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 94 | ctx.close(); 95 | System.out.println("异常信息:\r\n" + cause.getMessage()); 96 | } 97 | 98 | } 99 | ``` 100 | >NettyServer.java 101 | 102 | ```java 103 | /** 104 | * 虫洞栈:https://bugstack.cn 105 | * 公众号:bugstack虫洞栈 {获取学习源码} 106 | * Create by fuzhengwei on 2019 107 | */ 108 | public class NettyServer { 109 | 110 | public static void main(String[] args) { 111 | new NettyServer().bing(7397); 112 | } 113 | 114 | private void bing(int port) { 115 | //配置服务端NIO线程组 116 | EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); 117 | EventLoopGroup childGroup = new NioEventLoopGroup(); 118 | try { 119 | ServerBootstrap b = new ServerBootstrap(); 120 | b.group(parentGroup, childGroup) 121 | .channel(NioServerSocketChannel.class) //非阻塞模式 122 | .option(ChannelOption.SO_BACKLOG, 128) 123 | .childHandler(new MyChannelInitializer()); 124 | ChannelFuture f = b.bind(port).sync(); 125 | System.out.println("itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}"); 126 | f.channel().closeFuture().sync(); 127 | } catch (InterruptedException e) { 128 | e.printStackTrace(); 129 | } finally { 130 | childGroup.shutdownGracefully(); 131 | parentGroup.shutdownGracefully(); 132 | } 133 | 134 | } 135 | 136 | } 137 | ``` 138 | 139 | ## 测试结果 140 | >启动服务端NettyServer 141 | 142 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty04-1.png) 143 | 144 | >启动模拟器NetAssist 发送数据测试 145 | 146 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty04-2.png) 147 | 148 | >执行结果 149 | 150 | ```java 151 | itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码} 152 | 链接报告开始 153 | 链接报告信息:有一客户端链接到本服务端 154 | 链接报告IP:10.13.28.13 155 | 链接报告Port:7397 156 | 链接报告完毕 157 | 2019-08-05 16:03:31 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 158 | 2019-08-05 16:03:32 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 159 | 2019-08-05 16:03:33 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 160 | 2019-08-05 16:03:33 接收到消息:你好,服务端。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 161 | 客户端断开链接/10.13.28.13:7397 162 | 163 | Process finished with exit code -1 164 | ``` -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-11-netty案例,netty4.1基础入门篇八《NettyClient半包粘包处理、编码解码处理、收发数据方式》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | Netty开发中,客户端与服务端需要保持同样的;半包粘包处理,编码解码处理、收发数据方式,这样才能保证数据通信正常。在前面NettyServer的章节中我们也同样处理了;半包粘包、编码解码等,为此在本章节我们可以把这些知识模块开发到NettyClient中。本章节涉及到的知识点有;LineBasedFrameDecoder、StringDecoder、StringEncoder、ChannelInboundHandlerAdapter等。 3 | ## 开发环境 4 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 5 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 6 | 3. telnet 测试【可以现在你的win7机器上测试这个命令,用于链接到服务端的测试命令】 7 | ## 代码示例 8 | ```java 9 | itstack-demo-netty-1-08 10 | └── src 11 | ├── main 12 | │ └── java 13 | │ └── org.itstack.demo.netty.client 14 | │ ├── MyChannelInitializer.java 15 | │ ├── MyClientHandler.java 16 | │ └── NettyClient.java 17 | └── test 18 | └── java 19 | └── org.itstack.demo.netty.test 20 | └── ApiTest.java 21 | ``` 22 | >MyChannelInitializer.java 23 | 24 | ```java 25 | /** 26 | * 虫洞栈:https://bugstack.cn 27 | * 公众号:bugstack虫洞栈 {获取学习源码} 28 | * Create by fuzhengwei on 2019 29 | */ 30 | public class MyChannelInitializer extends ChannelInitializer { 31 | 32 | @Override 33 | protected void initChannel(SocketChannel channel) throws Exception { 34 | // 基于换行符号 35 | channel.pipeline().addLast(new LineBasedFrameDecoder(1024)); 36 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 37 | channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK"))); 38 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 39 | channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK"))); 40 | // 在管道中添加我们自己的接收数据实现方法 41 | channel.pipeline().addLast(new MyClientHandler()); 42 | } 43 | 44 | } 45 | ``` 46 | >MyClientHandler.java 47 | 48 | ```java 49 | /** 50 | * 虫洞栈:https://bugstack.cn 51 | * 公众号:bugstack虫洞栈 {获取学习源码} 52 | * Create by fuzhengwei on 2019 53 | */ 54 | public class MyClientHandler extends ChannelInboundHandlerAdapter { 55 | 56 | /** 57 | * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据 58 | */ 59 | @Override 60 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 61 | SocketChannel channel = (SocketChannel) ctx.channel(); 62 | System.out.println("链接报告开始"); 63 | System.out.println("链接报告信息:本客户端链接到服务端。channelId:" + channel.id()); 64 | System.out.println("链接报告IP:" + channel.localAddress().getHostString()); 65 | System.out.println("链接报告Port:" + channel.localAddress().getPort()); 66 | System.out.println("链接报告完毕"); 67 | //通知客户端链接建立成功 68 | String str = "通知服务端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n"; 69 | ctx.writeAndFlush(str); 70 | } 71 | 72 | /** 73 | * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据 74 | */ 75 | @Override 76 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 77 | System.out.println("断开链接" + ctx.channel().localAddress().toString()); 78 | } 79 | 80 | @Override 81 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 82 | //接收msg消息{与上一章节相比,此处已经不需要自己进行解码} 83 | System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg); 84 | //通知客户端链消息发送成功 85 | String str = "客户端收到:" + new Date() + " " + msg + "\r\n"; 86 | ctx.writeAndFlush(str); 87 | } 88 | 89 | /** 90 | * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接 91 | */ 92 | @Override 93 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 94 | ctx.close(); 95 | System.out.println("异常信息:\r\n" + cause.getMessage()); 96 | } 97 | 98 | } 99 | ``` 100 | >NettyClient.java 101 | 102 | ```java 103 | /** 104 | * 虫洞栈:https://bugstack.cn 105 | * 公众号:bugstack虫洞栈 {获取学习源码} 106 | * Create by fuzhengwei on 2019 107 | */ 108 | public class NettyClient { 109 | 110 | public static void main(String[] args) { 111 | new NettyClient().connect("127.0.0.1", 7397); 112 | } 113 | 114 | private void connect(String inetHost, int inetPort) { 115 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 116 | try { 117 | Bootstrap b = new Bootstrap(); 118 | b.group(workerGroup); 119 | b.channel(NioSocketChannel.class); 120 | b.option(ChannelOption.AUTO_READ, true); 121 | b.handler(new MyChannelInitializer()); 122 | ChannelFuture f = b.connect(inetHost, inetPort).sync(); 123 | System.out.println("itstack-demo-netty client start done. {关注公众号:bugstack虫洞栈,获取源码}"); 124 | f.channel().closeFuture().sync(); 125 | } catch (InterruptedException e) { 126 | e.printStackTrace(); 127 | } finally { 128 | workerGroup.shutdownGracefully(); 129 | } 130 | } 131 | 132 | } 133 | ``` 134 | ## 测试结果 135 | >启动模拟器NetAssist 设置TCP Server 136 | 137 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty-1-08-1.png) 138 | 139 | >启动客户端NettyClient 140 | 141 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty-1-08-2.png) 142 | 143 | >执行结果 144 | 145 | ```java 146 | /** 147 | * 虫洞栈:https://bugstack.cn 148 | * 公众号:bugstack虫洞栈 {获取学习源码} 149 | * Create by fuzhengwei on 2019 150 | */ 151 | public class NettyClient { 152 | 153 | public static void main(String[] args) { 154 | new NettyClient().connect("127.0.0.1", 7397); 155 | } 156 | 157 | private void connect(String inetHost, int inetPort) { 158 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 159 | try { 160 | Bootstrap b = new Bootstrap(); 161 | b.group(workerGroup); 162 | b.channel(NioSocketChannel.class); 163 | b.option(ChannelOption.AUTO_READ, true); 164 | b.handler(new MyChannelInitializer()); 165 | ChannelFuture f = b.connect(inetHost, inetPort).sync(); 166 | System.out.println("itstack-demo-netty client start done. {关注公众号:bugstack虫洞栈,获取源码}"); 167 | f.channel().closeFuture().sync(); 168 | } catch (InterruptedException e) { 169 | e.printStackTrace(); 170 | } finally { 171 | workerGroup.shutdownGracefully(); 172 | } 173 | } 174 | 175 | } 176 | ``` -------------------------------------------------------------------------------- /docs/_media/wxbugstack.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 公众号 17 | 公众号 18 | bugstack虫洞栈 19 | bugstack虫洞栈 20 | 21 | 23 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-09-netty案例,netty4.1基础入门篇六《NettyServer群发消息》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 在微信或者QQ的聊天中我们经常会用到一些群聊,把你的信息发送给所有用户。那么为了实现群发消息,在netty中我们可以使用ChannelGroup方式进行群发消息。如果为了扩展验证比如你实际聊天有不同的群,那么可以定义ConcurrentHashMap结构来存放ChannelGroup。ChannelGroup中提供了一些基础的方法;添加、异常、查找、清空、发放消息、关闭等。 3 | ## 开发环境 4 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 5 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 6 | 3. telnet 测试【可以现在你的win7机器上测试这个命令,用于链接到服务端的测试命令】 7 | ## 代码示例 8 | ```java 9 | itstack-demo-netty-1-06 10 | └── src 11 | ├── main 12 | │ └── java 13 | │ └── org.itstack.demo.netty.server 14 | │ ├── ChannelHandler.java 15 | │ ├── MyChannelInitializer.java 16 | │ ├── MyServerHandler.java 17 | │ └── NettyServer.java 18 | └── test 19 | └── java 20 | └── org.itstack.demo.netty.test 21 | └── ApiTest.java 22 | ``` 23 | >ChannelHandler.java 24 | 25 | ```java 26 | 27 | /** 28 | * 虫洞栈:https://bugstack.cn 29 | * 公众号:bugstack虫洞栈 {获取学习源码} 30 | * Create by fuzhengwei on 2019 31 | */ 32 | public class ChannelHandler { 33 | 34 | //用于存放用户Channel信息,也可以建立map结构模拟不同的消息群 35 | public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); 36 | 37 | } 38 | ``` 39 | >MyChannelInitializer.java 40 | 41 | ```java 42 | /** 43 | * 虫洞栈:https://bugstack.cn 44 | * 公众号:bugstack虫洞栈 {获取学习源码} 45 | * Create by fuzhengwei on 2019 46 | */ 47 | public class MyChannelInitializer extends ChannelInitializer { 48 | 49 | @Override 50 | protected void initChannel(SocketChannel channel) { 51 | // 基于换行符号 52 | channel.pipeline().addLast(new LineBasedFrameDecoder(1024)); 53 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 54 | channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK"))); 55 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 56 | channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK"))); 57 | // 在管道中添加我们自己的接收数据实现方法 58 | channel.pipeline().addLast(new MyServerHandler()); 59 | } 60 | 61 | } 62 | ``` 63 | >MyServerHandler.java 64 | 65 | ```java 66 | /** 67 | * 虫洞栈:https://bugstack.cn 68 | * 公众号:bugstack虫洞栈 {获取学习源码} 69 | * Create by fuzhengwei on 2019 70 | */ 71 | public class MyServerHandler extends ChannelInboundHandlerAdapter { 72 | 73 | /** 74 | * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据 75 | */ 76 | @Override 77 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 78 | //当有客户端链接后,添加到channelGroup通信组 79 | ChannelHandler.channelGroup.add(ctx.channel()); 80 | //日志信息 81 | SocketChannel channel = (SocketChannel) ctx.channel(); 82 | System.out.println("链接报告开始"); 83 | System.out.println("链接报告信息:有一客户端链接到本服务端"); 84 | System.out.println("链接报告IP:" + channel.localAddress().getHostString()); 85 | System.out.println("链接报告Port:" + channel.localAddress().getPort()); 86 | System.out.println("链接报告完毕"); 87 | //通知客户端链接建立成功 88 | String str = "通知客户端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n"; 89 | ctx.writeAndFlush(str); 90 | } 91 | 92 | /** 93 | * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据 94 | */ 95 | @Override 96 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 97 | System.out.println("客户端断开链接" + ctx.channel().localAddress().toString()); 98 | // 当有客户端退出后,从channelGroup中移除。 99 | ChannelHandler.channelGroup.remove(ctx.channel()); 100 | } 101 | 102 | @Override 103 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 104 | //接收msg消息{与上一章节相比,此处已经不需要自己进行解码} 105 | System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg); 106 | //收到消息后,群发给客户端 107 | String str = "服务端收到:" + new Date() + " " + msg + "\r\n"; 108 | ChannelHandler.channelGroup.writeAndFlush(str); 109 | } 110 | 111 | /** 112 | * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接 113 | */ 114 | @Override 115 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 116 | ctx.close(); 117 | System.out.println("异常信息:\r\n" + cause.getMessage()); 118 | } 119 | 120 | } 121 | ``` 122 | >NettyServer.java 123 | 124 | ```java 125 | /** 126 | * 虫洞栈:https://bugstack.cn 127 | * 公众号:bugstack虫洞栈 {获取学习源码} 128 | * Create by fuzhengwei on 2019 129 | */ 130 | public class NettyServer { 131 | 132 | public static void main(String[] args) { 133 | new NettyServer().bing(7397); 134 | } 135 | 136 | private void bing(int port) { 137 | //配置服务端NIO线程组 138 | EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); 139 | EventLoopGroup childGroup = new NioEventLoopGroup(); 140 | try { 141 | ServerBootstrap b = new ServerBootstrap(); 142 | b.group(parentGroup, childGroup) 143 | .channel(NioServerSocketChannel.class) //非阻塞模式 144 | .option(ChannelOption.SO_BACKLOG, 128) 145 | .childHandler(new MyChannelInitializer()); 146 | ChannelFuture f = b.bind(port).sync(); 147 | System.out.println("itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}"); 148 | f.channel().closeFuture().sync(); 149 | } catch (InterruptedException e) { 150 | e.printStackTrace(); 151 | } finally { 152 | childGroup.shutdownGracefully(); 153 | parentGroup.shutdownGracefully(); 154 | } 155 | 156 | } 157 | 158 | } 159 | 160 | ``` 161 | ## 测试结果 162 | >启动服务端NettyServer 163 | 164 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty-1-06-1.png) 165 | 166 | >启动**2**模拟器NetAssist 互相发送数据测试 167 | 168 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty-1-06.png) 169 | 170 | >执行结果 171 | 172 | ```java 173 | itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码} 174 | 链接报告开始 175 | 链接报告信息:有一客户端链接到本服务端 176 | 链接报告IP:10.13.28.13 177 | 链接报告Port:7397 178 | 链接报告完毕 179 | 链接报告开始 180 | 链接报告信息:有一客户端链接到本服务端 181 | 链接报告IP:10.13.28.13 182 | 链接报告Port:7397 183 | 链接报告完毕 184 | 2019-08-06 09:20:33 接收到消息:你好,服务端;我是小A。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 185 | 2019-08-06 09:20:35 接收到消息:你好,服务端;我是小A。我是公众号,关注我获取源码。“我的结尾是一个换行符,用于传输半包粘包处理” 186 | 2019-08-06 09:20:37 接收到消息:你好,服务端;我是小B。我是公众号,关注我获取源码。"我的结尾是一个换行符,用于传输半包粘包处理" 187 | 2019-08-06 09:20:38 接收到消息:你好,服务端;我是小B。我是公众号,关注我获取源码。"我的结尾是一个换行符,用于传输半包粘包处理" 188 | 客户端断开链接/10.13.28.13:7397 189 | 客户端断开链接/10.13.28.13:7397 190 | ``` -------------------------------------------------------------------------------- /docs/notes/itstack-demo-agent/2019-07-11-基于JavaAgent的全链路监控二《通过字节码增加监控执行耗时》.md: -------------------------------------------------------------------------------- 1 | ## 案例简述 2 | 通过上一章节的介绍《嗨!JavaAgent》,我们已经知道通过配置-javaagent:文件.jar后,在java程序启动时候会执行premain方法。接下来我们使用javassist字节码增强的方式,来监控方法程序的执行耗时。 3 | 4 | >Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。 5 | 6 | >关于java字节码的处理,目前有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。 7 | 8 | ## 环境准备 9 | 1. IntelliJ IDEA Community Edition 10 | 2. jdk1.8.0_45 64位 11 | 12 | ## 配置信息(路径相关修改为自己的) 13 | 1. 配置位置:Run/Debug Configurations -> VM options 14 | 2. 配置内容:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-02\target\itstack-demo-agent-02-1.0.0-SNAPSHOT.jar=testargs 15 | 16 | ## 代码示例 17 | ```java 18 | itstack-demo-agent-02 19 | ├── pom.xml 20 | └── src 21 | ├── main 22 | │ ├── java 23 | │ │ └── org.itstack.demo.agent 24 | │ │ ├── MyAgent.java 25 | │ │ └── MyMonitorTransformer.java 26 | │ └── resources 27 | │ └── META-INF 28 | │ └── MANIFEST.MF 29 | └── test 30 | └── java 31 | └── org.itstack.demo.test 32 | └── ApiTest.java 33 | ``` 34 | 35 | >pom.xml (引入javassist并打入到Agent包中) 36 | 37 | ```xml 38 | 39 | 40 | -Xms512m -Xmx512m 41 | false 42 | true 43 | utf-8 44 | true 45 | 46 | src/main/resources/META-INF/MANIFEST.MF 47 | 48 | 49 | 50 | 51 | javassist 52 | javassist 53 | 3.12.1.GA 54 | jar 55 | 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-shade-plugin 62 | 63 | 64 | package 65 | 66 | shade 67 | 68 | 69 | 70 | 71 | 72 | 73 | javassist:javassist:jar: 74 | 75 | 76 | 77 | 78 | ``` 79 | >MyAgent.java 80 | 81 | ```java 82 | /** 83 | * 博客:http://itstack.org 84 | * 论坛:http://bugstack.cn 85 | * 公众号:bugstack虫洞栈 {获取学习源码} 86 | * create by fuzhengwei on 2019 87 | */ 88 | public class MyAgent { 89 | 90 | //JVM 首先尝试在代理类上调用以下方法 91 | public static void premain(String agentArgs, Instrumentation inst) { 92 | System.out.println("this is my agent:" + agentArgs); 93 | MyMonitorTransformer monitor = new MyMonitorTransformer(); 94 | inst.addTransformer(monitor); 95 | } 96 | 97 | //如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法 98 | public static void premain(String agentArgs) { 99 | } 100 | 101 | } 102 | ``` 103 | 104 | >MyMonitorTransformer.java 105 | 106 | ```java 107 | /** 108 | * 博客:http://itstack.org 109 | * 论坛:http://bugstack.cn 110 | * 公众号:bugstack虫洞栈 {获取学习源码} 111 | * create by fuzhengwei on 2019 112 | */ 113 | public class MyMonitorTransformer implements ClassFileTransformer { 114 | 115 | private static final Set classNameSet = new HashSet<>(); 116 | 117 | static { 118 | classNameSet.add("org.itstack.demo.test.ApiTest"); 119 | } 120 | 121 | @Override 122 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { 123 | try { 124 | String currentClassName = className.replaceAll("/", "."); 125 | if (!classNameSet.contains(currentClassName)) { // 提升classNameSet中含有的类 126 | return null; 127 | } 128 | System.out.println("transform: [" + currentClassName + "]"); 129 | 130 | CtClass ctClass = ClassPool.getDefault().get(currentClassName); 131 | CtBehavior[] methods = ctClass.getDeclaredBehaviors(); 132 | for (CtBehavior method : methods) { 133 | enhanceMethod(method); 134 | } 135 | return ctClass.toBytecode(); 136 | } catch (Exception e) { 137 | e.printStackTrace(); 138 | } 139 | 140 | return null; 141 | 142 | } 143 | 144 | 145 | private void enhanceMethod(CtBehavior method) throws Exception { 146 | if (method.isEmpty()) { 147 | return; 148 | } 149 | String methodName = method.getName(); 150 | if ("main".equalsIgnoreCase(methodName)) { 151 | return; 152 | } 153 | 154 | final StringBuilder source = new StringBuilder(); 155 | // 前置增强: 打入时间戳 156 | // 保留原有的代码处理逻辑 157 | source.append("{") 158 | .append("long start = System.nanoTime();\n") //前置增强: 打入时间戳 159 | .append("$_ = $proceed($$);\n") //调用原有代码,类似于method();($$)表示所有的参数 160 | .append("System.out.print(\"method:[") 161 | .append(methodName).append("]\");").append("\n") 162 | .append("System.out.println(\" cost:[\" +(System.nanoTime() - start)+ \"ns]\");") // 后置增强,计算输出方法执行耗时 163 | .append("}"); 164 | 165 | ExprEditor editor = new ExprEditor() { 166 | @Override 167 | public void edit(MethodCall methodCall) throws CannotCompileException { 168 | methodCall.replace(source.toString()); 169 | } 170 | }; 171 | method.instrument(editor); 172 | } 173 | 174 | } 175 | ``` 176 | >MANIFEST.MF 177 | 178 | ```其他语言 179 | Manifest-Version: 1.0 180 | Premain-Class: org.itstack.demo.agent.MyAgent 181 | Can-Redefine-Classes: true 182 | ``` 183 | >ApiTest.java 184 | 185 | ```java 186 | /** 187 | * 博客:http://itstack.org 188 | * 论坛:http://bugstack.cn 189 | * 公众号:bugstack虫洞栈 {获取学习源码} 190 | * create by fuzhengwei on 2019 191 | * 192 | * VM options: 193 | * -javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-02\target\itstack-demo-agent-02-1.0.0-SNAPSHOT.jar=testargs 194 | * 195 | */ 196 | public class ApiTest { 197 | 198 | public static void main(String[] args) { 199 | ApiTest apiTest = new ApiTest(); 200 | apiTest.echoHi(); 201 | } 202 | 203 | private void echoHi(){ 204 | System.out.println("hi agent"); 205 | } 206 | 207 | } 208 | ``` 209 | ## 测试结果 210 | ```java 211 | this is my agent:testargs 212 | transform: [org.itstack.demo.test.ApiTest] 213 | hi agent 214 | method:[echoHi] cost:[294845ns] 215 | ``` -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-12-netty案例,netty4.1基础入门篇九《自定义编码解码器,处理半包、粘包数据》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 在实际应用场景里,只要是支持sokcet通信的都可以和Netty交互,比如中继器、下位机、PLC等。这些场景下就非常需要自定义编码解码器,来处理字节码传输,并控制半包、粘包以及安全问题。那么本章节我们通过实现ByteToMessageDecoder、MessageToByteEncoder来实现我们的需求。 3 | 4 | >数据传输过程中有各种情况;整包数据、半包数据、粘包数据,比如我们设定开始符号02、结束符号03; 5 | 整包数据;02 89 78 54 03 6 | 半包数据;02 89 78 7 | 粘包数据;02 89 78 54 03 02 89 8 | 9 | 10 | ## 环境准备 11 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 12 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 13 | 3. telnet 测试【可以现在你的win7机器上测试这个命令,用于链接到服务端的测试命令】 14 | 15 | ## 代码示例 16 | ```java 17 | itstack-demo-netty-1-09 18 | └── src 19 | ├── main 20 | │ └── java 21 | │ └── org.itstack.demo.netty 22 | │ ├── codec 23 | │ │ ├── MyDecoder.java 24 | │ │ └── MyEncoder.java 25 | │ └── server 26 | │ ├── MyChannelInitializer.java 27 | │ ├── MyServerHandler.java 28 | │ └── NettyServer.java 29 | └── test 30 | └── java 31 | └── org.itstack.demo.test 32 | └── ApiTest.java 33 | ``` 34 | 35 | >MyDecoder.java *用于处理解码,02开始 03结束 36 | 37 | ```java 38 | /** 39 | * 自定义解码器 40 | * 虫洞栈:https://bugstack.cn 41 | * 公众号:bugstack虫洞栈 {关注获取学习源码} 42 | * 虫洞群:①群5398358 ②群5360692 43 | * Create by fuzhengwei on 2019 44 | */ 45 | public class MyDecoder extends ByteToMessageDecoder { 46 | 47 | //数据包基础长度 48 | private final int BASE_LENGTH = 4; 49 | 50 | @Override 51 | protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List out) throws Exception { 52 | 53 | //基础长度不足,我们设定基础长度为4 54 | if (in.readableBytes() < BASE_LENGTH) { 55 | return; 56 | } 57 | 58 | int beginIdx; //记录包头位置 59 | 60 | while (true) { 61 | // 获取包头开始的index 62 | beginIdx = in.readerIndex(); 63 | // 标记包头开始的index 64 | in.markReaderIndex(); 65 | // 读到了协议的开始标志,结束while循环 66 | if (in.readByte() == 0x02) { 67 | break; 68 | } 69 | // 未读到包头,略过一个字节 70 | // 每次略过,一个字节,去读取,包头信息的开始标记 71 | in.resetReaderIndex(); 72 | in.readByte(); 73 | // 当略过,一个字节之后, 74 | // 数据包的长度,又变得不满足 75 | // 此时,应该结束。等待后面的数据到达 76 | if (in.readableBytes() < BASE_LENGTH) { 77 | return; 78 | } 79 | 80 | } 81 | 82 | //剩余长度不足可读取数量[没有内容长度位] 83 | int readableCount = in.readableBytes(); 84 | if (readableCount <= 1) { 85 | in.readerIndex(beginIdx); 86 | return; 87 | } 88 | 89 | //长度域占4字节,读取int 90 | ByteBuf byteBuf = in.readBytes(1); 91 | String msgLengthStr = byteBuf.toString(Charset.forName("GBK")); 92 | int msgLength = Integer.parseInt(msgLengthStr); 93 | 94 | //剩余长度不足可读取数量[没有结尾标识] 95 | readableCount = in.readableBytes(); 96 | if (readableCount < msgLength + 1) { 97 | in.readerIndex(beginIdx); 98 | return; 99 | } 100 | 101 | ByteBuf msgContent = in.readBytes(msgLength); 102 | 103 | //如果没有结尾标识,还原指针位置[其他标识结尾] 104 | byte end = in.readByte(); 105 | if (end != 0x03) { 106 | in.readerIndex(beginIdx); 107 | return; 108 | } 109 | 110 | out.add(msgContent.toString(Charset.forName("GBK"))); 111 | } 112 | } 113 | ``` 114 | 115 | >MyEncoder.java *用于处理编码,在byte开始和结束加上02 03 116 | 117 | ```java 118 | /** 119 | * 自定义编码器 120 | * 虫洞栈:https://bugstack.cn 121 | * 公众号:bugstack虫洞栈 {关注获取学习源码} 122 | * 虫洞群:①群5398358 ②群5360692 123 | * Create by fuzhengwei on 2019 124 | */ 125 | public class MyEncoder extends MessageToByteEncoder { 126 | 127 | @Override 128 | protected void encode(ChannelHandlerContext channelHandlerContext, Object in, ByteBuf out) throws Exception { 129 | 130 | String msg = in.toString(); 131 | byte[] bytes = msg.getBytes(); 132 | 133 | byte[] send = new byte[bytes.length + 2]; 134 | System.arraycopy(bytes, 0, send, 1, bytes.length); 135 | send[0] = 0x02; 136 | send[send.length - 1] = 0x03; 137 | 138 | out.writeInt(send.length); 139 | out.writeBytes(send); 140 | 141 | } 142 | 143 | } 144 | ``` 145 | 146 | >MyChannelInitializer.java 147 | 148 | ```java 149 | /** 150 | * 虫洞栈:https://bugstack.cn 151 | * 公众号:bugstack虫洞栈 {关注获取学习源码} 152 | * 虫洞群:①群5398358 ②群5360692 153 | * Create by fuzhengwei on 2019 154 | */ 155 | public class MyChannelInitializer extends ChannelInitializer { 156 | 157 | @Override 158 | protected void initChannel(SocketChannel channel) { 159 | //自定义解码器 160 | channel.pipeline().addLast(new MyDecoder()); 161 | //自定义编码器 162 | channel.pipeline().addLast(new MyEncoder()); 163 | //在管道中添加我们自己的接收数据实现方法 164 | channel.pipeline().addLast(new MyServerHandler()); 165 | } 166 | 167 | } 168 | ``` 169 | 170 | >MyServerHandler.java 171 | 172 | ```java 173 | /** 174 | * 虫洞栈:https://bugstack.cn 175 | * 公众号:bugstack虫洞栈 {关注获取学习源码} 176 | * 虫洞群:①群5398358 ②群5360692 177 | * Create by fuzhengwei on 2019 178 | */ 179 | public class MyServerHandler extends ChannelInboundHandlerAdapter { 180 | 181 | @Override 182 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 183 | SocketChannel channel = (SocketChannel) ctx.channel(); 184 | System.out.println("链接报告开始 {公众号:bugstack虫洞栈 >获取学习源码}"); 185 | System.out.println("链接报告信息:有一客户端链接到本服务端"); 186 | System.out.println("链接报告IP:" + channel.localAddress().getHostString()); 187 | System.out.println("链接报告Port:" + channel.localAddress().getPort()); 188 | System.out.println("链接报告完毕"); 189 | } 190 | 191 | @Override 192 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 193 | //接收msg消息{与上一章节相比,此处已经不需要自己进行解码} 194 | System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg); 195 | 196 | ctx.writeAndFlush("hi I'm ok"); 197 | } 198 | 199 | } 200 | ``` 201 | 202 | ## 测试结果 203 | 204 | >启动NettyServer 205 | 206 | ```java 207 | itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码} 208 | 链接报告开始 {公众号:bugstack虫洞栈 >获取学习源码} 209 | 链接报告信息:有一客户端链接到本服务端 210 | 链接报告IP:127.0.0.1 211 | 链接报告Port:7397 212 | 链接报告完毕 213 | 2019-08-28 14:40:01 接收到消息:hihi -整包测试 214 | 2019-08-28 14:40:16 接收到消息:hihi -半包测试 215 | 2019-08-28 14:40:23 接收到消息:hihi -粘包测试 216 | 2019-08-28 14:40:27 接收到消息:hihi -粘包测试 217 | 218 | Process finished with exit code -1 219 | ``` 220 | 221 | >启动模拟器NetAssist,用TcpClient链接服务端 222 | 223 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/netty-1-09-2-1.png) 224 | 225 | >分别发送三组数据; 226 | 227 | ```java 228 | 02;开始位 229 | 03;结束位 230 | 34;变量,内容长度位 231 | 232 | 第一组;整包测试数据: 233 | 02 34 68 69 68 69 03 234 | 235 | 第二组;半包测试数据 236 | 02 34 68 69 68 69 237 | 03 238 | 239 | 第三组:粘包测试数据 240 | 02 34 68 69 68 69 03 02 34 241 | 68 69 68 69 03 242 | ``` -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-15-netty案例,netty4.1基础入门篇十二《简单实现一个基于Netty搭建的Http服务》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | Netty不仅可以搭建Socket服务,也可以搭建Http、Https服务。本章节我们通过一个简单的入门案例,来了解Netty搭建的Http服务,在我们后续的Netty网关服务中会使用到这样的功能点。 3 | 4 | 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。 5 | >在后端开发中接触HTTP协议的比较多,目前大部分都是基于Servlet容器实现的Http服务,往往有一些核心子系统对性能的要求非常高,这个时候我们可以考虑采用NIO的网络模型来实现HTTP服务,以此提高性能和吞吐量,Netty除了开发网络应用非常方便,还内置了HTTP相关的编解码器,让用户可以很方便的开发出高性能的HTTP协议的服务,Spring Webflux默认是使用的Netty。 6 | 7 | ## 环境准备 8 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 9 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 10 | 3. Postman接口调试器,可以从网上下载也可以联系我,微信公众号:bugstack虫洞栈 | 关注回复你的邮箱 11 | 12 | ## 代码示例 13 | 14 | ```java 15 | itstack-demo-netty-1-12 16 | └── src 17 | ├── main 18 | │ └── java 19 | │ └── org.itstack.demo.netty 20 | │ └── server 21 | │ ├── MyChannelInitializer.java 22 | │ ├── MyClientHandler.java 23 | │ └── NettyServer.java 24 | └── test 25 | └── java 26 | └── org.itstack.demo.netty.test 27 | └── ApiTest.java 28 | ``` 29 | 30 | >server/MyChannelInitializer.java | 添加了Http的处理协议 31 | 32 | ```java 33 | /** 34 | * 虫洞栈:https://bugstack.cn 35 | * 公众号:bugstack虫洞栈 {获取学习源码} 36 | * Create by fuzhengwei on 2019 37 | */ 38 | public class MyChannelInitializer extends ChannelInitializer { 39 | 40 | @Override 41 | protected void initChannel(SocketChannel channel) { 42 | // 数据解码操作 43 | channel.pipeline().addLast(new HttpResponseEncoder()); 44 | // 数据编码操作 45 | channel.pipeline().addLast(new HttpRequestDecoder()); 46 | // 在管道中添加我们自己的接收数据实现方法 47 | channel.pipeline().addLast(new MyServerHandler()); 48 | } 49 | 50 | } 51 | ``` 52 | 53 | >server/MyClientHandler.java | 模版代码基本没变,和其他的代码类似 54 | 55 | ```java 56 | /** 57 | * 虫洞栈:https://bugstack.cn 58 | * 公众号:bugstack虫洞栈 {获取学习源码} 59 | * Create by fuzhengwei on 2019 60 | */ 61 | public class MyServerHandler extends ChannelInboundHandlerAdapter { 62 | 63 | @Override 64 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 65 | 66 | if (msg instanceof HttpRequest) { 67 | DefaultHttpRequest request = (DefaultHttpRequest) msg; 68 | System.out.println("URI:" + request.getUri()); 69 | System.err.println(msg); 70 | } 71 | 72 | if (msg instanceof HttpContent) { 73 | LastHttpContent httpContent = (LastHttpContent) msg; 74 | ByteBuf byteData = httpContent.content(); 75 | if (!(byteData instanceof EmptyByteBuf)) { 76 | //接收msg消息 77 | byte[] msgByte = new byte[byteData.readableBytes()]; 78 | byteData.readBytes(msgByte); 79 | System.out.println(new String(msgByte, Charset.forName("UTF-8"))); 80 | } 81 | } 82 | 83 | String sendMsg = "微信公众号:bugstack虫洞栈,欢迎您的关注&获取源码!不平凡的岁月终究来自你每日不停歇的刻苦拼搏,每一次真正成长都因看清脚下路而抉择出的生活。愿你我;承遇朝霞,年少正恰,整装戎马,刻印风华。\n" + 84 | "博客栈:https://bugstack.cn\n" + 85 | "内容介绍:本公众号会每天定时推送科技资料;专题、源码、书籍、视频、咨询、面试、环境等方面内容。尤其在技术专题方面会提供更多的原创内容,让更多的程序员可以从最基础开始了解到技术全貌,目前已经对外提供的有;《手写RPC框架》、《用Java实现JVM》、《基于JavaAgent的全链路监控》、《Netty案例》等专题。\n" + 86 | "获取源码:\n" + 87 | "关注{bugstack虫洞栈}公众号获取源码,回复<用Java实现jvm源码>\n" + 88 | "关注{bugstack虫洞栈}公众号获取源码,回复\n" + 89 | "关注{bugstack虫洞栈}公众号获取源码,回复\n" + 90 | "关注{bugstack虫洞栈}公众号获取源码,回复<基于JavaAgent的全链路监控>"; 91 | 92 | FullHttpResponse response = new DefaultFullHttpResponse( 93 | HttpVersion.HTTP_1_1, 94 | HttpResponseStatus.OK, 95 | Unpooled.wrappedBuffer(sendMsg.getBytes(Charset.forName("UTF-8")))); 96 | response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8"); 97 | response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); 98 | response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); 99 | ctx.write(response); 100 | ctx.flush(); 101 | } 102 | 103 | @Override 104 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 105 | ctx.flush(); 106 | } 107 | 108 | @Override 109 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 110 | ctx.close(); 111 | cause.printStackTrace(); 112 | } 113 | 114 | } 115 | ``` 116 | 117 | >server/NettyServer.java | 模版代码基本没变,和其他的代码类似 118 | 119 | ```java 120 | /** 121 | * 虫洞栈:https://bugstack.cn 122 | * 公众号:bugstack虫洞栈 {获取学习源码} 123 | * Create by fuzhengwei on 2019 124 | */ 125 | public class NettyServer { 126 | 127 | public static void main(String[] args) { 128 | new NettyServer().bing(7397); 129 | } 130 | 131 | private void bing(int port) { 132 | //配置服务端NIO线程组 133 | EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); 134 | EventLoopGroup childGroup = new NioEventLoopGroup(); 135 | try { 136 | ServerBootstrap b = new ServerBootstrap(); 137 | b.group(parentGroup, childGroup) 138 | .channel(NioServerSocketChannel.class) //非阻塞模式 139 | .option(ChannelOption.SO_BACKLOG, 128) 140 | .childOption(ChannelOption.SO_KEEPALIVE, true) 141 | .childHandler(new MyChannelInitializer()); 142 | ChannelFuture f = b.bind(port).sync(); 143 | System.out.println("itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}"); 144 | f.channel().closeFuture().sync(); 145 | } catch (InterruptedException e) { 146 | e.printStackTrace(); 147 | } finally { 148 | childGroup.shutdownGracefully(); 149 | parentGroup.shutdownGracefully(); 150 | } 151 | 152 | } 153 | 154 | } 155 | ``` 156 | 157 | ## 测试结果 158 | 159 | >启动NettyServer 160 | 161 | ```java 162 | itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码} 163 | URI:/ 164 | DefaultHttpRequest(decodeResult: success, version: HTTP/1.1) 165 | GET / HTTP/1.1 166 | cache-control: no-cache 167 | Postman-Token: 28f37dfb-bb5a-4cb2-ae7a-87cf6cda900c 168 | User-Agent: PostmanRuntime/7.6.0 169 | Accept: */* 170 | Host: localhost:7397 171 | accept-encoding: gzip, deflate 172 | content-type: multipart/form-data; boundary=--------------------------359056973355668947377349 173 | content-length: 226 174 | Connection: keep-alive 175 | ----------------------------359056973355668947377349 176 | Content-Disposition: form-data; name="msg" 177 | 178 | 微信公众号:bugstack虫洞栈 | 欢迎关注并获取源码! 179 | ----------------------------359056973355668947377349-- 180 | 181 | 182 | Process finished with exit code -1 183 | ``` 184 | 185 | >Postman访问;http://localhost:7397 并设置参数 186 | 187 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/09/netty-1-12-1.png) -------------------------------------------------------------------------------- /docs/notes/itstack-demo-any/2020-01-18-似乎你总也记不住,byte的取值范围是-127~128还是-128~127.md: -------------------------------------------------------------------------------- 1 | # 似乎你总也记不住,byte取值范围是 -127~128 还是 -128~127 2 | 3 | --- 4 | 5 | >小傅哥 & [https://bugstack.cn](https://bugstack.cn)
沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例、源码分析等。

你用剑🗡、我用刀🔪,好的代码都很烧,望你不吝出招! 6 | 7 | ## 一、前言介绍 8 | 无论在面试过程中还是平时的技术交流中,似乎有很多小伙伴始终记不住java中byte类型的取值范围是多少。究其原因大部分程序员对这个取值范围是不在意的,因为知道与不知道都不影响你完成工作。另外这种知识点压根不是让你死记硬背的,当然如果你是从其他文科专业转过来学编程开发的,还情有可原。但对一个理科生来说,就不太应该了。 9 | 10 | ## 二、取值范围计算 11 | 12 | 在java中,byte占1个字节,8比特位,可以想象成8个小块的数据区间,首位用0、1代表符号位。**0[正]**、**1[负]**,那么绘制出一个表格如下; 13 | 14 | | byte | | | | | | | | | 15 | | :------|:------|:------|:------|:------|:------|:------|:------|------| 16 | |序号| 8 | 7 | 6 |5 |4 |3 |2 |1 | 17 | |2ⁿ| 2^7 |2^6 |2^5 |2^4 |2^3 |2^2 |2^1 | 2^0 | 18 | |值| 128 | 64 | 32 |16 |8 |4 |2 |1 | 19 | |+127| 0 | 1 | 1 |1 |1 |1 |1 |1 | 20 | |-128| 1 | 0 | 0 |0 |0 |0 |0 |0 | 21 | 22 | > +127 二进制求和 23 | 24 | ```java 25 | 2^0+2^1+2^2+2^3+2^4+2^5+2^6+2^7 26 | = 2^(n+1) - 1 27 | = 127 28 | ``` 29 | 30 | > -128 二进制求和 31 | 32 | ```java 33 | 2^8 34 | = 128 35 | ``` 36 | 37 | 好了,现在看懂逻辑就很清晰了,为什么是负数到-128,因为1代表负数的符号位,也就整整好好是2的8次方,-128。 38 | 39 | ## 三、进制数值转换 40 | 41 | 因为java语言与一些其他语言byte的取值范围不同,所以在有时候处理一些文件时候需要进行进制转换。也就是 -128~127 与 0~255 的转换处理; 42 | 43 | 比如我们现在将一个java中byte=120,转换成 0~255取值范围的数值; 44 | 45 | 一般可以进行与运算; 46 | 47 | ```java 48 | 120 & 0x0FF 49 | ``` 50 | 51 | 同时还可以进行增位运算;(也就是将8个字节长度的内容,放到16个长度中,进行转换) 52 | 53 | ```java 54 | byte[] val = {-120}; 55 | BigInteger bigInteger = new BigInteger(1, val); 56 | //有符号 57 | System.out.println(bigInteger.byteValue()); 58 | //无符号(增位) 59 | String str_hex = bigInteger.toString(16); 60 | System.out.println(Integer.parseInt(str_hex, 16)); // 136 61 | ``` 62 | 63 | ## 四、解析一段class字节码 64 | 65 | java的类文件都会被编译成class文件,那么class文件需要经过jvm的解析、验证,加载等处理才可以被虚拟机的指令执行操作。 66 | 67 | 如果下是一段class文件的byte数组,将内容解析出对应的结果; 68 | 69 | ```java 70 | public class ClassReaderTest { 71 | 72 | //取部分字节码:java.lang.String 73 | private static byte[] classData = { 74 | -54, -2, -70, -66, 0, 0, 0, 52, 2, 26, 3, 0, 0, -40, 0, 3, 0, 0, -37, -1, 3, 0, 0, -33, -1, 3, 0, 1, 0, 0, 8, 0, 75 | 59, 8, 0, 83, 8, 0, 86, 8, 0, 87, 8, 0, 110, 8, 0, -83, 8, 0, -77, 8, 0, -49, 8, 0, -47, 1, 0, 3, 40, 41, 73, 1, 76 | 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 20, 40, 41, 77 | 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 3, 40, 41, 86, 1, 0, 3, 78 | 40, 41, 90, 1, 0, 4, 40, 41, 91, 66, 1, 0, 4, 40, 41, 91, 67, 1, 0, 4, 40, 67, 41, 67, 1, 0, 21, 40, 68, 41, 76, 79 | 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 4, 40, 73, 41, 67, 1, 0, 4}; 80 | 81 | public static void main(String[] args) { 82 | 83 | //classData是我们的字节码,第一是-54,因为byte取值范围是-128~+127,所以如果想看到和其他虚拟机一样的值,需要进行与运算。 84 | System.out.println("* byte字节码与运算原值(-54)换行后(-54 & 0x0FF):" + (-54 & 0x0FF)); 85 | 86 | //校验魔数 87 | readAndCheckMagic(); 88 | 89 | //校验版本号 90 | readAndCheckVersion(); 91 | 92 | //接下来会依次读取[可以参照java版本虚拟机代码];constantPool、accessFlags、thisClassIdx、supperClassIdx、interfaces、fields、methods、attributes 93 | } 94 | 95 | /** 96 | * 校验魔数 97 | *

98 | * 很多文件格式都会规定满足该格式的文件必须以某几个固定字节开头,这几个字节主要起到标识作用,叫作魔数(magic number)。 99 | * 例如; 100 | * PDF文件以4字节“%PDF”(0x25、0x50、0x44、0x46)开头, 101 | * ZIP文件以2字节“PK”(0x50、0x4B)开头 102 | * class文件以4字节“0xCAFEBABE”开头 103 | */ 104 | private static void readAndCheckMagic() { 105 | System.out.println("\r\n------------ 校验魔数 ------------"); 106 | //从class字节码中读取前四位 107 | byte[] magic_byte = new byte[4]; 108 | System.arraycopy(classData, 0, magic_byte, 0, 4); 109 | 110 | //将4位byte字节转成16进制字符串 111 | String magic_hex_str = new BigInteger(1, magic_byte).toString(16); 112 | System.out.println("magic_hex_str:" + magic_hex_str); 113 | 114 | //byte_magic_str 是16进制的字符串,cafebabe,因为java中没有无符号整型,所以如果想要无符号只能放到更高位中 115 | long magic_unsigned_int32 = Long.parseLong(magic_hex_str, 16); 116 | System.out.println("magic_unsigned_int32:" + magic_unsigned_int32); 117 | 118 | //魔数比对,一种通过字符串比对,另外一种使用假设的无符号16进制比较。如果使用无符号比较需要将0xCAFEBABE & 0x0FFFFFFFFL与运算 119 | System.out.println("0xCAFEBABE & 0x0FFFFFFFFL:" + (0xCAFEBABE & 0x0FFFFFFFFL)); 120 | 121 | if (magic_unsigned_int32 == (0xCAFEBABE & 0x0FFFFFFFFL)) { 122 | System.out.println("class字节码魔数无符号16进制数值一致校验通过"); 123 | } else { 124 | System.out.println("class字节码魔数无符号16进制数值一致校验拒绝"); 125 | } 126 | 127 | } 128 | 129 | /** 130 | * 校验版本号 131 | *

132 | * 魔数之后是class文件的次版本号和主版本号,都是u2类型。假设某class文件的主版本号是M,次版本号是m,那么完整的版本号可以 133 | * 表示成“M.m”的形式。次版本号只在J2SE 1.2之前用过,从1.2开始基本上就没有什么用了(都是0)。主版本号在J2SE 1.2之前是45, 134 | * 从1.2开始,每次有大版本的Java版本发布,都会加1{45、46、47、48、49、50、51、52} 135 | */ 136 | private static void readAndCheckVersion() { 137 | System.out.println("\r\n------------ 校验版本号 ------------"); 138 | 139 | //从class字节码第4位开始读取,读取2位 140 | byte[] minor_byte = new byte[2]; 141 | System.arraycopy(classData, 4, minor_byte, 0, 2); 142 | //将2位byte字节转成16进制字符串 143 | String minor_hex_str = new BigInteger(1, minor_byte).toString(16); 144 | System.out.println("minor_hex_str:" + minor_hex_str); 145 | //minor_unsigned_int32 转成无符号16进制 146 | int minor_unsigned_int32 = Integer.parseInt(minor_hex_str, 16); 147 | System.out.println("minor_unsigned_int32:" + minor_unsigned_int32); 148 | 149 | //从class字节码第6位开始读取,读取2位 150 | byte[] major_byte = new byte[2]; 151 | System.arraycopy(classData, 6, major_byte, 0, 2); 152 | //将2位byte字节转成16进制字符串 153 | String major_hex_str = new BigInteger(1, major_byte).toString(16); 154 | System.out.println("major_hex_str:" + major_hex_str); 155 | //major_unsigned_int32 转成无符号16进制 156 | int major_unsigned_int32 = Integer.parseInt(major_hex_str, 16); 157 | System.out.println("major_unsigned_int32:" + major_unsigned_int32); 158 | 159 | System.out.println("版本号:" + major_unsigned_int32 + "." + minor_unsigned_int32); 160 | 161 | } 162 | 163 | } 164 | ``` 165 | 166 | **测试结果:** 167 | 168 | ```java 169 | * byte字节码与运算原值(-54)换行后(-54 & 0x0FF):202 170 | 171 | ------------ 校验魔数 ------------ 172 | magic_hex_str:cafebabe 173 | magic_unsigned_int32:3405691582 174 | 0xCAFEBABE & 0x0FFFFFFFFL:3405691582 175 | class字节码魔数无符号16进制数值一致校验通过 176 | 177 | ------------ 校验版本号 ------------ 178 | minor_hex_str:0 179 | minor_unsigned_int32:0 180 | major_hex_str:34 181 | major_unsigned_int32:52 182 | 版本号:52.0 183 | 184 | Process finished with exit code 0 185 | ``` 186 | 187 | ## 五、综上总结 188 | 189 | - 关于byte在文章;[《用java实现jvm虚拟机》](https://bugstack.cn/itstack-demo-jvm/itstack-demo-jvm.html)中讲过,但是没有单独拿出来分析,现在单独分析下也增强记忆。 190 | - 任何一个可能不起眼的知识点,不是他不重要,而是你还没有用到。就像有句话说,不是读书没用,而是你没用。国语博大精深! 191 | - 认认真真对待每一个知识点,不断的夯实自己的地基,这就像是盖房子在打地基一样。越深越稳,最终所服能于你的上层架构才会更加精进。 192 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-agent/2019-07-12-基于JavaAgent的全链路监控三《ByteBuddy操作监控方法字节码》.md: -------------------------------------------------------------------------------- 1 | ## 案例简述 2 | 在第二章中我们已经可以监控方法执行耗时,虽然它能完成我们一些基本需要,但是为了增强代码的扩展性,我们需要使用字节码操作工具ByteBuddy来帮助我们实现更完善的监控程序。 3 | >[Byte Buddy](http://bytebuddy.net/#/) is a code generation and manipulation library for creating and modifying Java classes during the runtime of a Java application and without the help of a compiler. Other than the code generation utilities that [ship with the Java Class Library](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html), Byte Buddy allows the creation of arbitrary classes and is not limited to implementing interfaces for the creation of runtime proxies. Furthermore, Byte Buddy offers a convenient API for changing classes either manually, using a Java agent or during a build. 4 | 5 | ## 环境准备 6 | 1. IntelliJ IDEA Community Edition 7 | 2. jdk1.8.0_45 64位 8 | 9 | ## 配置信息(路径相关修改为自己的) 10 | 1. 配置位置:Run/Debug Configurations -> VM options 11 | 2. 配置内容:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-03\target\itstack-demo-agent-03-1.0.0-SNAPSHOT.jar=testargs 12 | 13 | ## 代码示例 14 | ```java 15 | itstack-demo-agent-03 16 | ├── pom.xml 17 | └── src 18 | ├── main 19 | │ ├── java 20 | │ │ └── org.itstack.demo.agent 21 | │ │ ├── MethodCostTime.java 22 | │ │ └── MyAgent.java 23 | │ └── resources 24 | │ └── META-INF 25 | │ └── MANIFEST.MF 26 | └── test 27 | └── java 28 | └── org.itstack.demo.test 29 | └── ApiTest.java 30 | ``` 31 | >pom.xml (引入ByteBuddy并打入到Agent包中) 32 | 33 | ```xml 34 | 35 | 36 | -Xms512m -Xmx512m 37 | false 38 | true 39 | utf-8 40 | true 41 | 42 | src/main/resources/META-INF/MANIFEST.MF 43 | 44 | 45 | 46 | 47 | javassist 48 | javassist 49 | 3.12.1.GA 50 | jar 51 | 52 | 53 | net.bytebuddy 54 | byte-buddy 55 | 1.8.20 56 | 57 | 58 | net.bytebuddy 59 | byte-buddy-agent 60 | 1.8.20 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-shade-plugin 68 | 69 | 70 | package 71 | 72 | shade 73 | 74 | 75 | 76 | 77 | 78 | 79 | javassist:javassist:jar: 80 | net.bytebuddy:byte-buddy:jar: 81 | net.bytebuddy:byte-buddy-agent:jar: 82 | 83 | 84 | 85 | 86 | ``` 87 | >MethodCostTime.java 88 | 89 | ```java 90 | /** 91 | * 博客:http://itstack.org 92 | * 论坛:http://bugstack.cn 93 | * 公众号:bugstack虫洞栈 {获取学习源码} 94 | * create by fuzhengwei on 2019 95 | */ 96 | public class MethodCostTime { 97 | 98 | @RuntimeType 99 | public static Object intercept(@Origin Method method, @SuperCall Callable callable) throws Exception { 100 | long start = System.currentTimeMillis(); 101 | try { 102 | // 原有函数执行 103 | return callable.call(); 104 | } finally { 105 | System.out.println(method + " 方法耗时: " + (System.currentTimeMillis() - start) + "ms"); 106 | } 107 | } 108 | 109 | } 110 | ``` 111 | 112 | >MyAgent.java 113 | 114 | ```java 115 | /** 116 | * javaagent 117 | * 博客:http://itstack.org 118 | * 论坛:http://bugstack.cn 119 | * 公众号:bugstack虫洞栈 {获取学习源码} 120 | * create by fuzhengwei on 2019 121 | */ 122 | public class MyAgent { 123 | 124 | //JVM 首先尝试在代理类上调用以下方法 125 | public static void premain(String agentArgs, Instrumentation inst) { 126 | System.out.println("this is my agent:" + agentArgs); 127 | 128 | AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> { 129 | return builder 130 | .method(ElementMatchers.any()) // 拦截任意方法 131 | .intercept(MethodDelegation.to(MethodCostTime.class)); // 委托 132 | }; 133 | 134 | AgentBuilder.Listener listener = new AgentBuilder.Listener() { 135 | @Override 136 | public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) { 137 | 138 | } 139 | 140 | @Override 141 | public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) { 142 | 143 | } 144 | 145 | @Override 146 | public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) { 147 | 148 | } 149 | 150 | @Override 151 | public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) { 152 | 153 | } 154 | 155 | @Override 156 | public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) { 157 | 158 | } 159 | 160 | }; 161 | 162 | new AgentBuilder 163 | .Default() 164 | .type(ElementMatchers.nameStartsWith("org.itstack.demo.test")) // 指定需要拦截的类 165 | .transform(transformer) 166 | .with(listener) 167 | .installOn(inst); 168 | } 169 | 170 | 171 | //如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法 172 | public static void premain(String agentArgs) { 173 | } 174 | 175 | } 176 | ``` 177 | >MANIFEST.MF 178 | 179 | ```其他语言 180 | Manifest-Version: 1.0 181 | Premain-Class: org.itstack.demo.agent.MyAgent 182 | Can-Redefine-Classes: true 183 | ``` 184 | >ApiTest.java 185 | 186 | ```java 187 | /** 188 | * 博客:http://itstack.org 189 | * 论坛:http://bugstack.cn 190 | * 公众号:bugstack虫洞栈 {获取学习源码} 191 | * create by fuzhengwei on 2019 192 | * 193 | * VM options: 194 | * -javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-03\target\itstack-demo-agent-03-1.0.0-SNAPSHOT.jar=testargs 195 | */ 196 | public class ApiTest { 197 | 198 | public static void main(String[] args) throws InterruptedException { 199 | ApiTest apiTest = new ApiTest(); 200 | apiTest.echoHi(); 201 | } 202 | 203 | private void echoHi() throws InterruptedException { 204 | System.out.println("hi agent"); 205 | Thread.sleep((long) (Math.random() * 500)); 206 | } 207 | 208 | } 209 | ``` 210 | ## 测试结果 211 | 212 | ```java 213 | this is my agent:testargs 214 | hi agent 215 | private void org.itstack.demo.test.ApiTest.echoHi() throws java.lang.InterruptedException 方法耗时: 329ms 216 | public static void org.itstack.demo.test.ApiTest.main(java.lang.String[]) throws java.lang.InterruptedException 方法耗时: 329ms 217 | 218 | Process finished with exit code 0 219 | ``` 220 | 221 | 222 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-any/2019-12-21-[有点干货]JDK、CGLIB动态代理使用以及源码分析.md: -------------------------------------------------------------------------------- 1 | # 有点干货 | JDK、CGLIB动态代理使用以及源码分析 2 | 3 | --- 4 | 5 | ## 前言介绍 6 | 在Java中动态代理是非常重要也是非常有用的一个技术点,如果没有动态代理技术几乎也就不会有各种优秀框架的出现,包括Spring。 7 | 其实在动态代理的使用中,除了我们平时用的Spring还有很多中间件和服务都用了动态代理,例如; 8 | 1. RPC通信框架Dubbo,在通信的时候由服务端提供一个接口描述信息的Jar,调用端进行引用,之后在调用端引用后生成了对应的代理类,当执行方法调用的时候,实际需要走到代理类向服务提供端发送请求信息,直至内容回传。 9 | 2. 另外在使用Mybatis时候可以知道只需要定义一个接口,不需要实现具体方法就可以调用到Mapper中定义的数据库操作信息了。这样极大的简化了代码的开发,又增强了效率。 10 | 3. 最后不知道你自己是否尝试过开发一些基于代理类的框架,以此来优化业务代码。也就是将业务代码中非业务逻辑又通用性的功能抽离出来,开发为独立的组件。**推荐个案例,方便知道代理类的应用:**[手写RPC框架第三章《RPC中间件》](https://bugstack.cn/itstack-demo-netty-3/2019/09/03/%E6%89%8B%E5%86%99RPC%E6%A1%86%E6%9E%B6%E7%AC%AC%E4%B8%89%E7%AB%A0-RPC%E4%B8%AD%E9%97%B4%E4%BB%B6.html) 11 | 12 | ## 代理方式 13 | 动态代理可以使用Jdk方式也可以使用CGLB,他们的区别,如下; 14 | 15 | | 类型 | 机制 | 回调方式 |适用场景 | 效率 | 16 | |:--------|:-------|:-------|:-------|:-------| 17 | |JDK | 委托机制,代理类和目标类都实现了同样的接口,InvocationHandler持有目标类,代理类委托InvocationHandler去调用目标类的原始方法 |反射 | 目标类是接口类| 效率瓶颈在反射调用稍慢| 18 | | CGLIB| 继承机制,代理类继承了目标类并重写了目标方法,通过回调函数MethodInterceptor调用父类方法执行原始逻辑|通过FastClass方法索引调用 | 非接口类,非final类,非final方法|第一次调用因为要生成多个Class对象较JDK方式慢,多次调用因为有方法索引较反射方式快,如果方法过多switch case过多其效率还需测试 | 19 | 20 | ## 案例工程 21 | 22 | ```java 23 | itstack-demo-test 24 | └── src 25 | ├── main 26 | │ └── java 27 | │ └── org.itstack.demo 28 | │ ├── proxy 29 | │ │ └── cglib 30 | │ │ └── CglibProxy.java 31 | │ ├── jdk 32 | │ │ ├── reflect 33 | │ │ │ ├── JDKInvocationHandler.java 34 | │ │ │ └── JDKProxy.java 35 | │ │ └── util 36 | │ │ └── ClassLoaderUtils.java 37 | │ └── service 38 | │ ├── IUserService.java 39 | │ └── UserService.java 40 | └── test 41 | └── java 42 | └── org.itstack.demo.test 43 | └── ApiTest.java 44 | ``` 45 | 46 | ### 基础接口和方法便于验证 47 | 48 | >service/IUserService.java 49 | 50 | ```java 51 | public interface IUserService { 52 | 53 | String queryUserNameById(String userId); 54 | 55 | } 56 | ``` 57 | 58 | >service/UserService.java 59 | 60 | ```java 61 | public class UserService implements IUserService { 62 | 63 | public String queryUserNameById(String userId) { 64 | return "hi user " + userId; 65 | } 66 | 67 | } 68 | ``` 69 | 70 | ### JDK动态代理 71 | 72 | >reflect/JDKInvocationHandler.java & 代理类反射调用 73 | 74 | - 实现InvocationHandler.invoke,用于方法增强{监控、执行其他业务逻辑、远程调用等} 75 | - 如果有需要额外的参数可以提供构造方法 76 | 77 | ```java 78 | public class JDKInvocationHandler implements InvocationHandler { 79 | 80 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 81 | System.out.println(method.getName()); 82 | return "我被JDKProxy代理了"; 83 | } 84 | 85 | } 86 | ``` 87 | 88 | >reflect/JDKProxy.java & 定义一个代理类获取的服务 89 | 90 | - Proxy.newProxyInstance 来实际生成代理类,过程如下; 91 | 1. Class cl = getProxyClass0(loader, intfs); 查找或生成指定的代理类 92 | 2. proxyClassCache.get(loader, interfaces); 代理类的缓存中获取 93 | 3. subKeyFactory.apply(key, parameter) 继续下一层 94 | 4. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); 生成代理类的字节码 95 | 96 | ```java 97 | public class JDKProxy { 98 | 99 | public static T getProxy(Class interfaceClass) throws Exception { 100 | InvocationHandler handler = new JDKInvocationHandler(); 101 | ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader(); 102 | T result = (T) Proxy.newProxyInstance(classLoader, new Class[]{interfaceClass}, handler); 103 | return result; 104 | } 105 | 106 | } 107 | ``` 108 | 109 | >ApiTest.test_proxy_jdk() & 执行调用并输出反射类的字节码 110 | 111 | - 代理后调用方法验证 112 | - 通过使用ProxyGenerator.generateProxyClass获取实际的字节码,查看代理类的内容 113 | 114 | ```java 115 | @Test 116 | public void test_proxy_jdk() throws Exception { 117 | 118 | IUserService proxy = (IUserService) JDKProxy.getProxy(ClassLoaderUtils.forName("org.itstack.demo.service.IUserService")); 119 | String userName = proxy.queryUserNameById("10001"); 120 | System.out.println(userName); 121 | 122 | String name = "ProxyUserService"; 123 | byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{IUserService.class}); 124 | 125 | // 输出类字节码 126 | FileOutputStream out = null; 127 | try { 128 | out = new FileOutputStream(name + ".class"); 129 | System.out.println((new File("")).getAbsolutePath()); 130 | out.write(data); 131 | } catch (FileNotFoundException e) { 132 | e.printStackTrace(); 133 | } catch (IOException e) { 134 | e.printStackTrace(); 135 | } finally { 136 | if (null != out) try { 137 | out.close(); 138 | } catch (IOException e) { 139 | e.printStackTrace(); 140 | } 141 | } 142 | 143 | } 144 | ``` 145 | 146 | **输出结果** 147 | 148 | ```java 149 | queryUserNameById 150 | 我被JDKProxy代理了 151 | ``` 152 | 153 | **将生成的代理类进行反编译jd-gui** 154 | 155 | 部分内容抽取,可以看到比较核心的方法,也就是我们在调用的时候走到了这里 156 | 157 | ```java 158 | public final String queryUserNameById(String paramString) 159 | throws 160 | { 161 | try 162 | { 163 | return (String)this.h.invoke(this, m3, new Object[] { paramString }); 164 | } 165 | catch (Error|RuntimeException localError) 166 | { 167 | throw localError; 168 | } 169 | catch (Throwable localThrowable) 170 | { 171 | throw new UndeclaredThrowableException(localThrowable); 172 | } 173 | } 174 | 175 | 176 | static 177 | { 178 | try 179 | { 180 | m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); 181 | m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 182 | m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 183 | m3 = Class.forName("org.itstack.demo.service.IUserService").getMethod("queryUserNameById", new Class[] { Class.forName("java.lang.String") }); 184 | return; 185 | } 186 | catch (NoSuchMethodException localNoSuchMethodException) 187 | { 188 | throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); 189 | } 190 | catch (ClassNotFoundException localClassNotFoundException) 191 | { 192 | throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); 193 | } 194 | } 195 | ``` 196 | 197 | ### CGLIB动态代理 198 | 199 | >cglib/CglibProxy.java 200 | 201 | - 提供构造方法,生成CGLIB的代理类,回调this 202 | - intercept可以进行方法的增强,处理相关业务逻辑 203 | - CGLIB是通过ASM来操作字节码生成类 204 | 205 | ```java 206 | public class CglibProxy implements MethodInterceptor { 207 | 208 | public Object newInstall(Object object) { 209 | return Enhancer.create(object.getClass(), this); 210 | } 211 | 212 | public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 213 | System.out.println("我被CglibProxy代理了"); 214 | return methodProxy.invokeSuper(o, objects); 215 | } 216 | 217 | } 218 | ``` 219 | 220 | >ApiTest.test_proxy_cglib() & 调用代理类 221 | 222 | ```java 223 | @Test 224 | public void test_proxy_cglib() { 225 | CglibProxy cglibProxy = new CglibProxy(); 226 | UserService userService = (UserService) cglibProxy.newInstall(new UserService()); 227 | String userName = userService.queryUserNameById("10001"); 228 | System.out.println(userName); 229 | } 230 | ``` 231 | 232 | **输出结果** 233 | 234 | ```java 235 | 我被CglibProxy代理了 236 | hi user 10001 237 | ``` 238 | 239 | ## 综上总结 240 | 241 | - 在我们实际使用中两种方式都用所有使用,也可以依照不同的诉求进行选择 242 | - 往往动态代理会和注解共同使用,代理类拿到以后获取方法的注解,并做相应的业务操作 243 | - 有时候你是否会遇到增加AOP不生效,因为有时候有些类是被代理操作的,并没有执行你的自定义注解也就是切面 -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-4/2019-09-13-netty案例,netty4.1源码分析篇四《ByteBuf的数据结构在使用方式中的剖析》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 在Netty中ByteBuf是一个非常重要的类,它可以以高效易用的数据结构方式来满足网络通信过程中处理数据包内字节码序列的移动。 3 | 4 | ## 数据结构 5 | 6 | ```java 7 | +-------------------+------------------+------------------+ 8 | | discardable bytes | readable bytes | writable bytes | 9 | | | (CONTENT) | | 10 | +-------------------+------------------+------------------+ 11 | | | | | 12 | 0 <= readerIndex <= writerIndex <= capacity 13 | ``` 14 | 那么这种数据结构之所以能高效的处理数据传输处理并解决半包粘包,主要得益于ByteBuf中有两个不同的索引;读索引(readIndex)、写索引(writerIndex)。 15 | 读索引:当我们从ByteBuf读取数据时,readerIndex指针位置也会指向到读取字节位置 16 | 写索引:当我们向ByteBuf写入数据时,writerIndex指针位置也会指向到写入字节位置 17 | 18 | discardable bytes:当从ByteBuf读取一部分数据后,这部分数据就属于discardable,他们是可以被废弃的。 19 | readable bytes:剩余可以继续读取的内容区域,就像一个长条的鸡蛋卡槽,一边放一边拿。从已经拿完到剩余的鸡蛋位置属于可拿区域。 20 | writable bytes:同上一样,这一部分就是可以继续放置鸡蛋的位置。 21 | 22 | ## 功能案例 23 | 24 | ```java 25 | // 62 75 67 73 74 61 63 6B B3 E6 B6 B4 D5 BB 26 | public static void main(String[] args) throws UnsupportedEncodingException { 27 | // 1.创建一个非池化的ByteBuf,大小为14个字节 28 | ByteBuf buffer = Unpooled.buffer(14); 29 | System.out.println("1.创建一个非池化的ByteBuf,大小为14个字节"); 30 | System.out.println("ByteBuf空间大小:" + buffer.capacity()); 31 | // 2.写入3个字节 32 | buffer.writeByte(62); 33 | buffer.writeByte(75); 34 | buffer.writeByte(67); 35 | System.out.println("\r\n2.写入3个字节"); 36 | System.out.println("readerIndex位置:" + buffer.readerIndex()); 37 | System.out.println("writerIndex位置:" + buffer.writerIndex()); 38 | // 3.写入一段字节 39 | byte[] bytes = {73, 74, 61, 63, 0x6B}; 40 | buffer.writeBytes(bytes); 41 | System.out.println("\r\n3.写入一段字节"); 42 | System.out.println("readerIndex位置:" + buffer.readerIndex()); 43 | System.out.println("writerIndex位置:" + buffer.writerIndex()); 44 | // 4.读取全部内容 45 | byte[] allBytes = new byte[buffer.readableBytes()]; 46 | buffer.readBytes(allBytes); 47 | System.out.println("\r\n4.读取全部内容"); 48 | System.out.println("readerIndex位置:" + buffer.readerIndex()); 49 | System.out.println("writerIndex位置:" + buffer.writerIndex()); 50 | System.out.println("读取全部内容:" + Arrays.toString(allBytes)); 51 | // 5.重置指针位置 52 | buffer.resetReaderIndex(); 53 | System.out.println("\r\n5.重置指针位置"); 54 | System.out.println("readerIndex位置:" + buffer.readerIndex()); 55 | System.out.println("writerIndex位置:" + buffer.writerIndex()); 56 | // 6.读取3个字节 57 | byte b0 = buffer.readByte(); 58 | byte b1 = buffer.readByte(); 59 | byte b2 = buffer.readByte(); 60 | System.out.println("\r\n6.读取3个字节"); 61 | System.out.println("readerIndex位置:" + buffer.readerIndex()); 62 | System.out.println("writerIndex位置:" + buffer.writerIndex()); 63 | System.out.println("读取3个字节:" + Arrays.toString(new byte[]{b0, b1, b2})); 64 | // 7.读取一段字节 65 | ByteBuf byteBuf = buffer.readBytes(5); 66 | byte[] dst = new byte[5]; 67 | byteBuf.readBytes(dst); 68 | System.out.println("\r\n7.读取一段字节"); 69 | System.out.println("readerIndex位置:" + buffer.readerIndex()); 70 | System.out.println("writerIndex位置:" + buffer.writerIndex()); 71 | System.out.println("读取一段字节:" + Arrays.toString(dst)); 72 | // 8.丢弃已读内容 73 | buffer.discardReadBytes(); 74 | System.out.println("\r\n8.丢弃已读内容"); 75 | System.out.println("readerIndex位置:" + buffer.readerIndex()); 76 | System.out.println("writerIndex位置:" + buffer.writerIndex()); 77 | // 9.清空指针位置 78 | buffer.clear(); 79 | System.out.println("\r\n9.清空指针位置"); 80 | System.out.println("readerIndex位置:" + buffer.readerIndex()); 81 | System.out.println("writerIndex位置:" + buffer.writerIndex()); 82 | // 10.ByteBuf中还有很多其他方法;拷贝、标记、跳过字节,多用于自定义解码器进行半包粘包处理 83 | } 84 | ``` 85 | 86 | ```java 87 | protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List out) throws Exception { 88 | 89 | //基础长度不足,我们设定基础长度为4 90 | if (in.readableBytes() < BASE_LENGTH) { 91 | return; 92 | } 93 | 94 | int beginIdx; //记录包头位置 95 | 96 | while (true) { 97 | // 获取包头开始的index 98 | beginIdx = in.readerIndex(); 99 | // 标记包头开始的index 100 | in.markReaderIndex(); 101 | // 读到了协议的开始标志,结束while循环 102 | if (in.readByte() == 0x02) { 103 | break; 104 | } 105 | // 未读到包头,略过一个字节 106 | // 每次略过,一个字节,去读取,包头信息的开始标记 107 | in.resetReaderIndex(); 108 | in.readByte(); 109 | // 当略过,一个字节之后, 110 | // 数据包的长度,又变得不满足 111 | // 此时,应该结束。等待后面的数据到达 112 | if (in.readableBytes() < BASE_LENGTH) { 113 | return; 114 | } 115 | 116 | } 117 | 118 | //剩余长度不足可读取数量[没有内容长度位] 119 | int readableCount = in.readableBytes(); 120 | if (readableCount <= 1) { 121 | in.readerIndex(beginIdx); 122 | return; 123 | } 124 | 125 | //长度域占4字节,读取int 126 | ByteBuf byteBuf = in.readBytes(1); 127 | String msgLengthStr = byteBuf.toString(Charset.forName("GBK")); 128 | int msgLength = Integer.parseInt(msgLengthStr); 129 | 130 | //剩余长度不足可读取数量[没有结尾标识] 131 | readableCount = in.readableBytes(); 132 | if (readableCount < msgLength + 1) { 133 | in.readerIndex(beginIdx); 134 | return; 135 | } 136 | 137 | ByteBuf msgContent = in.readBytes(msgLength); 138 | 139 | //如果没有结尾标识,还原指针位置[其他标识结尾] 140 | byte end = in.readByte(); 141 | if (end != 0x03) { 142 | in.readerIndex(beginIdx); 143 | return; 144 | } 145 | 146 | out.add(msgContent.toString(Charset.forName("GBK"))); 147 | } 148 | ``` 149 | 150 | ## 内存模型 151 | 152 | ** 1、堆内内存(JVM堆空间内) ** 153 | 最常用的ByteBuf模式是将数据存储在JVM的堆空间中。它能在没有使用池化的情况下提供快速的分配和释放。 154 | 155 | ** 2、堆外内存(本机直接内存)** 156 | JDK允许JVM实现通过本地调用来分配内存。主要是为了避免每次调用本地I/O操作之前(或者之后)将缓冲区的内容复制到一个中间缓冲区(或者从中间缓冲区把内容复制到缓冲区)。 157 | 158 | ** 3、复合缓冲区(以上2种缓冲区多个混合)** 159 | 常用类:CompositeByteBuf,它为多个ByteBuf提供一个聚合视图,将多个缓冲区表示为单个合并缓冲区的虚拟表示。 160 | 比如:HTTP协议:头部和主体这两部分由应用程序的不同模块产生。这个时候把这两部分合并的话,选择CompositeByteBuf是比较好的。 161 | 162 | ## 源码解读 163 | 164 | >ByteBuf实现了ReferenceCounted与Comparable两个接口 165 | 166 | ```java 167 | public abstract class ByteBuf implements ReferenceCounted, Comparable 168 | ``` 169 | 170 | >创建一个指定容量大小堆缓冲区,并按需扩充容量{与list集和很像},指针位置都是从0开始 171 | 172 | ```java 173 | Unpooled.buffer(14); 174 | 175 | /** 176 | * Creates a new big-endian Java heap buffer with the specified {@code capacity}, which 177 | * expands its capacity boundlessly on demand. The new buffer's {@code readerIndex} and 178 | * {@code writerIndex} are {@code 0}. 179 | */ 180 | public static ByteBuf buffer(int initialCapacity) { 181 | return ALLOC.heapBuffer(initialCapacity); 182 | } 183 | ``` 184 | 185 | >跟进代码的创建过程会发现,UnpooledHeapByteBuf.UnpooledHeapByteBuf用来创建非池化堆Buf 186 | 187 | ```java 188 | ** 189 | * Creates a new heap buffer with an existing byte array. 190 | * 191 | * @param initialArray the initial underlying byte array 192 | * @param maxCapacity the max capacity of the underlying byte array 193 | */ 194 | protected UnpooledHeapByteBuf(ByteBufAllocator alloc, byte[] initialArray, int maxCapacity) { 195 | super(maxCapacity); 196 | 197 | checkNotNull(alloc, "alloc"); 198 | checkNotNull(initialArray, "initialArray"); 199 | 200 | if (initialArray.length > maxCapacity) { 201 | throw new IllegalArgumentException(String.format( 202 | "initialCapacity(%d) > maxCapacity(%d)", initialArray.length, maxCapacity)); 203 | } 204 | 205 | this.alloc = alloc; 206 | setArray(initialArray); 207 | setIndex(0, initialArray.length); 208 | } 209 | ``` 210 | 211 | ## 内容总结 212 | - ByteBuf提供了两个指针;读、写,分别用来标记“可读”、“可写”、“可丢弃”的字节 213 | - 通过调用write*方法写入数据后,写指针将会向后移动 214 | - 通过调用read*方法读取数据后,读指针将会向后移动 215 | - 写入数据或读取数据时会检查是否有足够多的空间可以写入和是否有数据可以读取 216 | - 写入数据之前会进行容量检查,当剩余可写的容量小于需要写入的容量时,需要执行扩容操作 217 | - clear等修改读写指针的方法,只会更改读写指针的值,并不会影响ByteBuf中已有的内容 -------------------------------------------------------------------------------- /docs/notes/itstack-demo-jvm/2019-05-04-用Java实现JVM第三章《解析class文件》附[classReader拆解].md: -------------------------------------------------------------------------------- 1 | ## 案例介绍 2 | 按照如下虚拟机规范,本文主要介绍java版本jvm提取class字节码方式。在java中没有无符号类型,例如js中byte取值是0~256、java中是-128 ~ +172,所以在实际处理字节码时[虚拟机规范u1、u2、u4],需要进行转换。 3 | 4 | >[java虚拟机规范]每个Class文件都是由8字节为单位的字节流组成,所有的16位、32位和64位长度的数据将被构造成2个、4个和8个8字节单位来表示。多字节数据项总是按照 Big-Endian的顺序进行存储。 5 | 6 | >①Big-Endian 顺序是指按高位字节在地址最低位,最低字节在地址最高位来存储数据,它是 SPARC、PowerPC等处理器的默认多字节存储顺序,而 x86等处理器则是使用了相反的 Little-Endian顺序来存储数据。为了保证 Class 文件在不同硬件上具备同样的含义,因此在 Java 虚拟机规范中是有必要严格规定了数据存储顺序的 7 | 8 | ## ClassFile结构体 u1[1字节=8比特位]、u2[2字节=2×8比特位]、u4[4字节=4×8比特位] 9 | ```java 10 | u4 magic; 11 | u2 minor_version; 12 | u2 major_version; 13 | u2 constant_pool_count; 14 | cp_info constant_pool[constant_pool_count-1]; 15 | u2 access_flags; 16 | u2 this_class; 17 | u2 super_class; 18 | u2 interfaces_count; 19 | u2 interfaces[interfaces_count]; 20 | u2 fields_count; 21 | field_info fields[fields_count]; 22 | u2 methods_count; 23 | method_info methods[methods_count]; 24 | u2 attributes_count; 25 | attribute_info attributes[attributes_count]; 26 | ``` 27 | 28 | ## 字节码介绍 29 | 30 | >在JAVA中一共有八种基本数据类型,他们分别是byte、short、int、long、float、double、char、boolean 31 | 其中byte、short、int、long都是表示整数的,只不过他们的取值范围不一样 32 | byte的取值范围为-128~127,占用1个字节(-2的7次方到2的7次方-1) 33 | short的取值范围为-32768~32767,占用2个字节(-2的15次方到2的15次方-1) 34 | int的取值范围为(-2147483648~2147483647),占用4个字节(-2的31次方到2的31次方-1) 35 | long的取值范围为(-9223372036854774808~9223372036854774807),占用8个字节(-2的63次方到2的63次方-1) 36 | 37 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/byte表.png) 38 | 39 | ```java 40 | 二进制求和(127):2^0+2^1+2^2+2^3+2^4+2^5+2^6+2^7 41 | = 2^(n+1) - 1 42 | = 127 43 | ``` 44 | 45 | ```java 46 | /** 47 | * byte 取值范围 48 | * +127 = [0][1][1][1][1][1][1][1] 49 | * -128 = [1][0][0][0][0][0][0][0] 50 | * 51 | * 有符号 52 | * -120 = [1][1][1][1][1][0][0][0] 53 | * 无符号(增位) 136 = 256 - 120 54 | * 136 = [0][0][0][0][0][0][0][0][1][0][0][0][1][0][0][0] 55 | * 56 | * 输出二进制:new BigInteger("-120", 10).toString(2)) 57 | */ 58 | public class HelloWorld { 59 | 60 | public static void main(String[] args) { 61 | 62 | byte[] val = {-120}; 63 | 64 | BigInteger bigInteger = new BigInteger(1, val); 65 | 66 | //无符号(增位) 67 | String str_hex = bigInteger.toString(16); 68 | System.out.println(Integer.parseInt(str_hex, 16)); 69 | 70 | //有符号 71 | System.out.println(bigInteger.byteValue()); 72 | 73 | } 74 | 75 | } 76 | ``` 77 | ```java 78 | 测试输出: 79 | 136 80 | -120 81 | ``` 82 | 83 | ## 如下读取字节码并进行解析 84 | ```java 85 | package org.itstack.demo.test; 86 | 87 | import java.math.BigInteger; 88 | 89 | public class ClassReaderTest { 90 | 91 | //取部分字节码:java.lang.String 92 | private static byte[] classData = { 93 | -54, -2, -70, -66, 0, 0, 0, 52, 2, 26, 3, 0, 0, -40, 0, 3, 0, 0, -37, -1, 3, 0, 0, -33, -1, 3, 0, 1, 0, 0, 8, 0, 94 | 59, 8, 0, 83, 8, 0, 86, 8, 0, 87, 8, 0, 110, 8, 0, -83, 8, 0, -77, 8, 0, -49, 8, 0, -47, 1, 0, 3, 40, 41, 73, 1, 95 | 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 20, 40, 41, 96 | 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 3, 40, 41, 86, 1, 0, 3, 97 | 40, 41, 90, 1, 0, 4, 40, 41, 91, 66, 1, 0, 4, 40, 41, 91, 67, 1, 0, 4, 40, 67, 41, 67, 1, 0, 21, 40, 68, 41, 76, 98 | 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 4, 40, 73, 41, 67, 1, 0, 4}; 99 | 100 | public static void main(String[] args) { 101 | 102 | //classData是我们的字节码,第一是-54,因为byte取值范围是-128~+127,所以如果想看到和其他虚拟机一样的值,需要进行与运算。 103 | System.out.println("* byte字节码与运算原值(-54)换行后(-54 & 0x0FF):" + (-54 & 0x0FF)); 104 | 105 | //校验魔数 106 | readAndCheckMagic(); 107 | 108 | //校验版本号 109 | readAndCheckVersion(); 110 | 111 | //接下来会依次读取[可以参照java版本虚拟机代码];constantPool、accessFlags、thisClassIdx、supperClassIdx、interfaces、fields、methods、attributes 112 | } 113 | 114 | /** 115 | * 校验魔数 116 | *

117 | * 很多文件格式都会规定满足该格式的文件必须以某几个固定字节开头,这几个字节主要起到标识作用,叫作魔数(magic number)。 118 | * 例如; 119 | * PDF文件以4字节“%PDF”(0x25、0x50、0x44、0x46)开头, 120 | * ZIP文件以2字节“PK”(0x50、0x4B)开头 121 | * class文件以4字节“0xCAFEBABE”开头 122 | */ 123 | private static void readAndCheckMagic() { 124 | System.out.println("\r\n------------ 校验魔数 ------------"); 125 | //从class字节码中读取前四位 126 | byte[] magic_byte = new byte[4]; 127 | System.arraycopy(classData, 0, magic_byte, 0, 4); 128 | 129 | //将4位byte字节转成16进制字符串 130 | String magic_hex_str = new BigInteger(1, magic_byte).toString(16); 131 | System.out.println("magic_hex_str:" + magic_hex_str); 132 | 133 | //byte_magic_str 是16进制的字符串,cafebabe,因为java中没有无符号整型,所以如果想要无符号只能放到更高位中 134 | long magic_unsigned_int32 = Long.parseLong(magic_hex_str, 16); 135 | System.out.println("magic_unsigned_int32:" + magic_unsigned_int32); 136 | 137 | //魔数比对,一种通过字符串比对,另外一种使用假设的无符号16进制比较。如果使用无符号比较需要将0xCAFEBABE & 0x0FFFFFFFFL与运算 138 | System.out.println("0xCAFEBABE & 0x0FFFFFFFFL:" + (0xCAFEBABE & 0x0FFFFFFFFL)); 139 | 140 | if (magic_unsigned_int32 == (0xCAFEBABE & 0x0FFFFFFFFL)) { 141 | System.out.println("class字节码魔数无符号16进制数值一致校验通过"); 142 | } else { 143 | System.out.println("class字节码魔数无符号16进制数值一致校验拒绝"); 144 | } 145 | 146 | } 147 | 148 | /** 149 | * 校验版本号 150 | *

151 | * 魔数之后是class文件的次版本号和主版本号,都是u2类型。假设某class文件的主版本号是M,次版本号是m,那么完整的版本号可以 152 | * 表示成“M.m”的形式。次版本号只在J2SE 1.2之前用过,从1.2开始基本上就没有什么用了(都是0)。主版本号在J2SE 1.2之前是45, 153 | * 从1.2开始,每次有大版本的Java版本发布,都会加1{45、46、47、48、49、50、51、52} 154 | */ 155 | private static void readAndCheckVersion() { 156 | System.out.println("\r\n------------ 校验版本号 ------------"); 157 | 158 | //从class字节码第4位开始读取,读取2位 159 | byte[] minor_byte = new byte[2]; 160 | System.arraycopy(classData, 4, minor_byte, 0, 2); 161 | //将2位byte字节转成16进制字符串 162 | String minor_hex_str = new BigInteger(1, minor_byte).toString(16); 163 | System.out.println("minor_hex_str:" + minor_hex_str); 164 | //minor_unsigned_int32 转成无符号16进制 165 | int minor_unsigned_int32 = Integer.parseInt(minor_hex_str, 16); 166 | System.out.println("minor_unsigned_int32:" + minor_unsigned_int32); 167 | 168 | //从class字节码第6位开始读取,读取2位 169 | byte[] major_byte = new byte[2]; 170 | System.arraycopy(classData, 6, major_byte, 0, 2); 171 | //将2位byte字节转成16进制字符串 172 | String major_hex_str = new BigInteger(1, major_byte).toString(16); 173 | System.out.println("major_hex_str:" + major_hex_str); 174 | //major_unsigned_int32 转成无符号16进制 175 | int major_unsigned_int32 = Integer.parseInt(major_hex_str, 16); 176 | System.out.println("major_unsigned_int32:" + major_unsigned_int32); 177 | 178 | System.out.println("版本号:" + major_unsigned_int32 + "." + minor_unsigned_int32); 179 | 180 | } 181 | 182 | } 183 | ``` 184 | 185 | ## 测试结果 186 | ```java 187 | * byte字节码与运算原值(-54)换行后(-54 & 0x0FF):202 188 | 189 | ------------ 校验魔数 ------------ 190 | magic_hex_str:cafebabe 191 | magic_unsigned_int32:3405691582 192 | 0xCAFEBABE & 0x0FFFFFFFFL:3405691582 193 | class字节码魔数无符号16进制数值一致校验通过 194 | 195 | ------------ 校验版本号 ------------ 196 | minor_hex_str:0 197 | minor_unsigned_int32:0 198 | major_hex_str:34 199 | major_unsigned_int32:52 200 | 版本号:52.0 201 | 202 | Process finished with exit code 0 203 | ``` 204 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-14-netty案例,netty4.1基础入门篇十一《nettyudp通信方式案例Demo》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 在Netty通信中UDP的实现方式也非常简单,只要注意部分代码区别于TCP即可。本章节需要注意的知识点 ;NioDatagramChannel、ChannelOption.SO_BROADCAST 3 | 4 | >Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据报的方法。RFC 768 [1] 描述了 UDP。 5 | Internet 的传输层有两个主要协议,互为补充。无连接的是 UDP,它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的的事情。面向连接的是 TCP,该协议几乎做了所有的事情。 6 | 7 | ## 环境准备 8 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 9 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 10 | 3. NetAssist 网络调试助手,可以从网上下载也可以联系我,微信公众号:bugstack虫洞栈 | 关注回复你的邮箱 11 | 12 | ## 代码示例 13 | 14 | ```java 15 | itstack-demo-netty-1-11 16 | └── src 17 | ├── main 18 | │ └── java 19 | │ └── org.itstack.demo.netty 20 | │ ├── client 21 | │ │ ├── MyChannelInitializer.java 22 | │ │ ├── MyClientHandler.java 23 | │ │ └── NettyClient.java 24 | │ └── server 25 | │ ├── MyChannelInitializer.java 26 | │ ├── MyServerHandler.java 27 | │ └── NettyServer.java 28 | └── test 29 | └── java 30 | └── org.itstack.demo.netty.test 31 | └── ApiTest.java 32 | ``` 33 | 34 | >client/MyChannelInitializer.java 35 | 36 | ```java 37 | /** 38 | * 虫洞栈:https://bugstack.cn 39 | * 公众号:bugstack虫洞栈 {获取学习源码} 40 | * 虫洞群:①群5398358 ②群5360692 41 | * Create by fuzhengwei on @2019 42 | */ 43 | public class MyChannelInitializer extends ChannelInitializer { 44 | 45 | @Override 46 | protected void initChannel(NioDatagramChannel ch) throws Exception { 47 | ChannelPipeline pipeline = ch.pipeline(); 48 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 49 | //pipeline.addLast("stringDecoder", new StringDecoder(Charset.forName("GBK"))); 50 | pipeline.addLast(new MyClientHandler()); 51 | } 52 | 53 | } 54 | ``` 55 | 56 | >client/MyClientHandler.java 57 | 58 | ```java 59 | /** 60 | * 虫洞栈:https://bugstack.cn 61 | * 公众号:bugstack虫洞栈 {获取学习源码} 62 | * 虫洞群:①群5398358 ②群5360692 63 | * Create by fuzhengwei on @2019 64 | */ 65 | public class MyClientHandler extends SimpleChannelInboundHandler { 66 | 67 | //接受服务端发送的内容 68 | @Override 69 | protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { 70 | String msg = packet.content().toString(Charset.forName("GBK")); 71 | System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " UDP客户端接收到消息:" + msg); 72 | } 73 | 74 | } 75 | ``` 76 | 77 | >client/NettyClient.java 78 | 79 | ```java 80 | /** 81 | * 虫洞栈:https://bugstack.cn 82 | * 公众号:bugstack虫洞栈 {获取学习源码} 83 | * 虫洞群:①群5398358 ②群5360692 84 | * Create by fuzhengwei on @2019 85 | */ 86 | public class NettyClient { 87 | 88 | public static void main(String[] args) { 89 | EventLoopGroup group = new NioEventLoopGroup(); 90 | try { 91 | Bootstrap b = new Bootstrap(); 92 | b.group(group).channel(NioDatagramChannel.class) 93 | .handler(new MyChannelInitializer()); 94 | Channel ch = b.bind(7398).sync().channel(); 95 | //向目标端口发送信息 96 | ch.writeAndFlush(new DatagramPacket( 97 | Unpooled.copiedBuffer("你好端口7397的bugstack虫洞栈,我是客户端小爱,你在吗!", Charset.forName("GBK")), 98 | new InetSocketAddress("127.0.0.1", 7397))).sync(); 99 | ch.closeFuture().await(); 100 | } catch (Exception e) { 101 | e.printStackTrace(); 102 | } finally { 103 | group.shutdownGracefully(); 104 | } 105 | } 106 | 107 | } 108 | ``` 109 | 110 | >server/MyChannelInitializer.java 111 | 112 | ```java 113 | /** 114 | * 虫洞栈:https://bugstack.cn 115 | * 公众号:bugstack虫洞栈 {获取学习源码} 116 | * 虫洞群:①群5398358 ②群5360692 117 | * Create by fuzhengwei on @2019 118 | */ 119 | public class MyChannelInitializer extends ChannelInitializer { 120 | 121 | private EventLoopGroup group = new NioEventLoopGroup(); 122 | 123 | @Override 124 | protected void initChannel(NioDatagramChannel ch) throws Exception { 125 | ChannelPipeline pipeline = ch.pipeline(); 126 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 127 | //pipeline.addLast("stringDecoder", new StringDecoder(Charset.forName("GBK"))); 128 | pipeline.addLast(group, new MyServerHandler()); 129 | } 130 | 131 | } 132 | ``` 133 | 134 | >server/MyServerHandler.java 135 | 136 | ```java 137 | /** 138 | * 虫洞栈:https://bugstack.cn 139 | * 公众号:bugstack虫洞栈 {获取学习源码} 140 | * 虫洞群:①群5398358 ②群5360692 141 | * Create by fuzhengwei on @2019 142 | */ 143 | public class MyServerHandler extends SimpleChannelInboundHandler { 144 | 145 | @Override 146 | protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { 147 | String msg = packet.content().toString(Charset.forName("GBK")); 148 | System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " UDP服务端接收到消息:" + msg); 149 | 150 | //向客户端发送消息 151 | String json = "微信公众号:bugstack虫洞栈,通知:我已经收到你的消息\r\n"; 152 | // 由于数据报的数据是以字符数组传的形式存储的,所以传转数据 153 | byte[] bytes = json.getBytes(Charset.forName("GBK")); 154 | DatagramPacket data = new DatagramPacket(Unpooled.copiedBuffer(bytes), packet.sender()); 155 | ctx.writeAndFlush(data);//向客户端发送消息 156 | } 157 | 158 | } 159 | ``` 160 | 161 | >server/NettyServer.java 162 | 163 | ```java 164 | /** 165 | * 虫洞栈:https://bugstack.cn 166 | * 公众号:bugstack虫洞栈 {获取学习源码} 167 | * 虫洞群:①群5398358 ②群5360692 168 | * Create by fuzhengwei on @2019 169 | */ 170 | public class NettyServer { 171 | 172 | public static void main(String[] args) throws InterruptedException { 173 | EventLoopGroup group = new NioEventLoopGroup(); 174 | try { 175 | Bootstrap b = new Bootstrap(); 176 | b.group(group) 177 | .channel(NioDatagramChannel.class) 178 | .option(ChannelOption.SO_BROADCAST, true) //广播 179 | .option(ChannelOption.SO_RCVBUF, 2048 * 1024)// 设置UDP读缓冲区为2M 180 | .option(ChannelOption.SO_SNDBUF, 1024 * 1024)// 设置UDP写缓冲区为1M 181 | .handler(new MyChannelInitializer()); 182 | 183 | ChannelFuture f = b.bind(7397).sync(); 184 | System.out.println("itstack-demo-netty udp server start done. {关注公众号:bugstack虫洞栈,获取源码}"); 185 | f.channel().closeFuture().sync(); 186 | } finally { 187 | //优雅的关闭释放内存 188 | group.shutdownGracefully(); 189 | } 190 | 191 | } 192 | 193 | } 194 | ``` 195 | 196 | ## 测试结果 197 | 198 | >启动NettyServer 199 | 200 | ```java 201 | itstack-demo-netty udp server start done. {关注公众号:bugstack虫洞栈,获取源码} 202 | 2019-09-01 16:58:34 UDP服务端接收到消息:你好端口7397的bugstack虫洞栈,我是客户端小爱,你在吗! 203 | 2019-09-01 16:59:15 UDP服务端接收到消息:你好,有人在关注bugstack公众号,关注可以获得源码! 204 | 2019-09-01 16:59:15 UDP服务端接收到消息:你好,有人在关注bugstack公众号,关注可以获得源码! 205 | 2019-09-01 16:59:16 UDP服务端接收到消息:你好,有人在关注bugstack公众号,关注可以获得源码! 206 | 2019-09-01 16:59:17 UDP服务端接收到消息:你好,有人在关注bugstack公众号,关注可以获得源码! 207 | 2019-09-01 16:59:17 UDP服务端接收到消息:你好,有人在关注bugstack公众号,关注可以获得源码! 208 | 2019-09-01 16:59:18 UDP服务端接收到消息:你好,有人在关注bugstack公众号,关注可以获得源码! 209 | 2019-09-01 16:59:18 UDP服务端接收到消息:你好,有人在关注bugstack公众号,关注可以获得源码! 210 | 2019-09-01 16:59:19 UDP服务端接收到消息:你好,有人在关注bugstack公众号,关注可以获得源码! 211 | 2019-09-01 16:59:19 UDP服务端接收到消息:你好,有人在关注bugstack公众号,关注可以获得源码! 212 | 213 | Process finished with exit code -1 214 | 215 | ``` 216 | 217 | >启动NettyClient 218 | 219 | ```java 220 | 2019-09-01 16:58:34 UDP客户端接收到消息:微信公众号:bugstack虫洞栈,通知:我已经收到你的消息 221 | 222 | 223 | Process finished with exit code -1 224 | ``` 225 | 226 | >在启动一个网络调试助手,NetAssist | 这样方便我们验证 227 | 228 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/09/netty-1-11-1.png) -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-1/2019-08-13-netty案例,netty4.1基础入门篇十《关于ChannelOutboundHandlerAdapter简单使用》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | ChannelOutboundHandlerAdapter与ChannelInboundHandlerAdapter都是继承于ChannelHandler,并实现自己的ChannelXxxHandler。用于在消息管道中不同时机下处理处理消息。 3 | 4 | >ChannelInboundHandler拦截和处理入站事件,ChannelOutboundHandler拦截和处理出站事件。ChannelHandler和ChannelHandlerContext通过组合或继承的方式关联到一起成对使用。事件通过ChannelHandlerContext主动调用如read(msg)、write(msg)和fireXXX()等方法,将事件传播到下一个处理器。注意:入站事件在ChannelPipeline双向链表中由头到尾正向传播,出站事件则方向相反。 5 | 当客户端连接到服务器时,Netty新建一个ChannelPipeline处理其中的事件,而一个ChannelPipeline中含有若干ChannelHandler。如果每个客户端连接都新建一个ChannelHandler实例,当有大量客户端时,服务器将保存大量的ChannelHandler实例。为此,Netty提供了Sharable注解,如果一个ChannelHandler状态无关,那么可将其标注为Sharable,如此,服务器只需保存一个实例就能处理所有客户端的事件。 6 | 7 | ------------ 8 | 9 | 10 | #### ** ChannelHandler类图 ** 11 | ![ChannelHandler类图](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/ChannelHandler类图.png) 12 | 13 | ## 环境准备 14 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 15 | 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 16 | 17 | ## 代码示例 18 | ```java 19 | itstack-demo-netty-1-10 20 | └── src 21 | ├── main 22 | │ └── java 23 | │ └── org.itstack.demo.netty 24 | │ ├── client 25 | │ │ ├── MyChannelInitializer.java 26 | │ │ ├── MyInMsgHandler.java 27 | │ │ ├── MyOutMsgHandler.java 28 | │ │ └── NettyClient.java 29 | │ └── server 30 | │ ├── MyChannelInitializer.java 31 | │ ├── MyServerHandler.java 32 | │ └── NettyServer.java 33 | └── test 34 | └── java 35 | └── org.itstack.demo.test 36 | └── ApiTest.java 37 | ``` 38 | 39 | >client/MyChannelInitializer.java | 添加Handler执行器 40 | 41 | ```java 42 | /** 43 | * 虫洞栈:https://bugstack.cn 44 | * 公众号:bugstack虫洞栈 {获取学习源码} 45 | * Create by fuzhengwei on 2019 46 | */ 47 | public class MyChannelInitializer extends ChannelInitializer { 48 | 49 | @Override 50 | protected void initChannel(SocketChannel channel) throws Exception { 51 | // 基于换行符号 52 | channel.pipeline().addLast(new LineBasedFrameDecoder(1024)); 53 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 54 | channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK"))); 55 | // 解码转String,注意调整自己的编码格式GBK、UTF-8 56 | channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK"))); 57 | // 在管道中添加我们自己的接收数据实现方法 58 | channel.pipeline().addLast(new MyOutMsgHandler());//消息出站处理器,在Client发送消息时候会触发此处理器 59 | channel.pipeline().addLast(new MyInMsgHandler()); //消息入站处理器 60 | } 61 | 62 | } 63 | 64 | ``` 65 | 66 | >client/MyInMsgHandler.java | 继承于ChannelInboundHandlerAdapter 67 | 68 | ```java 69 | /** 70 | * 虫洞栈:https://bugstack.cn 71 | * 公众号:bugstack虫洞栈 {获取学习源码} 72 | * Create by fuzhengwei on 2019 73 | */ 74 | public class MyInMsgHandler extends ChannelInboundHandlerAdapter { 75 | 76 | /** 77 | * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据 78 | */ 79 | @Override 80 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 81 | SocketChannel channel = (SocketChannel) ctx.channel(); 82 | System.out.println("链接报告开始"); 83 | System.out.println("链接报告信息:本客户端链接到服务端。channelId:" + channel.id()); 84 | System.out.println("链接报告IP:" + channel.localAddress().getHostString()); 85 | System.out.println("链接报告Port:" + channel.localAddress().getPort()); 86 | System.out.println("链接报告完毕"); 87 | //通知客户端链接建立成功 88 | String str = "通知服务端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n"; 89 | ctx.writeAndFlush(str); 90 | } 91 | 92 | /** 93 | * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据 94 | */ 95 | @Override 96 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 97 | System.out.println("断开链接" + ctx.channel().localAddress().toString()); 98 | } 99 | 100 | @Override 101 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 102 | //接收msg消息{与上一章节相比,此处已经不需要自己进行解码} 103 | System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 客户端接收到消息:" + msg); 104 | //通知客户端链消息发送成功 105 | String str = "随机数:" + Math.random() * 10 + "\r\n"; 106 | ctx.writeAndFlush(str); 107 | } 108 | 109 | /** 110 | * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接 111 | */ 112 | @Override 113 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 114 | ctx.close(); 115 | System.out.println("异常信息:\r\n" + cause.getMessage()); 116 | } 117 | 118 | } 119 | ``` 120 | 121 | >client/MyOutMsgHandler.java | 处理出站信息,read与write方法,其他方法可以自行添加验证 122 | 123 | ```java 124 | /** 125 | * 虫洞栈:https://bugstack.cn 126 | * 公众号:bugstack虫洞栈 {获取学习源码} 127 | * Create by fuzhengwei on 2019 128 | */ 129 | public class MyOutMsgHandler extends ChannelOutboundHandlerAdapter { 130 | 131 | @Override 132 | public void read(ChannelHandlerContext ctx) throws Exception { 133 | ctx.writeAndFlush("ChannelOutboundHandlerAdapter.read 发来一条消息\r\n"); 134 | super.read(ctx); 135 | } 136 | 137 | @Override 138 | public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { 139 | ctx.writeAndFlush("ChannelOutboundHandlerAdapter.write 发来一条消息\r\n"); 140 | super.write(ctx, msg, promise); 141 | } 142 | 143 | } 144 | ``` 145 | 146 | >server/MyServerHandler.java 147 | 148 | ```java 149 | /** 150 | * 虫洞栈:https://bugstack.cn 151 | * 公众号:bugstack虫洞栈 {获取学习源码} 152 | * Create by fuzhengwei on 2019 153 | */ 154 | public class MyServerHandler extends ChannelInboundHandlerAdapter { 155 | 156 | /** 157 | * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据 158 | */ 159 | @Override 160 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 161 | SocketChannel channel = (SocketChannel) ctx.channel(); 162 | System.out.println("链接报告开始"); 163 | System.out.println("链接报告信息:有一客户端链接到本服务端"); 164 | System.out.println("链接报告IP:" + channel.localAddress().getHostString()); 165 | System.out.println("链接报告Port:" + channel.localAddress().getPort()); 166 | System.out.println("链接报告完毕"); 167 | //通知客户端链接建立成功 168 | String str = "通知客户端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n"; 169 | //ctx.writeAndFlush(str); 170 | } 171 | 172 | /** 173 | * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据 174 | */ 175 | @Override 176 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 177 | System.out.println("客户端断开链接" + ctx.channel().localAddress().toString()); 178 | } 179 | 180 | @Override 181 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 182 | //接收msg消息{与上一章节相比,此处已经不需要自己进行解码} 183 | System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 服务端接收到消息:" + msg); 184 | //通知客户端链消息发送成功 185 | String str = "随机数:" + Math.random() * 10 + "\r\n"; 186 | //ctx.writeAndFlush(str); 187 | } 188 | 189 | /** 190 | * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接 191 | */ 192 | @Override 193 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 194 | ctx.close(); 195 | System.out.println("异常信息:\r\n" + cause.getMessage()); 196 | } 197 | 198 | } 199 | ``` 200 | 201 | ## 测试结果 202 | 203 | >启动NettyServer 204 | 205 | ```java 206 | itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码} 207 | 链接报告开始 208 | 链接报告信息:有一客户端链接到本服务端 209 | 链接报告IP:127.0.0.1 210 | 链接报告Port:7397 211 | 链接报告完毕 212 | 2019-08-25 13:38:34 服务端接收到消息:ChannelOutboundHandlerAdapter.write 发来一条消息 213 | 2019-08-25 13:38:34 服务端接收到消息:通知服务端链接建立成功 Tue Aug 27 13:38:34 CST 2019 127.0.0.1 214 | 2019-08-25 13:38:34 服务端接收到消息:ChannelOutboundHandlerAdapter.read 发来一条消息 215 | 216 | Process finished with exit code -1 217 | ``` 218 | 219 | >启动NettyClient 220 | 221 | ```java 222 | 链接报告开始 223 | itstack-demo-netty client start done. {关注公众号:bugstack虫洞栈,获取源码} 224 | 链接报告信息:本客户端链接到服务端。channelId:47e1b185 225 | 链接报告IP:127.0.0.1 226 | 链接报告Port:63884 227 | 链接报告完毕 228 | 异常信息: 229 | 远程主机强迫关闭了一个现有的连接。 230 | 断开链接/127.0.0.1:63884 231 | 232 | Process finished with exit code -1 233 | 234 | ``` -------------------------------------------------------------------------------- /docs/notes/itstack-demo-netty/itstack-demo-netty-4/2019-09-12-netty案例,netty4.1源码分析篇三《Netty服务端初始化过程以及反射工厂的作用》.md: -------------------------------------------------------------------------------- 1 | 本章节主要分析Netty在启动过程中的配置内容以及最终调用bind方法是如何启动Netty服务端的。 2 | 3 | >Netty服务启动模板代码 4 | 5 | ```java 6 | private void bing(int port) { 7 | //配置服务端NIO线程组 8 | EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); 9 | EventLoopGroup childGroup = new NioEventLoopGroup(); 10 | try { 11 | ServerBootstrap b = new ServerBootstrap(); 12 | b.group(parentGroup, childGroup) 13 | .channel(NioServerSocketChannel.class) //非阻塞模式 14 | .option(ChannelOption.SO_BACKLOG, 128) 15 | .childHandler(new MyChannelInitializer()); 16 | ChannelFuture f = b.bind(port).sync(); 17 | System.out.println("itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}"); 18 | f.channel().closeFuture().sync(); 19 | } catch (InterruptedException e) { 20 | e.printStackTrace(); 21 | } finally { 22 | childGroup.shutdownGracefully(); 23 | parentGroup.shutdownGracefully(); 24 | } 25 | 26 | } 27 | ``` 28 | 29 | ** ServerBootstrap ** 30 | 31 | - 设定相关属性的服务类 32 | - 实现了AbstractBootstrap方法,里面的泛型ServerChannel是一个标记接口,不做实际方法 33 | - ServerSocketChannel提供了三个方法;配置、本地地址、远程地址,用于接收处理TCP/IP连接 34 | 35 | ```java 36 | public interface ServerSocketChannel extends ServerChannel { 37 | @Override 38 | ServerSocketChannelConfig config(); 39 | @Override 40 | InetSocketAddress localAddress(); 41 | @Override 42 | InetSocketAddress remoteAddress(); 43 | } 44 | ``` 45 | 46 | 在Netty启动过程中,我们分别通过调用.group、.channel、.option、.childHandler来配置服务端信息,最后调用.bind()来启动服务。 47 | 48 | >.group | 用于处理事件循环组的方法 49 | 50 | ```java 51 | public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) { 52 | super.group(parentGroup); 53 | if (childGroup == null) { 54 | throw new NullPointerException("childGroup"); 55 | } 56 | if (this.childGroup != null) { 57 | throw new IllegalStateException("childGroup set already"); 58 | } 59 | this.childGroup = childGroup; 60 | return this; 61 | } 62 | ``` 63 | 64 | group方法通过为parentGroup、childGroup设置事件循环组(EventLoopGroup),用于处理事件内容与IO请求。也就是我们用于等待接收客户端连接与信息内容交互。 65 | 66 | >.channel | 通过反射方式创建通信通道的方法 67 | 68 | ```java 69 | public B channel(Class channelClass) { 70 | if (channelClass == null) { 71 | throw new NullPointerException("channelClass"); 72 | } 73 | return channelFactory(new ReflectiveChannelFactory(channelClass)); 74 | } 75 | ``` 76 | 77 | 在这个方法中ReflectiveChannelFactory反射工厂类通过构造函数,传递channelClass这个参数,来实例化反射工厂。这个channelClass类,就是我们在配置中传递的异步NIO流的服务端Socket管道,NioServerSocketChannel。最后将工厂信息传递到channel中,用于后续实例化无参的构造函数,并在后续提供调用NioServerSocketChannel方法的能力。 78 | 79 | ReflectiveChannelFactory类中仅是提供了一个非常简单的方法,用于获取实例化; 80 | ```java 81 | public T newChannel() { 82 | try { 83 | return constructor.newInstance(); 84 | } catch (Throwable t) { 85 | throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t); 86 | } 87 | } 88 | ``` 89 | 90 | >.option | 是一个选项配置类,可以增加一些配置参数 91 | 92 | ```java 93 | public B option(ChannelOption option, T value) { 94 | if (option == null) { 95 | throw new NullPointerException("option"); 96 | } 97 | if (value == null) { 98 | synchronized (options) { 99 | options.remove(option); 100 | } 101 | } else { 102 | synchronized (options) { 103 | options.put(option, value); 104 | } 105 | } 106 | return self(); 107 | } 108 | ``` 109 | 110 | option是Netty为我们提供的配置选项,它包含但不限于;ChannelOption.SO_BACKLOG、ChannelOption.SO_TIMEOUT、ChannelOption.TCP_NODELAY等,option并不是非的配置,如果不配置也是可以正常启动的。 111 | 112 | ------------ 113 | 114 | 115 | 1、ChannelOption.SO_BACKLOG 116 | ChannelOption.SO_BACKLOG对应的是tcp/ip协议listen函数中的backlog参数,函数listen(int socketfd,int backlog)用来初始化服务端可连接队列,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小 117 | 118 | 2、ChannelOption.SO_REUSEADDR 119 | ChanneOption.SO_REUSEADDR对应于套接字选项中的SO_REUSEADDR,这个参数表示允许重复使用本地地址和端口, 120 | 比如,某个服务器进程占用了TCP的80端口进行监听,此时再次监听该端口就会返回错误,使用该参数就可以解决问题,该参数允许共用该端口,这个在服务器程序中比较常使用, 121 | 比如某个进程非正常退出,该程序占用的端口可能要被占用一段时间才能允许其他进程使用,而且程序死掉以后,内核一需要一定的时间才能够释放此端口,不设置SO_REUSEADDR就无法正常使用该端口。 122 | 123 | 3、ChannelOption.SO_KEEPALIVE 124 | Channeloption.SO_KEEPALIVE参数对应于套接字选项中的SO_KEEPALIVE,该参数用于设置TCP连接,当设置该选项以后,连接会测试链接的状态,这个选项用于可能长时间没有数据交流的连接。当设置该选项以后,如果在两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文。 125 | 126 | 4、ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF 127 | ChannelOption.SO_SNDBUF参数对应于套接字选项中的SO_SNDBUF,ChannelOption.SO_RCVBUF参数对应于套接字选项中的SO_RCVBUF这两个参数用于操作接收缓冲区和发送缓冲区的大小,接收缓冲区用于保存网络协议站内收到的数据,直到应用程序读取成功,发送缓冲区用于保存发送数据,直到发送成功。 128 | 129 | 5、ChannelOption.SO_LINGER 130 | ChannelOption.SO_LINGER参数对应于套接字选项中的SO_LINGER,Linux内核默认的处理方式是当用户调用close()方法的时候,函数返回,在可能的情况下,尽量发送数据,不一定保证会发生剩余的数据,造成了数据的不确定性,使用SO_LINGER可以阻塞close()的调用时间,直到数据完全发送 131 | 132 | 6、ChannelOption.TCP_NODELAY 133 | ChannelOption.TCP_NODELAY参数对应于套接字选项中的TCP_NODELAY,该参数的使用与Nagle算法有关,Nagle算法是将小的数据包组装为更大的帧然后进行发送,而不是输入一次发送一次,因此在数据包不足的时候会等待其他数据的到了,组装成大的数据包进行发送,虽然该方式有效提高网络的有效负载,但是却造成了延时,而该参数的作用就是禁止使用Nagle算法,使用于小数据即时传输,于TCP_NODELAY相对应的是TCP_CORK,该选项是需要等到发送的数据量最大的时候,一次性发送数据,适用于文件传输。 134 | 135 | 7、IP_TOS 136 | IP参数,设置IP头部的Type-of-Service字段,用于描述IP包的优先级和QoS选项。 137 | 138 | 8、ALLOW_HALF_CLOSURE 139 | Netty参数,一个连接的远端关闭时本地端是否关闭,默认值为False。值为False时,连接自动关闭;为True时,触发ChannelInboundHandler的userEventTriggered()方法,事件为ChannelInputShutdownEvent。 140 | 141 | ------------ 142 | 143 | 144 | >.childHandler | 设置自己的管道服务,接收信息处理 (另外还有一个handler方法是被parentGroup所使用) 145 | 146 | ```java 147 | public ServerBootstrap childHandler(ChannelHandler childHandler) { 148 | if (childHandler == null) { 149 | throw new NullPointerException("childHandler"); 150 | } 151 | this.childHandler = childHandler; 152 | return this; 153 | } 154 | ``` 155 | 156 | ** 以上信息配置完成后,我们的服务端通过调用bind()方法来启动服务端 ** 157 | 158 | ```java 159 | b.bind(port).sync(); 160 | ``` 161 | 162 | ```java 163 | private ChannelFuture doBind(final SocketAddress localAddress) { 164 | final ChannelFuture regFuture = initAndRegister(); 165 | final Channel channel = regFuture.channel(); 166 | if (regFuture.cause() != null) { 167 | return regFuture; 168 | } 169 | 170 | if (regFuture.isDone()) { 171 | // At this point we know that the registration was complete and successful. 172 | ChannelPromise promise = channel.newPromise(); 173 | doBind0(regFuture, channel, localAddress, promise); 174 | return promise; 175 | } else { 176 | // Registration future is almost always fulfilled already, but just in case it's not. 177 | final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel); 178 | regFuture.addListener(new ChannelFutureListener() { 179 | @Override 180 | public void operationComplete(ChannelFuture future) throws Exception { 181 | Throwable cause = future.cause(); 182 | if (cause != null) { 183 | // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an 184 | // IllegalStateException once we try to access the EventLoop of the Channel. 185 | promise.setFailure(cause); 186 | } else { 187 | // Registration was successful, so set the correct executor to use. 188 | // See https://github.com/netty/netty/issues/2586 189 | promise.registered(); 190 | 191 | doBind0(regFuture, channel, localAddress, promise); 192 | } 193 | } 194 | }); 195 | return promise; 196 | } 197 | } 198 | ``` 199 | 200 | 这里面的第一行代码initAndRegister,里面通过反射工厂使用了我们的配置的NioServerSocketChannel.class,来实例化NioServerSocketChannel。实例化后NioServerSocketChannel会随之启动Netty服务; 201 | 202 | ```java 203 | private static ServerSocketChannel newSocket(SelectorProvider provider) { 204 | try { 205 | /** 206 | * Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in 207 | * {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise. 208 | * 209 | * See #2308. 210 | */ 211 | return provider.openServerSocketChannel(); 212 | } catch (IOException e) { 213 | throw new ChannelException( 214 | "Failed to open a server socket.", e); 215 | } 216 | } 217 | ``` 218 | 219 | ```java 220 | ServerSocketChannelImpl(SelectorProvider sp) throws IOException { 221 | super(sp); 222 | this.fd = Net.serverSocket(true); 223 | this.fdVal = IOUtil.fdVal(fd); 224 | this.state = ST_INUSE; 225 | } 226 | ``` -------------------------------------------------------------------------------- /docs/assets/js/prism-bash.js: -------------------------------------------------------------------------------- 1 | (function(Prism) { 2 | // $ set | grep '^[A-Z][^[:space:]]*=' | cut -d= -f1 | tr '\n' '|' 3 | // + LC_ALL, RANDOM, REPLY, SECONDS. 4 | // + make sure PS1..4 are here as they are not always set, 5 | // - some useless things. 6 | var envVars = '\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b'; 7 | var insideString = { 8 | 'environment': { 9 | pattern: RegExp("\\$" + envVars), 10 | alias: 'constant' 11 | }, 12 | 'variable': [ 13 | // [0]: Arithmetic Environment 14 | { 15 | pattern: /\$?\(\([\s\S]+?\)\)/, 16 | greedy: true, 17 | inside: { 18 | // If there is a $ sign at the beginning highlight $(( and )) as variable 19 | 'variable': [ 20 | { 21 | pattern: /(^\$\(\([\s\S]+)\)\)/, 22 | lookbehind: true 23 | }, 24 | /^\$\(\(/ 25 | ], 26 | 'number': /\b0x[\dA-Fa-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee]-?\d+)?/, 27 | // Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic 28 | 'operator': /--?|-=|\+\+?|\+=|!=?|~|\*\*?|\*=|\/=?|%=?|<<=?|>>=?|<=?|>=?|==?|&&?|&=|\^=?|\|\|?|\|=|\?|:/, 29 | // If there is no $ sign at the beginning highlight (( and )) as punctuation 30 | 'punctuation': /\(\(?|\)\)?|,|;/ 31 | } 32 | }, 33 | // [1]: Command Substitution 34 | { 35 | pattern: /\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/, 36 | greedy: true, 37 | inside: { 38 | 'variable': /^\$\(|^`|\)$|`$/ 39 | } 40 | }, 41 | // [2]: Brace expansion 42 | { 43 | pattern: /\$\{[^}]+\}/, 44 | greedy: true, 45 | inside: { 46 | 'operator': /:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/, 47 | 'punctuation': /[\[\]]/, 48 | 'environment': { 49 | pattern: RegExp("(\\{)" + envVars), 50 | lookbehind: true, 51 | alias: 'constant' 52 | } 53 | } 54 | }, 55 | /\$(?:\w+|[#?*!@$])/ 56 | ], 57 | // Escape sequences from echo and printf's manuals, and escaped quotes. 58 | 'entity': /\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})/ 59 | }; 60 | 61 | Prism.languages.bash = { 62 | 'shebang': { 63 | pattern: /^#!\s*\/.*/, 64 | alias: 'important' 65 | }, 66 | 'comment': { 67 | pattern: /(^|[^"{\\$])#.*/, 68 | lookbehind: true 69 | }, 70 | 'function-name': [ 71 | // a) function foo { 72 | // b) foo() { 73 | // c) function foo() { 74 | // but not “foo {” 75 | { 76 | // a) and c) 77 | pattern: /(\bfunction\s+)\w+(?=(?:\s*\(?:\s*\))?\s*\{)/, 78 | lookbehind: true, 79 | alias: 'function' 80 | }, 81 | { 82 | // b) 83 | pattern: /\b\w+(?=\s*\(\s*\)\s*\{)/, 84 | alias: 'function' 85 | } 86 | ], 87 | // Highlight variable names as variables in for and select beginnings. 88 | 'for-or-select': { 89 | pattern: /(\b(?:for|select)\s+)\w+(?=\s+in\s)/, 90 | alias: 'variable', 91 | lookbehind: true 92 | }, 93 | // Highlight variable names as variables in the left-hand part 94 | // of assignments (“=” and “+=”). 95 | 'assign-left': { 96 | pattern: /(^|[\s;|&]|[<>]\()\w+(?=\+?=)/, 97 | inside: { 98 | 'environment': { 99 | pattern: RegExp("(^|[\\s;|&]|[<>]\\()" + envVars), 100 | lookbehind: true, 101 | alias: 'constant' 102 | } 103 | }, 104 | alias: 'variable', 105 | lookbehind: true 106 | }, 107 | 'string': [ 108 | // Support for Here-documents https://en.wikipedia.org/wiki/Here_document 109 | { 110 | pattern: /((?:^|[^<])<<-?\s*)(\w+?)\s*(?:\r?\n|\r)(?:[\s\S])*?(?:\r?\n|\r)\2/, 111 | lookbehind: true, 112 | greedy: true, 113 | inside: insideString 114 | }, 115 | // Here-document with quotes around the tag 116 | // → No expansion (so no “inside”). 117 | { 118 | pattern: /((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s*(?:\r?\n|\r)(?:[\s\S])*?(?:\r?\n|\r)\3/, 119 | lookbehind: true, 120 | greedy: true 121 | }, 122 | // “Normal” string 123 | { 124 | pattern: /(["'])(?:\\[\s\S]|\$\([^)]+\)|`[^`]+`|(?!\1)[^\\])*\1/, 125 | greedy: true, 126 | inside: insideString 127 | } 128 | ], 129 | 'environment': { 130 | pattern: RegExp("\\$?" + envVars), 131 | alias: 'constant' 132 | }, 133 | 'variable': insideString.variable, 134 | 'function': { 135 | pattern: /(^|[\s;|&]|[<>]\()(?:add|apropos|apt|aptitude|apt-cache|apt-get|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/, 136 | lookbehind: true 137 | }, 138 | 'keyword': { 139 | pattern: /(^|[\s;|&]|[<>]\()(?:if|then|else|elif|fi|for|while|in|case|esac|function|select|do|done|until)(?=$|[)\s;|&])/, 140 | lookbehind: true 141 | }, 142 | // https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html 143 | 'builtin': { 144 | pattern: /(^|[\s;|&]|[<>]\()(?:\.|:|break|cd|continue|eval|exec|exit|export|getopts|hash|pwd|readonly|return|shift|test|times|trap|umask|unset|alias|bind|builtin|caller|command|declare|echo|enable|help|let|local|logout|mapfile|printf|read|readarray|source|type|typeset|ulimit|unalias|set|shopt)(?=$|[)\s;|&])/, 145 | lookbehind: true, 146 | // Alias added to make those easier to distinguish from strings. 147 | alias: 'class-name' 148 | }, 149 | 'boolean': { 150 | pattern: /(^|[\s;|&]|[<>]\()(?:true|false)(?=$|[)\s;|&])/, 151 | lookbehind: true 152 | }, 153 | 'file-descriptor': { 154 | pattern: /\B&\d\b/, 155 | alias: 'important' 156 | }, 157 | 'operator': { 158 | // Lots of redirections here, but not just that. 159 | pattern: /\d?<>|>\||\+=|==?|!=?|=~|<<[<-]?|[&\d]?>>|\d?[<>]&?|&[>&]?|\|[&|]?|<=?|>=?/, 160 | inside: { 161 | 'file-descriptor': { 162 | pattern: /^\d/, 163 | alias: 'important' 164 | } 165 | } 166 | }, 167 | 'punctuation': /\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/, 168 | 'number': { 169 | pattern: /(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/, 170 | lookbehind: true 171 | } 172 | }; 173 | 174 | /* Patterns in command substitution. */ 175 | var toBeCopied = [ 176 | 'comment', 177 | 'function-name', 178 | 'for-or-select', 179 | 'assign-left', 180 | 'string', 181 | 'environment', 182 | 'function', 183 | 'keyword', 184 | 'builtin', 185 | 'boolean', 186 | 'file-descriptor', 187 | 'operator', 188 | 'punctuation', 189 | 'number' 190 | ]; 191 | var inside = insideString.variable[1].inside; 192 | for(var i = 0; i < toBeCopied.length; i++) { 193 | inside[toBeCopied[i]] = Prism.languages.bash[toBeCopied[i]]; 194 | } 195 | 196 | Prism.languages.shell = Prism.languages.bash; 197 | })(Prism); 198 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-ddd/2019-10-15-DDD专题案例一《初识领域驱动设计DDD落地》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | >DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。 3 | 4 | ![微信公众号:bugstack虫洞栈 & DDD概述](https://fuzhengwei.github.io/assets/images/pic-content/2019/10/DDD-01-1.png) 5 | 6 | **开发目标** 7 | 8 | 依靠领域驱动设计的设计思想,通过事件风暴建立领域模型,合理划分领域逻辑和物理边界,建立领域对象及服务矩阵和服务架构图,定义符合DDD分层架构思想的代码结构模型,保证业务模型与代码模型的一致性。通过上述设计思想、方法和过程,指导团队按照DDD设计思想完成微服务设计和开发。 9 | 1. 拒绝泥球小单体、拒绝污染功能与服务、拒绝一加功能排期一个月 10 | 2. 架构出高可用极易符合互联网高速迭代的应用服务 11 | 3. 物料化、组装化、可编排的服务,提高人效 12 | 13 | **服务架构** 14 | ![微信公众号:bugstack虫洞栈 & 服务架构](https://fuzhengwei.github.io/assets/images/pic-content/2019/10/DDD-01-2.png) 15 | 16 | - 应用层{application} 17 | 18 | - 应用服务位于应用层。用来表述应用和用户行为,负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装。 19 | - 应用层的服务包括应用服务和领域事件相关服务。 20 | - 应用服务可对微服务内的领域服务以及微服务外的应用服务进行组合和编排,或者对基础层如文件、缓存等数据直接操作形成应用服务,对外提供粗粒度的服务。 21 | - 领域事件服务包括两类:领域事件的发布和订阅。通过事件总线和消息队列实现异步数据传输,实现微服务之间的解耦。 22 | 23 | - 领域层{domain} 24 | 25 | - 领域服务位于领域层,为完成领域中跨实体或值对象的操作转换而封装的服务,领域服务以与实体和值对象相同的方式参与实施过程。 26 | - 领域服务对同一个实体的一个或多个方法进行组合和封装,或对多个不同实体的操作进行组合或编排,对外暴露成领域服务。领域服务封装了核心的业务逻辑。实体自身的行为在实体类内部实现,向上封装成领域服务暴露。 27 | - 为隐藏领域层的业务逻辑实现,所有领域方法和服务等均须通过领域服务对外暴露。 28 | - 为实现微服务内聚合之间的解耦,原则上禁止跨聚合的领域服务调用和跨聚合的数据相互关联。 29 | 30 | - 基础层{infrastructrue} 31 | 32 | - 基础服务位于基础层。为各层提供资源服务(如数据库、缓存等),实现各层的解耦,降低外部资源变化对业务逻辑的影响。 33 | - 基础服务主要为仓储服务,通过依赖反转的方式为各层提供基础资源服务,领域服务和应用服务调用仓储服务接口,利用仓储实现持久化数据对象或直接访问基础资源。 34 | 35 | - 接口层{interfaces} 36 | 37 | - 接口服务位于用户接口层,用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给应用层。 38 | 39 | ## 开发环境 40 | 1. jdk1.8【jdk1.7以下只能部分支持netty】 41 | 2. springboot 2.0.6.RELEASE 42 | 3. idea + maven 43 | 44 | ## 代码示例 45 | ```java 46 | itstack-demo-ddd-01 47 | └── src 48 | ├── main 49 | │ ├── java 50 | │ │ └── org.itstack.demo 51 | │ │ ├── application 52 | │ │ │ ├── event 53 | │ │ │ │ └── ApplicationRunner.java 54 | │ │ │ └── service 55 | │ │ │ └── UserService.java 56 | │ │ ├── domain 57 | │ │ │ ├── model 58 | │ │ │ │ ├── aggregates 59 | │ │ │ │ │ └── UserRichInfo.java 60 | │ │ │ │ └── vo 61 | │ │ │ │ ├── UserInfo.java 62 | │ │ │ │ └── UserSchool.java 63 | │ │ │ ├── repository 64 | │ │ │ │ └── IuserRepository.java 65 | │ │ │ └── service 66 | │ │ │ └── UserServiceImpl.java 67 | │ │ ├── infrastructure 68 | │ │ │ ├── dao 69 | │ │ │ │ ├── impl 70 | │ │ │ │ │ └── UserDaoImpl.java 71 | │ │ │ │ └── UserDao.java 72 | │ │ │ ├── po 73 | │ │ │ │ └── UserEntity.java 74 | │ │ │ ├── repository 75 | │ │ │ │ ├── mysql 76 | │ │ │ │ │ └── UserMysqlRepository.java 77 | │ │ │ │ ├── redis 78 | │ │ │ │ │ └── UserRedisRepository.java 79 | │ │ │ │ └── UserRepository.java 80 | │ │ │ └── util 81 | │ │ │ └── RdisUtil.java 82 | │ │ ├── interfaces 83 | │ │ │ ├── dto 84 | │ │ │ │ └── UserInfoDto.java 85 | │ │ │ └── facade 86 | │ │ │ └── DDDController.java 87 | │ │ └── DDDApplication.java 88 | │ ├── resources 89 | │ │ └── application.yml 90 | │ └── webapp 91 | │ └── WEB-INF 92 | │ └── index.jsp 93 | └── test 94 | └── java 95 | └── org.itstack.demo.test 96 | └── ApiTest.java 97 | ``` 98 | 99 | **演示部分重点代码块,完整代码下载关注公众号;bugstack虫洞栈 | 回复DDD落地** 100 | 101 | >application/UserService.java | 应用层用户服务,领域层服务做具体实现 102 | 103 | ```java 104 | /** 105 | * 应用层用户服务 106 | * 虫洞栈:https://bugstack.cn 107 | * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码 108 | * Create by fuzhengwei on @2019 109 | */ 110 | public interface UserService { 111 | 112 | UserRichInfo queryUserInfoById(Long id); 113 | 114 | } 115 | ``` 116 | 117 | >domain/repository/IuserRepository.java | 领域层资源库,由基础层实现 118 | 119 | ```java 120 | /** 121 | * 虫洞栈:https://bugstack.cn 122 | * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码 123 | * Create by fuzhengwei on @2019 124 | */ 125 | public interface IUserRepository { 126 | 127 | void save(UserEntity userEntity); 128 | 129 | UserEntity query(Long id); 130 | 131 | } 132 | ``` 133 | 134 | >domain/service/UserServiceImpl.java | 应用层实现类,应用层是很薄的一层可以只做服务编排 135 | 136 | ```java 137 | /** 138 | * 虫洞栈:https://bugstack.cn 139 | * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码 140 | * Create by fuzhengwei on @2019 141 | */ 142 | @Service("userService") 143 | public class UserServiceImpl implements UserService { 144 | 145 | @Resource(name = "userRepository") 146 | private IUserRepository userRepository; 147 | 148 | @Override 149 | public UserRichInfo queryUserInfoById(Long id) { 150 | 151 | // 查询资源库 152 | UserEntity userEntity = userRepository.query(id); 153 | 154 | UserInfo userInfo = new UserInfo(); 155 | userInfo.setName(userEntity.getName()); 156 | 157 | // TODO 查询学校信息,外部接口 158 | UserSchool userSchool_01 = new UserSchool(); 159 | userSchool_01.setSchoolName("振华高级实验中学"); 160 | 161 | UserSchool userSchool_02 = new UserSchool(); 162 | userSchool_02.setSchoolName("东北电力大学"); 163 | 164 | List userSchoolList = new ArrayList<>(); 165 | userSchoolList.add(userSchool_01); 166 | userSchoolList.add(userSchool_02); 167 | 168 | UserRichInfo userRichInfo = new UserRichInfo(); 169 | userRichInfo.setUserInfo(userInfo); 170 | userRichInfo.setUserSchoolList(userSchoolList); 171 | 172 | return userRichInfo; 173 | } 174 | 175 | } 176 | ``` 177 | 178 | >infrastructure/po/UserEntity.java | 数据库对象类 179 | 180 | ```java 181 | /** 182 | * 数据库实体对象;用户实体 183 | * 虫洞栈:https://bugstack.cn 184 | * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码 185 | * Create by fuzhengwei on @2019 186 | */ 187 | public class UserEntity { 188 | 189 | private Long id; 190 | private String name; 191 | 192 | get/set ... 193 | } 194 | ``` 195 | 196 | >infrastructrue/repository/UserRepository.java | 领域层定义接口,基础层资源库实现 197 | 198 | ```java 199 | /** 200 | * 虫洞栈:https://bugstack.cn 201 | * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码 202 | * Create by fuzhengwei on @2019 203 | */ 204 | @Repository("userRepository") 205 | public class UserRepository implements IUserRepository { 206 | 207 | @Resource(name = "userMysqlRepository") 208 | private IUserRepository userMysqlRepository; 209 | 210 | @Resource(name = "userRedisRepository") 211 | private IUserRepository userRedisRepository; 212 | 213 | @Override 214 | public void save(UserEntity userEntity) { 215 | //保存到DB 216 | userMysqlRepository.save(userEntity); 217 | 218 | //保存到Redis 219 | userRedisRepository.save(userEntity); 220 | } 221 | 222 | @Override 223 | public UserEntity query(Long id) { 224 | 225 | UserEntity userEntityRedis = userRedisRepository.query(id); 226 | if (null != userEntityRedis) return userEntityRedis; 227 | 228 | UserEntity userEntityMysql = userMysqlRepository.query(id); 229 | if (null != userEntityMysql){ 230 | //保存到Redis 231 | userRedisRepository.save(userEntityMysql); 232 | return userEntityMysql; 233 | } 234 | 235 | // 查询为NULL 236 | return null; 237 | } 238 | 239 | } 240 | ``` 241 | 242 | >interfaces/dto/UserInfoDto.java | DTO对象类,隔离数据库类 243 | 244 | ```java 245 | /** 246 | * 虫洞栈:https://bugstack.cn 247 | * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码 248 | * Create by fuzhengwei on @2019 249 | */ 250 | public class UserInfoDto { 251 | 252 | private Long id; // ID 253 | 254 | public Long getId() { 255 | return id; 256 | } 257 | 258 | public void setId(Long id) { 259 | this.id = id; 260 | } 261 | 262 | } 263 | ``` 264 | 265 | >interfaces/facade/DDDController.java | 门面接口 266 | 267 | ```java 268 | /** 269 | * 虫洞栈:https://bugstack.cn 270 | * 公众号:bugstack虫洞栈 | 欢迎关注并获取更多专题案例源码 271 | * Create by fuzhengwei on @2019 272 | */ 273 | @Controller 274 | public class DDDController { 275 | 276 | @Resource(name = "userService") 277 | private UserService userService; 278 | 279 | @RequestMapping("/index") 280 | public String index(Model model) { 281 | return "index"; 282 | } 283 | 284 | @RequestMapping("/api/user/queryUserInfo") 285 | @ResponseBody 286 | public ResponseEntity queryUserInfo(@RequestBody UserInfoDto request) { 287 | return new ResponseEntity<>(userService.queryUserInfoById(request.getId()), HttpStatus.OK); 288 | } 289 | 290 | } 291 | ``` 292 | 293 | ## 综上总结 294 | - **以上基于DDD一个基本入门的结构演示完成,实际开发可以按照此模式进行调整。** 295 | - **目前这个架构分层还不能很好的进行分离,以及层级关系的引用还不利于扩展。** 296 | - **后续会持续完善以及可以组合搭建RPC框架等,让整个架构更利于互联网开发。** 297 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-jvm/2019-05-05-用Java实现JVM第四章《运行时数据区》.md: -------------------------------------------------------------------------------- 1 | ![](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/jvm04.png) 2 | 3 | ## 案例介绍 4 | 本案例初步实现运行时数据区里;线程、Java虚拟机栈、帧、操作数栈、局部变量表。 5 | >在运行Java程序时,Java虚拟机需要使用内存来存放各种各样的数据。Java虚拟机规范把这些内存区域叫作运行时数据区。运行时数据区可以分为两类:一类是多线程共享的,另一类则是线程私有的。多线程共享的运行时数据区需要在Java虚拟机启动时创建好在Java虚拟机推出时销毁。线程私有的运行时数据区则在创建线程时才创建,线程退出时销毁。 6 | 7 | >线程私有的运行时数据区用于辅助执行Java字节码。每个线程都有自己的pc寄存器(Program Counter)和Java虚拟机栈(JVM Stack)。Java虚拟机栈又由栈帧(Stack Frame,后面简称帧)构成,帧中保存方法执行的状态,包括局部变量表(Local Variable)和操作数栈(Operand Stack)等。在任一时刻,某一线程肯定是在执行某个方法。这个方法叫作该线程的当前方法;执行该方法的帧叫作线程的当前帧;声明该方法的类叫作当前类。如果当前方法是Java方法,则pc寄存器中存放当前正在执行的Java虚拟机指令的地址,否则,当前方法是本地方法,pc寄存器中的值没有明确定义。 8 | 9 | ```java 10 | Run-Time Data Area 11 | ├── Thread 12 | │ └── pc 13 | │ └── Jvm Stack 14 | │ └── Frame 15 | │ ├── Local Variable 16 | │ └── Operand Stack 17 | └── Heap 18 | ├── Method Area 19 | │ └── Class 20 | │ ├── Run-Time 21 | │ └── Constant Pool 22 | └── Object 23 | ``` 24 | 25 | ## 环境准备 26 | 1. jdk 1.8.0 27 | 2. IntelliJ IDEA Community Edition 2018.3.1 x64 28 | 29 | ## 配置信息 30 | 1. 调试配置 31 | 1. 配置位置:Run/Debug Configurations -> program arguments 32 | 2. 配置内容:-Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-04\target\test-classes\org\itstack\demo\test\HelloWorld 33 | 34 | ## 代码示例 35 | ```java 36 | itstack-demo-jvm-04 37 | ├── pom.xml 38 | └── src 39 | └── main 40 | │ └── java 41 | │ └── org.itstack.demo.jvm 42 | │ ├── classfile 43 | │ │ ├── attributes {BootstrapMethods/Code/ConstantValue...} 44 | │ │ ├── constantpool {CONSTANT_TAG_CLASS/CONSTANT_TAG_FIELDREF/CONSTANT_TAG_METHODREF...} 45 | │ │ ├── ClassFile.java 46 | │ │ ├── ClassReader.java 47 | │ │ └── MemberInfo.java 48 | │ ├── classpath 49 | │ │ ├── impl 50 | │ │ │ ├── CompositeEntry.java 51 | │ │ │ ├── DirEntry.java 52 | │ │ │ ├── WildcardEntry.java 53 | │ │ │ └── ZipEntry.java 54 | │ │ ├── Classpath.java 55 | │ │ └── Entry.java 56 | │ ├── rtda 57 | │ │ ├── Frame.java 58 | │ │ ├── JvmStack.java 59 | │ │ ├── LocalVars.java 60 | │ │ ├── OperandStack.java 61 | │ │ ├── Slot.java 62 | │ │ └── Thread.java 63 | │ ├── Cmd.java 64 | │ └── Main.java 65 | └── test 66 | └── java 67 | └── org.itstack.demo.test 68 | └── HelloWorld.java 69 | ``` 70 | 71 | >Frame.java 72 | 73 | ```java 74 | package org.itstack.demo.jvm.rtda; 75 | 76 | /** 77 | * http://www.itstack.org 78 | * create by fuzhengwei on 2019/4/26 79 | * 栈帧 80 | */ 81 | public class Frame { 82 | 83 | //stack is implemented as linked list 84 | Frame lower; 85 | 86 | //局部变量表 87 | private LocalVars localVars; 88 | 89 | //操作数栈 90 | private OperandStack operandStack; 91 | 92 | public Frame(int maxLocals, int maxStack) { 93 | this.localVars = new LocalVars(maxLocals); 94 | this.operandStack = new OperandStack(maxStack); 95 | } 96 | 97 | public LocalVars localVars(){ 98 | return localVars; 99 | } 100 | 101 | public OperandStack operandStack(){ 102 | return operandStack; 103 | } 104 | 105 | } 106 | ``` 107 | 108 | >JvmStack.java 109 | 110 | ```java 111 | package org.itstack.demo.jvm.rtda; 112 | 113 | /** 114 | * http://www.itstack.org 115 | * create by fuzhengwei on 2019/4/26 116 | * 虚拟机栈 117 | */ 118 | public class JvmStack { 119 | 120 | private int maxSize; 121 | private int size; 122 | private Frame _top; 123 | 124 | public JvmStack(int maxSize) { 125 | this.maxSize = maxSize; 126 | } 127 | 128 | public void push(Frame frame) { 129 | if (this.size > this.maxSize) { 130 | throw new StackOverflowError(); 131 | } 132 | 133 | if (this._top != null) { 134 | frame.lower = this._top; 135 | } 136 | 137 | this._top = frame; 138 | this.size++; 139 | } 140 | 141 | public Frame pop() { 142 | if (this._top == null) { 143 | throw new RuntimeException("jvm stack is empty!"); 144 | } 145 | 146 | Frame top = this._top; 147 | this._top = top.lower; 148 | top.lower = null; 149 | this.size--; 150 | 151 | return top; 152 | } 153 | 154 | public Frame top(){ 155 | if (this._top == null){ 156 | throw new RuntimeException("jvm stack is empty!"); 157 | } 158 | return this._top; 159 | } 160 | 161 | } 162 | ``` 163 | >LocalVars.java 164 | 165 | ```java 166 | package org.itstack.demo.jvm.rtda; 167 | 168 | /** 169 | * http://www.itstack.org 170 | * create by fuzhengwei on 2019/4/26 171 | * 局部变量表 172 | */ 173 | public class LocalVars { 174 | 175 | private Slot[] slots; 176 | 177 | public LocalVars(int maxLocals) { 178 | if (maxLocals > 0) { 179 | slots = new Slot[maxLocals]; 180 | for (int i = 0; i < maxLocals; i++) { 181 | slots[i] = new Slot(); 182 | } 183 | } 184 | } 185 | 186 | public void setInt(int idx, int val) { 187 | this.slots[idx].num = val; 188 | } 189 | 190 | public int getInt(int idx) { 191 | return slots[idx].num; 192 | } 193 | 194 | public void setFloat(int idx, float val) { 195 | this.slots[idx].num = (Float.valueOf(val)).intValue(); 196 | } 197 | 198 | public Float getFloat(int idx) { 199 | int num = this.slots[idx].num; 200 | return (float) num; 201 | } 202 | 203 | public void setLong(int idx, long val) { 204 | this.slots[idx].num = (int) val; 205 | this.slots[idx + 1].num = (int) (val >> 32); 206 | } 207 | 208 | public Long getLong(int idx) { 209 | int low = this.slots[idx].num; 210 | int high = this.slots[idx + 1].num; 211 | return ((long) high << 32) | (long) low; 212 | } 213 | 214 | public void setDouble(int idx, double val) { 215 | setLong(idx, (long) val); 216 | } 217 | 218 | public Double getDouble(int idx) { 219 | return Double.valueOf(getLong(idx)); 220 | } 221 | 222 | public void setRef(int idx, Object ref) { 223 | slots[idx].ref = ref; 224 | } 225 | 226 | public Object getRef(int idx) { 227 | return slots[idx].ref; 228 | } 229 | 230 | } 231 | ``` 232 | 233 | >OperandStack.java 234 | 235 | ```java 236 | package org.itstack.demo.jvm.rtda; 237 | 238 | /** 239 | * http://www.itstack.org 240 | * create by fuzhengwei on 2019/4/26 241 | * 操作数栈 242 | */ 243 | public class OperandStack { 244 | 245 | private int size = 0; 246 | private Slot[] slots; 247 | 248 | public OperandStack(int maxStack) { 249 | if (maxStack > 0) { 250 | slots = new Slot[maxStack]; 251 | for (int i = 0; i < maxStack; i++) { 252 | slots[i] = new Slot(); 253 | } 254 | } 255 | } 256 | 257 | public void pushInt(int val) { 258 | slots[size].num = val; 259 | size++; 260 | } 261 | 262 | public int popInt(){ 263 | size --; 264 | return slots[size].num; 265 | } 266 | 267 | public void pushRef(Object ref){ 268 | slots[size].ref = ref; 269 | size++; 270 | } 271 | 272 | public Object popRef(){ 273 | size --; 274 | Object ref = slots[size].ref; 275 | slots[size].ref = null; 276 | return ref; 277 | } 278 | 279 | } 280 | ``` 281 | 282 | >Slot.java 283 | 284 | ```java 285 | package org.itstack.demo.jvm.rtda; 286 | 287 | /** 288 | * http://www.itstack.org 289 | * create by fuzhengwei on 2019/4/26 290 | * 数据槽 291 | */ 292 | public class Slot { 293 | 294 | int num; 295 | Object ref; 296 | 297 | } 298 | ``` 299 | 300 | >Thread.java 301 | 302 | ```java 303 | package org.itstack.demo.jvm.rtda; 304 | 305 | /** 306 | * http://www.itstack.org 307 | * create by fuzhengwei on 2019/4/26 308 | * 线程 309 | */ 310 | public class Thread { 311 | 312 | //Program Counter 寄存器 313 | private int pc; 314 | 315 | //虚拟机栈 316 | private JvmStack stack; 317 | 318 | public Thread(){ 319 | this.stack = new JvmStack(1024); 320 | } 321 | 322 | public int pc(){ 323 | return this.pc; 324 | } 325 | 326 | public void setPC(int pc){ 327 | this.pc = pc; 328 | } 329 | 330 | public void pushFrame(Frame frame){ 331 | this.stack.push(frame); 332 | } 333 | 334 | public Frame popFrame(){ 335 | return this.stack.pop(); 336 | } 337 | 338 | public Frame currentFrame(){ 339 | return this.stack.top(); 340 | } 341 | 342 | } 343 | ``` 344 | 345 | ## 测试结果 346 | ```java 347 | 100 348 | -100 349 | null 350 | -100 351 | ``` 352 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-agent/2019-07-14-基于JavaAgent的全链路监控五《ThreadLocal链路追踪》.md: -------------------------------------------------------------------------------- 1 | ## 案例简述 2 | >Google开源的Dapper链路追踪组件,并在2010年发表了论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,这篇文章是业内实现链路追踪的标杆和理论基础,具有非常大的参考价值。目前,链路追踪组件有Google的Dapper,Twitter 的Zipkin,以及阿里的Eagleeye (鹰眼)等,它们都是非常优秀的链路追踪开源组件。本文主要讲述如何在Spring Cloud Sleuth中集成Zipkin。在Spring Cloud Sleuth中集成Zipkin非常的简单,只需要引入相应的依赖和做相关的配置即可。 3 | 4 | ![链路追踪Dapper](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/17387004-c9295b1ffd21eb27.png) 5 | 当业务程序代码在线上运行时,实例A、实例B、实例C,他们直接可能从上到下依次调用,为了能很好的监控程序的调用链路,我们需要对调用链路进行追踪监控。实例的外部可能是通过RPC、HTTP、SOCKET、WEBSERVICE等方式进行调用,内部是方法逻辑依次执行。外部例如http可以通过在头部写入追踪ID进行监控,内部使用threadlocal进行保存上下文关系。{ThreadLocal变量特殊的地方在于:对变量值的任何操作实际都是对这个变量在线程中的一份copy进行操作,不会影响另外一个线程中同一个ThreadLocal变量的值。} 6 | 7 | ## 环境准备 8 | 1. IntelliJ IDEA Community Edition 9 | 2. jdk1.8.0_45 64位 10 | 11 | ## 配置信息(路径相关修改为自己的) 12 | 1. 配置位置:Run/Debug Configurations -> VM options 13 | 2. 配置内容:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-05\target\itstack-demo-agent-05-1.0.0-SNAPSHOT.jar=testargs 14 | 15 | ## 代码示例 16 | ```java 17 | itstack-demo-agent-05 18 | ├── pom.xml 19 | └── src 20 | ├── main 21 | │ ├── java 22 | │ │ └── org.itstack.demo.agent 23 | │ │ ├──track 24 | │ │ │ ├── TrackContext.java 25 | │ │ │ └── TrackManager.java 26 | │ │ ├── MyAdvice.java 27 | │ │ └── MyAgent.java 28 | │ └── resources 29 | │ └── META-INF 30 | │ └── MANIFEST.MF 31 | └── test 32 | └── java 33 | └── org.itstack.demo.test 34 | └── ApiTest.java 35 | ``` 36 | >TrackContext.java 37 | 38 | ```java 39 | /** 40 | * 博客:http://itstack.org 41 | * 论坛:http://bugstack.cn 42 | * 公众号:bugstack虫洞栈 {获取学习源码} 43 | * create by fuzhengwei on 2019 44 | */ 45 | public class TrackContext { 46 | 47 | private static final ThreadLocal trackLocal = new ThreadLocal(); 48 | 49 | public static void clear(){ 50 | trackLocal.remove(); 51 | } 52 | 53 | public static String getLinkId(){ 54 | return trackLocal.get(); 55 | } 56 | 57 | public static void setLinkId(String linkId){ 58 | trackLocal.set(linkId); 59 | } 60 | 61 | } 62 | ``` 63 | >TrackManager.java 64 | 65 | ```java 66 | /** 67 | * 追踪管控 68 | * 博客:http://itstack.org 69 | * 论坛:http://bugstack.cn 70 | * 公众号:bugstack虫洞栈 {获取学习源码} 71 | * create by fuzhengwei on 2019 72 | */ 73 | public class TrackManager { 74 | 75 | private static final ThreadLocal> track = new ThreadLocal>(); 76 | 77 | private static String createSpan() { 78 | Stack stack = track.get(); 79 | if (stack == null) { 80 | stack = new Stack<>(); 81 | track.set(stack); 82 | } 83 | String linkId; 84 | if (stack.isEmpty()) { 85 | linkId = TrackContext.getLinkId(); 86 | if (linkId == null) { 87 | linkId = "nvl"; 88 | TrackContext.setLinkId(linkId); 89 | } 90 | } else { 91 | linkId = stack.peek(); 92 | TrackContext.setLinkId(linkId); 93 | } 94 | return linkId; 95 | } 96 | 97 | public static String createEntrySpan() { 98 | String span = createSpan(); 99 | Stack stack = track.get(); 100 | stack.push(span); 101 | return span; 102 | } 103 | 104 | 105 | public static String getExitSpan() { 106 | Stack stack = track.get(); 107 | if (stack == null || stack.isEmpty()) { 108 | TrackContext.clear(); 109 | return null; 110 | } 111 | return stack.pop(); 112 | } 113 | 114 | public static String getCurrentSpan() { 115 | Stack stack = track.get(); 116 | if (stack == null || stack.isEmpty()) { 117 | return null; 118 | } 119 | return stack.peek(); 120 | } 121 | 122 | 123 | } 124 | ``` 125 | >MyAdvice.java 126 | 127 | ```java 128 | /** 129 | * 博客:http://itstack.org 130 | * 论坛:http://bugstack.cn 131 | * 公众号:bugstack虫洞栈 {获取学习源码} 132 | * create by fuzhengwei on 2019 133 | */ 134 | public class MyAdvice { 135 | 136 | @Advice.OnMethodEnter() 137 | public static void enter(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName) { 138 | String linkId = TrackManager.getCurrentSpan(); 139 | if (null == linkId) { 140 | linkId = UUID.randomUUID().toString(); 141 | TrackContext.setLinkId(linkId); 142 | } 143 | String entrySpan = TrackManager.createEntrySpan(); 144 | System.out.println("链路追踪:" + entrySpan + " " + className + "." + methodName); 145 | 146 | } 147 | 148 | @Advice.OnMethodExit() 149 | public static void exit(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName) { 150 | TrackManager.getExitSpan(); 151 | } 152 | 153 | } 154 | ``` 155 | >MyAgent.java 156 | 157 | ```java 158 | /** 159 | * javaagent 160 | * 博客:http://itstack.org 161 | * 论坛:http://bugstack.cn 162 | * 公众号:bugstack虫洞栈 {获取学习源码} 163 | * create by fuzhengwei on 2019 164 | */ 165 | public class MyAgent { 166 | 167 | //JVM 首先尝试在代理类上调用以下方法 168 | public static void premain(String agentArgs, Instrumentation inst) { 169 | System.out.println("基于javaagent链路追踪"); 170 | 171 | AgentBuilder agentBuilder = new AgentBuilder.Default(); 172 | 173 | 174 | AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> { 175 | builder = builder.visit( 176 | Advice.to(MyAdvice.class) 177 | .on(ElementMatchers.isMethod() 178 | .and(ElementMatchers.any()).and(ElementMatchers.not(ElementMatchers.nameStartsWith("main"))))); 179 | return builder; 180 | }; 181 | 182 | agentBuilder = agentBuilder.type(ElementMatchers.nameStartsWith("org.itstack.demo.test")).transform(transformer).asDecorator(); 183 | 184 | //监听 185 | AgentBuilder.Listener listener = new AgentBuilder.Listener() { 186 | @Override 187 | public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) { 188 | 189 | } 190 | 191 | @Override 192 | public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) { 193 | System.out.println("onTransformation:" + typeDescription); 194 | } 195 | 196 | @Override 197 | public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) { 198 | 199 | } 200 | 201 | @Override 202 | public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) { 203 | 204 | } 205 | 206 | @Override 207 | public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) { 208 | 209 | } 210 | 211 | }; 212 | 213 | agentBuilder.with(listener).installOn(inst); 214 | 215 | } 216 | 217 | //如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法 218 | public static void premain(String agentArgs) { 219 | } 220 | 221 | } 222 | ``` 223 | >MANIFEST.MF 224 | 225 | ```其他语言 226 | Manifest-Version: 1.0 227 | Premain-Class: org.itstack.demo.agent.MyAgent 228 | Can-Redefine-Classes: true 229 | ``` 230 | >ApiTest.java 231 | 232 | ```java 233 | /** 234 | * 线程内方法追踪 235 | * 博客:http://itstack.org 236 | * 论坛:http://bugstack.cn 237 | * 公众号:bugstack虫洞栈 {获取学习源码} 238 | * create by fuzhengwei on 2019 239 | * VM options: 240 | * -javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-05\target\itstack-demo-agent-05-1.0.0-SNAPSHOT.jar=testargs 241 | */ 242 | public class ApiTest { 243 | 244 | public static void main(String[] args) { 245 | 246 | //线程一 247 | new Thread(() -> new ApiTest().http_lt1()).start(); 248 | 249 | //线程二 250 | new Thread(() -> { 251 | new ApiTest().http_lt1(); 252 | }).start(); 253 | } 254 | 255 | 256 | public void http_lt1() { 257 | System.out.println("测试结果:hi1"); 258 | http_lt2(); 259 | } 260 | 261 | public void http_lt2() { 262 | System.out.println("测试结果:hi2"); 263 | http_lt3(); 264 | } 265 | 266 | public void http_lt3() { 267 | System.out.println("测试结果:hi3"); 268 | } 269 | 270 | 271 | } 272 | ``` 273 | ## 测试结果 274 | ```java 275 | 基于javaagent链路追踪 276 | onTransformation:class org.itstack.demo.test.ApiTest 277 | 链路追踪:7dfd98e8-c474-461c-87b9-1da3bf6072c2 org.itstack.demo.test.ApiTest.http_lt1 278 | 测试结果:hi1 279 | 链路追踪:7dfd98e8-c474-461c-87b9-1da3bf6072c2 org.itstack.demo.test.ApiTest.http_lt2 280 | 测试结果:hi2 281 | 链路追踪:7dfd98e8-c474-461c-87b9-1da3bf6072c2 org.itstack.demo.test.ApiTest.http_lt3 282 | 测试结果:hi3 283 | 链路追踪:69cdf9d3-1f42-4cf6-8d80-5362dd7ea140 org.itstack.demo.test.ApiTest.http_lt1 284 | 测试结果:hi1 285 | 链路追踪:69cdf9d3-1f42-4cf6-8d80-5362dd7ea140 org.itstack.demo.test.ApiTest.http_lt2 286 | 测试结果:hi2 287 | 链路追踪:69cdf9d3-1f42-4cf6-8d80-5362dd7ea140 org.itstack.demo.test.ApiTest.http_lt3 288 | 测试结果:hi3 289 | 290 | Process finished with exit code 0 291 | ``` 292 | -------------------------------------------------------------------------------- /docs/notes/itstack-demo-ddd/2019-10-17-DDD专题案例三《领域驱动设计架构基于SpringCloud搭建微服务》.md: -------------------------------------------------------------------------------- 1 | ## 前言介绍 2 | 微服务不是泥球小单体,而是具备更加清晰职责边界的完整一体的业务功能服务。领域驱动设计的思想通过Domain的功能域设计,可以把核心功能与支撑功能很好的区分开。而在MVC的设计模式尝尝是把所有的;数据服务、定义的属性类、提供的功能都在一条线上,这样是非常快速的开发方式但在做微服务部署时候确很麻烦。 3 | 4 | 按照不同的业务场景可能设计出软件在数据库使用上会有单库单表或者分库分表,如果是一个体量足够需要分库分表设计的系统,在扩容时候它是否能满足你的需求包括; 5 | 1. 核心计算不涉及库扩容,但是系统功能都在一起怎么办,已扩容都扩容了很浪费 6 | 2. 所有的扩容都涉及到数据库连接数增加,但并不是每个行为都直达到所有库表 7 | 3. 持续发展的业务会带来数据激增,将来怎么进行扩展,重新洗数据并不是很好的选择 8 | 9 | 那么实际开发大泥球架构时,不只是会遇到上面的问题,还可能会遇到工期很赶加个人也不提升效率,反复交接代码扶不过三代等等,因此我们将服务拆分为独立单体具备此核心域完整功能的系统是非常必要的。 10 | 11 | 如图,是微服务数据库使用的一种思想,我们希望路由层从最开始就被执行,用户分群动态扩展 12 | ![微信公众号:bugstack虫洞栈 & 微服务数据库路由](https://fuzhengwei.github.io/assets/images/pic-content/2019/10/ddd-03-1.png) 13 | 14 | ## 案例目标 15 | 本案例通过使用SpringCloud将我们的服务架构扩展为通过路由调用的微服务 16 | 1. 首先通过Eureka作为服务注册与发现中心 17 | 2. 然后使用Feign模式作为调用API接口 18 | 3. 最后依赖于zuul设置路由转发功能 19 | 20 | 为了方便测试,本案例会在itstack-demo-ddd-03中建4个工程; 21 | - itstack-demo-ddd-case{基于DDD的微服务} 22 | - itstack-demo-ddd-eureka-server{服务注册与发现} 23 | - itstack-demo-ddd-feign{调用方,通过API接口调用} 24 | - itstack-demo-ddd-zuul{网关路由组件} 25 | 26 | ## 开发环境 27 | 1、jdk1.8 28 | 2、springboot 2.0.6.RELEASE 以及SpringCloud相关服务 29 | 3、idea + maven 30 | 31 | ## 代码示例 32 | 33 | ### itstack-demo-ddd-case | 基于DDD的微服务 {本段代码在上一章节已经演示} 34 | 35 | ```java 36 | itstack-demo-ddd-case 37 | └── src 38 | ├── main 39 | │ ├── java 40 | │ │ └── org.itstack.demo 41 | │ │ ├── application 42 | │ │ │ ├── MallRuleService.java 43 | │ │ │ └── MallTreeService.java 44 | │ │ ├── domain 45 | │ │ │ ├── rule 46 | │ │ │ │ ├── model 47 | │ │ │ │ │ ├── aggregates 48 | │ │ │ │ │ │ └── UserRichInfo.java 49 | │ │ │ │ │ └── vo 50 | │ │ │ │ │ ├── DecisionMatter.java 51 | │ │ │ │ │ ├── EngineResult.java 52 | │ │ │ │ │ ├── TreeNodeInfo.java 53 | │ │ │ │ │ ├── TreeNodeLineInfo.java 54 | │ │ │ │ │ └── UserSchool.java 55 | │ │ │ │ ├── repository 56 | │ │ │ │ │ └── IRuleRepository.java 57 | │ │ │ │ └── service 58 | │ │ │ │ ├── engine 59 | │ │ │ │ │ ├── impl 60 | │ │ │ │ │ └── EngineFilter.java 61 | │ │ │ │ ├── logic 62 | │ │ │ │ │ ├── impl 63 | │ │ │ │ │ └── LogicFilter.java 64 | │ │ │ │ └── MallRuleServiceImpl.java 65 | │ │ │ └── tree 66 | │ │ │ ├── model 67 | │ │ │ │ ├── aggregates 68 | │ │ │ │ │ └── TreeCollect.java 69 | │ │ │ │ └── vo 70 | │ │ │ │ ├── TreeInfo.java 71 | │ │ │ │ └── TreeRulePoint.java 72 | │ │ │ ├── repository 73 | │ │ │ │ └── ITreeRepository.java 74 | │ │ │ └── service 75 | │ │ │ └── MallTreeServiceImpl.java 76 | │ │ ├── infrastructure 77 | │ │ │ ├── common 78 | │ │ │ │ └── Constants.java 79 | │ │ │ ├── dao 80 | │ │ │ │ ├── RuleTreeDao.java 81 | │ │ │ │ ├── RuleTreeNodeDao.java 82 | │ │ │ │ └── RuleTreeNodeLineDao.java 83 | │ │ │ ├── po 84 | │ │ │ │ ├── RuleTree.java 85 | │ │ │ │ ├── RuleTreeConfig.java 86 | │ │ │ │ ├── RuleTreeNode.java 87 | │ │ │ │ └── RuleTreeNodeLine.java 88 | │ │ │ ├── repository 89 | │ │ │ │ ├── cache 90 | │ │ │ │ │ └── RuleCacheRepository.java 91 | │ │ │ │ ├── mysql 92 | │ │ │ │ │ ├── RuleMysqlRepository.java 93 | │ │ │ │ │ └── TreeMysqlRepository.java 94 | │ │ │ │ ├── RuleRepository.java 95 | │ │ │ │ └── TreeRepository.java 96 | │ │ │ └── util 97 | │ │ │ └── CacheUtil.java 98 | │ │ ├── interfaces 99 | │ │ │ ├── dto 100 | │ │ │ │ ├── DecisionMatterDTO.java 101 | │ │ │ │ └── TreeDTO.java 102 | │ │ │ └── DDDController.java 103 | │ │ └── DDDApplication.java 104 | │ └── resources 105 | │ ├── mybatis 106 | │ └── application.yml 107 | └── test 108 | └── java 109 | └── org.itstack.demo.test 110 | └── ApiTest.java 111 | ``` 112 | 113 | ### itstack-demo-ddd-eureka-server | 服务注册与发现 114 | 115 | ```java 116 | itstack-demo-ddd-eureka-server 117 | └── src 118 | ├── main 119 | │ ├── java 120 | │ │ └── org.itstack.demo 121 | │ │ └── EurekaServerApplication.java 122 | │ └── resources 123 | │ └── application.yml 124 | └── test 125 | └── java 126 | └── org.itstack.demo.test 127 | └── ApiTest.java 128 | ``` 129 | 130 | >EurekaServerApplication.java | 启动服务 131 | 132 | ```java 133 | /** 134 | * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例 135 | * 论坛:http://bugstack.cn 136 | * Create by 付政委 on @2019 137 | */ 138 | @SpringBootApplication 139 | @EnableEurekaServer 140 | public class EurekaServerApplication { 141 | 142 | public static void main(String[] args) { 143 | SpringApplication.run( EurekaServerApplication.class, args ); 144 | } 145 | 146 | } 147 | ``` 148 | 149 | >application.yml | 服务配置 150 | 151 | ```java 152 | server: 153 | port: 8989 154 | 155 | eureka: 156 | instance: 157 | hostname: localhost 158 | client: 159 | registerWithEureka: false 160 | fetchRegistry: false 161 | serviceUrl: 162 | defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ 163 | 164 | spring: 165 | application: 166 | name: itstack-demo-ddd-eureka-server 167 | ``` 168 | 169 | ### itstack-demo-ddd-feign | 调用方,通过API接口调用 170 | 171 | ```java 172 | itstack-demo-ddd-feign 173 | └── src 174 | ├── main 175 | │ ├── java 176 | │ │ └── org.itstack.demo 177 | │ │ ├── domain 178 | │ │ │ └── TreeDTO.java 179 | │ │ ├── service 180 | │ │ │ └── MallService.java 181 | │ │ ├── web 182 | │ │ │ └── FeignController.java 183 | │ │ └── FeignApplication.java 184 | │ └── resources 185 | │ └── application.yml 186 | └── test 187 | └── java 188 | └── org.itstack.demo.test 189 | └── ApiTest.java 190 | ``` 191 | 192 | >MallService.java | 通过注册方式调用API 193 | 194 | ```java 195 | /** 196 | * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例 197 | * 论坛:http://bugstack.cn 198 | * Create by 付政委 on @2019 199 | */ 200 | @FeignClient(value = "itstack-demo-ddd-case") 201 | public interface MallService { 202 | 203 | @RequestMapping(value = "/api/tree/queryTreeSummaryInfo", method = RequestMethod.POST) 204 | Object queryTreeSummaryInfo(@RequestBody TreeDTO request); 205 | 206 | } 207 | ``` 208 | 209 | >FeignApplication.java | 启动服务 210 | 211 | ```java 212 | /** 213 | * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例 214 | * 论坛:http://bugstack.cn 215 | * Create by 付政委 on @2019 216 | */ 217 | @SpringBootApplication 218 | @EnableEurekaClient 219 | @EnableDiscoveryClient 220 | @EnableFeignClients 221 | public class FeignApplication { 222 | 223 | public static void main(String[] args) { 224 | SpringApplication.run(FeignApplication.class, args); 225 | } 226 | 227 | } 228 | ``` 229 | 230 | >application.yml | 服务配置 231 | 232 | ```java 233 | server: 234 | port: 9090 235 | 236 | spring: 237 | application: 238 | name: itstack-demo-ddd-feign 239 | 240 | eureka: 241 | client: 242 | serviceUrl: 243 | defaultZone: http://localhost:8989/eureka/ 244 | ``` 245 | 246 | ### itstack-demo-ddd-zuul| 网关路由组件 247 | 248 | ```java 249 | itstack-demo-ddd-zuul 250 | └── src 251 | ├── main 252 | │ ├── java 253 | │ │ └── org.itstack.demo 254 | │ │ └── ZuulApplication.java 255 | │ └── resources 256 | │ └── application.yml 257 | └── test 258 | └── java 259 | └── org.itstack.demo.test 260 | └── ApiTest.java 261 | ``` 262 | 263 | >ZuulApplication.java | 启动服务 264 | 265 | ```java 266 | /** 267 | * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例 268 | * 论坛:http://bugstack.cn 269 | * Create by 付政委 on @2019 270 | */ 271 | @SpringBootApplication 272 | @EnableZuulProxy 273 | @EnableEurekaClient 274 | @EnableDiscoveryClient 275 | public class ZuulApplication { 276 | 277 | public static void main(String[] args) { 278 | SpringApplication.run(ZuulApplication.class, args); 279 | } 280 | 281 | } 282 | ``` 283 | 284 | >application.yml | 服务配置{本案例是静态路由,按需可以开发为动态路由} 285 | 286 | ```java 287 | server: 288 | port: 9191 289 | 290 | spring: 291 | application: 292 | name: itstack-demo-ddd-zuul 293 | 294 | eureka: 295 | client: 296 | serviceUrl: 297 | defaultZone: http://localhost:8989/eureka/ 298 | zuul: 299 | routes: 300 | api-a: 301 | path: /route-a/** 302 | serviceId: itstack-demo-ddd-feign 303 | ``` 304 | 305 | ## 测试验证 306 | 307 | 按照顺序启动;itstack-demo-ddd-eureka-server、itstack-demo-ddd-case{可以模拟启动多个}、itstack-demo-ddd-feign、itstack-demo-ddd-zuul 308 | 309 | >访问;http://localhost:8989/ | 服务中心 310 | ![微信公众号:bugstack虫洞栈 & 服务中心](https://fuzhengwei.github.io/assets/images/pic-content/2019/10/ddd-03-2.png) 311 | 312 | >访问:http://localhost:9191/route-a/api/queryTreeSummaryInfo?treeId=10001 | 通过网关路由调用DDD服务接口 313 | 314 | ![微信公众号:bugstack虫洞栈 & 调用网关接口测试](https://fuzhengwei.github.io/assets/images/pic-content/2019/10/ddd-03-3.png) 315 | 316 | ## 综上总结 317 | 1. DDD的设计模式加上SpringBoot与SpringCloud非常适合开发微服务 318 | 2. 以上案例可以进行扩展,使不同的用户群体在网关接口调用时就打到不同的服务上 319 | 3. 另外目前没有使用dubbo类型的rpc框架,也就是没有对外提供定义接口jar包,后续会进行延展 320 | --------------------------------------------------------------------------------