From 45b28d7c6830da3e8b14638fde81af0745e3f69f Mon Sep 17 00:00:00 2001
From: zsy0216 <594983498@qq.com>
Date: Tue, 18 Aug 2020 17:34:40 +0800
Subject: [PATCH 01/63] fix: fix springboot version
---
...\273\237\345\210\235\345\247\213\345\214\226\345\231\250.md" | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git "a/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\347\263\273\347\273\237\345\210\235\345\247\213\345\214\226\345\231\250.md" "b/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\347\263\273\347\273\237\345\210\235\345\247\213\345\214\226\345\231\250.md"
index fb79859..4a6b4b4 100644
--- "a/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\347\263\273\347\273\237\345\210\235\345\247\213\345\214\226\345\231\250.md"
+++ "b/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\347\263\273\347\273\237\345\210\235\345\247\213\345\214\226\345\231\250.md"
@@ -1,7 +1,7 @@
## 前言
前一章已经讲解了SpringBoot的SpringFactoriesLoader类的功能以及作用,即读取spring.factories文件中的工厂类,其中就包括了系统初始化器。在SpringBoot中,系统初始化器名称为ApplicationContextInitializer,它是一个接口,只定义了一个initialize方法。下面将详细介绍下SpringBoot的系统初始化器的原理以及作用,并且自定义一个系统初始化器,并在此基础上讲解下常见的使用场景。
-**SpringBoot版本:5.2.1.RELEASE**
+**SpringBoot 版本:2.2.1.RELEASE**
## 正文
From 8cd23b26c9129e5280aa85b7fb6085913aaa41df Mon Sep 17 00:00:00 2001
From: liuhao <326047736@qq.com>
Date: Wed, 21 Oct 2020 19:11:47 +0800
Subject: [PATCH 02/63] Just add code block language..
---
...15\345\212\241\346\232\264\351\234\262.md" | 8 +--
...0\224\342\200\224 Dubbo\347\232\204URL.md" | 2 +-
...66\357\274\210\344\270\212\357\274\211.md" | 14 ++---
...66\357\274\210\344\270\213\357\274\211.md" | 28 +++++-----
...66\357\274\210\344\270\255\357\274\211.md" | 2 +-
...245\345\255\246\344\271\240ThreadLocal.md" | 10 ++--
...le\345\205\263\351\224\256\345\255\227.md" | 8 +--
...02\357\274\210\344\270\200\357\274\211.md" | 24 ++++-----
...02\357\274\210\344\272\214\357\274\211.md" | 54 +++++++++----------
...25\345\261\202\346\272\220\347\240\201.md" | 18 +++----
...01\344\270\216\345\216\237\347\220\206.md" | 52 +++++++++---------
.../\344\272\214\350\277\233\345\210\266.md" | 4 +-
...26\347\225\245\346\250\241\345\274\217.md" | 28 +++++-----
...50\357\274\210\344\270\212\357\274\211.md" | 44 +++++++--------
...50\357\274\210\344\270\213\357\274\211.md" | 12 ++---
...AnnotationAwareAspectJAutoProxyCreator.md" | 26 ++++-----
...36\347\247\230\351\235\242\347\272\261.md" | 28 +++++-----
...AnnotationAwareAspectJAutoProxyCreator.md" | 34 ++++++------
...0\224\342\200\224SpringFactoriesLoader.md" | 16 +++---
...50\345\212\240\350\275\275\345\231\250.md" | 10 ++--
...13\344\273\266\346\234\272\345\210\266.md" | 34 ++++++------
...35\345\247\213\345\214\226\345\231\250.md" | 14 ++---
...\345\210\235\350\257\206SpringSecurity.md" | 8 +--
...70\345\277\203\351\200\273\350\276\221.md" | 22 ++++----
...46\273\244\345\231\250FilterChainProxy.md" | 34 ++++++------
...04\350\277\207\346\273\244\345\231\250.md" | 20 +++----
26 files changed, 277 insertions(+), 277 deletions(-)
diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\342\200\224\342\200\224\346\234\215\345\212\241\346\232\264\351\234\262.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\342\200\224\342\200\224\346\234\215\345\212\241\346\232\264\351\234\262.md"
index 758f10a..eede346 100644
--- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\342\200\224\342\200\224\346\234\215\345\212\241\346\232\264\351\234\262.md"
+++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\342\200\224\342\200\224\346\234\215\345\212\241\346\232\264\351\234\262.md"
@@ -1,4 +1,4 @@
-```
+```Java
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
@@ -33,7 +33,7 @@ public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
}
```
-```
+```Java
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
@@ -71,7 +71,7 @@ public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory
}
```
-```
+```Java
package org.apache.dubbo.remoting;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adaptive implements org.apache.dubbo.remoting.Transporter {
@@ -99,7 +99,7 @@ public class Transporter$Adaptive implements org.apache.dubbo.remoting.Transport

JavassistProxyFactory代码如下:
-```
+```Java
public class JavassistProxyFactory extends AbstractProxyFactory {
@Override
diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204URL.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204URL.md"
index 1119488..99cea39 100644
--- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204URL.md"
+++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204URL.md"
@@ -34,7 +34,7 @@ dubbo://172.17.32.91:20880/org.apache.dubbo.demo.DemoService?anyhost=true&applic
先看下Dubbo中org.apache.dubbo.common包下的URL类源码:
-```
+```Java
public /*final**/
class URL implements Serializable {
diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\212\357\274\211.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\212\357\274\211.md"
index fc509ca..b0d01fa 100644
--- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\212\357\274\211.md"
+++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\212\357\274\211.md"
@@ -28,13 +28,13 @@ Java SPI的定义及使用步骤如下:
在com.test.spi包目录下,定义了一个PrintService接口和一个PrintServiceImpl实现类,然后在resources目录下定义了一个META-INF/services/com.test.spi.PrintService,注意这里定义的是一个
全路径名称的文件。
-```
+```Java
public interface Printservice (
void printlnfo();
}
```
-```
+```Java
public class PrintServicelmpl implements Printservice {
@Override
public void printlnfo() {
@@ -43,7 +43,7 @@ public class PrintServicelmpl implements Printservice {
}
```
-```
+```Java
public static void main(String[] args) (
ServiceLoader serviceServiceLoader =
ServiceLoader.load(PrintService.class);
@@ -119,7 +119,7 @@ ExtensionLoader即扩展点加载器,它是Dubbo SPI的核心,负责加载
上图清楚的展示了LoadingStrategy接口及其实现类的关系。LoadingStrategy继承了Prioritized,因而其实现类会有优先级之分,而Dubbo默认是使用的DubboInternalLoadingStrategy,查看其三个类的源码:
-```
+```Java
public class DubboInternalLoadingStrategy implements LoadingStrategy {
// 表示要加载的目录位置
@@ -136,7 +136,7 @@ public class DubboInternalLoadingStrategy implements LoadingStrategy {
}
```
-```
+```Java
public class DubboLoadingStrategy implements LoadingStrategy {
// 表示要加载的目录位置
@@ -160,7 +160,7 @@ public class DubboLoadingStrategy implements LoadingStrategy {
}
```
-```
+```Java
public class ServicesLoadingStrategy implements LoadingStrategy {
// 表示要加载的目录位置
@@ -185,7 +185,7 @@ public class ServicesLoadingStrategy implements LoadingStrategy {
这里的MAX_PRIORITY、NORMAL_PRIORITY和MIN_PRIORITY时定义在Prioritized这个接口中的,查看一下Prioritized中定义的值以及实现的compareTo方法:
-```
+```Java
/**
* The maximum priority
*/
diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md"
index 91b2449..d99cbec 100644
--- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md"
+++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\213\357\274\211.md"
@@ -12,7 +12,7 @@
另外在@SPI注解的value值指定了扩展点默认的实现类名,例如SimpleExt注解由@SPI("impl1")修饰,则表示它的实现类名为:SimpleExtImpl1,查看SPI的配置文件可证:
-```
+```Java
# Comment 1
impl1=org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl1#Hello World
impl2=org.apache.dubbo.common.extension.ext1.impl.SimpleExtImpl2 # Comment 2
@@ -24,13 +24,13 @@ Dubbo通过ExtensionLoader去加载上述SPI配置文件,然后读取到@SPI("
Dubbo SPI的核心逻辑几乎都封装在ExtensionLoader之中,ExtensionLoader存放于dubbo-common模块的extension保重,功能类似于JDK SPI中的java.util.ServiceLoader。
下面展示了ExtensionLoader最常用的使用方式:
-```
+```Java
SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getDefaultExtension();
```
首先时调用ExtensionLoader#getExtensionLoader(SimpleExt.class),来获取SimpleExt类型的ExtensionLoader。查看ExtensionLoader源码如下:
-```
+```Java
public static ExtensionLoader getExtensionLoader(Class type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
@@ -51,11 +51,11 @@ SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getDefaultEx
}
return loader;
}
-```
+```Java
getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经缓存了该类型的扩展点加载器,如果没有则new一个该类型的ExtensionLoader并添加进EXTENSION_LOADERS中。但需要注意的是ExtensionLoader的构造方法
中,是会先创建默认的ExtensionFactory类型的ExtensionLoader对象,然后调用getAdaptiveExtension()方法创建适配类型的扩展点实现类。
-```
+```Java
private ExtensionLoader(Class> type) {
this.type = type;
// 从此处可以知道,对于默认的ExtensionFactory.class来说,是没有objectFactory熟悉对象值的
@@ -68,7 +68,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经
被赋值为AdaptiveExtensionFactory。
下面看下getExtensionClass()方法的逻辑
-```
+```Java
private Class> getExtensionClass(String name) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
@@ -81,7 +81,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经
}
```
-```
+```Java
private Map> getExtensionClasses() {
Map> classes = cachedClasses.get();
// 双重检测,防止并发环境下指令重排序,cachedClasses是static类型
@@ -99,7 +99,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经
}
```
-```
+```Java
private Map> loadExtensionClasses() {
// 缓存默认的扩展点名称,这里会去读取@SPI注解
cacheDefaultExtensionName();
@@ -140,7 +140,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经
}
```
-```
+```Java
// 加载SPI配置文件目录
private void loadDirectory(Map> extensionClasses, String dir, String type,
boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
@@ -173,7 +173,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经
}
```
-```
+```Java
private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class> clazz, String name,
boolean overridden) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz)) {
@@ -218,7 +218,7 @@ getExtensionLoader方法首先回去判断EXTENSION_LOADERS缓存中是否已经

在ExtensionFactory接口上有@SPI注解修饰,而Dubbo会在调用ExtensionFactory时,会去调用ExtensionFactory的SPI配置文件中的扩展点名称以及扩展点实现类,查看下其SPI配置文件:
-```
+```Java
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory
```
@@ -232,7 +232,7 @@ AdaptiveExtensionFactory会根据运行时状态来决定给ExtensionFactory赋
下面看下AdaptiveExtensionFactory类:
-```
+```Java
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
@@ -275,7 +275,7 @@ public class AdaptiveExtensionFactory implements ExtensionFactory {
下面看看ExtensionLoader的方法:
-```
+```Java
private Class> getAdaptiveExtensionClass() {
// 获取扩展点实现类,如果缓存中没有则去扫描SPI文件,扫描到扩展点实现类后则存入cachedClasses缓存中
getExtensionClasses(); // ------------------------ ②
@@ -325,7 +325,7 @@ public class AdaptiveExtensionFactory implements ExtensionFactory {
的扩展点实现类,就会去通过Javassist来生成代理代码,即生成对于的Xxx@Adaptive代码。
下面就是通过Javassist代理生产的适配类。(再Dubbo源码中的dubbo-common模块test目录下的org.apache.dubbo.extension包中有对应的测试类)
-```
+```Java
package org.apache.dubbo.common.extension.ext1;
import org.apache.dubbo.common.extension.ExtensionLoader;
diff --git "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\255\357\274\211.md" "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\255\357\274\211.md"
index 58f5d93..d9c7e01 100644
--- "a/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\255\357\274\211.md"
+++ "b/note/Dubbo/Dubbo\345\272\225\345\261\202\346\272\220\347\240\201\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224 Dubbo\347\232\204SPI\346\234\272\345\210\266\357\274\210\344\270\255\357\274\211.md"
@@ -65,7 +65,7 @@
接下来的原理分析通过Dubbo源码中的test包下的代码来进行说明。(想学好开源框架,要好好利用开源框架中各种Test用例)
-```
+```Java
@Test
public void test_getDefaultExtension() throws Exception {
SimpleExt ext = getExtensionLoader(SimpleExt.class).getDefaultExtension();
diff --git "a/note/JDK/\344\270\200\347\257\207\346\226\207\347\253\240\345\277\253\351\200\237\346\267\261\345\205\245\345\255\246\344\271\240ThreadLocal.md" "b/note/JDK/\344\270\200\347\257\207\346\226\207\347\253\240\345\277\253\351\200\237\346\267\261\345\205\245\345\255\246\344\271\240ThreadLocal.md"
index fbe3c0c..0235a9f 100644
--- "a/note/JDK/\344\270\200\347\257\207\346\226\207\347\253\240\345\277\253\351\200\237\346\267\261\345\205\245\345\255\246\344\271\240ThreadLocal.md"
+++ "b/note/JDK/\344\270\200\347\257\207\346\226\207\347\253\240\345\277\253\351\200\237\346\267\261\345\205\245\345\255\246\344\271\240ThreadLocal.md"
@@ -108,7 +108,7 @@
#### 2.1 ThreadLocal内部使用了哪些数据结构?
首先,我们来看下ThreadLocal中几个比较重要的数据结构。
-```
+```Java
/**
* 用于ThreadLocal内部ThreadLocalMap数据结构的哈希值,用于降低哈希冲突。
*/
@@ -135,7 +135,7 @@ private static int nextHashCode() {
下面将是ThreadLocal最终要的一个数据结构:ThreadLocalMap
-```
+```Java
/**
* ThreadLocalMap其实就是一个用于ThreadLocal的自定义HashMap,它和HashMap很像。在其内部有一个自定义的Entry类,
* 并且有一个Entry数组来存储这个类的实例对象。类似于HashMap,ThreadLocalMap同样的拥有初始大小,拥有扩容阈值。
@@ -197,7 +197,7 @@ static class ThreadLocalMap {
下面回到关于ThreadLocal源码的介绍,先看看set()和get()方法源码:
-```
+```Java
// ThreadLocal中的set()方法
public void set(T value) {
Thread t = Thread.currentThread();
@@ -244,7 +244,7 @@ static class ThreadLocalMap {
-```
+```Java
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
@@ -280,7 +280,7 @@ static class ThreadLocalMap {
知道怎么存储以及获取ThreadLocal之后,还要知道怎么清除ThreadLocal,防止内存泄漏,下面看下remove()源码:
-```
+```Java
// ThreadLocal的remove()方法
public void remove() {
// 获取当前线程中的ThreadLocalMap
diff --git "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md" "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md"
index 728a6ba..79f3bfd 100644
--- "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md"
+++ "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md"
@@ -56,7 +56,7 @@ Lock引起的将当前处理器缓存该变量的数据写回到系统内存中
为了实现volatile的内存语义,编译期在生成字节码时会对使用volatile关键字修饰的变量进行处理,在字节码文件里对应位置生成一个Lock前缀指令,Lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。
下面代码来演示一下禁止指令重排序:
-```
+```Java
a = 1; //语句一
b = 2; //语句二
flag = true; //语句三,flag为volatile变量
@@ -137,7 +137,7 @@ LoadLoad,StoreStore,LoadStore,StoreLoad实际上是Java对上面两种屏障的
下面来谈谈volatile的应用场景:
1. 状态标志:多个线程以一个volatile变量作为为状态标志,例如完成**初始化**或者**状态同步**。典型例子AQS的同步状态:
-```
+```Java
/**
* The synchronization state.
*/
@@ -146,7 +146,7 @@ private volatile int state;
2. 一次性安全发布
最典型的例子就是安全的单例模式:
-```
+```Java
private static Singleton instance;
public static Singleton getInstance() {
//第一次null检查
@@ -164,7 +164,7 @@ public static Singleton getInstance() {
上面这种写法,仍然会出现问题——多线程调用getInstance方法时,有可能一个线程会获得还**没有初始化的对象**!这都是因为重排序的原因,具体分析这里不展开。
解决办法及时用volatile对instance进行修饰
-```
+```Java
private static volatile Singleton instance;
```
这就是经典的“双重检查锁定与延迟初始化”。
diff --git "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md" "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md"
index 4418513..7b87bdf 100644
--- "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md"
+++ "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md"
@@ -30,7 +30,7 @@
#### 1.1 String的修饰符与实现类
打开String源码,可以看到String类的由final修饰的,并且实现了Serializable,Comparable,CharSequence接口。
-```
+```Java
public final class String
implements java.io.Serializable, Comparable, CharSequence {
}
@@ -41,7 +41,7 @@ public final class String
#### 1.2 String类的成员变量
-```
+```Java
public final class String
implements java.io.Serializable, Comparable, CharSequence {
@@ -66,7 +66,7 @@ hash值将用于String类的hashCode()方法的计算,这里先不作具体讲
##### 1.2.4 serialPersistentFields属性
了解过JAVA序列化的,应该清楚transient是用于指定哪个字段不被默认序列化,对于不需要序列化的属性直接用transient修饰即可。而serialPersistentFields用于指定哪些字段需要被默认序列化,具体用法如下:
-```
+```Java
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("name", String.class),
new ObjectStreamField("age", Integer.Type)
@@ -77,15 +77,15 @@ private static final ObjectStreamField[] serialPersistentFields = {
#### 1.3 创建String对象
1. 直接使用"",换句话说就是使用"字面量"赋值
- ```
+ ```Java
String name = "bruis";
```
2. 使用连接符"+"来赋值
- ```
+ ```Java
String name = "ca" + "t";
```
3. 使用关键字new来创建对象
- ```
+ ```Java
String name = new String("bruis");
```
4. 除了上面最常见的几种创建String对象的方式外,还有以下方法可以创建String对象
@@ -126,7 +126,7 @@ JAVA的运行时数据区包括以下几个区域:
#### 2.2 String与JAVA内存区域
下面看看使用""和new的方式创建的字符串在底层都发生了些什么
-```
+```Java
public class TestString {
public static void main(String[] args) {
String name = "bruis";
@@ -296,7 +296,7 @@ SourceFile: "TestString.java"
```
这里有一个需要注意的地方,在java中使用"+"连接符时,一定要注意到"+"的连接符效率非常低下,因为"+"连接符的原理就是通过StringBuilder.append()来实现的。所以如:String name = "a" + "b";在底层是先new 出一个StringBuilder对象,然后再调用该对象的append()方法来实现的,调用过程等同于:
-```
+```Java
// String name = "a" + "b";
String name = new StringBuilder().append("a").append("b").toString();
```
@@ -307,7 +307,7 @@ String name = new StringBuilder().append("a").append("b").toString();
官方文档解释为字符串常量池由String独自维护,当调用intern()方法时,如果字符串常量池中包含该字符串,则直接返回字符串常量池中的字符串。否则将此String对象添加到字符串常量池中,并返回对此String对象的引用。
下面先看看这几句代码,猜猜结果是true还是false
-```
+```Java
String a1 = new String("AA") + new String("BB");
System.out.println("a1 == a1.intern() " + (a1 == a1.intern()));
@@ -325,7 +325,7 @@ String name = new StringBuilder().append("a").append("b").toString();
使用字面量的方式创建字符串,要分两种情况。
① 如果字符串常量池中没有值,则直接创建字符串,并将值存入字符串常量池中;
-```
+```Java
String name = "bruis";
```
对于字面量形式创建出来的字符串,JVM会在编译期时对其进行优化并将字面量值存放在字符串常量池中。运行期在虚拟机栈栈帧中的局部变量表里创建一个name局部变量,然后指向字符串常量池中的值,如图所示:
@@ -335,7 +335,7 @@ String name = "bruis";
2. 使用new的方式创建字符串
-```
+```Java
String name = new String("bruis");
```
首先在堆中new出一个对象,然后常量池中创建一个指向堆中"bruis"的引用。
@@ -343,7 +343,7 @@ String name = new String("bruis");
##### 2.3.2 解析
-```
+```Java
/**
* 首先对于new出的两个String()对象进行字符串连接操作,编译器无法进行优化,只有等到运行期期间,通过各自的new操作创建出对象之后,然后使 用"+"连接符拼接字符串,再从字符串常量池中创建三个分别指向堆中"AA"、"BB",而"AABB"是直接在池中创建的字面量值,这一点可以通过类的反编译来证明,这里就不具体展开了。
*/
diff --git "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\272\214\357\274\211.md" "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\272\214\357\274\211.md"
index cd33048..09008df 100644
--- "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\272\214\357\274\211.md"
+++ "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\272\214\357\274\211.md"
@@ -26,7 +26,7 @@
### 1. String的equals方法
String源码的equals方法如下:
-```
+```Java
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
@@ -55,7 +55,7 @@ String源码的equals方法如下:
### 2. String的hashcode方法
String源码中hashcode方法如下:
-```
+```Java
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
@@ -70,7 +70,7 @@ String源码中hashcode方法如下:
}
```
在String类中,有个字段hash存储着String的哈希值,如果字符串为空,则hash的值为0。String类中的hashCode计算方法就是以31为权,每一位为字符的ASCII值进行运算,用自然溢出来等效取模,经过第一次的hashcode计算之后,属性hash就会赋哈希值。从源码的英文注释可以了解到哈希的计算公式:
-```
+```Java
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
```
@@ -78,7 +78,7 @@ s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
这是一个很经典的话题了,下面来深入研究一下这两个方法。由上面的介绍,可以知道String的equals()方法实际比较的是两个字符串的内容,而String的hashCode()方法比较的是字符串的hash值,那么单纯的a.equals(b)为true,就可以断定a字符串等于b字符串了吗?或者单纯的a.hash == b.hash为true,就可以断定a字符串等于b字符串了吗?答案是否定的。
比如下面两个字符串:
-```
+```Java
String a = "gdejicbegh";
String b = "hgebcijedg";
System.out.println("a.hashcode() == b.hashcode() " + (a.hashCode() == b.hashCode()));
@@ -100,7 +100,7 @@ false
### 4. String的compareTo()方法
-```
+```Java
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
@@ -124,7 +124,7 @@ false
### 5. String的startWith(String prefix)方法
-```
+```Java
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
}
@@ -150,7 +150,7 @@ false
如果参数字符序列是该字符串字符序列的前缀,则返回true;否则返回false;
示例:
-```
+```Java
String a = "abc";
String b = "abcd";
System.out.println(b.startsWith(a));
@@ -161,7 +161,7 @@ true
### 6. String的endsWith(String suffix)方法
查看String的endsWith(String suffix)方法源码:
-```
+```Java
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}
@@ -169,7 +169,7 @@ true
其实endsWith()方法就是服用了startsWith()方法而已,传进的toffset参数值时value和suffix长度差值。
示例:
-```
+```Java
String a = "abcd";
String b = "d";
System.out.println(a.endsWith(b));
@@ -179,7 +179,7 @@ true
### 7. String的indexOf(int ch)方法
-```
+```Java
public int indexOf(int ch) {
return indexOf(ch, 0);
}
@@ -209,7 +209,7 @@ true
对于String的indexOf(int ch)方法,查看其源码可知其方法入参为ASCII码值,然后和目标字符串的ASCII值来进行比较的。其中常量Character.MIN_SUPPLEMENTARY_CODE_POINT表示的是0x010000——十六进制的010000,十进制的值为65536,这个值表示的是十六进制的最大值。
下面再看看indexOfSupplementary(ch, fromIndex)方法
-```
+```Java
private int indexOfSupplementary(int ch, int fromIndex) {
if (Character.isValidCodePoint(ch)) {
final char[] value = this.value;
@@ -228,7 +228,7 @@ true
java中特意对超过两个字节的字符进行了处理,例如emoji之类的字符。处理逻辑就在indexOfSupplementary(int ch, int fromIndex)方法里。
Character.class
-```
+```Java
public static boolean isValidCodePoint(int codePoint) {
// Optimized form of:
// codePoint >= MIN_CODE_POINT && codePoint <= MAX_CODE_POINT
@@ -238,7 +238,7 @@ Character.class
```
对于方法isValidCodePoint(int codePoint)方法,用于确定指定代码点是否是一个有效的Unicode代码点。代码
-```
+```Java
int plane = codePoint >>> 16;
return plane < ((MAX_CODE_POINT + 1) >>> 16);
```
@@ -246,7 +246,7 @@ return plane < ((MAX_CODE_POINT + 1) >>> 16);
### 8. String的split(String regex, int limit)方法
-```
+```Java
public String[] split(String regex, int limit) {
char ch = 0;
if (((regex.value.length == 1 &&
@@ -301,7 +301,7 @@ return plane < ((MAX_CODE_POINT + 1) >>> 16);
split(String regex, int limit)方法内部逻辑非常复杂,需要静下心来分析。
if判断中**第一个括号**先判断一个字符的情况,并且这个字符不是任何特殊的正则表达式。也就是下面的代码:
-```
+```Java
(regex.value.length == 1 &&
".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1)
```
@@ -309,17 +309,17 @@ if判断中**第一个括号**先判断一个字符的情况,并且这个字
在if判断中,**第二个括号**判断有两个字符的情况,并且如果这两个字符是以```\```开头的,并且不是字母或者数字的时候。如下列代码所示:
-```
+```Java
(regex.length() == 2 && regex.charAt(0) == '\\' && (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 && ((ch-'a')|('z'-ch)) < 0 && ((ch-'A')|('Z'-ch)) < 0)
```
判断完之后,在进行**第三个括号**判断,判断是否是两字节的unicode字符。如下列代码所示:
-```
+```Java
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE)
```
对于下面这段复杂的代码,我们结合示例一句一句来分析。
-```
+```Java
int off = 0;
int next = 0;
boolean limited = limit > 0;
@@ -357,7 +357,7 @@ if判断中**第一个括号**先判断一个字符的情况,并且这个字
#### 8.2 源码分析2
示例代码1:
-```
+```Java
String splitStr1 = "what,is,,,,split";
String[] strs1 = splitStr1.split(",");
for (String s : strs1) {
@@ -375,7 +375,7 @@ split
```
示例代码2:
-```
+```Java
String splitStr1 = "what,is,,,,";
String[] strs1 = splitStr1.split(",");
for (String s : strs1) {
@@ -392,7 +392,7 @@ is
示例代码3:
-```
+```Java
String splitStr1 = "what,is,,,,";
String[] strs1 = splitStr1.split(",", -1);
for (String s : strs1) {
@@ -419,7 +419,7 @@ is
o
```
由于regex为',',所以满足if括号里的判断。一开始next和off指针都在0位置,limit为0,在while里的判断逻辑指的是获取','索引位置,由上图拆分的字符数组可知,next会分别为4,7,8,9,10。由于limited = limit > 0,得知limited为false,则逻辑会走到
-```
+```Java
if (!limited || list.size() < limit - 1) {
list.add(substring(off, next));
off = next + 1;
@@ -468,7 +468,7 @@ list集合里就会添加进空字符串""
[what,is, , , ,]
```
当程序走到时,
-```
+```Java
if(!limited || list.size() < limit) {
list.add(substring(off, value.length);
}
@@ -486,7 +486,7 @@ list集合里就会添加进空字符串""
```
这里相信小伙伴们都知道示例1和示例2的区别在那里了,是因为示例2最后索引位置的list为空字符串,所以list.get(resultSize-1).length()为0,则会调用下面的代码逻辑:
-```
+```Java
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
}
@@ -504,7 +504,7 @@ list集合里就会添加进空字符串""
就以示例代码一为例,对于字符串"what,is,,,,"。
**对于limit > 0**,由于代码:
-```
+```Java
boolean limited = limit > 0; // limited为true
..
..
@@ -528,7 +528,7 @@ what,is,,,,
**对于limit = 0**,由于代码:
-```
+```Java
if (limit == 0) {
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
@@ -543,7 +543,7 @@ is
```
**对于limit < 0**,由于代码:
-```
+```Java
if (!limited || list.size() < limit)
list.add(substring(off, value.length));
```
diff --git "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Thread\345\272\225\345\261\202\346\272\220\347\240\201.md" "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Thread\345\272\225\345\261\202\346\272\220\347\240\201.md"
index b0b44b9..fa16533 100644
--- "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Thread\345\272\225\345\261\202\346\272\220\347\240\201.md"
+++ "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Thread\345\272\225\345\261\202\346\272\220\347\240\201.md"
@@ -50,7 +50,7 @@
#### 3.1 线程优先级
优先级代表线程执行的机会的大小,优先级高的可能先执行,低的可能后执行,在 Java 源码中,优先级从低到高分别是 1 到 10,线程默认 new 出来的优先级都是 5,源码如下:
-```
+```Java
// 最低优先级
public final static int MIN_PRIORITY = 1;
@@ -75,7 +75,7 @@ public final static int MAX_PRIORITY = 10;
#### 4.1 start 启动线程
-```
+```Java
// 该方法可以创建一个新的线程出来
public synchronized void start() {
// 如果没有初始化,抛异常
@@ -111,7 +111,7 @@ private native void start0();
下面只贴出部分关键源码:
-```
+```Java
// 无参构造器,线程名字自动生成
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
@@ -161,13 +161,13 @@ private void init(ThreadGroup g, Runnable target, String name,
当我们调用某个线程的这个方法时,这个方法会挂起调用线程,直到被调用线程结束执行,调用线程才会继续执行。
-```
+```Java
public final void join() throws InterruptedException {
join(0);
}
```
-```
+```Java
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
@@ -206,7 +206,7 @@ private void init(ThreadGroup g, Runnable target, String name,
yield 是个 native 方法,底层代码如下:
-```
+```Java
public static native void yield();
```
@@ -220,7 +220,7 @@ sleep 也是 native 方法,可以接受毫秒的一个入参,也可以接受
接受毫秒和纳秒两个入参时,如果给定纳秒大于等于 0.5 毫秒,算一个毫秒,否则不算。
-```
+```Java
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
@@ -239,7 +239,7 @@ sleep 也是 native 方法,可以接受毫秒的一个入参,也可以接受
sleep(millis);
}
```
-```
+```Java
public static native void sleep(long millis) throws InterruptedException;
```
@@ -252,7 +252,7 @@ interrupt 中文是打断的意思,意思是可以打断中止正在运行的
我们举一个例子来说明如何打断 WAITING 的线程,代码如下:
-```
+```Java
@Test
public void testInterrupt() throws InterruptedException {
Thread thread = new Thread(new Runnable() {
diff --git "a/note/JDK/\346\267\261\345\205\245\350\247\243\350\257\273CompletableFuture\346\272\220\347\240\201\344\270\216\345\216\237\347\220\206.md" "b/note/JDK/\346\267\261\345\205\245\350\247\243\350\257\273CompletableFuture\346\272\220\347\240\201\344\270\216\345\216\237\347\220\206.md"
index 5a43c54..d3271c8 100644
--- "a/note/JDK/\346\267\261\345\205\245\350\247\243\350\257\273CompletableFuture\346\272\220\347\240\201\344\270\216\345\216\237\347\220\206.md"
+++ "b/note/JDK/\346\267\261\345\205\245\350\247\243\350\257\273CompletableFuture\346\272\220\347\240\201\344\270\216\345\216\237\347\220\206.md"
@@ -43,7 +43,7 @@ CompletableFuture类提供了非常多的方法供我们使用,包括了runAsy
**runAsync()**,异步运行,
-```
+```Java
@Test
public void runAsyncExample() throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
@@ -77,7 +77,7 @@ CompletedFuture...isDown
**supplyAsync()**
supply有供应的意思,supplyAsync就可以理解为异步供应,查看supplyAsync()方法入参可以知道,其有两个入参:
-- Supplier supplier,
+- Supplier\ supplier,
- Executor executor
这里先简单介绍下Supplier接口,Supplier接口是JDK8引入的新特性,它也是用于创建对象的,只不过调用Supplier的get()方法时,才会去通过构造方法去创建对象,并且每次创建出的对象都不一样。Supplier常用语法为:
@@ -85,7 +85,7 @@ supply有供应的意思,supplyAsync就可以理解为异步供应,查看sup
Supplier sup= MySupplier::new;
```
再展示代码例子之前,再讲一个thenAccept()方法,可以发现thenAccept()方法的入参如下:
-- Comsumer super T>
+- Comsumer\ super T\>
Comsumer接口同样是java8新引入的特性,它有两个重要接口方法:
1. accept()
@@ -94,7 +94,7 @@ Comsumer接口同样是java8新引入的特性,它有两个重要接口方法
thenAccept()可以理解为接收CompletableFuture的结果然后再进行处理。
下面看下supplyAsync()和thenAccept()的例子:
-```
+```Java
public void thenApply() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
CompletableFuture cf = CompletableFuture.supplyAsync(() -> { //实现了Supplier的get()方法
@@ -132,8 +132,8 @@ public void thenApply() throws Exception {
从代码逻辑可以看出,thenApply_test等到了pool-1-thread-1线程完成任务后,才进行的调用,并且拿到了supplye()方法返回的结果,而main则异步执行了,这就避免了Future获取结果时需要阻塞或轮询的弊端。
**exceptionally**
-当任务在执行过程中报错了咋办?exceptionally()方法很好的解决了这个问题,当报错时会去调用exceptionally()方法,它的入参为:Function fn,fn为执行任务报错时的回调方法,下面看看代码示例:
-```
+当任务在执行过程中报错了咋办?exceptionally()方法很好的解决了这个问题,当报错时会去调用exceptionally()方法,它的入参为:Function\ fn,fn为执行任务报错时的回调方法,下面看看代码示例:
+```Java
public void exceptionally() {
ExecutorService executorService = Executors.newSingleThreadExecutor();
CompletableFuture cf = CompletableFuture.supplyAsync(() -> {
@@ -172,7 +172,7 @@ thenAcceptAsync: helloworld java.lang.RuntimeException: 测试exceptionally...
CompletableFuture类实现了Future接口和CompletionStage接口,Future大家都经常遇到,但是这个CompletionStage接口就有点陌生了,这里的CompletionStage实际上是一个任务执行的一个“阶段”,CompletionStage详细的内容在下文有介绍。
-```
+```Java
public class CompletableFuture implements Future, CompletionStage {
volatile Object result; // CompletableFuture的结果值或者是一个异常的报装对象AltResult
volatile Completion stack; // 依赖操作栈的栈顶
@@ -207,13 +207,13 @@ public class CompletableFuture implements Future, CompletionStage {
runAsync()做的事情就是异步的执行任务,返回的是CompletableFuture对象,不过CompletableFuture对象不包含结果。runAsync()方法有两个重载方法,这两个重载方法的区别是Executor可以指定为自己想要使用的线程池,而runAsync(Runnable)则使用的是ForkJoinPool.commonPool()。
下面先来看看runAsync(Runnable)的源码:
-```
+```Java
public static CompletableFuture runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
```
这里的asyncPool是一个静态的成员变量:
-```
+```Java
private static final boolean useCommonPool =
(ForkJoinPool.getCommonPoolParallelism() > 1); // 并行级别
private static final Executor asyncPool = useCommonPool ?
@@ -221,7 +221,7 @@ private static final Executor asyncPool = useCommonPool ?
```
回到asyncRunStage()源码:
-```
+```Java
static CompletableFuture asyncRunStage(Executor e, Runnable f) {
if (f == null) throw new NullPointerException();
CompletableFuture d = new CompletableFuture();
@@ -230,7 +230,7 @@ private static final Executor asyncPool = useCommonPool ?
}
```
看到asyncRunStage()源码,可以知道任务是由Executor来执行的,那么可想而知Async类一定是实现了Callable接口或者继承了Runnable类,查看Async类:
-```
+```Java
static final class AsyncRun extends ForkJoinTask
implements Runnable, AsynchronousCompletionTask {
CompletableFuture dep; Runnable fn;
@@ -265,7 +265,7 @@ postComplete()的源码还是有点复杂的,先不急着分析。**先看看C
#### Completion
下面先看看Completion的源码:
-```
+```Java
abstract static class Completion extends ForkJoinTask
implements Runnable, AsynchronousCompletionTask {
volatile Completion next;
@@ -291,7 +291,7 @@ volatile Completion stack;
```
由这个属性可以看出,CompletableFuture其实就是一个链表的一个数据结构。
-```
+```Java
abstract static class UniCompletion extends Completion {
Executor executor; // executor to use (null if none)
CompletableFuture dep; // 代表的依赖的CompletableFuture
@@ -322,7 +322,7 @@ abstract static class UniCompletion extends Completion {
```
claim方法要在执行action前调用,若claim方法返回false,则不能调用action,原则上要保证action只执行一次。
-```
+```Java
static final class UniAccept extends UniCompletion {
Consumer super T> fn;
UniAccept(Executor executor, CompletableFuture dep,
@@ -342,7 +342,7 @@ static final class UniAccept extends UniCompletion {
}
}
```
-```
+```Java
final boolean uniAccept(CompletableFuture a,
Consumer super S> f, UniAccept c) {
Object r; Throwable x;
@@ -370,7 +370,7 @@ final boolean uniAccept(CompletableFuture a,
}
```
对于Completion的执行,还有几个关键的属性:
-```
+```Java
static final int SYNC = 0;//同步
static final int ASYNC = 1;//异步
static final int NESTED = -1;//嵌套
@@ -391,14 +391,14 @@ Completion在CompletableFuture中是如何工作的呢?现在先不着急了
- Runable既不产生结果也不消耗结果
下面看看一个Stage的调用例子:
-```
+```Java
stage.thenApply(x -> square(x)).thenAccept(x -> System.out.println(x)).thenRun(() -> System.out.println())
```
这里x -> square(x)就是一个Function类型的Stage,它返回了x。x -> System.out.println(x)就是一个Comsumer类型的Stage,用于接收上一个Stage的结果x。() ->System.out.println()就是一个Runnable类型的Stage,既不消耗结果也不产生结果。
一个、两个或者任意一个CompletionStage的完成都会触发依赖的CompletionStage的执行,CompletionStage的依赖动作可以由带有then的前缀方法来实现。如果一个Stage被两个Stage的完成给触发,则这个Stage可以通过相应的Combine方法来结合它们的结果,相应的Combine方法包括:thenCombine、thenCombineAsync。但如果一个Stage是被两个Stage中的其中一个触发,则无法去combine它们的结果,因为这个Stage无法确保这个结果是那个与之依赖的Stage返回的结果。
-```
+```Java
@Test
public void testCombine() throws Exception {
String result = CompletableFuture.supplyAsync(() -> {
@@ -422,7 +422,7 @@ stage.thenApply(x -> square(x)).thenAccept(x -> System.out.println(x)).thenRun((
下面开始介绍CompletableFuture的几个核心方法:
**postComplete**
-```
+```Java
final void postComplete() {
CompletableFuture> f = this; Completion h; //this表示当前的CompletableFuture
while ((h = f.stack) != null || //判断stack栈是否为空
@@ -444,7 +444,7 @@ final void postComplete() {
postComplete()方法可以理解为当任务完成之后,调用的一个“后完成”方法,主要用于触发其他依赖任务。
**uniAccept**
-```
+```Java
final boolean uniAccept(CompletableFuture a,
Consumer super S> f, UniAccept c) {
Object r; Throwable x;
@@ -476,7 +476,7 @@ final boolean uniAccept(CompletableFuture a,
**pushStack**
-```
+```Java
final void pushStack(Completion c) {
do {} while (!tryPushStack(c)); //使用CAS自旋方式压入栈,避免了加锁竞争
}
@@ -493,7 +493,7 @@ final boolean uniAccept(CompletableFuture a,
```
光分析源码也没法深入理解其代码原理,下面结合一段示例代码来对代码原理进行分析。
-```
+```Java
@Test
public void thenApply() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
@@ -533,19 +533,19 @@ final boolean uniAccept(CompletableFuture a,
CompletedFuture...isDown
*/
-这段示例代码所做的事情就是supplyAsync(Supplier supplier)休眠200秒之后,返回一个字符串,thenAccept(Consumer super T> action)等到任务完成之后接收这个字符串,并且调用thenApply_test()方法,随后输出 hello world。
+这段示例代码所做的事情就是supplyAsync(Supplier\ supplier)休眠200秒之后,返回一个字符串,thenAccept(Consumer\ super T\> action)等到任务完成之后接收这个字符串,并且调用thenApply_test()方法,随后输出 hello world。
代码中让线程休眠200秒是为了方便观察CompletableFuture的传递过程。
下面就描述下程序的整个运作流程。
**①** 主线程调用CompletableFuture的supplyAsync()方法,传入Supplier和Executor。在supplyAsync()中又继续调用CompletableFuture的asyncSupplyStage(Executor, Supplier)方法。

-来到asyncSupplyStage()方法中,调用指定的线程池,并执行execute(new AsyncSupply(d,f)),这里d就是我们的“源任务”,接下来thenApply()要依赖着这个源任务进行后续逻辑操作,f就是Supplier的函数式编程。
+来到asyncSupplyStage()方法中,调用指定的线程池,并执行execute(new AsyncSupply\(d,f)),这里d就是我们的“源任务”,接下来thenApply()要依赖着这个源任务进行后续逻辑操作,f就是Supplier的函数式编程。

AsyncSupply实现了Runnable的run()方法,核心逻辑就在run()方法里。在run()方法里,先判断d.result == null,判断该任务是否已经完成,防止并发情况下其他线程完成此任务了。f.get()就是调用的Supplier的函数式编程,这里会休眠200秒,所以executor线程池开启的线程会在这里阻塞200秒。
**②** 虽然executor线程池线程阻塞了,但是main线程任然会继续执行接下来的代码。

-main线程会在asyncSupplyStage()方法中返回d,就是我们的“依赖任务”,而这个任务此时还处在阻塞中。接下来main线程会继续执行CompletableFuture的thenAccept(Comsumer super T> action)方法,然后调用CompletableFuture的uniAcceptStage()方法。
+main线程会在asyncSupplyStage()方法中返回d,就是我们的“依赖任务”,而这个任务此时还处在阻塞中。接下来main线程会继续执行CompletableFuture的thenAccept(Comsumer\ super T\> action)方法,然后调用CompletableFuture的uniAcceptStage()方法。

在uniAcceptStage()方法中,会将“依赖任务”、“源任务”、线程池以及Comsumer报装程一个UniAccept对象,然后调用push()压入stack的栈顶中。随后调用UniAccept的tryFire()方法。

@@ -555,7 +555,7 @@ main线程会在asyncSupplyStage()方法中返回d,就是我们的“依赖任
**③** 回到“源任务”,虽然main线程已经结束了整个生命周期,但是executor线程池的线程任然阻塞着的,休眠了200秒之后,继续执行任务。

-然后来到了postComplete()方法。这个方法在前面已经介绍到了,它是CompletableFuture的核心方法之一,做了许多事情。最重要的一件事情就是触发其他依赖任务,接下来调用的方法依次为:UniAccept.tryFire(mode) ——> CompletableFuture.uniAccept(..) ——> Comsumer.accept(s) ——> 输出“hello world”,并输出当前调用线程的线程名。因这个调用链已经在②中介绍过了,所以就不再详细介绍其运作逻辑。
+然后来到了postComplete()方法。这个方法在前面已经介绍到了,它是CompletableFuture的核心方法之一,做了许多事情。最重要的一件事情就是触发其他依赖任务,接下来调用的方法依次为:UniAccept.tryFire(mode) ——\> CompletableFuture.uniAccept(..) ——\> Comsumer.accept(s) ——\> 输出“hello world”,并输出当前调用线程的线程名。因这个调用链已经在②中介绍过了,所以就不再详细介绍其运作逻辑。
**小结:** 通过这个小示例,终于理解到了“源任务”和“依赖任务”之间的调用关系,以及CompletableFuture的基本运作原理。然而CompletableFuture还有其他的方法需要去深入分析,由于篇幅所限就不再赘述,感兴趣的读者可以以debug的模式去一点一点分析CompletableFuture其他方法的底层原理。这里不得不说Java并发包作者Doug Lea大神真的太厉害了,阅读他的源码之后,可以发现他写的代码不能以技术来形容,而应该使用“艺术”来形容。
diff --git "a/note/Netty/\344\272\214\350\277\233\345\210\266.md" "b/note/Netty/\344\272\214\350\277\233\345\210\266.md"
index 69bfb3b..97f7667 100644
--- "a/note/Netty/\344\272\214\350\277\233\345\210\266.md"
+++ "b/note/Netty/\344\272\214\350\277\233\345\210\266.md"
@@ -48,7 +48,7 @@
对于二进制运算,记住一个口诀:
1. 与(&)运算
-
+
运算规则:
``` 0&0=0, 0&1=0, 1&0=0, 1&1=1 ```
@@ -143,7 +143,7 @@ a = 10
**在开源框架底层中算法会用到大量的二进制运算,** 例如:在最近学习的Netty底层源码中,DefaultEventExecutorChooserFactory的底层源码有一个方法, 就是通过 a & (-a)来运算的。
-```
+```Java
@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
diff --git "a/note/Spring/\344\273\216Spring\346\272\220\347\240\201\344\270\255\345\255\246\344\271\240\342\200\224\342\200\224\347\255\226\347\225\245\346\250\241\345\274\217.md" "b/note/Spring/\344\273\216Spring\346\272\220\347\240\201\344\270\255\345\255\246\344\271\240\342\200\224\342\200\224\347\255\226\347\225\245\346\250\241\345\274\217.md"
index a35adcc..d26db14 100644
--- "a/note/Spring/\344\273\216Spring\346\272\220\347\240\201\344\270\255\345\255\246\344\271\240\342\200\224\342\200\224\347\255\226\347\225\245\346\250\241\345\274\217.md"
+++ "b/note/Spring/\344\273\216Spring\346\272\220\347\240\201\344\270\255\345\255\246\344\271\240\342\200\224\342\200\224\347\255\226\347\225\245\346\250\241\345\274\217.md"
@@ -24,7 +24,7 @@
### 一、先看看初学者都会的多重if-else判断
-```
+```Java
public int count(int num1, int num2, String operation) {
if (operation.equals("+")) {
return num1 + num2;
@@ -45,7 +45,7 @@ public int count(int num1, int num2, String operation) {
### 二、策略模式实现
#### 2.1 定义一个策略接口: Strategy.class
-```
+```Java
public interface Strategy {
public int doOperation(int num1, int num2);
}
@@ -54,7 +54,7 @@ public interface Strategy {
#### 2.2 创建接口的实现类
Add.java
-```
+```Java
public class Add implements Strategy{
@Override
public int doOperation(int num1, int num2) {
@@ -64,7 +64,7 @@ public class Add implements Strategy{
```
Substract.java
-```
+```Java
public class Substract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
@@ -74,7 +74,7 @@ public class Substract implements Strategy{
```
Multiply.java
-```
+```Java
public class Multiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
@@ -84,7 +84,7 @@ public class Multiply implements Strategy{
```
Divide.java
-```
+```Java
public class Divide implements Strategy{
@Override
public int doOperation(int num1, int num2) {
@@ -95,7 +95,7 @@ public class Divide implements Strategy{
#### 2.3 创建Context类
-```
+```Java
public class Context {
private Strategy strategy;
@@ -110,7 +110,7 @@ public class Context {
```
#### 2.4 创建实现类
-```
+```Java
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context();
@@ -158,7 +158,7 @@ public class StrategyPatternDemo {
在学习BeanDefinitionReader之前,要先了解一下什么是BeanDefinition
接口BeanDefinition.java
-```
+```Java
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
@@ -172,7 +172,7 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
可以看到BeanDefinition作为一个接口,主要是用于存储从XML配置文件读取Bean信息到JVM内存的一个载体,具体是存储在了BeanDefinition的实现类——RootBeanDefinition中,下面来看看RootBeanDefinition。
-```
+```Java
public class RootBeanDefinition extends AbstractBeanDefinition {
@Nullable
private BeanDefinitionHolder decoratedDefinition;
@@ -187,7 +187,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
```
可以看到RootBeanDefinition不是真正存储Bean信息的载体,继续查看BeanDefinitionHolder
-```
+```Java
public class BeanDefinitionHolder implements BeanMetadataElement {
private final BeanDefinition beanDefinition;
@@ -244,13 +244,13 @@ public interface ResourceLoader {
由于```resourceLoader instanceof ResourcePatternResolver为true```,所以走如下逻辑:
AbstractBeanDefinitionReader.java
-```
+```Java
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
```
AbstractApplicationContext.java
-```
+```Java
@Override
public Resource[] getResources(String locationPattern) throws IOException {
return this.resourcePatternResolver.getResources(locationPattern);
@@ -258,7 +258,7 @@ AbstractApplicationContext.java
```
PathMatchingResourcePatternResolver.java
-```
+```Java
@Override
public Resource[] getResources(String locationPattern) throws IOException {
diff --git "a/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\212\357\274\211.md" "b/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\212\357\274\211.md"
index dc0a23f..436b444 100644
--- "a/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\212\357\274\211.md"
+++ "b/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\212\357\274\211.md"
@@ -17,10 +17,10 @@ Spring容器就相当于一个大的水桶,里面装着很多水——bean对
## 进入正题
在Spring容器的设计中,有两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这个接口实现了容器最基本的功能;另一个是ApplicationContext应用上下文,作为容器的高级形态而存在,它用于扩展BeanFactory中现有的功能。ApplicationContext和BeanFactory两者都是用于加载Bean的,但是相比之下,ApplicationContext提供了更多的扩展功能,简单一点说:ApplicationContext包含BeanFactory的所有功能。绝大多数“典型”的企业应用和系统,ApplicationContext就是你需要使用的。下面展示一下分别使用BeanFactory和ApplicationContext读取xml配置文件的方式:
-```
+```Java
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
```
-```
+```Java
ApplicationContext bf = new ClassPathXmlApplicationContext("applicationContext.xml");
```
下面先介绍Spring最核心的两个类。
@@ -63,7 +63,7 @@ XML配置文件的读取是Spring中最重要的功能,因为Spring的大部
下面演示一个使用ApplicationContext接口获取xml配置,从而实现一个helloword级别的spring程序:
applicationContext.xml
-```
+```Java
```
测试类
-```
+```Java
public class SpringMain {
public static void main(String[] args) {
//使用spring容器
@@ -101,11 +101,11 @@ Person{name='Bruis', age=23}
通过在断点debug,跟踪程序运行。
1. SpringMain.class
-```
+```Java
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
```
2. ClassPathXmlApplicationContext.class
-```
+```Java
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
@@ -123,7 +123,7 @@ public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh,
}
```
3. AbstractRefreshableConfigApplicationContext.class
-```
+```Java
//给configLocations字符串数组设置值,支持多个配置文件已数组方式同时传入。
public void setConfigLocations(String... locations) {
if (locations != null) {
@@ -149,7 +149,7 @@ public void setConfigLocations(String... locations) {

1. AbstractApplicationContext.class
-```
+```Java
/*
简单来说,Spring容器的初始化时右refresh()方法来启动的,这个方法标志着IOC容器的正式启动。具体来说,这里的启动包括了BeanDefinition和Resource的定位、载入和注册三个基本过程。
*/
@@ -198,7 +198,7 @@ public void refresh() throws BeansException, IllegalStateException {
}
```
2. AbstractRefreshableApplicationContext.class
-```
+```Java
/*
通知子类刷新内部bean工厂,初始化BeanFactory并进行XML文件的解析、读取。obtain就是指获得的含义,这个方法obtaiinFreshBeanFactory正是实现BeanFactory的地方,也就是经过这个方法,ApplicationContext就已经拥有了BeanFactory的全部功能(也就是BeanFactory包含在了Spring容器里了)。
*/
@@ -246,7 +246,7 @@ protected final void refreshBeanFactory() throws BeansException {
那么,person的属性是怎么被封装到beanFactory里面的呢?请看下面的源码解析。
3. AbstractXmlApplicationContext.class
-```
+```Java
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//为给定的BeanFactory创建一个新的XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
@@ -272,7 +272,7 @@ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansE
首先在refreshBeanFactory()方法中已经初始化了DefaultListableBeanFactory,对于读取XML配置文件,还需要使用XmlBeanDefinitionReader。所以在上述loadBeanDefinitions()中就需要初始化XmlBeanDefinitionReader。在DefaultListableBeanFactory和XmlBeanDefinitionReader后就可以进行配置文件的读取了。要注意的地方时,在XmlBeanDefinitionReader初始化时就已经把DefaultListableBeanFactory给注册进去了,所以在XmlBeanDefinitionReader读取的BeanDefinition都会注册到DefaultListableBeanFactory中,也就是经过上述的loadingBeanDefinitions(),类型DefaultListableBeanFactory的变量beanFactory就已经包含了所有**解析好的配置**了。
4. AbstractBeanDefinitionReader.class
-```
+```Java
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
@@ -321,7 +321,7 @@ public int loadBeanDefinitions(String location, @Nullable Set actualRe
}
```
5. PathMatchingResourcePatternResolver.class
-```
+```Java
@Override
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
@@ -352,7 +352,7 @@ public Resource[] getResources(String locationPattern) throws IOException {
}
```
6. XmlBeanDefinitionReader.class
-```
+```Java
/*
从XML配置文件中获取bean定义信息
*/
@@ -412,7 +412,7 @@ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)

1. XmlBeanDefinitionReader.class
-```
+```Java
/*
注册给定DOM文档中包含的bean定义
*/
@@ -424,7 +424,7 @@ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanD
}
```
2. DefaultBeanDefinitionDocumentReader.class
-```
+```Java
/*
此实现根据“spring-beans”XSD解析bean定义
*/
@@ -507,7 +507,7 @@ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate d
```
2. BeanDefinitionParserDelegate.class
-```
+```Java
/*
解析bean定义本身,而不考虑名称或别名,如果解析期间出错则返回null。
*/
@@ -640,7 +640,7 @@ public void parsePropertyElement(Element ele, BeanDefinition bd) {

然后,就会一路返回到refresh()方法里的加载bean定义信息的方法——loadBeanDefinitions(),此时beanFactory里面就会存在一个带有KV对的ConcurrentHashMap,而这个beanFactory会存放在Spring容器里面。
-```
+```Java
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
@@ -673,7 +673,7 @@ bean的创建和初始化过程是在refresh方法里的invokeBeanFactoryPostPro

1. AbstractApplicationContext.class
-```
+```Java
public void refresh() throws BeansException, IllegalStateException {
...
// 实例剩余的(非懒加载)的单例
@@ -719,7 +719,7 @@ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory b
这里的懒加载的意思,指的是bean单例不是在Spring容器初始化的时候就创建的,而是在要使用该bean的时候,才会创建该bean。
2. DefaultListableBeanFactory.class
-```
+```Java
// 实例剩余的(非懒加载)的单例
@Override
public void preInstantiateSingletons() throws BeansException {
@@ -780,7 +780,7 @@ public void preInstantiateSingletons() throws BeansException {
```
3. AbstractBeanFactory.class
-```
+```Java
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 去除name上存在的工厂bean的前缀
@@ -895,7 +895,7 @@ protected T doGetBean(final String name, @Nullable final Class requiredTy
```
4. DefaultSingletonBeanRegistry.class
-```
+```Java
/*
尝试从缓存中获取单例对象,如果缓存中有该单例对象,并且该对象正在被创建,则从缓存中获取。
*/
@@ -970,7 +970,7 @@ public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {

5. AbstractAutowireCapableBeanFactory.class
-```
+```Java
/*
该类的中心方法:创建bean实例,实例化bean实例,应用bean的后置处理器
diff --git "a/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\213\357\274\211.md" "b/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\213\357\274\211.md"
index faa364d..7f8691e 100644
--- "a/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\213\357\274\211.md"
+++ "b/note/Spring/\346\267\261\345\205\245Spring\346\272\220\347\240\201\347\263\273\345\210\227\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224\346\267\261\345\205\245Spring\345\256\271\345\231\250\357\274\214\351\200\232\350\277\207\346\272\220\347\240\201\351\230\205\350\257\273\345\222\214\346\227\266\345\272\217\345\233\276\346\235\245\345\275\273\345\272\225\345\274\204\346\207\202Spring\345\256\271\345\231\250\357\274\210\344\270\213\357\274\211.md"
@@ -84,7 +84,7 @@ IOC容器的启动过程就是建立Spring上下文的过程,该上下文是
结合着时序图,再去调试源码,思路会清晰很多。
ContextLoaderListener.class
-```
+```Java
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
@@ -117,7 +117,7 @@ public class ContextLoaderListener extends ContextLoader implements ServletConte
这里的ContextLoaderListener是Spring的类,但实现了ServletContextListener接口。这个接口是Servlet API中定义的,提供了与Servlet生命周期结合的回调,也就是说Servlet调用contextInitialized()方法初始化容器时,会回调ContextLoaderListener中实现的contextInitialized()方法,Servlet中的contextDestroyed()方法也同理。观察源码可知,在Web容器中,建立WebApplicationContext的过程是在contextInitialized()方法中完成的。
ContextLoader.class
-```
+```Java
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
...
// 判断在web容器中是否存在WebApplicationContext,因为在配置中只允许申明一次ServletContextListener,多次声明会扰乱Spring的执行逻辑。
@@ -168,7 +168,7 @@ public WebApplicationContext initWebApplicationContext(ServletContext servletCon
由ContextLoader的源码可知,SpringIOC的载入过程是在ContextLoader类的initWebApplicationContext()方法中完成的。
这里还要介绍一个重要的接口——WebApplicationContext
-```
+```Java
public interface WebApplicationContext extends ApplicationContext {
/**
@@ -184,7 +184,7 @@ public interface WebApplicationContext extends ApplicationContext {
}
```
而WebApplicationContext接口是由XMLWebApplicationContext来实现具体的功能,然后再通过ApplicationContext接口与BeanFactory接口对接,完成Spring容器的功能。然而对于具体的一些Spring容器的实现都是在AbstractRefreshableWebApplicationContext中完成的,这一点和**上篇**讲解的AbstractRefreshableConfigApplicationContext功能类似。initWebApplicationContext()方法最后返回的是一个WebApplicationContext接口,而实际返回的就是XMLWebApplicationContext实现类。XMLWebApplicationContext在基本的ApplicationContext功能的基础上,增加了对**Web环境**和XML配置定义的处理。在XMLWebApplicationContext的初始化过程中,Web容器中的IOC容器被建立起来,从而再整个Web容器中建立起Spring应用。
-```
+```Java
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/** 默认读取Spring配置文件的根路径,如果指定其他配置文件,则从这个默认的根路径读取。 */
@@ -234,7 +234,7 @@ public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationC
从源码中可以看到,XMLWebApplicationContext中成员变量存放着默认的读取Spring配置文件的根目录,在生成IOC容器过程中,就会从默认路径/WEB-INF/applicationContext.xml配置文件中或者指定的配置文件路径获取,然后再通过熟悉的loadBeanDefinitions()方法来获取Bean定义信息,最终完成整个上下文的初始化过程。
ContextLoader.class
-```
+```Java
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 这里判断使用什么样的类在Web容器中作为IOC容器
Class> contextClass = determineContextClass(sc);
@@ -274,7 +274,7 @@ protected Class> determineContextClass(ServletContext servletContext) {
下面看看默认的IOC容器是什么。有图有真相:

-```
+```Java
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
diff --git "a/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224\346\263\250\345\206\214AnnotationAwareAspectJAutoProxyCreator.md" "b/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224\346\263\250\345\206\214AnnotationAwareAspectJAutoProxyCreator.md"
index 5003158..014dd27 100644
--- "a/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224\346\263\250\345\206\214AnnotationAwareAspectJAutoProxyCreator.md"
+++ "b/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224\346\263\250\345\206\214AnnotationAwareAspectJAutoProxyCreator.md"
@@ -13,7 +13,7 @@
- TestMain //main测试方法入口
TestBean.java
-```
+```Java
public class TestBean {
private String testStr = "testStr";
@@ -32,7 +32,7 @@ public class TestBean {
```
AspectJTest.java
-```
+```Java
@Aspect
public class AspectJTest {
@@ -96,7 +96,7 @@ aspectTest.xml
```
TestMain.java
-```
+```Java
public class TestMain {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aspectTest.xml");
@@ -157,7 +157,7 @@ http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNam
无图无真相啊,原来Spring将配置文件中的xmlns配置都解析成了一个一个Java命名解析器。回到我们的关注重点——AopNamespaceHandler,查看源码:
AopNamespaceHandler.class
-```
+```Java
public class AopNamespaceHandler extends NamespaceHandlerSupport {
public AopNamespaceHandler() {
}
@@ -171,7 +171,7 @@ public class AopNamespaceHandler extends NamespaceHandlerSupport {
}
```
可以看到,在init()方法里,Spring对aspectj-autoproxy也就是AnnotationAwareAspectJAutoProxyCreator进行了注册。在详细了解注册原理之前,先说明下在Spring中,所有的解析器都是对BeanDefinitionParser接口的同一实现:
-```
+```Java
public interface BeanDefinitionParser {
@Nullable
BeanDefinition parse(Element var1, ParserContext var2);
@@ -180,7 +180,7 @@ public interface BeanDefinitionParser {
解析入口都是从parse方法开始的。
进入AspectJAutoProxyBeanDefinitionParser类中查看parse的实现逻辑:
-```
+```Java
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
...
@Nullable
@@ -196,7 +196,7 @@ class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
AopNamspaceUtils
-```
+```Java
public abstract class AopNamespaceUtils {
public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";
private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";
@@ -231,7 +231,7 @@ public abstract class AopNamespaceUtils {
```
-```
+```Java
public abstract class AopConfigUtils {
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
@@ -264,7 +264,7 @@ registerOrEscalateApcAsRequired方法的作用就是获取AnnotationAwareAspectJ
看看如果proxy-target-class和expose-proxy都为true时,代码的逻辑。
-```
+```Java
public abstract class AopConfigUtils {
...
/*
@@ -307,7 +307,7 @@ public abstract class AopConfigUtils {
经过了useClassProxyingIfNecessary()方法的调用,ParserContext对象中存放好了注册的额外信息(proxy-target-class、expose-proxy值等),这里暂且将ParserContext称为解析上下文。由上面的源码可知,在AopNamespaceUtils类的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法中,将获取的org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition和解析上下文一起传入registerComponentIfNecessary方法中,进行Component组件注册。
在随后的registerComponentIfNecessary方法中,经过new BeanComponentDefinition()构造方法的调用,已经将AnnotationAwareAspectJAutoProxyCreator的BeanDefinition注册到了SpringIOC中。
-```
+```Java
public abstract class AopConfigUtils {
...
private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
@@ -318,14 +318,14 @@ public abstract class AopConfigUtils {
}
}
```
-```
+```Java
public class BeanComponentDefinition extends BeanDefinitionHolder implements ComponentDefinition {
public BeanComponentDefinition(BeanDefinition beanDefinition, String beanName) {
this(new BeanDefinitionHolder(beanDefinition, beanName));
}
}
```
-```
+```Java
public class BeanDefinitionHolder implements BeanMetadataElement {
public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName) {
this(beanDefinition, beanName, (String[])null);
@@ -341,7 +341,7 @@ public class BeanDefinitionHolder implements BeanMetadataElement {
}
```
然后一路返回,将BeanDefinition存放在解析上下文(ParserContext)中,并在AspectJAutoProxyBeanDefinitionParser类的extendBeanDefinition方法中取出。
-```
+```Java
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
private void extendBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinition beanDef = parserContext.getRegistry().getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
diff --git "a/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224\346\217\255\345\274\200JDK\345\212\250\346\200\201\344\273\243\347\220\206\345\222\214CGLIB\344\273\243\347\220\206\347\232\204\347\245\236\347\247\230\351\235\242\347\272\261.md" "b/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224\346\217\255\345\274\200JDK\345\212\250\346\200\201\344\273\243\347\220\206\345\222\214CGLIB\344\273\243\347\220\206\347\232\204\347\245\236\347\247\230\351\235\242\347\272\261.md"
index a83ee68..026a120 100644
--- "a/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224\346\217\255\345\274\200JDK\345\212\250\346\200\201\344\273\243\347\220\206\345\222\214CGLIB\344\273\243\347\220\206\347\232\204\347\245\236\347\247\230\351\235\242\347\272\261.md"
+++ "b/note/SpringAOP/\346\267\261\345\205\245\345\255\246\344\271\240SpringAOP\346\272\220\347\240\201\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224\346\217\255\345\274\200JDK\345\212\250\346\200\201\344\273\243\347\220\206\345\222\214CGLIB\344\273\243\347\220\206\347\232\204\347\245\236\347\247\230\351\235\242\347\272\261.md"
@@ -14,7 +14,7 @@
#### 1.1 引入简单的CGLIB例子
在讲解CGLIB动态代理之前,先看一下最简单的CGLIB动态代理的例子。
-```
+```Java
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
@@ -71,7 +71,7 @@ com.bruis.learnaop.testcglibaop.EnhancerDemo$$EnhancerByCGLIB$$413eae0d@53e25b76
回到SpringAOP源码。在《深入学习SpringAOP源码(二)》中,介绍到DefaultAopProxyFactory源码部分
-```
+```Java
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
@@ -88,7 +88,7 @@ public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
}
```
从createAopProxy()源码中可以看到,创建SpringAOP有两种方式,一、JDK动态代理;二、CGLIB动态代理;点进ObjenesisCglibAopProxy源码,发现它继承了CglibAopFactory
-```
+```Java
class ObjenesisCglibAopProxy extends CglibAopProxy {
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
// 通过增强器获取代理类的class对象
@@ -122,7 +122,7 @@ class ObjenesisCglibAopProxy extends CglibAopProxy {
createProxyClassAndInstance方法和前面总结的CGLIB创建代理的步骤一样。
继续查看CglibAopProxy是如何准备Enhancer增强器以及创建拦截器链的。
-```
+```Java
class CglibAopProxy implements AopProxy, Serializable {
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
@@ -229,7 +229,7 @@ class CglibAopProxy implements AopProxy, Serializable {
#### 1.3 DynamicAdvisedInterceptor都做了些啥工作?
-```
+```Java
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
@@ -280,7 +280,7 @@ class CglibAopProxy implements AopProxy, Serializable {
#### 1.4 啥是拦截器链?拦截器链从哪获取?
啥是拦截器链?从哪获取拦截器链?下面继续深入DefaultAdvisorChainFactory方法的getInterceptorsAndDynamicInterceptionAdvice()方法
-```
+```Java
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
public List
@@ -130,7 +133,7 @@ SpringCloud源码
- [Netty底层源码解析-Netty服务端启动分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-Netty%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%90%AF%E5%8A%A8%E5%88%86%E6%9E%90.md)
- [Netty底层源码解析-NioEventLoop原理分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-NioEventLoop%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.md)
- Netty底层源码解析-Channel分析
- - [Netty底层源码解析-ChannelPipeline分析(上)]()
+ - [Netty底层源码解析-ChannelPipeline分析(上)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-ChannelPipeline%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%8A%EF%BC%89.md)
- Netty底层源码解析-ChannelPipeline分析(下)
- Netty底层源码解析-FastThreadLocal原理分析
- Netty底层源码解析-内存分配原理分析
From 5558fb8821425a63ed2f5936a21837e32f11e94e Mon Sep 17 00:00:00 2001
From: Bruis
Date: Wed, 25 Aug 2021 22:25:26 +0800
Subject: [PATCH 32/63] fix rocketmq badge
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 82f96be..888129f 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
-
+
From 81961ac5ccba0ecff97a8926c5b7f345f0d6ff9e Mon Sep 17 00:00:00 2001
From: Bruis
Date: Wed, 25 Aug 2021 22:27:04 +0800
Subject: [PATCH 33/63] rocketmq -> RocketMQ
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 888129f..089f454 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
-
+
From 20615190037de5dfec38f28b038685148d317a64 Mon Sep 17 00:00:00 2001
From: Bruis
Date: Wed, 25 Aug 2021 23:18:17 +0800
Subject: [PATCH 34/63] =?UTF-8?q?fix=20=E7=89=88=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index 089f454..94e0586 100644
--- a/README.md
+++ b/README.md
@@ -99,14 +99,6 @@ SpringCloud源码
- [深入浅出SpringBoot源码——系统初始化器](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94%E7%B3%BB%E7%BB%9F.md)
- [深入浅出SpringBoot源码——启动加载器](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94%E5%90%AF%E5%8A%A8%E5%8A%A0%E8%BD%BD%E5%99%A8.md)
-
-- Netty源码学习
- - [二进制运算以及源码、反码以及补码学习](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/%E4%BA%8C%E8%BF%9B%E5%88%B6.md)
- - [Netty源码包结构](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E6%BA%90%E7%A0%81%E5%8C%85%E7%BB%93%E6%9E%84.md)
- - [Netty中的EventLoopGroup](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E4%B8%AD%E7%9A%84EventLoopGroup%E6%98%AF%E4%BB%80%E4%B9%88.md)
- - [Netty中的ChannelPipeline]()
- - [Netty中的内存分配]()
-
- SpringSecurity&OAuth2源码学习
- SpringSecurity版本:5.1.0.RELEASE
- [深入浅出SpringSecurity和OAuth2(一)—— 初识SpringSecurity](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%20%E5%88%9D%E8%AF%86SpringSecurity.md)
@@ -129,6 +121,10 @@ SpringCloud源码
- Dubbo底层源码学习(七)—— Dubbo的服务消费
- Netty底层源码解析
+ - Netty版本:4.1.60.Final
+ - [二进制运算以及源码、反码以及补码学习](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/%E4%BA%8C%E8%BF%9B%E5%88%B6.md)
+ - [Netty源码包结构](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E6%BA%90%E7%A0%81%E5%8C%85%E7%BB%93%E6%9E%84.md)
+ - [Netty中的EventLoopGroup](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E4%B8%AD%E7%9A%84EventLoopGroup%E6%98%AF%E4%BB%80%E4%B9%88.md)
- [Netty底层源码解析-初始Netty及其架构](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-%E5%88%9D%E5%A7%8BNetty%E5%8F%8A%E5%85%B6%E6%9E%B6%E6%9E%84.md)
- [Netty底层源码解析-Netty服务端启动分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-Netty%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%90%AF%E5%8A%A8%E5%88%86%E6%9E%90.md)
- [Netty底层源码解析-NioEventLoop原理分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-NioEventLoop%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.md)
@@ -140,6 +136,7 @@ SpringCloud源码
- Netty底层源码解析-RocketMQ底层使用到的Netty
- RocketMQ底层源码解析
+ - RocketMQ版本:4.9.0
- RocketMQ底层源码解析-RocketMQ环境搭建
- RocketMQ底层源码解析-本地调试RocketMQ源码
- RocketMQ底层源码解析-NameServer分析
@@ -148,9 +145,8 @@ SpringCloud源码
todo
-- 完成Dubbo源码分析
-- RocketMQ源码分析
-- Netty源码分析
+- Redis底层源码分析
+- JUC底层源码分析
# 支持
From 051fa5707cc62493bf5ca5fca74f6cf50fe3735c Mon Sep 17 00:00:00 2001
From: Bruis
Date: Mon, 30 Aug 2021 00:10:49 +0800
Subject: [PATCH 35/63] =?UTF-8?q?add=20Netty=E6=BA=90=E7=A0=81=E5=88=86?=
=?UTF-8?q?=E6=9E=90-ChannelPipeline=E4=B8=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...20\357\274\210\344\270\213\357\274\211.md" | 322 ++++++++++++++++++
1 file changed, 322 insertions(+)
create mode 100644 "note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-ChannelPipeline\345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md"
diff --git "a/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-ChannelPipeline\345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md" "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-ChannelPipeline\345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md"
new file mode 100644
index 0000000..9683989
--- /dev/null
+++ "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-ChannelPipeline\345\210\206\346\236\220\357\274\210\344\270\213\357\274\211.md"
@@ -0,0 +1,322 @@
+## 1. 概览
+
+上篇已经讲解了ChannelPipeline以及ChannelHandler的关系以及对应的类继承关系图,本节来详细分析一下inbound和outbound的原理。
+
+## 2. DefaultChannelPipeline源码分析
+
+在DefaultChannelPipeline中,定义了一个head“头结点”和一个tail“尾结点”,它们都是AbstractChannelhandlerContext类的节点,我们都知道在ChannelPipeline中AbstractChannelHandlerContext就是节点元素的抽象类实现,而这个handlerContext持有ChannelHandler。
+
+在Netty中我们还需要知道inbound和outbound类型的ChannelHandler节点的执行顺序。
+
+下面来先看下一个Netty的demo
+
+该Netty的demo中,分别定义了六个Handler,分为两组,一组是inboundHandler,另一组是outboundHandler。
+
+
+InBoundHandlerA
+```java
+public class InBoundHandlerA extends ChannelInboundHandlerAdapter {
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ System.out.println("InBoundHandlerA: " + msg);
+ ctx.fireChannelRead(msg);
+ }
+}
+```
+
+InBoundHandlerB
+```java
+public class OutBoundHandlerB extends ChannelOutboundHandlerAdapter {
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+ System.out.println("OutBoundHandlerB: " + msg);
+ ctx.write(msg, promise);
+ }
+
+
+ @Override
+ public void handlerAdded(final ChannelHandlerContext ctx) {
+ ctx.executor().schedule(() -> {
+ ctx.channel().write("ctx.channel().write -> hello world");
+ ctx.write("hello world");
+ }, 3, TimeUnit.SECONDS);
+ }
+}
+```
+
+InBoundHandlerC
+```java
+public class InBoundHandlerC extends ChannelInboundHandlerAdapter {
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ System.out.println("InBoundHandlerC: " + msg);
+ ctx.fireChannelRead(msg);
+ }
+}
+```
+
+```java
+public final class Server {
+
+ public static void main(String[] args) throws Exception {
+ EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+ EventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ try {
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .childOption(ChannelOption.TCP_NODELAY, true)
+ .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
+ .childHandler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) {
+ ch.pipeline().addLast(new InBoundHandlerA());
+ ch.pipeline().addLast(new InBoundHandlerB());
+ ch.pipeline().addLast(new InBoundHandlerC());
+ }
+ });
+
+ ChannelFuture f = b.bind(8888).sync();
+
+ f.channel().closeFuture().sync();
+ } finally {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+}
+```
+
+执行结果如下:
+```
+InBoundHandlerA: hello world
+InBoundHandlerB: hello world
+InBoundHandlerC: hello world
+```
+
+可以发现Netty中,对于inboundHandler来说是按照顺序执行操作的。
+
+接着在看看outboundHandler定义如下
+
+OutBoundHandlerA
+```java
+public class OutBoundHandlerA extends ChannelOutboundHandlerAdapter {
+
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+ System.out.println("OutBoundHandlerA: " + msg);
+ ctx.write(msg, promise);
+ }
+}
+```
+
+OutBoundHandlerB
+```java
+public class OutBoundHandlerB extends ChannelOutboundHandlerAdapter {
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+ System.out.println("OutBoundHandlerB: " + msg);
+ ctx.write(msg, promise);
+ }
+}
+```
+
+OutBoundHandlerC
+```java
+public class OutBoundHandlerC extends ChannelOutboundHandlerAdapter {
+
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+ System.out.println("OutBoundHandlerC: " + msg);
+ ctx.write(msg, promise);
+ }
+}
+```
+
+
+然后修改Server类为如下,
+
+```java
+public final class Server {
+
+ public static void main(String[] args) throws Exception {
+ EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+ EventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ try {
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .childOption(ChannelOption.TCP_NODELAY, true)
+ .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
+ .childHandler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) {
+ ch.pipeline().addLast(new OutBoundHandlerA());
+ ch.pipeline().addLast(new OutBoundHandlerB());
+ ch.pipeline().addLast(new OutBoundHandlerC());
+ }
+ });
+
+ ChannelFuture f = b.bind(8888).sync();
+
+ f.channel().closeFuture().sync();
+ } finally {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+}
+```
+
+执行结果如下:
+```
+OutBoundHandlerC: ctx.channel().write -> hello world
+OutBoundHandlerB: ctx.channel().write -> hello world
+OutBoundHandlerA: ctx.channel().write -> hello world
+OutBoundHandlerA: hello world
+```
+
+可以看到在Netty中对于ountboundHandler来说,是倒序执行的。
+
+整个Netty执行ChannelHandler可以用下图来描述。
+
+
+
+
+上图描述的Head节点顺序执行,Tail节点逆序执行的源码是在DefaultChannelPipeline中,在《Netty-ChannelPipeline-上》文章开头就已经说明了,对于inboundHandler类型的Handler,主要还是用于监听Channel的read、register、active、exceptionCaught等事件,而对于outboundHandler类型来说,主要是用于bind、connect、write、flush等事件,回顾了这一点后,我们在继续看DefaultChannelPipeline源码
+
+```java
+public class DefaultChannelPipeline implements ChannelPipeline {
+ ... 省略
+
+ @Override
+ public final ChannelPipeline fireChannelRead(Object msg) {
+ AbstractChannelHandlerContext.invokeChannelRead(head, msg);
+ return this;
+ }
+
+ @Override
+ public final ChannelFuture write(Object msg) {
+ return tail.write(msg);
+ }
+
+ ... 省略
+}
+```
+
+分别以inbound类型的channelRead和outbound类型的write来分析。
+
+DefaultChannelPipeline.java
+```java
+ @Override
+ public final ChannelPipeline fireChannelRead(Object msg) {
+ AbstractChannelHandlerContext.invokeChannelRead(head, msg);
+ return this;
+ }
+```
+在AbstractChannelHandlerContext#invokeChannelRead方法中,传入了一个重要的入参:head,这里就是传入的Head头结点,这一重要调用得以让inbound类型handler在ChannelPipeline中按顺序执行。
+
+AbstractChannelHandlerContext.java
+```java
+ static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
+ final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
+ EventExecutor executor = next.executor();
+ // 在NioEventLoop线程内,next这里传入的是head头结点
+ if (executor.inEventLoop()) {
+ next.invokeChannelRead(m);
+ } else {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ next.invokeChannelRead(m);
+ }
+ });
+ }
+ }
+
+ private void invokeChannelRead(Object msg) {
+ if (invokeHandler()) {
+ try {
+ ((ChannelInboundHandler) handler()).channelRead(this, msg);
+ } catch (Throwable t) {
+ invokeExceptionCaught(t);
+ }
+ } else {
+ fireChannelRead(msg);
+ }
+ }
+
+```
+
+ChannelInboundHandler#channelRead的调用,会最终来到InBoundHandlerA里的channelRead方法。
+```java
+public class InBoundHandlerA extends ChannelInboundHandlerAdapter {
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ System.out.println("InBoundHandlerA: " + msg);
+ ctx.fireChannelRead(msg);
+ }
+}
+```
+
+经过AbstractChannelHandlerContext#fireChannelRead,会在ChannelPipeline中寻找下一个inbound,然后继续执行channelRead。
+
+```java
+ @Override
+ public ChannelHandlerContext fireChannelRead(final Object msg) {
+ invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
+ return this;
+ }
+```
+
+
+细看OutBoundHandlerB#handlerAdded方法由两个write,一个是ctx.channel.write,另一个是ctx.write,这两个有啥区别呢?为啥输出结果是三条:ctx.channel().write -> hello world,一条hello world呢?
+
+启动Server启动类之后,再cmd窗口输入连接socket的命令debug之后分析得
+
+```
+telnet 127.0.0.1 8888
+```
+
+在客户端socket连接进Netty之后,会先注册channel并init初始化,这时会调用Server类里ServerBootstrap注入的ChannelInitilizer的initChannel方法,最终得以向ChannelPipeline里添加进OutBoundHandlerA、OutBoundHandlerB、OutBoundHandlerC,随后调用
+
+```java
+ch.pipeline().addLast(new xxxx)
+```
+只有会触发DefaultChannelPipeline#callHandlerAdded0()方法,最终来到OutBoundHandler里的handlerAdded()方法,并向Netty的定时任务队列里添加了一个匿名内部任务,也就是:
+
+```java
+ @Override
+ public void handlerAdded(final ChannelHandlerContext ctx) {
+ ctx.executor().schedule(() -> {
+ ctx.channel().write("ctx.channel().write -> hello world");
+ ctx.write("hello world");
+ }, 3, TimeUnit.SECONDS);
+ }
+```
+
+随后完成客户端Socket的初始化工作。此时服务端的selector继续执行for死循环,执行到任务队列,此时发现任务队列中有一个定时任务需要执行,则拿出任务并执行任务,执行过程会跳转到上面的匿名内部类,并依次执行ctx.channel().write()和ctx.write()两个方法。
+
+```java
+ctx.channel().write()
+```
+方法会从ChannelPipeline的尾部tail开始执行(上文已经总结过,outboundHandler都是从tail节点开始执行handler) ,所以字符串“ctx.channel().write -> hello world”就会按outboundHandlerC、outboundHandlerB、outboundHandlerC这个顺序开始执行,执行完head节点之后会一路往上返回到Ctx.channel().write()
+方法,并最后去执行ctx.write()方法,而ctx.write()方法会从当前的handler节点开始向前执行,所以当前outboundHandlerB的前节点是outboundHandlerA,所以最终控制台打印出:
+```
+OutBoundHandlerC: ctx.channel().write -> hello world
+OutBoundHandlerB: ctx.channel().write -> hello world
+OutBoundHandlerA: ctx.channel().write -> hello world
+OutBoundHandlerA: hello world
+```
+
+整个过程比较复杂,也比较绕,下面用一张流程图来描述整个过程。
+
+
+
+
+- TODO ChannelPipeline优化?MASK
+- TODO SimpleChannelInboundHandler源码分析
From afba7afe95a4e3ed8660ac322a89ada885bb7c3a Mon Sep 17 00:00:00 2001
From: Bruis
Date: Mon, 30 Aug 2021 00:12:27 +0800
Subject: [PATCH 36/63] update ReadME
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 94e0586..6882d72 100644
--- a/README.md
+++ b/README.md
@@ -130,7 +130,7 @@ SpringCloud源码
- [Netty底层源码解析-NioEventLoop原理分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-NioEventLoop%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.md)
- Netty底层源码解析-Channel分析
- [Netty底层源码解析-ChannelPipeline分析(上)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-ChannelPipeline%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%8A%EF%BC%89.md)
- - Netty底层源码解析-ChannelPipeline分析(下)
+ - [Netty底层源码解析-ChannelPipeline分析(下)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-ChannelPipeline%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%8B%EF%BC%89.md)
- Netty底层源码解析-FastThreadLocal原理分析
- Netty底层源码解析-内存分配原理分析
- Netty底层源码解析-RocketMQ底层使用到的Netty
From 9ff2bba5ed1d49911b04bc0c9a5b88962c7d22e5 Mon Sep 17 00:00:00 2001
From: hacryq
Date: Tue, 12 Oct 2021 21:45:26 +0800
Subject: [PATCH 37/63] =?UTF-8?q?fix:=20=E9=94=99=E5=88=AB=E5=AD=97?=
=?UTF-8?q?=E4=BF=AE=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...\220\257\345\212\250\345\212\240\350\275\275\345\231\250.md" | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git "a/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\345\220\257\345\212\250\345\212\240\350\275\275\345\231\250.md" "b/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\345\220\257\345\212\250\345\212\240\350\275\275\345\231\250.md"
index d533a52..b3819b9 100644
--- "a/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\345\220\257\345\212\250\345\212\240\350\275\275\345\231\250.md"
+++ "b/note/SpringBoot/\346\267\261\345\205\245SpringBoot\346\272\220\347\240\201\345\255\246\344\271\240\344\271\213\342\200\224\342\200\224\345\220\257\345\212\250\345\212\240\350\275\275\345\231\250.md"
@@ -98,7 +98,7 @@ public class FirstApplicationRunner implements ApplicationRunner {
### 3. SpringBoot启动加载器原理
-其实SpringBoot启动加载器原理比较简单,在底层源码调用逻辑比较清除。
+其实SpringBoot启动加载器原理比较简单,在底层源码调用逻辑比较清楚。

在DefaultApplicationArguments里,有一个不可忽略的类:Source
From 25e93ee6e18e5d918cdb2567cac68a16acd87726 Mon Sep 17 00:00:00 2001
From: yuuyoo
Date: Tue, 11 Jan 2022 11:41:25 +0800
Subject: [PATCH 38/63] =?UTF-8?q?Update=20=E6=B7=B1=E5=85=A5=E5=AD=A6?=
=?UTF-8?q?=E4=B9=A0String=E6=BA=90=E7=A0=81=E4=B8=8E=E5=BA=95=E5=B1=82?=
=?UTF-8?q?=EF=BC=88=E4=B8=80=EF=BC=89.md?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
"程序技术器"应为“程序计数器”
---
...\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md" | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md" "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md"
index 4418513..55a20b3 100644
--- "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md"
+++ "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240String\346\272\220\347\240\201\344\270\216\345\272\225\345\261\202\357\274\210\344\270\200\357\274\211.md"
@@ -112,7 +112,7 @@ JAVA的运行时数据区包括以下几个区域:
2. Java堆区(Heap)
3. 本地方法栈(Native Method Stack)
4. 虚拟机栈(VM Stack)
-5. 程序技术器(Program Conter Register)
+5. 程序计数器(Program Conter Register)
具体内容不在这里进行介绍。为方便读者能够理解下面的内容,请学习下[总结Java内存区域和常量池](https://blog.csdn.net/CoderBruis/article/details/85240273)
From 40dd72607e83663246b2632e7b739ddf92c1019a Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Thu, 27 Jan 2022 16:25:17 +0800
Subject: [PATCH 39/63] =?UTF-8?q?[U]=20todo=E5=86=85=E5=AE=B9=EF=BC=9Bbio?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 6 +++
...12\345\205\266\346\236\266\346\236\204.md" | 39 ++++++++++++++++++-
2 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 6882d72..648b81e 100644
--- a/README.md
+++ b/README.md
@@ -145,6 +145,12 @@ SpringCloud源码
todo
+2021年年底完成了人生的两件大事,所以一直没时间持续输出源码分析,2022年开始需要继续努力,完成这个源码分析项目!
+
+- 完成Netty剩余源码分析文章
+- 完成RocketMQ剩余源码分析文章
+- 完成Dubbo剩余源码分析文章
+- C语言基础学习(为Redis底层源码学习做准备)
- Redis底层源码分析
- JUC底层源码分析
diff --git "a/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-\345\210\235\345\247\213Netty\345\217\212\345\205\266\346\236\266\346\236\204.md" "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-\345\210\235\345\247\213Netty\345\217\212\345\205\266\346\236\266\346\236\204.md"
index d969cdd..d721716 100644
--- "a/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-\345\210\235\345\247\213Netty\345\217\212\345\205\266\346\236\266\346\236\204.md"
+++ "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-\345\210\235\345\247\213Netty\345\217\212\345\205\266\346\236\266\346\236\204.md"
@@ -1,6 +1,43 @@
## 1. 回顾BIO和NIO
-show you the code! TODO
+```java
+public class BIO {
+ public static void main(String[] args) throws Exception {
+ ServerSocket serverSocket = new ServerSocket(666);
+ System.out.println("Server started...");
+ while (true) {
+ System.out.println("socket accepting...");
+ Socket socket = serverSocket.accept();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ byte[] bytes = new byte[1024];
+ InputStream inputStream = socket.getInputStream();
+ while (true) {
+ System.out.println("reading...");
+ int read = inputStream.read(bytes);
+ if (read != -1) {
+ System.out.println(new String(bytes, 0, read));
+ } else {
+ break;
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }).start();
+ }
+ }
+}
+```
BIO流程图如下:
From 672fb251dbaecc689e8c0cb59aacdb8d13ad94f8 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Thu, 10 Feb 2022 17:30:22 +0800
Subject: [PATCH 40/63] =?UTF-8?q?[A]=20=E6=B7=BB=E5=8A=A0Netty=E9=80=9A?=
=?UTF-8?q?=E8=AE=AF=E6=A8=A1=E6=8B=9F=E4=BB=A3=E7=A0=81=EF=BC=8C=E9=80=9A?=
=?UTF-8?q?=E8=BF=87=E5=A4=9A=E7=BA=BF=E7=A8=8B=E6=A8=A1=E6=8B=9FNetty?=
=?UTF-8?q?=E9=80=9A=E8=AE=AF=E8=BF=87=E7=A8=8B=EF=BC=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../thread/reentranlock/FutureMain.java | 64 +++++++++
.../thread/reentranlock/RequestFuture.java | 123 ++++++++++++++++++
.../thread/reentranlock/Response.java | 28 ++++
.../thread/reentranlock/SubThread.java | 30 +++++
.../thread/synchronize/FutureMain.java | 64 +++++++++
.../thread/synchronize/RequestFuture.java | 103 +++++++++++++++
.../thread/synchronize/Response.java | 28 ++++
.../thread/synchronize/SubThread.java | 31 +++++
8 files changed, 471 insertions(+)
create mode 100644 JdkLearn/src/main/java/com/learnjava/thread/reentranlock/FutureMain.java
create mode 100644 JdkLearn/src/main/java/com/learnjava/thread/reentranlock/RequestFuture.java
create mode 100644 JdkLearn/src/main/java/com/learnjava/thread/reentranlock/Response.java
create mode 100644 JdkLearn/src/main/java/com/learnjava/thread/reentranlock/SubThread.java
create mode 100644 JdkLearn/src/main/java/com/learnjava/thread/synchronize/FutureMain.java
create mode 100644 JdkLearn/src/main/java/com/learnjava/thread/synchronize/RequestFuture.java
create mode 100644 JdkLearn/src/main/java/com/learnjava/thread/synchronize/Response.java
create mode 100644 JdkLearn/src/main/java/com/learnjava/thread/synchronize/SubThread.java
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/FutureMain.java b/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/FutureMain.java
new file mode 100644
index 0000000..71e797e
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/FutureMain.java
@@ -0,0 +1,64 @@
+package com.learnjava.thread.reentranlock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 模拟Netty通讯过程
+ * 主线程,获取子线程的结果
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class FutureMain {
+ private static List reqs = new ArrayList<>();
+ public static void main(String[] args) {
+ mockClient();
+ mockServer();
+ }
+
+ /**
+ * 模拟服务端 接受结果
+ */
+ private static void mockServer() {
+ for (RequestFuture req : reqs) {
+ /**
+ * 主线程获取结果
+ */
+ Object result = req.get();
+ System.out.println("服务端接受到响应结果:" + result.toString());
+ }
+ }
+ /**
+ * 模拟客户端 发送请求
+ */
+ private static void mockClient() {
+ for (int i = 0; i < 100; i++) {
+ long id = i;
+ RequestFuture req = new RequestFuture();
+ req.setId(id);
+ req.setRequest("hello world");
+ /**
+ * 把请求缓存起来
+ */
+ RequestFuture.addFuture(req);
+ /**
+ * 将请求放入到请求列表中
+ */
+ reqs.add(req);
+ sendMsg(req);
+ SubThread subThread = new SubThread(req);
+ /**
+ * 开启子线程
+ */
+ subThread.start();
+ }
+ }
+ /**
+ * 模拟请求处理
+ * @param req
+ */
+ private static void sendMsg(RequestFuture req) {
+ System.out.println("客户端发送数据,请求id为===============" + req.getId());
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/RequestFuture.java b/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/RequestFuture.java
new file mode 100644
index 0000000..96106dc
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/RequestFuture.java
@@ -0,0 +1,123 @@
+package com.learnjava.thread.reentranlock;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 模拟客户端请求类,用于构建请求对象
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class RequestFuture {
+ public static Map futures = new ConcurrentHashMap<>();
+ private final Lock lock = new ReentrantLock();
+ private final Condition condition = lock.newCondition();
+ private long id;
+ /**
+ * 请求参数
+ */
+ private Object request;
+ /**
+ * 响应结果
+ */
+ private Object result;
+ /**
+ * 超时时间
+ */
+ private long timeout = 5000;
+
+ /**
+ * 把请求放入本地缓存中
+ * @param future
+ */
+ public static void addFuture(RequestFuture future) {
+ futures.put(future.getId(), future);
+ }
+
+ /**
+ * 同步获取响应结果
+ * @return
+ */
+ public Object get() {
+ lock.lock();
+ try {
+ while (this.result == null) {
+ try {
+ // 主线程默认等待5s,然后查看下结果
+ condition.await(timeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ return this.result;
+ }
+
+ /**
+ * 异步线程将结果返回主线程
+ * @param result
+ */
+ public static void received(Response result) {
+ RequestFuture future = futures.remove(result.getId());
+ if (null != future) {
+ future.setResult(result.getResult());
+ }
+ /**
+ * 通知主线程
+ */
+ Objects.requireNonNull(future, "RequestFuture").getLock().lock();
+ try {
+ future.getCondition().signalAll();
+ } finally {
+ Objects.requireNonNull(future, "RequestFuture").getLock().unlock();
+ }
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Object getRequest() {
+ return request;
+ }
+
+ public void setRequest(Object request) {
+ this.request = request;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+
+ public long getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(long timeout) {
+ this.timeout = timeout;
+ }
+
+ public Lock getLock() {
+ return lock;
+ }
+
+ public Condition getCondition() {
+ return condition;
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/Response.java b/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/Response.java
new file mode 100644
index 0000000..cc40e9f
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/Response.java
@@ -0,0 +1,28 @@
+package com.learnjava.thread.reentranlock;
+
+/**
+ * 响应结果类
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class Response {
+ private long id;
+ private Object result;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/SubThread.java b/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/SubThread.java
new file mode 100644
index 0000000..22431bf
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/SubThread.java
@@ -0,0 +1,30 @@
+package com.learnjava.thread.reentranlock;
+
+/**
+ * 子线程,用于模拟服务端处理
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class SubThread extends Thread {
+
+ private RequestFuture request;
+
+ public SubThread(RequestFuture request) {
+ this.request = request;
+ }
+
+ @Override
+ public void run() {
+ Response response = new Response();
+ response.setId(request.getId());
+ response.setResult("服务端响应了结果,线程id: " + Thread.currentThread().getId() + ", 请求id:" + response.getId());
+ // 子线程睡眠1s
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ RequestFuture.received(response);
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/FutureMain.java b/JdkLearn/src/main/java/com/learnjava/thread/synchronize/FutureMain.java
new file mode 100644
index 0000000..e9454ba
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/thread/synchronize/FutureMain.java
@@ -0,0 +1,64 @@
+package com.learnjava.thread.synchronize;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 模拟Netty通讯过程
+ * 主线程,获取子线程的结果
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class FutureMain {
+ private static List reqs = new ArrayList<>();
+ public static void main(String[] args) {
+ mockClient();
+ mockServer();
+ }
+
+ /**
+ * 模拟服务端 接受结果
+ */
+ private static void mockServer() {
+ for (RequestFuture req : reqs) {
+ /**
+ * 主线程获取结果
+ */
+ Object result = req.get();
+ System.out.println("服务端接受到响应结果:" + result.toString());
+ }
+ }
+ /**
+ * 模拟客户端 发送请求
+ */
+ private static void mockClient() {
+ for (int i = 0; i < 100; i++) {
+ long id = i;
+ RequestFuture req = new RequestFuture();
+ req.setId(id);
+ req.setRequest("hello world");
+ /**
+ * 把请求缓存起来
+ */
+ RequestFuture.addFuture(req);
+ /**
+ * 将请求放入到请求列表中
+ */
+ reqs.add(req);
+ sendMsg(req);
+ SubThread subThread = new SubThread(req);
+ /**
+ * 开启子线程
+ */
+ subThread.start();
+ }
+ }
+ /**
+ * 模拟请求处理
+ * @param req
+ */
+ private static void sendMsg(RequestFuture req) {
+ System.out.println("客户端发送数据,请求id为===============" + req.getId());
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/RequestFuture.java b/JdkLearn/src/main/java/com/learnjava/thread/synchronize/RequestFuture.java
new file mode 100644
index 0000000..57d9ddf
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/thread/synchronize/RequestFuture.java
@@ -0,0 +1,103 @@
+package com.learnjava.thread.synchronize;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 模拟客户端请求类,用于构建请求对象
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class RequestFuture {
+ public static Map futures = new ConcurrentHashMap<>();
+ private long id;
+ /**
+ * 请求参数
+ */
+ private Object request;
+ /**
+ * 响应结果
+ */
+ private Object result;
+ /**
+ * 超时时间
+ */
+ private long timeout = 5000;
+
+ /**
+ * 把请求放入本地缓存中
+ * @param future
+ */
+ public static void addFuture(RequestFuture future) {
+ futures.put(future.getId(), future);
+ }
+
+ /**
+ * 同步获取响应结果
+ * @return
+ */
+ public Object get() {
+ synchronized (this) {
+ while (this.result == null) {
+ try {
+ // 主线程默认等待5s,然后查看下结果
+ this.wait(timeout);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return this.result;
+ }
+
+ /**
+ * 异步线程将结果返回主线程
+ * @param result
+ */
+ public static void received(Response result) {
+ RequestFuture future = futures.remove(result.getId());
+ if (null != future) {
+ future.setResult(result.getResult());
+ }
+ /**
+ * 通知主线程
+ */
+ synchronized (Objects.requireNonNull(future, "RequestFuture")) {
+ future.notify();
+ }
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Object getRequest() {
+ return request;
+ }
+
+ public void setRequest(Object request) {
+ this.request = request;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+
+ public long getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(long timeout) {
+ this.timeout = timeout;
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/Response.java b/JdkLearn/src/main/java/com/learnjava/thread/synchronize/Response.java
new file mode 100644
index 0000000..ea591ee
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/thread/synchronize/Response.java
@@ -0,0 +1,28 @@
+package com.learnjava.thread.synchronize;
+
+/**
+ * 响应结果类
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class Response {
+ private long id;
+ private Object result;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/SubThread.java b/JdkLearn/src/main/java/com/learnjava/thread/synchronize/SubThread.java
new file mode 100644
index 0000000..9ebb376
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/thread/synchronize/SubThread.java
@@ -0,0 +1,31 @@
+package com.learnjava.thread.synchronize;
+
+/**
+ * 子线程,用于模拟服务端处理
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class SubThread extends Thread {
+
+ private RequestFuture request;
+
+ public SubThread(RequestFuture request) {
+ this.request = request;
+ }
+
+ @Override
+ public void run() {
+ Response response = new Response();
+ response.setId(request.getId());
+ response.setResult("服务端响应了结果,线程id: " + Thread.currentThread().getId() + ", 请求id:" + response.getId());
+ // 子线程睡眠1s
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ System.out.println(this + " -> 当前线程准备调用received: " + Thread.currentThread().getName());
+ RequestFuture.received(response);
+ }
+}
From 7dc4b25f48e816e7d9934faae36bdbf1a1b9652d Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Fri, 11 Feb 2022 15:42:20 +0800
Subject: [PATCH 41/63] =?UTF-8?q?[U]=20=E5=B0=86Netty=E4=BB=A3=E7=A0=81?=
=?UTF-8?q?=E7=A7=BB=E5=8A=A8=E5=88=B0Spring-Netty=E6=A8=A1=E5=9D=97?=
=?UTF-8?q?=EF=BC=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/bruis/learnnetty/thread/reentranlock}/FutureMain.java | 2 +-
.../bruis/learnnetty}/thread/reentranlock/RequestFuture.java | 2 +-
.../com/bruis/learnnetty}/thread/reentranlock/Response.java | 2 +-
.../com/bruis/learnnetty}/thread/reentranlock/SubThread.java | 2 +-
.../com/bruis/learnnetty/thread/synchronize}/FutureMain.java | 2 +-
.../com/bruis/learnnetty}/thread/synchronize/RequestFuture.java | 2 +-
.../java/com/bruis/learnnetty}/thread/synchronize/Response.java | 2 +-
.../com/bruis/learnnetty}/thread/synchronize/SubThread.java | 2 +-
8 files changed, 8 insertions(+), 8 deletions(-)
rename {JdkLearn/src/main/java/com/learnjava/thread/synchronize => Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock}/FutureMain.java (96%)
rename {JdkLearn/src/main/java/com/learnjava => Spring-Netty/src/main/java/com/bruis/learnnetty}/thread/reentranlock/RequestFuture.java (98%)
rename {JdkLearn/src/main/java/com/learnjava => Spring-Netty/src/main/java/com/bruis/learnnetty}/thread/reentranlock/Response.java (88%)
rename {JdkLearn/src/main/java/com/learnjava => Spring-Netty/src/main/java/com/bruis/learnnetty}/thread/reentranlock/SubThread.java (93%)
rename {JdkLearn/src/main/java/com/learnjava/thread/reentranlock => Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize}/FutureMain.java (97%)
rename {JdkLearn/src/main/java/com/learnjava => Spring-Netty/src/main/java/com/bruis/learnnetty}/thread/synchronize/RequestFuture.java (97%)
rename {JdkLearn/src/main/java/com/learnjava => Spring-Netty/src/main/java/com/bruis/learnnetty}/thread/synchronize/Response.java (88%)
rename {JdkLearn/src/main/java/com/learnjava => Spring-Netty/src/main/java/com/bruis/learnnetty}/thread/synchronize/SubThread.java (94%)
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/FutureMain.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/FutureMain.java
similarity index 96%
rename from JdkLearn/src/main/java/com/learnjava/thread/synchronize/FutureMain.java
rename to Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/FutureMain.java
index e9454ba..d0de69c 100644
--- a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/FutureMain.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/FutureMain.java
@@ -1,4 +1,4 @@
-package com.learnjava.thread.synchronize;
+package com.bruis.learnnetty.thread.reentranlock;
import java.util.ArrayList;
import java.util.List;
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/RequestFuture.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/RequestFuture.java
similarity index 98%
rename from JdkLearn/src/main/java/com/learnjava/thread/reentranlock/RequestFuture.java
rename to Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/RequestFuture.java
index 96106dc..1fd7dec 100644
--- a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/RequestFuture.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/RequestFuture.java
@@ -1,4 +1,4 @@
-package com.learnjava.thread.reentranlock;
+package com.bruis.learnnetty.thread.reentranlock;
import java.util.Map;
import java.util.Objects;
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/Response.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/Response.java
similarity index 88%
rename from JdkLearn/src/main/java/com/learnjava/thread/reentranlock/Response.java
rename to Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/Response.java
index cc40e9f..ae1852c 100644
--- a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/Response.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/Response.java
@@ -1,4 +1,4 @@
-package com.learnjava.thread.reentranlock;
+package com.bruis.learnnetty.thread.reentranlock;
/**
* 响应结果类
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/SubThread.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/SubThread.java
similarity index 93%
rename from JdkLearn/src/main/java/com/learnjava/thread/reentranlock/SubThread.java
rename to Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/SubThread.java
index 22431bf..9101e54 100644
--- a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/SubThread.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/SubThread.java
@@ -1,4 +1,4 @@
-package com.learnjava.thread.reentranlock;
+package com.bruis.learnnetty.thread.reentranlock;
/**
* 子线程,用于模拟服务端处理
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/FutureMain.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/FutureMain.java
similarity index 97%
rename from JdkLearn/src/main/java/com/learnjava/thread/reentranlock/FutureMain.java
rename to Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/FutureMain.java
index 71e797e..bd4ee93 100644
--- a/JdkLearn/src/main/java/com/learnjava/thread/reentranlock/FutureMain.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/FutureMain.java
@@ -1,4 +1,4 @@
-package com.learnjava.thread.reentranlock;
+package com.bruis.learnnetty.thread.synchronize;
import java.util.ArrayList;
import java.util.List;
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/RequestFuture.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/RequestFuture.java
similarity index 97%
rename from JdkLearn/src/main/java/com/learnjava/thread/synchronize/RequestFuture.java
rename to Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/RequestFuture.java
index 57d9ddf..f1dca80 100644
--- a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/RequestFuture.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/RequestFuture.java
@@ -1,4 +1,4 @@
-package com.learnjava.thread.synchronize;
+package com.bruis.learnnetty.thread.synchronize;
import java.util.Map;
import java.util.Objects;
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/Response.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/Response.java
similarity index 88%
rename from JdkLearn/src/main/java/com/learnjava/thread/synchronize/Response.java
rename to Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/Response.java
index ea591ee..c5dfc05 100644
--- a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/Response.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/Response.java
@@ -1,4 +1,4 @@
-package com.learnjava.thread.synchronize;
+package com.bruis.learnnetty.thread.synchronize;
/**
* 响应结果类
diff --git a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/SubThread.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/SubThread.java
similarity index 94%
rename from JdkLearn/src/main/java/com/learnjava/thread/synchronize/SubThread.java
rename to Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/SubThread.java
index 9ebb376..16e2a57 100644
--- a/JdkLearn/src/main/java/com/learnjava/thread/synchronize/SubThread.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/SubThread.java
@@ -1,4 +1,4 @@
-package com.learnjava.thread.synchronize;
+package com.bruis.learnnetty.thread.synchronize;
/**
* 子线程,用于模拟服务端处理
From 79b8ced8b72e0d79d7e0df3c8b2992de8b042af4 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Wed, 16 Feb 2022 11:40:28 +0800
Subject: [PATCH 42/63] =?UTF-8?q?[A]=20=E5=9F=BA=E4=BA=8ENetty=E7=9A=84?=
=?UTF-8?q?=E7=9F=AD=E8=BF=9E=E6=8E=A5=E3=80=81=E9=95=BF=E8=BF=9E=E6=8E=A5?=
=?UTF-8?q?demo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../longconnections/ClientHandler.java | 17 +++
.../longconnections/NettyClient.java | 91 ++++++++++++
.../longconnections/NettyServer.java | 52 +++++++
.../longconnections/RequestFuture.java | 130 ++++++++++++++++++
.../connections/longconnections/Response.java | 28 ++++
.../shortconnections/ClientHandler.java | 28 ++++
.../shortconnections/NettyClient.java | 66 +++++++++
.../shortconnections/NettyServer.java | 51 +++++++
.../shortconnections/ServerHandler.java | 32 +++++
9 files changed, 495 insertions(+)
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/ClientHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/NettyClient.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/NettyServer.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/RequestFuture.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/Response.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/ClientHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/NettyClient.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/NettyServer.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/ServerHandler.java
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/ClientHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/ClientHandler.java
new file mode 100644
index 0000000..7d69f53
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/ClientHandler.java
@@ -0,0 +1,17 @@
+package com.bruis.learnnetty.netty.connections.longconnections;
+
+import com.alibaba.fastjson.JSONObject;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+
+/**
+ * @author lhy
+ * @date 2022/2/11
+ */
+public class ClientHandler extends ChannelInboundHandlerAdapter {
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ Response response = JSONObject.parseObject(msg.toString(), Response.class);
+ RequestFuture.received(response);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/NettyClient.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/NettyClient.java
new file mode 100644
index 0000000..cd3a840
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/NettyClient.java
@@ -0,0 +1,91 @@
+package com.bruis.learnnetty.netty.connections.longconnections;
+
+import com.alibaba.fastjson.JSONObject;
+import com.bruis.learnnetty.netty.connections.longconnections.ClientHandler;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.springframework.util.StringUtils;
+
+import java.nio.charset.Charset;
+
+/**
+ * @author lhy
+ * @date 2022/2/16
+ */
+public class NettyClient {
+ public static EventLoopGroup group = null;
+ public static Bootstrap bootstrap = null;
+ public static ChannelFuture future = null;
+ static {
+ bootstrap = new Bootstrap();
+ group = new NioEventLoopGroup();
+ bootstrap.channel(NioSocketChannel.class);
+ bootstrap.group(group);
+ bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
+ final ClientHandler clientHandler = new ClientHandler();
+ bootstrap.handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(NioSocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,
+ 0, 4, 0, 4));
+ ch.pipeline().addLast(new StringDecoder());
+ ch.pipeline().addLast(clientHandler);
+ ch.pipeline().addLast(new LengthFieldPrepender(4, false));
+ ch.pipeline().addLast(new StringEncoder(Charset.forName("utf-8")));
+ }
+ });
+ try {
+ future = bootstrap.connect("127.0.0.1", 8080).sync();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 说明:对于这个长连接的例子中,使用了静态化,即单链接、长连接,如果是多链接的话不可使用静态化,需使用线程池。
+ * @param msg
+ * @return
+ */
+ public Object sendRequest(Object msg) {
+ try {
+ RequestFuture request = new RequestFuture();
+ request.setRequest(msg);
+ String requestStr = JSONObject.toJSONString(request);
+ future.channel().writeAndFlush(requestStr);
+ myselfPrint("我阻塞了", null);
+ return request.get();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+ public static void main(String[] args) {
+ NettyClient nettyClient = new NettyClient();
+ for (int i = 0; i < 10; i++) {
+ Object result = nettyClient.sendRequest("hello");
+ myselfPrint("拿到结果了", result);
+ }
+ }
+
+ public static void myselfPrint(String description, Object value) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(Thread.currentThread().getName());
+ if (!StringUtils.isEmpty(description)) {
+ builder.append("-").append(description);
+ }
+ if (!StringUtils.isEmpty(value)) {
+ builder.append("-").append(value);
+ }
+ System.out.println(builder.toString());
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/NettyServer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/NettyServer.java
new file mode 100644
index 0000000..d668c6b
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/NettyServer.java
@@ -0,0 +1,52 @@
+package com.bruis.learnnetty.netty.connections.longconnections;
+
+import com.bruis.learnnetty.netty.connections.shortconnections.ServerHandler;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+
+/**
+ * 基于短连接的Netty服务端
+ *
+ * @author lhy
+ * @date 2022/2/11
+ */
+public class NettyServer {
+ public static void main(String[] args) {
+ EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+ EventLoopGroup workerGroup = new NioEventLoopGroup();
+ try {
+ ServerBootstrap serverBootstrap = new ServerBootstrap();
+ serverBootstrap.group(bossGroup, workerGroup);
+ serverBootstrap.channel(NioServerSocketChannel.class);
+
+ serverBootstrap.option(ChannelOption.SO_BACKLOG, 128)
+ .childHandler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))
+ .addLast(new StringDecoder())
+ .addLast(new ServerHandler())
+ .addLast(new LengthFieldPrepender(4, false))
+ .addLast(new StringEncoder());
+ }
+ });
+ ChannelFuture future = serverBootstrap.bind(8080).sync();
+ future.channel().closeFuture().sync();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/RequestFuture.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/RequestFuture.java
new file mode 100644
index 0000000..6c5167a
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/RequestFuture.java
@@ -0,0 +1,130 @@
+package com.bruis.learnnetty.netty.connections.longconnections;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 模拟客户端请求类,用于构建请求对象
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class RequestFuture {
+ public static Map futures = new ConcurrentHashMap<>();
+ private final Lock lock = new ReentrantLock();
+ private final Condition condition = lock.newCondition();
+ private long id;
+ /**
+ * 请求参数
+ */
+ private Object request;
+ /**
+ * 响应结果
+ */
+ private Object result;
+ /**
+ * 超时时间
+ */
+ private long timeout = 5000;
+ public static final AtomicLong aid = new AtomicLong();
+
+ public RequestFuture() {
+ id = aid.incrementAndGet();
+ addFuture(this);
+ }
+
+ /**
+ * 把请求放入本地缓存中
+ * @param future
+ */
+ public static void addFuture(RequestFuture future) {
+ futures.put(future.getId(), future);
+ }
+
+ /**
+ * 同步获取响应结果
+ * @return
+ */
+ public Object get() {
+ lock.lock();
+ try {
+ while (this.result == null) {
+ try {
+ // 主线程默认等待5s,然后查看下结果
+ condition.await(timeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ return this.result;
+ }
+
+ /**
+ * 表明服务端发送过来的结果已经接收到了,可以signal了
+ * @param result
+ */
+ public static void received(Response result) {
+ RequestFuture future = futures.remove(result.getId());
+ if (null != future) {
+ future.setResult(result.getResult());
+ }
+ /**
+ * 通知主线程
+ */
+ Objects.requireNonNull(future, "RequestFuture").getLock().lock();
+ try {
+ future.getCondition().signalAll();
+ } finally {
+ Objects.requireNonNull(future, "RequestFuture").getLock().unlock();
+ }
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Object getRequest() {
+ return request;
+ }
+
+ public void setRequest(Object request) {
+ this.request = request;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+
+ public long getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(long timeout) {
+ this.timeout = timeout;
+ }
+
+ public Lock getLock() {
+ return lock;
+ }
+
+ public Condition getCondition() {
+ return condition;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/Response.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/Response.java
new file mode 100644
index 0000000..34ee0d0
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/longconnections/Response.java
@@ -0,0 +1,28 @@
+package com.bruis.learnnetty.netty.connections.longconnections;
+
+/**
+ * 响应结果类
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class Response {
+ private long id;
+ private Object result;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/ClientHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/ClientHandler.java
new file mode 100644
index 0000000..6918c68
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/ClientHandler.java
@@ -0,0 +1,28 @@
+package com.bruis.learnnetty.netty.connections.shortconnections;
+
+import com.alibaba.fastjson.JSONObject;
+import com.bruis.learnnetty.thread.synchronize.Response;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.concurrent.Promise;
+
+/**
+ * @author lhy
+ * @date 2022/2/11
+ */
+public class ClientHandler extends ChannelInboundHandlerAdapter {
+ private Promise promise;
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ Response response = JSONObject.parseObject(msg.toString(), Response.class);
+ promise.setSuccess(response);
+ }
+
+ public Promise getPromise() {
+ return promise;
+ }
+
+ public void setPromise(Promise promise) {
+ this.promise = promise;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/NettyClient.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/NettyClient.java
new file mode 100644
index 0000000..e00bc64
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/NettyClient.java
@@ -0,0 +1,66 @@
+package com.bruis.learnnetty.netty.connections.shortconnections;
+
+import com.alibaba.fastjson.JSONObject;
+import com.bruis.learnnetty.thread.synchronize.RequestFuture;
+import com.bruis.learnnetty.thread.synchronize.Response;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import io.netty.util.concurrent.DefaultPromise;
+import io.netty.util.concurrent.Promise;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @author lhy
+ * @date 2022/2/11
+ */
+public class NettyClient {
+ public static EventLoopGroup group = null;
+ public static Bootstrap bootstrap = null;
+ static {
+ bootstrap = new Bootstrap();
+ group = new NioEventLoopGroup();
+ bootstrap.channel(NioSocketChannel.class);
+ bootstrap.group(group);
+ bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
+ }
+ public static void main(String[] args) {
+ try {
+ Promise promise = new DefaultPromise<>(group.next());
+ final ClientHandler clientHandler = new ClientHandler();
+ clientHandler.setPromise(promise);
+ bootstrap.handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(NioSocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))
+ .addLast(new StringDecoder())
+ .addLast(clientHandler)
+ .addLast(new LengthFieldPrepender(4, false))
+ .addLast(new StringEncoder(StandardCharsets.UTF_8));
+ }
+ });
+ ChannelFuture future = bootstrap.connect("127.0.0.1", 8080).sync();
+ RequestFuture request = new RequestFuture();
+ request.setId(1);
+ request.setRequest("hello world!");
+ String requestString = JSONObject.toJSONString(request);
+ // 向服务端发送请求
+ future.channel().writeAndFlush(requestString);
+ // 同步阻塞等待服务端响应请求
+ Response response = promise.get();
+ System.out.println(JSONObject.toJSONString(response));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/NettyServer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/NettyServer.java
new file mode 100644
index 0000000..4453bd6
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/NettyServer.java
@@ -0,0 +1,51 @@
+package com.bruis.learnnetty.netty.connections.shortconnections;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+
+/**
+ * 基于短连接的Netty服务端
+ *
+ * @author lhy
+ * @date 2022/2/11
+ */
+public class NettyServer {
+ public static void main(String[] args) {
+ EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+ EventLoopGroup workerGroup = new NioEventLoopGroup();
+ try {
+ ServerBootstrap serverBootstrap = new ServerBootstrap();
+ serverBootstrap.group(bossGroup, workerGroup);
+ serverBootstrap.channel(NioServerSocketChannel.class);
+
+ serverBootstrap.option(ChannelOption.SO_BACKLOG, 128)
+ .childHandler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))
+ .addLast(new StringDecoder())
+ .addLast(new ServerHandler())
+ .addLast(new LengthFieldPrepender(4, false))
+ .addLast(new StringEncoder());
+ }
+ });
+ ChannelFuture future = serverBootstrap.bind(8080).sync();
+ future.channel().closeFuture().sync();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/ServerHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/ServerHandler.java
new file mode 100644
index 0000000..ec172ec
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/connections/shortconnections/ServerHandler.java
@@ -0,0 +1,32 @@
+package com.bruis.learnnetty.netty.connections.shortconnections;
+
+import com.alibaba.fastjson.JSONObject;
+import com.bruis.learnnetty.thread.synchronize.RequestFuture;
+import com.bruis.learnnetty.thread.synchronize.Response;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+
+/**
+ * @author lhy
+ * @date 2022/2/11
+ */
+public class ServerHandler extends ChannelInboundHandlerAdapter {
+ /**
+ * 接受客户端发送过来的请求
+ * @param ctx
+ * @param msg
+ * @throws Exception
+ */
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ RequestFuture request = JSONObject.parseObject(msg.toString(), RequestFuture.class);
+ long id = request.getId();
+ Response response = new Response();
+ response.setId(id);
+ response.setResult("我是服务端(" + id + ")");
+ /**
+ * 给客户端发送响应结果
+ */
+ ctx.channel().writeAndFlush(JSONObject.toJSONString(response));
+ }
+}
From 437fc315419a95aeefa92f7aa41a895622e6c21f Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Thu, 17 Feb 2022 17:51:07 +0800
Subject: [PATCH 43/63] =?UTF-8?q?[A]=20=E5=9F=BA=E4=BA=8ENetty=E5=AE=9E?=
=?UTF-8?q?=E7=8E=B0=E7=9A=84=E5=8D=95=E6=9C=BA=E7=89=88RPC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../nio/{demo01 => }/NIOClient.java | 2 +-
.../nio/{demo01 => }/NIOServer.java | 2 +-
.../learnnetty/rpc/client/ClientHandler.java | 19 +++
.../learnnetty/rpc/client/NettyClient.java | 92 +++++++++++
.../rpc/controller/UserController.java | 17 +++
.../rpc/server/ApplicationMain.java | 38 +++++
.../rpc/server/InitLoadRemoteMethod.java | 55 +++++++
.../rpc/server/NettyApplicationListener.java | 23 +++
.../learnnetty/rpc/server/NettyServer.java | 51 +++++++
.../learnnetty/rpc/server/ServerHandler.java | 29 ++++
.../bruis/learnnetty/rpc/utils/Mediator.java | 80 ++++++++++
.../bruis/learnnetty/rpc/utils/Remote.java | 16 ++
.../learnnetty/rpc/utils/RequestFuture.java | 143 ++++++++++++++++++
.../bruis/learnnetty/rpc/utils/Response.java | 28 ++++
14 files changed, 593 insertions(+), 2 deletions(-)
rename Spring-Netty/src/main/java/com/bruis/learnnetty/nio/{demo01 => }/NIOClient.java (96%)
rename Spring-Netty/src/main/java/com/bruis/learnnetty/nio/{demo01 => }/NIOServer.java (98%)
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/client/ClientHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/client/NettyClient.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/controller/UserController.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/ApplicationMain.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/InitLoadRemoteMethod.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/NettyApplicationListener.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/NettyServer.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/ServerHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Mediator.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Remote.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/RequestFuture.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Response.java
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/nio/demo01/NIOClient.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/nio/NIOClient.java
similarity index 96%
rename from Spring-Netty/src/main/java/com/bruis/learnnetty/nio/demo01/NIOClient.java
rename to Spring-Netty/src/main/java/com/bruis/learnnetty/nio/NIOClient.java
index e562ddb..92178e8 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/nio/demo01/NIOClient.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/nio/NIOClient.java
@@ -1,4 +1,4 @@
-package com.bruis.learnnetty.nio.demo01;
+package com.bruis.learnnetty.nio;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/nio/demo01/NIOServer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/nio/NIOServer.java
similarity index 98%
rename from Spring-Netty/src/main/java/com/bruis/learnnetty/nio/demo01/NIOServer.java
rename to Spring-Netty/src/main/java/com/bruis/learnnetty/nio/NIOServer.java
index c897f1e..15ec3f2 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/nio/demo01/NIOServer.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/nio/NIOServer.java
@@ -1,4 +1,4 @@
-package com.bruis.learnnetty.nio.demo01;
+package com.bruis.learnnetty.nio;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/client/ClientHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/client/ClientHandler.java
new file mode 100644
index 0000000..2ceb2a8
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/client/ClientHandler.java
@@ -0,0 +1,19 @@
+package com.bruis.learnnetty.rpc.client;
+
+import com.alibaba.fastjson.JSONObject;
+import com.bruis.learnnetty.rpc.utils.RequestFuture;
+import com.bruis.learnnetty.rpc.utils.Response;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+
+/**
+ * @author lhy
+ * @date 2022/2/11
+ */
+public class ClientHandler extends ChannelInboundHandlerAdapter {
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ Response response = JSONObject.parseObject(msg.toString(), Response.class);
+ RequestFuture.received(response);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/client/NettyClient.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/client/NettyClient.java
new file mode 100644
index 0000000..e80d8c1
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/client/NettyClient.java
@@ -0,0 +1,92 @@
+package com.bruis.learnnetty.rpc.client;
+
+import com.alibaba.fastjson.JSONObject;
+import com.bruis.learnnetty.rpc.utils.RequestFuture;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.springframework.util.StringUtils;
+
+import java.nio.charset.Charset;
+
+/**
+ * @author lhy
+ * @date 2022/2/16
+ */
+public class NettyClient {
+ public static EventLoopGroup group = null;
+ public static Bootstrap bootstrap = null;
+ public static ChannelFuture future = null;
+ static {
+ bootstrap = new Bootstrap();
+ group = new NioEventLoopGroup();
+ bootstrap.channel(NioSocketChannel.class);
+ bootstrap.group(group);
+ bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
+ final ClientHandler clientHandler = new ClientHandler();
+ bootstrap.handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(NioSocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,
+ 0, 4, 0, 4));
+ ch.pipeline().addLast(new StringDecoder());
+ ch.pipeline().addLast(clientHandler);
+ ch.pipeline().addLast(new LengthFieldPrepender(4, false));
+ ch.pipeline().addLast(new StringEncoder(Charset.forName("utf-8")));
+ }
+ });
+ try {
+ future = bootstrap.connect("127.0.0.1", 8080).sync();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 说明:对于这个长连接的例子中,使用了静态化,即单链接、长连接,如果是多链接的话不可使用静态化,需使用线程池。
+ * @param msg
+ * @return
+ */
+ public Object sendRequest(Object msg, String path) {
+ try {
+ RequestFuture request = new RequestFuture();
+ request.setRequest(msg);
+ request.setPath(path);
+ String requestStr = JSONObject.toJSONString(request);
+ future.channel().writeAndFlush(requestStr);
+ myselfPrint("我阻塞了", null);
+ return request.get();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+ public static void main(String[] args) {
+ NettyClient nettyClient = new NettyClient();
+ for (int i = 0; i < 10; i++) {
+ Object result = nettyClient.sendRequest("hello-" + i, "getUserNameById");
+ myselfPrint("拿到结果了", result);
+ }
+ }
+
+ public static void myselfPrint(String description, Object value) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(Thread.currentThread().getName());
+ if (!StringUtils.isEmpty(description)) {
+ builder.append("-").append(description);
+ }
+ if (!StringUtils.isEmpty(value)) {
+ builder.append("-").append(value);
+ }
+ System.out.println(builder.toString());
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/controller/UserController.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/controller/UserController.java
new file mode 100644
index 0000000..2df7483
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/controller/UserController.java
@@ -0,0 +1,17 @@
+package com.bruis.learnnetty.rpc.controller;
+
+import com.bruis.learnnetty.rpc.utils.Remote;
+import org.springframework.stereotype.Controller;
+
+/**
+ * @author lhy
+ * @date 2022/2/17
+ */
+@Controller
+public class UserController {
+ @Remote(value = "getUserNameById")
+ public Object getUserNameById(String userId) {
+ System.out.println(Thread.currentThread().getName() + "-> 接受到请求:" + userId);
+ return "做了业务处理了,结果是用户编号userId为" + userId + "的用户姓名为张三";
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/ApplicationMain.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/ApplicationMain.java
new file mode 100644
index 0000000..b8fbb85
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/ApplicationMain.java
@@ -0,0 +1,38 @@
+package com.bruis.learnnetty.rpc.server;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/**
+ * @author lhy
+ * @date 2022/2/17
+ */
+public class ApplicationMain {
+
+ private static volatile boolean running = true;
+
+ public static void main(String[] args) {
+ try {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.bruis.learnnetty.rpc");
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ try {
+ context.stop();
+ } catch (Exception e) {}
+
+ synchronized (ApplicationMain.class) {
+ running = false;
+ ApplicationMain.class.notify();
+ }
+ }));
+ context.start();
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ System.out.println("服务器已启动");
+ synchronized (ApplicationMain.class) {
+ try {
+ ApplicationMain.class.wait();
+ } catch (Exception e) {}
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/InitLoadRemoteMethod.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/InitLoadRemoteMethod.java
new file mode 100644
index 0000000..6942514
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/InitLoadRemoteMethod.java
@@ -0,0 +1,55 @@
+package com.bruis.learnnetty.rpc.server;
+
+import com.bruis.learnnetty.rpc.utils.Mediator;
+import com.bruis.learnnetty.rpc.utils.Remote;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.core.Ordered;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Controller;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * @author lhy
+ * @date 2022/2/17
+ */
+@Component
+public class InitLoadRemoteMethod implements ApplicationListener, Ordered {
+
+ @Override
+ public void onApplicationEvent(ContextRefreshedEvent context) {
+ // 获取Spring容器中带有@Controller的注解类
+ Map controllerBeans = context.getApplicationContext()
+ .getBeansWithAnnotation(Controller.class);
+ for (String beanName : controllerBeans.keySet()) {
+ Object beanObj = controllerBeans.get(beanName);
+ // 获取这个bean的方法集合
+ Method[] methods = beanObj.getClass().getMethods();
+ for (Method method : methods) {
+ // 判断这个方法是否带有@Remote注解
+ if (method.isAnnotationPresent(Remote.class)) {
+ Remote remote = method.getAnnotation(Remote.class);
+ // 注解的值
+ String remoteValue = remote.value();
+ // 缓存这个类
+ Mediator.MethodBean methodBean = new Mediator.MethodBean();
+ methodBean.setBean(beanObj);
+ methodBean.setMethod(method);
+ // @Remote的value值作为key,MethodBean作为value
+ Mediator.methodBeans.put(remoteValue, methodBean);
+ }
+ }
+ }
+ }
+
+ /**
+ * 值越小优先级越高
+ * @return
+ */
+ @Override
+ public int getOrder() {
+ return -1;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/NettyApplicationListener.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/NettyApplicationListener.java
new file mode 100644
index 0000000..dd63c71
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/NettyApplicationListener.java
@@ -0,0 +1,23 @@
+package com.bruis.learnnetty.rpc.server;
+
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author lhy
+ * @date 2022/2/17
+ */
+@Component
+public class NettyApplicationListener implements ApplicationListener {
+ @Override
+ public void onApplicationEvent(ContextRefreshedEvent event) {
+ // 开启额外线程启动Netty服务
+ new Thread() {
+ @Override
+ public void run() {
+ NettyServer.start();
+ }
+ }.start();
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/NettyServer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/NettyServer.java
new file mode 100644
index 0000000..cc8bfee
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/NettyServer.java
@@ -0,0 +1,51 @@
+package com.bruis.learnnetty.rpc.server;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+
+/**
+ * 基于短连接的Netty服务端
+ *
+ * @author lhy
+ * @date 2022/2/11
+ */
+public class NettyServer {
+ public static void start() {
+ EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+ EventLoopGroup workerGroup = new NioEventLoopGroup();
+ try {
+ ServerBootstrap serverBootstrap = new ServerBootstrap();
+ serverBootstrap.group(bossGroup, workerGroup);
+ serverBootstrap.channel(NioServerSocketChannel.class);
+
+ serverBootstrap.option(ChannelOption.SO_BACKLOG, 128)
+ .childHandler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))
+ .addLast(new StringDecoder())
+ .addLast(new ServerHandler())
+ .addLast(new LengthFieldPrepender(4, false))
+ .addLast(new StringEncoder());
+ }
+ });
+ ChannelFuture future = serverBootstrap.bind(8080).sync();
+ future.channel().closeFuture().sync();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/ServerHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/ServerHandler.java
new file mode 100644
index 0000000..7daa7ed
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/server/ServerHandler.java
@@ -0,0 +1,29 @@
+package com.bruis.learnnetty.rpc.server;
+
+import com.alibaba.fastjson.JSONObject;
+import com.bruis.learnnetty.rpc.utils.Mediator;
+import com.bruis.learnnetty.rpc.utils.RequestFuture;
+import com.bruis.learnnetty.rpc.utils.Response;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+
+/**
+ * @author lhy
+ * @date 2022/2/11
+ */
+@ChannelHandler.Sharable
+public class ServerHandler extends ChannelInboundHandlerAdapter {
+ /**
+ * 接受客户端发送过来的请求
+ * @param ctx
+ * @param msg
+ * @throws Exception
+ */
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ RequestFuture request = JSONObject.parseObject(msg.toString(), RequestFuture.class);
+ Response response = Mediator.process(request);
+ ctx.channel().writeAndFlush(JSONObject.toJSONString(response));
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Mediator.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Mediator.java
new file mode 100644
index 0000000..99ccf31
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Mediator.java
@@ -0,0 +1,80 @@
+package com.bruis.learnnetty.rpc.utils;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * 存储RPC中的映射以及方法Bean
+ *
+ * @author lhy
+ * @date 2022/2/17
+ */
+public class Mediator {
+
+ public static Map methodBeans;
+
+ static {
+ methodBeans = new HashMap<>();
+ }
+
+ public static Response process(RequestFuture future) {
+ Response response = new Response();
+ try {
+ String path = future.getPath();
+ MethodBean methodBean = methodBeans.get(path);
+ if (null != methodBean) {
+ Object bean = methodBean.getBean();
+ Method method = methodBean.getMethod();
+ Object request = future.getRequest();
+ Class[] parameterTypes = method.getParameterTypes();
+ // 此处只支持一个参数,所以写死固定0为索引
+ Class parameterType = parameterTypes[0];
+ Object param = null;
+ // 如果参数是List类型
+ if (parameterType.isAssignableFrom(List.class)) {
+ param = JSONArray.parseArray(JSONArray.toJSONString(request), parameterType);
+ } else if (parameterType.getName().equalsIgnoreCase(String.class.getName())) {
+ param = request;
+ } else {
+ param = JSONObject.parseObject(JSONObject.toJSONString(request), parameterType);
+ }
+ // 反射调用方法
+ Object result = method.invoke(bean, param);
+ response.setResult(result);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ response.setId(future.getId());
+ return response;
+ }
+
+ public static class MethodBean {
+
+ private Object bean;
+
+ private Method method;
+
+ public Object getBean() {
+ return bean;
+ }
+
+ public void setBean(Object bean) {
+ this.bean = bean;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public void setMethod(Method method) {
+ this.method = method;
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Remote.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Remote.java
new file mode 100644
index 0000000..c173567
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Remote.java
@@ -0,0 +1,16 @@
+package com.bruis.learnnetty.rpc.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author lhy
+ * @date 2022/2/17
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Remote {
+ String value();
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/RequestFuture.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/RequestFuture.java
new file mode 100644
index 0000000..340f30a
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/RequestFuture.java
@@ -0,0 +1,143 @@
+package com.bruis.learnnetty.rpc.utils;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 模拟客户端请求类,用于构建请求对象
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class RequestFuture {
+ public static Map futures = new ConcurrentHashMap<>();
+ private final Lock lock = new ReentrantLock();
+ private final Condition condition = lock.newCondition();
+ private long id;
+ /**
+ * 请求参数
+ */
+ private Object request;
+ /**
+ * 响应结果
+ */
+ private Object result;
+ /**
+ * 超时时间
+ */
+ private long timeout = 5000;
+ /**
+ * 请求路径
+ */
+ private String path;
+
+ public static final AtomicLong aid = new AtomicLong();
+
+ public RequestFuture() {
+ id = aid.incrementAndGet();
+ addFuture(this);
+ }
+
+ /**
+ * 把请求放入本地缓存中
+ * @param future
+ */
+ public static void addFuture(RequestFuture future) {
+ futures.put(future.getId(), future);
+ }
+
+ /**
+ * 同步获取响应结果
+ * @return
+ */
+ public Object get() {
+ lock.lock();
+ try {
+ while (this.result == null) {
+ try {
+ // 主线程默认等待5s,然后查看下结果
+ condition.await(timeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ return this.result;
+ }
+
+ /**
+ * 异步线程将结果返回主线程
+ * @param result
+ */
+ public static void received(Response result) {
+ RequestFuture future = futures.remove(result.getId());
+ if (null != future) {
+ future.setResult(result.getResult());
+ }
+ /**
+ * 通知主线程
+ */
+ Objects.requireNonNull(future, "RequestFuture").getLock().lock();
+ try {
+ future.getCondition().signalAll();
+ } finally {
+ Objects.requireNonNull(future, "RequestFuture").getLock().unlock();
+ }
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Object getRequest() {
+ return request;
+ }
+
+ public void setRequest(Object request) {
+ this.request = request;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+
+ public long getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(long timeout) {
+ this.timeout = timeout;
+ }
+
+ public Lock getLock() {
+ return lock;
+ }
+
+ public Condition getCondition() {
+ return condition;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Response.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Response.java
new file mode 100644
index 0000000..ac5478f
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/rpc/utils/Response.java
@@ -0,0 +1,28 @@
+package com.bruis.learnnetty.rpc.utils;
+
+/**
+ * 响应结果类
+ *
+ * @author lhy
+ * @date 2022/2/10
+ */
+public class Response {
+ private long id;
+ private Object result;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+}
From e8dd6579b35124b71319893a6b1d5ea9d9a466fd Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Fri, 18 Feb 2022 13:52:07 +0800
Subject: [PATCH 44/63] =?UTF-8?q?[U]=20=E6=9B=B4=E6=96=B0Netty=E6=96=87?=
=?UTF-8?q?=E7=AB=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...57\345\212\250\345\210\206\346\236\220.md" | 151 ++++++++++++++----
1 file changed, 123 insertions(+), 28 deletions(-)
diff --git "a/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-Netty\346\234\215\345\212\241\347\253\257\345\220\257\345\212\250\345\210\206\346\236\220.md" "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-Netty\346\234\215\345\212\241\347\253\257\345\220\257\345\212\250\345\210\206\346\236\220.md"
index 99d24f3..1ff812b 100644
--- "a/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-Netty\346\234\215\345\212\241\347\253\257\345\220\257\345\212\250\345\210\206\346\236\220.md"
+++ "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-Netty\346\234\215\345\212\241\347\253\257\345\220\257\345\212\250\345\210\206\346\236\220.md"
@@ -1,15 +1,24 @@
# Netty 服务端启动分析
-在Java中,网络通信是通过Socket来进行的,那么在Netty中,服务端的Socket是在哪里进行初始化的?并且在哪里进行accept连接? Netty里的Channel是啥,有啥作用呢?带着这三个问题,进入本文的Netty服务端启动分析。
+在Java中,网络通信是通过Socket来进行的,那么在Netty中,服务端要用到的Socket是在哪里进行初始化的?并且在哪里进行accept接受客户端连接的? Netty里的Channel是啥,有啥作用呢?带着这三个问题,进入本文的Netty服务端启动分析。
-本文分析将分为四大步:
+本文分析将分为五大步:
-1. 创建服务端Channel;
-2. 初始化服务端Channel;
-3. 注册selector;
-4. 端口绑定;
+1. Netty中的Channel;
+2. 创建服务端Channel;
+3. 初始化服务端Channel;
+4. 注册selector;
+5. 端口绑定;
-## 1. 创建服务端Channel
+## 1. Netty中的Channel
+
+在Netty中的Channel是用来定义对网络IO进行读/写的相关接口,与NIO中的Channel接口类似。Channel的功能主要有网络IO的读写、客户端发起的连接、主动关闭连接、关闭链路、获取通信双方的网络地址等。Channel接口下有一个重要的抽象类————AbstractChannel,一些公共的基础方法都在这个抽象类中实现,但对于一些特定的功能则需要不同的实现类去实现,这样最大限度地实现了功能和接口的重用。
+
+在AbstractChannel中的网络IO模型和协议种类比较多,除了TCP协议,Netty还支持了HTTP2协议,如:AbstractHttp2StreamChannel。
+
+Netty对于不同的网络模型以及IO模型,在AbstractChannel的基础上又抽象出了一层,如:AbstractNioChannel、AbstractEpollChannel、AbstractHttp2StreamChannel。
+
+## 2. 创建服务端Channel
创建服务端Channel又可以分为四步,如下:
@@ -54,13 +63,101 @@ public final class Server {
}
```
-服务端构建好ServerBootstrap之后,通过bind()方法进行绑定。进入ServerBootstrap的父类AbstractBootstrap后,一路调用来到AbstractBootstrap#doBind()方法,首先就是初始化并注册Channel。
+服务端构建好ServerBootstrap之后,通过bind()方法进行绑定。进入ServerBootstrap的父类AbstractBootstrap后,线程经过调用栈的调用后来到AbstractBootstrap#doBind()方法,首先就是初始化并注册Channel。
+
+AbstractBootstrap#doBind()
+```java
+ private ChannelFuture doBind(final SocketAddress localAddress) {
+ // 注册channel
+ final ChannelFuture regFuture = initAndRegister();
+ final Channel channel = regFuture.channel();
+ if (regFuture.cause() != null) {
+ return regFuture;
+ }
+
+ // regFuture如果完成了,则isDone为true,否则给regFuture添加一个监听器,当完成的时候再进行doBind0的操作
+ if (regFuture.isDone()) {
+ // 此时我们已经知道NioServerSocketChannel已经完成了注册
+ ChannelPromise promise = channel.newPromise();
+ doBind0(regFuture, channel, localAddress, promise);
+ return promise;
+ } else {
+ // Registration future is almost always fulfilled already, but just in case it's not.
+ final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
+
+ // 给regFuture添加一个监听器,当注册chanel完成的时候,会回调进来
+ regFuture.addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ Throwable cause = future.cause();
+ if (cause != null) {
+ // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
+ // IllegalStateException once we try to access the EventLoop of the Channel.
+ promise.setFailure(cause);
+ } else {
+ // Registration was successful, so set the correct executor to use.
+ // See https://github.com/netty/netty/issues/2586
+ promise.registered();
+
+ doBind0(regFuture, channel, localAddress, promise);
+ }
+ }
+ });
+ return promise;
+ }
+ }
+
+ final ChannelFuture initAndRegister() {
+ Channel channel = null;
+ try {
+ // 拿到ReflectiveChannelFactory,然后通过其newChannel生成一个服务端Channel,底层就是通过反射newInstance()获取实例
+ // 这里自然是NioServerSocketChannel实例对象
+ channel = channelFactory.newChannel();
+ // 初始化channel
+ init(channel);
+ } catch (Throwable t) {
+ if (channel != null) {
+ // channel can be null if newChannel crashed (eg SocketException("too many open files"))
+ channel.unsafe().closeForcibly();
+ // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
+ return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
+ }
+ // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
+ return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
+ }
+
+ /**
+ * config() -> ServerBootstrapConfig
+ * group() -> NioEventLoopGroup,返回的是MultithreadEventLoopGroup
+ * register() -> 就是通过chooser选取到NioEventLoop对象
+ */
+ ChannelFuture regFuture = config().group().register(channel);
+ if (regFuture.cause() != null) {
+ if (channel.isRegistered()) {
+ channel.close();
+ } else {
+ channel.unsafe().closeForcibly();
+ }
+ }
+ return regFuture;
+ }
+```
-
+在initAndRegister处channelFactory是ReflectiveChannelFactory,具体赋值处是在ServerBootstrap#channel()方法中定义的,并且传入的channel是:NioServerSocketChannel。
-在initAndRegister处channelFactory是ReflectiveChannelFactory,具体赋值处是在ServerBootstrap#channel()方法中定义的,并且传入的channel是:NioServerSocketChannel,上图中可以见。
+ReflectiveChannelFactory#newChannel
+```java
+ @Override
+ public T newChannel() {
+ try {
+ return constructor.newInstance();
+ } catch (Throwable t) {
+ throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
+ }
+ }
+```
-查看到ReflectiveChannelFactory#newChannel()方法,实际就是调用的NioServerSocketChannel#newInstance()方法反射构建一个channel对象。
+查看到ReflectiveChannelFactory#newChannel()方法,T的类型是NioServerSocketChannel,所以实际就是调用的NioServerSocketChannel#newInstance()方法反射构建一个channel对象。
那么,我们看下NioServerSocketChannel底层是如何获取通过反射创建服务端Channel的呢?
@@ -136,15 +233,14 @@ AbstractChannel类
}
```
-总结下服务端创建Channel的三件重要事情:
+通过源码阅读,可以总结出Netty服务端创建Channel的三件重要事情:
1. 通过反射来创建JDK底层的channel;
2. 设置Channel为非阻塞模式ch.configureBlocking(false);
3. 创建一个pipeline对象;
-
-## 2. 初始化服务端Channel
+## 3. 初始化服务端Channel
初始化服务端Channel可以分为如下的几步:
@@ -208,17 +304,13 @@ ServerBoostrap端初始化过程
}
```
-## 3. 注册selector
+## 4. 将Channel注册到selector
整个注册selector过程可以分为以下几步:
-- AbstractChannel#register(channel) 入口
- - this.eventLoop = eventLoop 绑定线程
- - register0() 实际注册
- - doRegister() 调用jdk底层进行注册
- - invokeHandlerAddedIfNeeded()
- - fireChannelRegistered() 传播注册成功的事件
-
+1. AbstractChannel$AbstractUnsafe#register(channel)
+2. AbstractUnsafe#register0()
+3. AbstractUnsafe#doRegister()
AbstractChannel
```java
@@ -322,15 +414,16 @@ AbstractNioChannel.java
就这样,NioServerSocketChannel就以Accept事件注册到了Selector上了。
-## 4. 端口绑定
+这里需要注意一点,javaChannel()返回的是AbstractSelectableChannel,调用其register方法用于在给定的selector上注册这个通道channel,并返回一个选这件selectionKey。传入的操作位为0表示对任何事件都不感兴趣,仅仅是完成注册操作。
+
+## 5. 端口绑定
端口绑定流程如下:
-- AbstractUnsafe#bind() 入口
- - dobind()
- - javaChannel().bind() jdk底层绑定
- - pipeline.fireChanelActive() 传播事件
- - HeadContext.readIfIsAutoRead()
+1. AbstractBootstrap#bind()
+2. AbstractBootstrap#dobind()
+3. AbstractChannel#bind()
+4. NioServerSocketChannel#doBind()
AbstractChannel.AbstractUnsafe#bind()
@@ -402,3 +495,5 @@ Netty服务端核心启动流程主要是为了创建NioServerSocketChannel,
- AbstractChannel.AbstractUnsafe#register() 将服务端Channel注册到Selector上
- AbstractChannel.AbstractUnsafe#doBind() 注册端口号
+
+
From 9538194332a2e12a32a04613a6e751a120ada195 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Sat, 19 Feb 2022 15:20:36 +0800
Subject: [PATCH 45/63] =?UTF-8?q?[A]=20=E6=96=B0=E5=A2=9ENetty=E5=BA=95?=
=?UTF-8?q?=E5=B1=82=E5=8E=9F=E7=90=86=E5=88=86=E6=9E=90=E6=96=87=E7=AB=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
JdkLearn/pom.xml | 1 -
README.md | 9 +-
Spring-Netty/learnnetty.iml | 2 +-
Spring-Netty/pom.xml | 2 -
...57\345\212\250\345\210\206\346\236\220.md" | 1 -
...37\347\220\206\345\210\206\346\236\220.md" | 264 ++++++++++++++++++
6 files changed, 270 insertions(+), 9 deletions(-)
create mode 100644 "note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-NioServerSocketChannel\346\216\245\345\217\227\346\225\260\346\215\256\345\216\237\347\220\206\345\210\206\346\236\220.md"
diff --git a/JdkLearn/pom.xml b/JdkLearn/pom.xml
index 4a18492..9fb74db 100644
--- a/JdkLearn/pom.xml
+++ b/JdkLearn/pom.xml
@@ -30,7 +30,6 @@
io.netty
netty-all
- 4.1.6.Final
diff --git a/README.md b/README.md
index 648b81e..41bc0c9 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@
-
+
@@ -121,16 +121,17 @@ SpringCloud源码
- Dubbo底层源码学习(七)—— Dubbo的服务消费
- Netty底层源码解析
- - Netty版本:4.1.60.Final
+ - Netty版本:4.1.43.Final
- [二进制运算以及源码、反码以及补码学习](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/%E4%BA%8C%E8%BF%9B%E5%88%B6.md)
- [Netty源码包结构](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E6%BA%90%E7%A0%81%E5%8C%85%E7%BB%93%E6%9E%84.md)
- - [Netty中的EventLoopGroup](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E4%B8%AD%E7%9A%84EventLoopGroup%E6%98%AF%E4%BB%80%E4%B9%88.md)
+ - [Netty底层源码解析-EventLoopGroup](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E4%B8%AD%E7%9A%84EventLoopGroup%E6%98%AF%E4%BB%80%E4%B9%88.md)
- [Netty底层源码解析-初始Netty及其架构](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-%E5%88%9D%E5%A7%8BNetty%E5%8F%8A%E5%85%B6%E6%9E%B6%E6%9E%84.md)
- [Netty底层源码解析-Netty服务端启动分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-Netty%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%90%AF%E5%8A%A8%E5%88%86%E6%9E%90.md)
- [Netty底层源码解析-NioEventLoop原理分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-NioEventLoop%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.md)
- - Netty底层源码解析-Channel分析
- [Netty底层源码解析-ChannelPipeline分析(上)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-ChannelPipeline%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%8A%EF%BC%89.md)
- [Netty底层源码解析-ChannelPipeline分析(下)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-ChannelPipeline%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%8B%EF%BC%89.md)
+ - Netty底层源码解析-NioServerSocketChannel接受数据原理分析
+ - Netty底层源码解析-NioSocketChannel接受、发送数据原理分析
- Netty底层源码解析-FastThreadLocal原理分析
- Netty底层源码解析-内存分配原理分析
- Netty底层源码解析-RocketMQ底层使用到的Netty
diff --git a/Spring-Netty/learnnetty.iml b/Spring-Netty/learnnetty.iml
index 8cc03a7..07cda18 100644
--- a/Spring-Netty/learnnetty.iml
+++ b/Spring-Netty/learnnetty.iml
@@ -92,7 +92,7 @@
-
+
\ No newline at end of file
diff --git a/Spring-Netty/pom.xml b/Spring-Netty/pom.xml
index d640f68..82d7684 100644
--- a/Spring-Netty/pom.xml
+++ b/Spring-Netty/pom.xml
@@ -16,7 +16,6 @@
1.8
- 4.1.6.Final
@@ -35,7 +34,6 @@
io.netty
netty-all
- ${netty-all.version}
diff --git "a/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-Netty\346\234\215\345\212\241\347\253\257\345\220\257\345\212\250\345\210\206\346\236\220.md" "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-Netty\346\234\215\345\212\241\347\253\257\345\220\257\345\212\250\345\210\206\346\236\220.md"
index 1ff812b..986e355 100644
--- "a/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-Netty\346\234\215\345\212\241\347\253\257\345\220\257\345\212\250\345\210\206\346\236\220.md"
+++ "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-Netty\346\234\215\345\212\241\347\253\257\345\220\257\345\212\250\345\210\206\346\236\220.md"
@@ -496,4 +496,3 @@ Netty服务端核心启动流程主要是为了创建NioServerSocketChannel,
- AbstractChannel.AbstractUnsafe#doBind() 注册端口号
-
diff --git "a/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-NioServerSocketChannel\346\216\245\345\217\227\346\225\260\346\215\256\345\216\237\347\220\206\345\210\206\346\236\220.md" "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-NioServerSocketChannel\346\216\245\345\217\227\346\225\260\346\215\256\345\216\237\347\220\206\345\210\206\346\236\220.md"
new file mode 100644
index 0000000..c1dbe36
--- /dev/null
+++ "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-NioServerSocketChannel\346\216\245\345\217\227\346\225\260\346\215\256\345\216\237\347\220\206\345\210\206\346\236\220.md"
@@ -0,0 +1,264 @@
+## NioServerSocketChannel读取数据原理分析
+
+NioServerSocketChannel是AbstractNioMessageChannel的子类,而NioSocketChannel是AbstractNioByteChannel的子类,并且他们都有一个公共的父类:AbstractChannel。
+
+在Netty中Channel是用来定义对网络IO的读写操作的相关接口,与NIO的Channel接口类似。Channel的功能主要有网络IO的读写、客户端发起的连接、主动关闭连接、关闭链路、获取通信双方的网络地址等。一些公共的基础方法都在这个AbstractChannel抽象类中实现,但对于一些特定的功能则需要不同的实现类去实现,这样最大限度地实现了功能和接口的重用。
+
+另外,AbstractChannel的构造方法中对Unsafe类和ChannelPipeline类进行了初始化。
+
+## 1. NioServerSocketChannel源码分析
+
+NioServerSocketChannel是AbstractNioMessageChannel的子类,由于它由服务端使用,并且只负责监听Socket的接入,不关心IO的读写,所以与NioSocketChannel相比要简单得多。
+
+NioServerSocketChannel封装了NIO中的ServerSocketChannel,并通过newSocket()方法打开了ServerSocketChannel
+
+NioServerSocketChannel.class
+
+```java
+ private static ServerSocketChannel newSocket(SelectorProvider provider) {
+ try {
+ return provider.openServerSocketChannel();
+ } catch (IOException e) {
+ throw new ChannelException(
+ "Failed to open a server socket.", e);
+ }
+ }
+```
+
+对于NioServerSocketChannel注册至selector上的操作,是在AbstractNioChannel中实现的,源码如下:
+
+```java
+ @Override
+ protected void doRegister() throws Exception {
+ boolean selected = false;
+ for (;;) {
+ try {
+ selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
+ return;
+ } catch (CancelledKeyException e) {
+ if (!selected) {
+ eventLoop().selectNow();
+ selected = true;
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+```
+
+在ServerChannel的开启,selector上的注册等前期工作完成后,NioServerSocketChannel的开始监听新连接的加入,源码如下:
+
+```java
+ @Override
+ protected int doReadMessages(List buf) throws Exception {
+ // 拿到jdk底层channel
+ SocketChannel ch = SocketUtils.accept(javaChannel());
+
+ try {
+ if (ch != null) {
+ // new出一个NioSocketChannel,将jdk SocketChannel封装成NioSocketChannel,并且这里给NioSocketChannel注册了一个SelectionKey.OP_READ事件
+ buf.add(new NioSocketChannel(this, ch)); // 往buf里写入NioSocketChannel
+ return 1;
+ }
+ } catch (Throwable t) {
+ logger.warn("Failed to create a new channel from an accepted socket.", t);
+
+ try {
+ ch.close();
+ } catch (Throwable t2) {
+ logger.warn("Failed to close a socket.", t2);
+ }
+ }
+
+ return 0;
+ }
+```
+
+上面的源码展示了Netty最终拿到新连接请求后,将jdk底层的SocketChannel封装NioSocketChannel的过程,那么selector是如何获取到accept事件后,调用到这个doReadMessages方法的呢?
+
+为了分析原理的延续,故事还要回到bossGroup的NioEventLoop里,当bossGroup启动,NioServerSocketChannel实例新建并注册到selector之后,Netty的bossGroup就会运行一个NioEventLoop,它的核心工作就是作为一个selector一直去监听客户端发出的accept、connect、read、write等事件。具体逻辑查看NioEventLoop#run()方法,详细的原理请回看之前的NioEventLoop的原理分析,此处只分析NioEventLoop#run()获取到链接事件到调用NioServerSocketChannel#doReadMessages()的链路。
+
+1. NioEventLoop#run()一直轮训,监听这客户端发出的事件,在轮训过程中如果有任务产生,则会优先执行这些任务,调用非阻塞的selectNow(),否则调用select(deadlineNanos)阻塞指定时间去监听客户端事件。
+2. 调用NioEventLoop#processSelectedKeys(),Netty默认用的是优化过后的selectedKey,所以调用的是NioEventLoop#processSelectedKeysOptimized()方法。
+3. 在processSelectedKeysOptimized方法里会遍历selectedKeys,去拿selectedKeys中的SelectionKey,这个key就是从网络中获取到的感兴趣事件。
+4. 先通过SelectionKey获取attachment,及对应的事件channel。由于这里是获取的是accept事件,所以SelectionKey#attachment()获取到的是NioServerSocketChannel对象。
+5. 在NioEventLoop#processSelectedKey()方法中,首先拿到NioServerSocketChannel父类AbstractNioMessageChannel中的NioMessageUnsafe对象,接着根据readyOps进行判断,这里当然就是SelectionKey.OP_ACCEPT事件。
+6. 调用NioMessageUnsafe#read()方法,最终该方法调用了NioServerSocketChannel#doReadMessages(),完了之后会新建一个对SelectionKey.OP_READ事件感兴趣的NioSocketChannel对象,并存放在readBuf的一个集合中。
+7. 接着调用ChannelPipeline#fireChannelRead()方法,目的在于最终调用ServerBootstrapAcceptor#channelRead()方法,调用childGroup#register(child),把新建的NioSocketChannel对象注册到selector上。
+
+这样,NioServerSocketChannel监听accept事件,接收到客户端连接后,封装客户端的“连接”到NioSocketChannel对象,并注册到selector上,后面的网络IO的读写操作都由这个NioSocketChannel对象来负责处理。
+
+上述核心的6步源码如下:
+
+NioEventLoop.class
+```java
+ @Override
+ protected void run() {
+ for (;;) {
+ try {
+ try {
+ switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
+ // ... 省略
+ case SelectStrategy.SELECT:
+ select(wakenUp.getAndSet(false));
+ // ... 省略
+ if (wakenUp.get()) {
+ selector.wakeup();
+ }
+ // fall through
+ default:
+ }
+ } catch (IOException e) {
+ rebuildSelector0();
+ handleLoopException(e);
+ continue;
+ }
+ // ... 省略
+
+ // 步骤1
+ processSelectedKeys();
+ runAllTasks();
+
+ // ... 省略
+ } catch (Throwable t) {
+ handleLoopException(t);
+ // ... 省略
+ }
+ }
+ }
+```
+
+NioEventLoop.class
+```java
+ // 步骤2
+ private void processSelectedKeysOptimized() {
+ for (int i = 0; i < selectedKeys.size; ++i) {
+ // 步骤3
+ final SelectionKey k = selectedKeys.keys[i];
+ selectedKeys.keys[i] = null;
+
+ // 步骤4
+ final Object a = k.attachment();
+
+ if (a instanceof AbstractNioChannel) {
+ // 步骤5
+ processSelectedKey(k, (AbstractNioChannel) a);
+ } else {
+ @SuppressWarnings("unchecked")
+ NioTask task = (NioTask) a;
+ processSelectedKey(k, task);
+ }
+
+ if (needsToSelectAgain) {
+ selectedKeys.reset(i + 1);
+
+ selectAgain();
+ i = -1;
+ }
+ }
+ }
+```
+
+NioEventLoop.class
+```java
+ private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
+ final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
+ if (!k.isValid()) {
+ final EventLoop eventLoop;
+ try {
+ eventLoop = ch.eventLoop();
+ } catch (Throwable ignored) {
+ return;
+ }
+ if (eventLoop != this || eventLoop == null) {
+ return;
+ }
+ unsafe.close(unsafe.voidPromise());
+ return;
+ }
+
+ try {
+ int readyOps = k.readyOps();
+ if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
+ int ops = k.interestOps();
+ ops &= ~SelectionKey.OP_CONNECT;
+ k.interestOps(ops);
+
+ unsafe.finishConnect();
+ }
+
+ if ((readyOps & SelectionKey.OP_WRITE) != 0) {
+ ch.unsafe().forceFlush();
+ }
+
+ // 步骤5
+ if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
+ unsafe.read();
+ }
+ } catch (CancelledKeyException ignored) {
+ unsafe.close(unsafe.voidPromise());
+ }
+ }
+```
+
+NioServerSocketChannel.class
+
+```java
+ @Override
+ protected int doReadMessages(List buf) throws Exception {
+ // 拿到jdk 的SocketChannel,代表着和客户端的一个连接socket
+ SocketChannel ch = SocketUtils.accept(javaChannel());
+
+ try {
+ if (ch != null) {
+ // 步骤6
+ // 封装一个NioSocketChannel对象,并且设置感兴趣事件为:SelectionKey.OP_READ
+ buf.add(new NioSocketChannel(this, ch));
+ return 1;
+ }
+ } catch (Throwable t) {
+ logger.warn("Failed to create a new channel from an accepted socket.", t);
+
+ try {
+ ch.close();
+ } catch (Throwable t2) {
+ logger.warn("Failed to close a socket.", t2);
+ }
+ }
+
+ return 0;
+ }
+```
+
+ServerBootstrapAcceptor.class
+
+```java
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
+ final Channel child = (Channel) msg;
+
+ child.pipeline().addLast(childHandler);
+
+ setChannelOptions(child, childOptions, logger);
+ setAttributes(child, childAttrs);
+
+ try {
+ // 步骤7
+ // 在workerGroup的NioEventLoop上的selector注册了NioSocketChannel
+ childGroup.register(child).addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (!future.isSuccess()) {
+ forceClose(child, future.cause());
+ }
+ }
+ });
+ } catch (Throwable t) {
+ forceClose(child, t);
+ }
+ }
+```
+
+以上就是Netty中有关NioServerSocketChannel读取数据的底层原理分析。
+
+下一篇分析NioSocketChannel的发送、读取数据底层原理。
\ No newline at end of file
From e2bd70e53ca761e7c7fb067ecc4f1a9da48bb409 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Sat, 19 Feb 2022 15:39:32 +0800
Subject: [PATCH 46/63] =?UTF-8?q?[U]=20=E6=9B=B4=E6=96=B0=E6=96=87?=
=?UTF-8?q?=E7=AB=A0=E7=9B=AE=E5=BD=95=E9=93=BE=E6=8E=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 41bc0c9..f96c8ea 100644
--- a/README.md
+++ b/README.md
@@ -130,7 +130,7 @@ SpringCloud源码
- [Netty底层源码解析-NioEventLoop原理分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-NioEventLoop%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.md)
- [Netty底层源码解析-ChannelPipeline分析(上)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-ChannelPipeline%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%8A%EF%BC%89.md)
- [Netty底层源码解析-ChannelPipeline分析(下)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-ChannelPipeline%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%8B%EF%BC%89.md)
- - Netty底层源码解析-NioServerSocketChannel接受数据原理分析
+ - [Netty底层源码解析-NioServerSocketChannel接受数据原理分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-NioServerSocketChannel%E6%8E%A5%E5%8F%97%E6%95%B0%E6%8D%AE%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.md)
- Netty底层源码解析-NioSocketChannel接受、发送数据原理分析
- Netty底层源码解析-FastThreadLocal原理分析
- Netty底层源码解析-内存分配原理分析
From 134feac257703f528a5e0dc84d43c1e7abaef534 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Sun, 20 Feb 2022 23:25:58 +0800
Subject: [PATCH 47/63] [U] fix
---
...345\216\237\347\220\206\345\210\206\346\236\220.md" | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git "a/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-NioServerSocketChannel\346\216\245\345\217\227\346\225\260\346\215\256\345\216\237\347\220\206\345\210\206\346\236\220.md" "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-NioServerSocketChannel\346\216\245\345\217\227\346\225\260\346\215\256\345\216\237\347\220\206\345\210\206\346\236\220.md"
index c1dbe36..05499f2 100644
--- "a/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-NioServerSocketChannel\346\216\245\345\217\227\346\225\260\346\215\256\345\216\237\347\220\206\345\210\206\346\236\220.md"
+++ "b/note/Netty/Netty\345\272\225\345\261\202\346\272\220\347\240\201\350\247\243\346\236\220-NioServerSocketChannel\346\216\245\345\217\227\346\225\260\346\215\256\345\216\237\347\220\206\345\210\206\346\236\220.md"
@@ -1,10 +1,10 @@
## NioServerSocketChannel读取数据原理分析
-NioServerSocketChannel是AbstractNioMessageChannel的子类,而NioSocketChannel是AbstractNioByteChannel的子类,并且他们都有一个公共的父类:AbstractChannel。
+NioServerSocketChannel是AbstractNioMessageChannel的子类,而NioSocketChannel是AbstractNioByteChannel的子类,并且他们都有两个公共的父类:AbstractNioChannel、AbstractChannel。
-在Netty中Channel是用来定义对网络IO的读写操作的相关接口,与NIO的Channel接口类似。Channel的功能主要有网络IO的读写、客户端发起的连接、主动关闭连接、关闭链路、获取通信双方的网络地址等。一些公共的基础方法都在这个AbstractChannel抽象类中实现,但对于一些特定的功能则需要不同的实现类去实现,这样最大限度地实现了功能和接口的重用。
-
-另外,AbstractChannel的构造方法中对Unsafe类和ChannelPipeline类进行了初始化。
+在Netty中Channel是用来定义对网络IO的读写操作的相关接口,与NIO的Channel接口类似。Channel的功能主要有网络IO的读写、客户端发起的连接、主动关闭连接、关闭链路、获取通信双方的网络地址等。
+一些公共的基础方法都在这个AbstractChannel抽象类中实现,几个核心的方法如:channel的注册,channel撤销注册,网络IO的读、写。但对于一些特定的功能则需要不同的实现类去实现,这样最大限度地实现了功能和接口的重用,
+就如AbstractNioChannel中主要定义了doRegister()、doConnect()、newDirectBuffer()方法。
## 1. NioServerSocketChannel源码分析
@@ -47,7 +47,7 @@ NioServerSocketChannel.class
}
```
-在ServerChannel的开启,selector上的注册等前期工作完成后,NioServerSocketChannel的开始监听新连接的加入,源码如下:
+在ServerSocketChannel的开启,selector上的注册等前期工作完成后,NioServerSocketChannel的开始监听新连接的加入,源码如下:
```java
@Override
From 34ed31b36ab07b1df8e685f2cdd926024d27560c Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Fri, 25 Feb 2022 21:15:48 +0800
Subject: [PATCH 48/63] =?UTF-8?q?fix=20SpringBoot=E7=B3=BB=E7=BB=9F?=
=?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E5=99=A8md=E6=97=A0=E6=B3=95?=
=?UTF-8?q?=E6=9F=A5=E7=9C=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index f96c8ea..e69a07d 100644
--- a/README.md
+++ b/README.md
@@ -96,7 +96,7 @@ SpringCloud源码
- [深入浅出SpringBoot源码——SpringFactoriesLoader](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94SpringFactoriesLoader.md)
- [深入浅出SpringBoot源码——监听器与事件机制](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94%E7%9B%91%E5%90%AC%E5%99%A8%E4%B8%8E%E4%BA%8B%E4%BB%B6%E6%9C%BA%E5%88%B6.md)
- - [深入浅出SpringBoot源码——系统初始化器](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94%E7%B3%BB%E7%BB%9F.md)
+ - [深入浅出SpringBoot源码——系统初始化器](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94%E7%B3%BB%E7%BB%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%99%A8.md)
- [深入浅出SpringBoot源码——启动加载器](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94%E5%90%AF%E5%8A%A8%E5%8A%A0%E8%BD%BD%E5%99%A8.md)
- SpringSecurity&OAuth2源码学习
From f2f9a9af79543518722af9739cd3f9995bfd43c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B5=B7=E9=98=B3=E7=BD=97?=
Date: Wed, 23 Mar 2022 17:56:47 +0800
Subject: [PATCH 49/63] =?UTF-8?q?=E5=9F=BA=E4=BA=8ENetty=E5=AE=9E=E7=8E=B0?=
=?UTF-8?q?im=E9=80=9A=E8=AE=AF=E5=B7=A5=E5=85=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Spring-Netty/learnnetty.iml | 98 -------------------
.../learnnetty/im/client/NettyClient.java | 95 ++++++++++++++++++
.../im/client/handler/FirstClientHandler.java | 35 +++++++
.../client/handler/LoginResponseHandler.java | 40 ++++++++
.../handler/MessageResponseHandler.java | 19 ++++
.../learnnetty/im/codec/PacketDecoder.java | 20 ++++
.../learnnetty/im/codec/PacketEncoder.java | 19 ++++
.../bruis/learnnetty/im/codec/Spliter.java | 30 ++++++
.../bruis/learnnetty/im/model/Attributes.java | 12 +++
.../bruis/learnnetty/im/model/Command.java | 44 +++++++++
.../im/model/LoginRequestPacket.java | 46 +++++++++
.../im/model/LoginResponsePacket.java | 36 +++++++
.../im/model/MessageRequestPacket.java | 32 ++++++
.../im/model/MessageResponsePacket.java | 27 +++++
.../com/bruis/learnnetty/im/model/Packet.java | 27 +++++
.../learnnetty/im/model/PacketCodeC.java | 90 +++++++++++++++++
.../learnnetty/im/serialize/Serializer.java | 29 ++++++
.../im/serialize/SerializerAlogrithm.java | 13 +++
.../im/serialize/impl/JSONSerializer.java | 29 ++++++
.../learnnetty/im/server/NettyServer.java | 60 ++++++++++++
.../im/server/handler/FirstServerHandler.java | 31 ++++++
.../server/handler/LoginRequestHandler.java | 39 ++++++++
.../server/handler/MessageRequestHandler.java | 25 +++++
.../bruis/learnnetty/im/util/LoginUtil.java | 23 +++++
24 files changed, 821 insertions(+), 98 deletions(-)
delete mode 100644 Spring-Netty/learnnetty.iml
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/FirstClientHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketDecoder.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketEncoder.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/Spliter.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Attributes.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Command.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginRequestPacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginResponsePacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageRequestPacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageResponsePacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Packet.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/Serializer.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/SerializerAlogrithm.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/impl/JSONSerializer.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/FirstServerHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/LoginUtil.java
diff --git a/Spring-Netty/learnnetty.iml b/Spring-Netty/learnnetty.iml
deleted file mode 100644
index 07cda18..0000000
--- a/Spring-Netty/learnnetty.iml
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
new file mode 100644
index 0000000..3a25d67
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
@@ -0,0 +1,95 @@
+package com.bruis.learnnetty.im.client;
+
+import com.bruis.learnnetty.im.client.handler.FirstClientHandler;
+import com.bruis.learnnetty.im.client.handler.LoginResponseHandler;
+import com.bruis.learnnetty.im.client.handler.MessageResponseHandler;
+import com.bruis.learnnetty.im.codec.PacketDecoder;
+import com.bruis.learnnetty.im.codec.PacketEncoder;
+import com.bruis.learnnetty.im.codec.Spliter;
+import com.bruis.learnnetty.im.model.MessageRequestPacket;
+import com.bruis.learnnetty.im.util.LoginUtil;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+
+import java.util.Date;
+import java.util.Scanner;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Description 客户端
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public class NettyClient {
+ private static final int MAX_RETRY = 5;
+ private static final String HOST = "127.0.0.1";
+ private static final int PORT = 8000;
+
+
+ public static void main(String[] args) {
+ NioEventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ Bootstrap bootstrap = new Bootstrap();
+ bootstrap
+ .group(workerGroup)
+ .channel(NioSocketChannel.class)
+ .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
+ .option(ChannelOption.SO_KEEPALIVE, true)
+ .option(ChannelOption.TCP_NODELAY, true)
+ .handler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) {
+ ch.pipeline().addLast(new Spliter());
+// ch.pipeline().addLast(new FirstClientHandler());
+ // 解码
+ ch.pipeline().addLast(new PacketDecoder());
+ ch.pipeline().addLast(new LoginResponseHandler());
+ ch.pipeline().addLast(new MessageResponseHandler());
+ // 编码
+ ch.pipeline().addLast(new PacketEncoder());
+ }
+ });
+
+ connect(bootstrap, HOST, PORT, MAX_RETRY);
+ }
+
+ private static void connect(Bootstrap bootstrap, String host, int port, int retry) {
+ bootstrap.connect(host, port).addListener(future -> {
+ if (future.isSuccess()) {
+ System.out.println(new Date() + ": 连接成功,启动控制台线程……");
+ Channel channel = ((ChannelFuture) future).channel();
+ startConsoleThread(channel);
+ } else if (retry == 0) {
+ System.err.println("重试次数已用完,放弃连接!");
+ } else {
+ // 第几次重连
+ int order = (MAX_RETRY - retry) + 1;
+ // 本次重连的间隔
+ int delay = 1 << order;
+ System.err.println(new Date() + ": 连接失败,第" + order + "次重连……");
+ bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retry - 1), delay, TimeUnit
+ .SECONDS);
+ }
+ });
+ }
+
+ private static void startConsoleThread(Channel channel) {
+ new Thread(() -> {
+ while (!Thread.interrupted()) {
+ if (LoginUtil.hasLogin(channel)) {
+ System.out.println("输入消息发送至服务端: ");
+ Scanner sc = new Scanner(System.in);
+ String line = sc.nextLine();
+
+ channel.writeAndFlush(new MessageRequestPacket(line));
+ }
+ }
+ }).start();
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/FirstClientHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/FirstClientHandler.java
new file mode 100644
index 0000000..6d03748
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/FirstClientHandler.java
@@ -0,0 +1,35 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.MessageRequestPacket;
+import com.bruis.learnnetty.im.model.MessageResponsePacket;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+
+import java.nio.charset.Charset;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class FirstClientHandler extends ChannelInboundHandlerAdapter {
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) {
+ for (int i = 0; i < 5; i++) {
+// ByteBuf buffer = getByteBuf(ctx);
+ MessageRequestPacket packet = new MessageRequestPacket();
+ packet.setMessage("你好啊,测试一下Netty的通讯!");
+ ctx.channel().writeAndFlush(packet);
+ }
+ }
+
+ private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
+// byte[] bytes = "你好,欢迎关注我的微信公众号,《闪电侠的博客》!".getBytes(Charset.forName("utf-8"));
+ byte[] bytes = "你好".getBytes(Charset.forName("utf-8"));
+ ByteBuf buffer = ctx.alloc().buffer();
+ buffer.writeBytes(bytes);
+
+ return buffer;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
new file mode 100644
index 0000000..61086f4
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
@@ -0,0 +1,40 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.LoginRequestPacket;
+import com.bruis.learnnetty.im.model.LoginResponsePacket;
+import com.bruis.learnnetty.im.util.LoginUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+import java.util.Date;
+import java.util.UUID;
+
+/**
+ * @Description 登录响应的reponse
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class LoginResponseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ // 创建登录对象
+ LoginRequestPacket loginRequestPacket = new LoginRequestPacket();
+ loginRequestPacket.setUserId(UUID.randomUUID().toString());
+ loginRequestPacket.setUserName("flash");
+ loginRequestPacket.setPassword("pwd");
+
+ // 写数据-发起登录
+ ctx.channel().writeAndFlush(loginRequestPacket);
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext channelHandlerContext, LoginResponsePacket loginResponsePacket) throws Exception {
+ if (loginResponsePacket.isSuccess()) {
+ System.out.println(new Date() + ": 客户端登录成功");
+ LoginUtil.markAsLogin(channelHandlerContext.channel());
+ } else {
+ System.out.println(new Date() + ": 客户端登录失败,原因:" + loginResponsePacket.getReason());
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java
new file mode 100644
index 0000000..70a2cdb
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java
@@ -0,0 +1,19 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.MessageResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+import java.util.Date;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class MessageResponseHandler extends SimpleChannelInboundHandler {
+ @Override
+ protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageResponsePacket messageResponsePacket) throws Exception {
+ System.out.println(new Date() + ": 收到服务端的消息: " + messageResponsePacket.getMessage());
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketDecoder.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketDecoder.java
new file mode 100644
index 0000000..751d007
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketDecoder.java
@@ -0,0 +1,20 @@
+package com.bruis.learnnetty.im.codec;
+
+import com.bruis.learnnetty.im.model.PacketCodeC;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+
+import java.util.List;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class PacketDecoder extends ByteToMessageDecoder {
+ @Override
+ protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List out) throws Exception {
+ out.add(PacketCodeC.INSTANCE.decode(byteBuf));
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketEncoder.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketEncoder.java
new file mode 100644
index 0000000..d3d4fa7
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketEncoder.java
@@ -0,0 +1,19 @@
+package com.bruis.learnnetty.im.codec;
+
+import com.bruis.learnnetty.im.model.Packet;
+import com.bruis.learnnetty.im.model.PacketCodeC;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class PacketEncoder extends MessageToByteEncoder {
+ @Override
+ protected void encode(ChannelHandlerContext channelHandlerContext, Packet packet, ByteBuf byteBuf) throws Exception {
+ PacketCodeC.INSTANCE.encode(byteBuf, packet);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/Spliter.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/Spliter.java
new file mode 100644
index 0000000..033611e
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/Spliter.java
@@ -0,0 +1,30 @@
+package com.bruis.learnnetty.im.codec;
+
+import com.bruis.learnnetty.im.model.PacketCodeC;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class Spliter extends LengthFieldBasedFrameDecoder {
+ private static final int LENGTH_FIELD_OFFSET = 7;
+ private static final int LENGTH_FIELD_LENGTH = 4;
+
+ public Spliter() {
+ super(Integer.MAX_VALUE, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH);
+ }
+
+ @Override
+ protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
+ if (in.getInt(in.readerIndex()) != PacketCodeC.MAGIC_NUMBER) {
+ ctx.channel().close();
+ return null;
+ }
+
+ return super.decode(ctx, in);
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Attributes.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Attributes.java
new file mode 100644
index 0000000..b87cac3
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Attributes.java
@@ -0,0 +1,12 @@
+package com.bruis.learnnetty.im.model;
+
+import io.netty.util.AttributeKey;
+
+/**
+ * @Description Netty 属性集
+ * @Author haiyangluo
+ * @Date 2022/3/22
+ */
+public interface Attributes {
+ AttributeKey LOGIN = AttributeKey.newInstance("login");
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Command.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Command.java
new file mode 100644
index 0000000..f84f29f
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Command.java
@@ -0,0 +1,44 @@
+package com.bruis.learnnetty.im.model;
+
+/**
+ * @Description
+ * @Author haiyangluo
+ * @Date 2022/3/22
+ */
+public interface Command {
+ Byte LOGIN_REQUEST = 1;
+
+ Byte LOGIN_RESPONSE = 2;
+
+ Byte MESSAGE_REQUEST = 3;
+
+ Byte MESSAGE_RESPONSE = 4;
+
+ Byte LOGOUT_REQUEST = 5;
+
+ Byte LOGOUT_RESPONSE = 6;
+
+ Byte CREATE_GROUP_REQUEST = 7;
+
+ Byte CREATE_GROUP_RESPONSE = 8;
+
+ Byte LIST_GROUP_MEMBERS_REQUEST = 9;
+
+ Byte LIST_GROUP_MEMBERS_RESPONSE = 10;
+
+ Byte JOIN_GROUP_REQUEST = 11;
+
+ Byte JOIN_GROUP_RESPONSE = 12;
+
+ Byte QUIT_GROUP_REQUEST = 13;
+
+ Byte QUIT_GROUP_RESPONSE = 14;
+
+ Byte GROUP_MESSAGE_REQUEST = 15;
+
+ Byte GROUP_MESSAGE_RESPONSE = 16;
+
+ Byte HEARTBEAT_REQUEST = 17;
+
+ Byte HEARTBEAT_RESPONSE = 18;
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginRequestPacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginRequestPacket.java
new file mode 100644
index 0000000..d1122bd
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginRequestPacket.java
@@ -0,0 +1,46 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.LOGIN_REQUEST;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public class LoginRequestPacket extends Packet {
+
+ private String userId;
+
+ private String userName;
+
+ private String password;
+
+ @Override
+ public Byte getCommand() {
+ return LOGIN_REQUEST;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginResponsePacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginResponsePacket.java
new file mode 100644
index 0000000..1b7480f
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginResponsePacket.java
@@ -0,0 +1,36 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.LOGIN_RESPONSE;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public class LoginResponsePacket extends Packet {
+ private boolean success;
+
+ private String reason;
+
+
+ @Override
+ public Byte getCommand() {
+ return LOGIN_RESPONSE;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageRequestPacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageRequestPacket.java
new file mode 100644
index 0000000..f2254d9
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageRequestPacket.java
@@ -0,0 +1,32 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.MESSAGE_REQUEST;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public class MessageRequestPacket extends Packet {
+
+ public MessageRequestPacket(){}
+
+ public MessageRequestPacket(String message) {
+ this.message = message;
+ }
+
+ private String message;
+
+ @Override
+ public Byte getCommand() {
+ return MESSAGE_REQUEST;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageResponsePacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageResponsePacket.java
new file mode 100644
index 0000000..3b955d8
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageResponsePacket.java
@@ -0,0 +1,27 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.MESSAGE_RESPONSE;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public class MessageResponsePacket extends Packet {
+
+ private String message;
+
+ @Override
+ public Byte getCommand() {
+
+ return MESSAGE_RESPONSE;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Packet.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Packet.java
new file mode 100644
index 0000000..0100f52
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Packet.java
@@ -0,0 +1,27 @@
+package com.bruis.learnnetty.im.model;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public abstract class Packet {
+ /**
+ * 协议版本
+ */
+ @JSONField(deserialize = false , serialize = false)
+ private Byte version = 1;
+
+ @JSONField(serialize = false)
+ public abstract Byte getCommand();
+
+ public Byte getVersion() {
+ return version;
+ }
+
+ public void setVersion(Byte version) {
+ this.version = version;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java
new file mode 100644
index 0000000..7e48ff1
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java
@@ -0,0 +1,90 @@
+package com.bruis.learnnetty.im.model;
+
+import com.bruis.learnnetty.im.serialize.Serializer;
+import com.bruis.learnnetty.im.serialize.impl.JSONSerializer;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+
+import java.util.*;
+
+import static com.bruis.learnnetty.im.model.Command.*;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public class PacketCodeC {
+ public static final int MAGIC_NUMBER = 0x12345678;
+ public static final PacketCodeC INSTANCE = new PacketCodeC();
+
+ private final Map> packetTypeMap;
+ private final Map serializerMap;
+
+
+ private PacketCodeC() {
+ packetTypeMap = new HashMap<>();
+ packetTypeMap.put(LOGIN_REQUEST, LoginRequestPacket.class);
+ packetTypeMap.put(LOGIN_RESPONSE, LoginResponsePacket.class);
+ packetTypeMap.put(MESSAGE_REQUEST, MessageRequestPacket.class);
+ packetTypeMap.put(MESSAGE_RESPONSE, MessageResponsePacket.class);
+
+ serializerMap = new HashMap<>();
+ Serializer serializer = new JSONSerializer();
+ serializerMap.put(serializer.getSerializerAlogrithm(), serializer);
+ }
+
+
+ public void encode(ByteBuf byteBuf, Packet packet) {
+ // 1. 序列化 java 对象
+ byte[] bytes = Serializer.DEFAULT.serialize(packet);
+
+ // 2. 实际编码过程
+ byteBuf.writeInt(MAGIC_NUMBER);
+ byteBuf.writeByte(packet.getVersion());
+ byteBuf.writeByte(Serializer.DEFAULT.getSerializerAlogrithm());
+ byteBuf.writeByte(packet.getCommand());
+ byteBuf.writeInt(bytes.length);
+ byteBuf.writeBytes(bytes);
+ }
+
+
+ public Packet decode(ByteBuf byteBuf) {
+ // 跳过 magic number
+ byteBuf.skipBytes(4);
+
+ // 跳过版本号
+ byteBuf.skipBytes(1);
+
+ // 序列化算法
+ byte serializeAlgorithm = byteBuf.readByte();
+
+ // 指令
+ byte command = byteBuf.readByte();
+
+ // 数据包长度
+ int length = byteBuf.readInt();
+
+ byte[] bytes = new byte[length];
+ byteBuf.readBytes(bytes);
+
+ Class extends Packet> requestType = getRequestType(command);
+ Serializer serializer = getSerializer(serializeAlgorithm);
+
+ if (requestType != null && serializer != null) {
+ return serializer.deserialize(requestType, bytes);
+ }
+
+ return null;
+ }
+
+ private Serializer getSerializer(byte serializeAlgorithm) {
+
+ return serializerMap.get(serializeAlgorithm);
+ }
+
+ private Class extends Packet> getRequestType(byte command) {
+
+ return packetTypeMap.get(command);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/Serializer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/Serializer.java
new file mode 100644
index 0000000..1bce9c9
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/Serializer.java
@@ -0,0 +1,29 @@
+package com.bruis.learnnetty.im.serialize;
+
+
+import com.bruis.learnnetty.im.serialize.impl.JSONSerializer;
+
+/**
+ * @Description
+ * @Author haiyangluo
+ * @Date 2022/3/22
+ */
+public interface Serializer {
+ Serializer DEFAULT = new JSONSerializer();
+
+ /**
+ * 序列化算法
+ * @return
+ */
+ byte getSerializerAlogrithm();
+
+ /**
+ * java 对象转换成二进制
+ */
+ byte[] serialize(Object object);
+
+ /**
+ * 二进制转换成 java 对象
+ */
+ T deserialize(Class clazz, byte[] bytes);
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/SerializerAlogrithm.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/SerializerAlogrithm.java
new file mode 100644
index 0000000..ef50887
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/SerializerAlogrithm.java
@@ -0,0 +1,13 @@
+package com.bruis.learnnetty.im.serialize;
+
+/**
+ * @Description
+ * @Author haiyangluo
+ * @Date 2022/3/22
+ */
+public interface SerializerAlogrithm {
+ /**
+ * json 序列化
+ */
+ byte JSON = 1;
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/impl/JSONSerializer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/impl/JSONSerializer.java
new file mode 100644
index 0000000..9a2881f
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/serialize/impl/JSONSerializer.java
@@ -0,0 +1,29 @@
+package com.bruis.learnnetty.im.serialize.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.bruis.learnnetty.im.serialize.Serializer;
+import com.bruis.learnnetty.im.serialize.SerializerAlogrithm;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public class JSONSerializer implements Serializer {
+ @Override
+ public byte getSerializerAlogrithm() {
+ return SerializerAlogrithm.JSON;
+ }
+
+ @Override
+ public byte[] serialize(Object object) {
+
+ return JSON.toJSONBytes(object);
+ }
+
+ @Override
+ public T deserialize(Class clazz, byte[] bytes) {
+
+ return JSON.parseObject(bytes, clazz);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
new file mode 100644
index 0000000..4b08222
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
@@ -0,0 +1,60 @@
+package com.bruis.learnnetty.im.server;
+
+import com.bruis.learnnetty.im.codec.PacketDecoder;
+import com.bruis.learnnetty.im.codec.PacketEncoder;
+import com.bruis.learnnetty.im.codec.Spliter;
+import com.bruis.learnnetty.im.server.handler.FirstServerHandler;
+import com.bruis.learnnetty.im.server.handler.LoginRequestHandler;
+import com.bruis.learnnetty.im.server.handler.MessageRequestHandler;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+
+import java.util.Date;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public class NettyServer {
+
+ private static final int PORT = 8000;
+
+ public static void main(String[] args) {
+ NioEventLoopGroup bossGroup = new NioEventLoopGroup();
+ NioEventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ ServerBootstrap serverBootstrap = new ServerBootstrap();
+ serverBootstrap.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .option(ChannelOption.SO_BACKLOG, 1024)
+ .childOption(ChannelOption.SO_KEEPALIVE, true)
+ .childOption(ChannelOption.TCP_NODELAY, true)
+ .childHandler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(NioSocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new Spliter());
+// ch.pipeline().addLast(new FirstServerHandler());
+ ch.pipeline().addLast(new PacketDecoder());
+ ch.pipeline().addLast(new LoginRequestHandler());
+ ch.pipeline().addLast(new MessageRequestHandler());
+ ch.pipeline().addLast(new PacketEncoder());
+ }
+ });
+ bind(serverBootstrap, PORT);
+ }
+
+ private static void bind(final ServerBootstrap serverBootstrap, final int port) {
+ serverBootstrap.bind(port).addListener(future -> {
+ if (future.isSuccess()) {
+ System.out.println(new Date() + ": 端口[" + port + "]绑定成功!");
+ } else {
+ System.err.println("端口[" + port + "]绑定失败!");
+ }
+ });
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/FirstServerHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/FirstServerHandler.java
new file mode 100644
index 0000000..d0d4679
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/FirstServerHandler.java
@@ -0,0 +1,31 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import com.bruis.learnnetty.im.model.MessageRequestPacket;
+import com.bruis.learnnetty.im.model.Packet;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+import java.nio.charset.Charset;
+import java.util.Date;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class FirstServerHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
+ ByteBuf byteBuf = (ByteBuf) msg;
+
+ System.out.println(new Date() + ": 服务端读到数据 -> " + byteBuf.toString(Charset.forName("utf-8")));
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, MessageRequestPacket msg) throws Exception {
+ System.out.println(new Date() + ": 服务端读到数据 -> " + msg.getMessage());
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
new file mode 100644
index 0000000..d54a75c
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
@@ -0,0 +1,39 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import com.bruis.learnnetty.im.model.LoginRequestPacket;
+import com.bruis.learnnetty.im.model.LoginResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+import java.util.Date;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class LoginRequestHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext context, LoginRequestPacket loginRequestPacket) throws Exception {
+ System.out.println(new Date() + ": 收到客户端登录请求……");
+
+ LoginResponsePacket loginResponsePacket = new LoginResponsePacket();
+ loginResponsePacket.setVersion(loginRequestPacket.getVersion());
+ if (valid(loginRequestPacket)) {
+ loginResponsePacket.setSuccess(true);
+ System.out.println(new Date() + ": 登录成功!");
+ } else {
+ loginResponsePacket.setReason("账号密码校验失败");
+ loginResponsePacket.setSuccess(false);
+ System.out.println(new Date() + ": 登录失败!");
+ }
+
+ // 登录响应
+ context.channel().writeAndFlush(loginResponsePacket);
+ }
+
+ private boolean valid(LoginRequestPacket loginRequestPacket) {
+ return true;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java
new file mode 100644
index 0000000..26cd823
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java
@@ -0,0 +1,25 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import com.bruis.learnnetty.im.model.MessageRequestPacket;
+import com.bruis.learnnetty.im.model.MessageResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+import java.util.Date;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class MessageRequestHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageRequestPacket messageRequestPacket) throws Exception {
+ MessageResponsePacket messageResponsePacket = new MessageResponsePacket();
+ System.out.println(new Date() + ": 收到客户端消息: " + messageRequestPacket.getMessage());
+ messageResponsePacket.setMessage("服务端回复【" + messageRequestPacket.getMessage() + "】");
+
+ channelHandlerContext.channel().writeAndFlush(messageResponsePacket);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/LoginUtil.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/LoginUtil.java
new file mode 100644
index 0000000..fccfc08
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/LoginUtil.java
@@ -0,0 +1,23 @@
+package com.bruis.learnnetty.im.util;
+
+import com.bruis.learnnetty.im.model.Attributes;
+import io.netty.channel.Channel;
+import io.netty.util.Attribute;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public class LoginUtil {
+
+ public static void markAsLogin(Channel channel) {
+ channel.attr(Attributes.LOGIN).set(true);
+ }
+
+ public static boolean hasLogin(Channel channel) {
+ Attribute loginAttr = channel.attr(Attributes.LOGIN);
+
+ return loginAttr.get() != null;
+ }
+}
From 63b992a94297bd7ef8e3b3865bd22e816f88339a Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Wed, 23 Mar 2022 23:17:55 +0800
Subject: [PATCH 50/63] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E4=BA=92?=
=?UTF-8?q?=E7=9B=B8=E9=80=9A=E4=BF=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../learnnetty/im/client/NettyClient.java | 41 ++++++++++++-----
.../im/client/handler/FirstClientHandler.java | 35 ---------------
.../client/handler/LoginResponseHandler.java | 27 +++++-------
.../handler/MessageResponseHandler.java | 6 +--
.../bruis/learnnetty/im/codec/Spliter.java | 4 +-
.../bruis/learnnetty/im/model/Attributes.java | 3 +-
.../im/model/LoginResponsePacket.java | 21 +++++++++
.../im/model/MessageRequestPacket.java | 17 +++++--
.../im/model/MessageResponsePacket.java | 20 +++++++++
.../learnnetty/im/server/NettyServer.java | 3 +-
.../im/server/handler/AuthHandler.java | 23 ++++++++++
.../im/server/handler/FirstServerHandler.java | 31 -------------
.../server/handler/LoginRequestHandler.java | 28 +++++++++---
.../server/handler/MessageRequestHandler.java | 24 +++++++---
.../bruis/learnnetty/im/session/Session.java | 34 ++++++++++++++
.../bruis/learnnetty/im/util/LoginUtil.java | 23 ----------
.../bruis/learnnetty/im/util/SessionUtil.java | 44 +++++++++++++++++++
17 files changed, 248 insertions(+), 136 deletions(-)
delete mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/FirstClientHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/AuthHandler.java
delete mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/FirstServerHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/session/Session.java
delete mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/LoginUtil.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/SessionUtil.java
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
index 3a25d67..e36c730 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
@@ -1,13 +1,13 @@
package com.bruis.learnnetty.im.client;
-import com.bruis.learnnetty.im.client.handler.FirstClientHandler;
import com.bruis.learnnetty.im.client.handler.LoginResponseHandler;
import com.bruis.learnnetty.im.client.handler.MessageResponseHandler;
import com.bruis.learnnetty.im.codec.PacketDecoder;
import com.bruis.learnnetty.im.codec.PacketEncoder;
import com.bruis.learnnetty.im.codec.Spliter;
+import com.bruis.learnnetty.im.model.LoginRequestPacket;
import com.bruis.learnnetty.im.model.MessageRequestPacket;
-import com.bruis.learnnetty.im.util.LoginUtil;
+import com.bruis.learnnetty.im.util.SessionUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
@@ -45,13 +45,15 @@ public static void main(String[] args) {
.handler(new ChannelInitializer() {
@Override
public void initChannel(SocketChannel ch) {
+ // 拆包粘包处理
ch.pipeline().addLast(new Spliter());
-// ch.pipeline().addLast(new FirstClientHandler());
- // 解码
+ // 编码
ch.pipeline().addLast(new PacketDecoder());
+ // 登录响应
ch.pipeline().addLast(new LoginResponseHandler());
+ // 消息返回
ch.pipeline().addLast(new MessageResponseHandler());
- // 编码
+ // 解码
ch.pipeline().addLast(new PacketEncoder());
}
});
@@ -80,16 +82,35 @@ private static void connect(Bootstrap bootstrap, String host, int port, int retr
}
private static void startConsoleThread(Channel channel) {
+ Scanner sc = new Scanner(System.in);
+ LoginRequestPacket loginRequestPacket = new LoginRequestPacket();
+
new Thread(() -> {
while (!Thread.interrupted()) {
- if (LoginUtil.hasLogin(channel)) {
- System.out.println("输入消息发送至服务端: ");
- Scanner sc = new Scanner(System.in);
- String line = sc.nextLine();
+ if (!SessionUtil.hasLogin(channel)) {
+ System.out.print("输入用户名登录: ");
+ String username = sc.nextLine();
+ loginRequestPacket.setUserName(username);
+
+ // 密码使用默认的
+ loginRequestPacket.setPassword("pwd");
- channel.writeAndFlush(new MessageRequestPacket(line));
+ // 发送登录数据包
+ channel.writeAndFlush(loginRequestPacket);
+ waitForLoginResponse();
+ } else {
+ String toUserId = sc.next();
+ String message = sc.next();
+ channel.writeAndFlush(new MessageRequestPacket(toUserId, message));
}
}
}).start();
}
+
+ private static void waitForLoginResponse() {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ignored) {
+ }
+ }
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/FirstClientHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/FirstClientHandler.java
deleted file mode 100644
index 6d03748..0000000
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/FirstClientHandler.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.bruis.learnnetty.im.client.handler;
-
-import com.bruis.learnnetty.im.model.MessageRequestPacket;
-import com.bruis.learnnetty.im.model.MessageResponsePacket;
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-
-import java.nio.charset.Charset;
-
-/**
- * @Description
- * @Author luohaiyang
- * @Date 2022/3/23
- */
-public class FirstClientHandler extends ChannelInboundHandlerAdapter {
- @Override
- public void channelActive(ChannelHandlerContext ctx) {
- for (int i = 0; i < 5; i++) {
-// ByteBuf buffer = getByteBuf(ctx);
- MessageRequestPacket packet = new MessageRequestPacket();
- packet.setMessage("你好啊,测试一下Netty的通讯!");
- ctx.channel().writeAndFlush(packet);
- }
- }
-
- private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
-// byte[] bytes = "你好,欢迎关注我的微信公众号,《闪电侠的博客》!".getBytes(Charset.forName("utf-8"));
- byte[] bytes = "你好".getBytes(Charset.forName("utf-8"));
- ByteBuf buffer = ctx.alloc().buffer();
- buffer.writeBytes(bytes);
-
- return buffer;
- }
-}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
index 61086f4..f519576 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
@@ -1,13 +1,12 @@
package com.bruis.learnnetty.im.client.handler;
-import com.bruis.learnnetty.im.model.LoginRequestPacket;
import com.bruis.learnnetty.im.model.LoginResponsePacket;
-import com.bruis.learnnetty.im.util.LoginUtil;
+import com.bruis.learnnetty.im.session.Session;
+import com.bruis.learnnetty.im.util.SessionUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.Date;
-import java.util.UUID;
/**
* @Description 登录响应的reponse
@@ -17,24 +16,20 @@
public class LoginResponseHandler extends SimpleChannelInboundHandler {
@Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- // 创建登录对象
- LoginRequestPacket loginRequestPacket = new LoginRequestPacket();
- loginRequestPacket.setUserId(UUID.randomUUID().toString());
- loginRequestPacket.setUserName("flash");
- loginRequestPacket.setPassword("pwd");
+ protected void channelRead0(ChannelHandlerContext ctx, LoginResponsePacket loginResponsePacket) throws Exception {
+ String userId = loginResponsePacket.getUserId();
+ String userName = loginResponsePacket.getUserName();
- // 写数据-发起登录
- ctx.channel().writeAndFlush(loginRequestPacket);
- }
-
- @Override
- protected void channelRead0(ChannelHandlerContext channelHandlerContext, LoginResponsePacket loginResponsePacket) throws Exception {
if (loginResponsePacket.isSuccess()) {
System.out.println(new Date() + ": 客户端登录成功");
- LoginUtil.markAsLogin(channelHandlerContext.channel());
+ SessionUtil.bindSession(new Session(userId, userName), ctx.channel());
} else {
System.out.println(new Date() + ": 客户端登录失败,原因:" + loginResponsePacket.getReason());
}
}
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ System.out.println("客户端连接被关闭");
+ }
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java
index 70a2cdb..ea6fac1 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java
@@ -4,8 +4,6 @@
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
-import java.util.Date;
-
/**
* @Description
* @Author luohaiyang
@@ -14,6 +12,8 @@
public class MessageResponseHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageResponsePacket messageResponsePacket) throws Exception {
- System.out.println(new Date() + ": 收到服务端的消息: " + messageResponsePacket.getMessage());
+ String fromUserId = messageResponsePacket.getFromUserId();
+ String fromUserName = messageResponsePacket.getFromUserName();
+ System.out.println(fromUserId + ":" + fromUserName + " -> " + messageResponsePacket.getMessage());
}
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/Spliter.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/Spliter.java
index 033611e..c09b096 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/Spliter.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/Spliter.java
@@ -6,7 +6,7 @@
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
/**
- * @Description
+ * @Description 拆包、粘包处理
* @Author luohaiyang
* @Date 2022/3/23
*/
@@ -20,11 +20,11 @@ public Spliter() {
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
+ // 校验协议
if (in.getInt(in.readerIndex()) != PacketCodeC.MAGIC_NUMBER) {
ctx.channel().close();
return null;
}
-
return super.decode(ctx, in);
}
}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Attributes.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Attributes.java
index b87cac3..fe72853 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Attributes.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Attributes.java
@@ -1,5 +1,6 @@
package com.bruis.learnnetty.im.model;
+import com.bruis.learnnetty.im.session.Session;
import io.netty.util.AttributeKey;
/**
@@ -8,5 +9,5 @@
* @Date 2022/3/22
*/
public interface Attributes {
- AttributeKey LOGIN = AttributeKey.newInstance("login");
+ AttributeKey SESSION = AttributeKey.newInstance("session");
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginResponsePacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginResponsePacket.java
index 1b7480f..32599fd 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginResponsePacket.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginResponsePacket.java
@@ -8,6 +8,11 @@
* @Date 2022/3/22
*/
public class LoginResponsePacket extends Packet {
+
+ private String userId;
+
+ private String userName;
+
private boolean success;
private String reason;
@@ -33,4 +38,20 @@ public String getReason() {
public void setReason(String reason) {
this.reason = reason;
}
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageRequestPacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageRequestPacket.java
index f2254d9..4dae50c 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageRequestPacket.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageRequestPacket.java
@@ -9,14 +9,17 @@
*/
public class MessageRequestPacket extends Packet {
+ private String toUserId;
+
+ private String message;
+
public MessageRequestPacket(){}
- public MessageRequestPacket(String message) {
+ public MessageRequestPacket(String toUserId, String message) {
+ this.toUserId = toUserId;
this.message = message;
}
- private String message;
-
@Override
public Byte getCommand() {
return MESSAGE_REQUEST;
@@ -29,4 +32,12 @@ public String getMessage() {
public void setMessage(String message) {
this.message = message;
}
+
+ public String getToUserId() {
+ return toUserId;
+ }
+
+ public void setToUserId(String toUserId) {
+ this.toUserId = toUserId;
+ }
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageResponsePacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageResponsePacket.java
index 3b955d8..372a33b 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageResponsePacket.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageResponsePacket.java
@@ -9,6 +9,10 @@
*/
public class MessageResponsePacket extends Packet {
+ private String fromUserId;
+
+ private String fromUserName;
+
private String message;
@Override
@@ -24,4 +28,20 @@ public String getMessage() {
public void setMessage(String message) {
this.message = message;
}
+
+ public String getFromUserId() {
+ return fromUserId;
+ }
+
+ public void setFromUserId(String fromUserId) {
+ this.fromUserId = fromUserId;
+ }
+
+ public String getFromUserName() {
+ return fromUserName;
+ }
+
+ public void setFromUserName(String fromUserName) {
+ this.fromUserName = fromUserName;
+ }
}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
index 4b08222..6ba48f8 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
@@ -3,7 +3,7 @@
import com.bruis.learnnetty.im.codec.PacketDecoder;
import com.bruis.learnnetty.im.codec.PacketEncoder;
import com.bruis.learnnetty.im.codec.Spliter;
-import com.bruis.learnnetty.im.server.handler.FirstServerHandler;
+import com.bruis.learnnetty.im.server.handler.AuthHandler;
import com.bruis.learnnetty.im.server.handler.LoginRequestHandler;
import com.bruis.learnnetty.im.server.handler.MessageRequestHandler;
import io.netty.bootstrap.ServerBootstrap;
@@ -41,6 +41,7 @@ protected void initChannel(NioSocketChannel ch) throws Exception {
// ch.pipeline().addLast(new FirstServerHandler());
ch.pipeline().addLast(new PacketDecoder());
ch.pipeline().addLast(new LoginRequestHandler());
+ ch.pipeline().addLast(new AuthHandler());
ch.pipeline().addLast(new MessageRequestHandler());
ch.pipeline().addLast(new PacketEncoder());
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/AuthHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/AuthHandler.java
new file mode 100644
index 0000000..33aad01
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/AuthHandler.java
@@ -0,0 +1,23 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class AuthHandler extends ChannelInboundHandlerAdapter {
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (!SessionUtil.hasLogin(ctx.channel())) {
+ ctx.channel().close();
+ } else {
+ ctx.pipeline().remove(this);
+ super.channelRead(ctx, msg);
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/FirstServerHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/FirstServerHandler.java
deleted file mode 100644
index d0d4679..0000000
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/FirstServerHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.bruis.learnnetty.im.server.handler;
-
-import com.bruis.learnnetty.im.model.MessageRequestPacket;
-import com.bruis.learnnetty.im.model.Packet;
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.SimpleChannelInboundHandler;
-
-import java.nio.charset.Charset;
-import java.util.Date;
-
-/**
- * @Description
- * @Author luohaiyang
- * @Date 2022/3/23
- */
-public class FirstServerHandler extends SimpleChannelInboundHandler {
-
- @Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) {
- ByteBuf byteBuf = (ByteBuf) msg;
-
- System.out.println(new Date() + ": 服务端读到数据 -> " + byteBuf.toString(Charset.forName("utf-8")));
- }
-
- @Override
- protected void channelRead0(ChannelHandlerContext ctx, MessageRequestPacket msg) throws Exception {
- System.out.println(new Date() + ": 服务端读到数据 -> " + msg.getMessage());
- }
-}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
index d54a75c..837f2d6 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
@@ -2,27 +2,34 @@
import com.bruis.learnnetty.im.model.LoginRequestPacket;
import com.bruis.learnnetty.im.model.LoginResponsePacket;
+import com.bruis.learnnetty.im.session.Session;
+import com.bruis.learnnetty.im.util.SessionUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.Date;
+import java.util.UUID;
/**
- * @Description
+ * @Description 接收客户端登录请求
* @Author luohaiyang
* @Date 2022/3/23
*/
public class LoginRequestHandler extends SimpleChannelInboundHandler {
@Override
- protected void channelRead0(ChannelHandlerContext context, LoginRequestPacket loginRequestPacket) throws Exception {
- System.out.println(new Date() + ": 收到客户端登录请求……");
-
+ protected void channelRead0(ChannelHandlerContext ctx, LoginRequestPacket loginRequestPacket) {
+ // 登录校验响应
LoginResponsePacket loginResponsePacket = new LoginResponsePacket();
loginResponsePacket.setVersion(loginRequestPacket.getVersion());
+ loginResponsePacket.setUserName(loginRequestPacket.getUserName());
+
if (valid(loginRequestPacket)) {
loginResponsePacket.setSuccess(true);
- System.out.println(new Date() + ": 登录成功!");
+ String userId = randomUserId();
+ loginResponsePacket.setUserId(userId);
+ System.out.println("[" + loginRequestPacket.getUserName() + "]登录成功");
+ SessionUtil.bindSession(new Session(userId, loginRequestPacket.getUserName()), ctx.channel());
} else {
loginResponsePacket.setReason("账号密码校验失败");
loginResponsePacket.setSuccess(false);
@@ -30,10 +37,19 @@ protected void channelRead0(ChannelHandlerContext context, LoginRequestPacket lo
}
// 登录响应
- context.channel().writeAndFlush(loginResponsePacket);
+ ctx.channel().writeAndFlush(loginResponsePacket);
}
private boolean valid(LoginRequestPacket loginRequestPacket) {
return true;
}
+
+ private static String randomUserId() {
+ return UUID.randomUUID().toString().split("-")[0];
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) {
+ SessionUtil.unBindSession(ctx.channel());
+ }
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java
index 26cd823..5943156 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java
@@ -2,11 +2,12 @@
import com.bruis.learnnetty.im.model.MessageRequestPacket;
import com.bruis.learnnetty.im.model.MessageResponsePacket;
+import com.bruis.learnnetty.im.session.Session;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
-import java.util.Date;
-
/**
* @Description
* @Author luohaiyang
@@ -16,10 +17,23 @@ public class MessageRequestHandler extends SimpleChannelInboundHandler loginAttr = channel.attr(Attributes.LOGIN);
-
- return loginAttr.get() != null;
- }
-}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/SessionUtil.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/SessionUtil.java
new file mode 100644
index 0000000..133a38b
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/SessionUtil.java
@@ -0,0 +1,44 @@
+package com.bruis.learnnetty.im.util;
+
+import com.bruis.learnnetty.im.model.Attributes;
+import com.bruis.learnnetty.im.session.Session;
+import io.netty.channel.Channel;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class SessionUtil {
+ private static final Map userIdChannelMap = new ConcurrentHashMap<>();
+
+ public static void bindSession(Session session, Channel channel) {
+ userIdChannelMap.put(session.getUserId(), channel);
+ channel.attr(Attributes.SESSION).set(session);
+ }
+
+ public static void unBindSession(Channel channel) {
+ if (hasLogin(channel)) {
+ userIdChannelMap.remove(getSession(channel).getUserId());
+ channel.attr(Attributes.SESSION).set(null);
+ }
+ }
+
+ public static boolean hasLogin(Channel channel) {
+
+ return channel.hasAttr(Attributes.SESSION);
+ }
+
+ public static Session getSession(Channel channel) {
+
+ return channel.attr(Attributes.SESSION).get();
+ }
+
+ public static Channel getChannel(String userId) {
+
+ return userIdChannelMap.get(userId);
+ }
+}
From d0c417f21b9a926c83e58358476d834b035feb2f Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Thu, 24 Mar 2022 10:46:13 +0800
Subject: [PATCH 51/63] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=BE=A4=E8=81=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../learnnetty/im/client/NettyClient.java | 33 +++++--------
.../handler/CreateGroupResponseHandler.java | 19 +++++++
.../client/handler/LoginResponseHandler.java | 6 +--
.../client/handler/LogoutResponseHandler.java | 19 +++++++
.../learnnetty/im/console/ConsoleCommand.java | 14 ++++++
.../im/console/ConsoleCommandManager.java | 39 +++++++++++++++
.../im/console/CreateGroupConsoleCommand.java | 28 +++++++++++
.../im/console/LoginConsoleCommand.java | 34 +++++++++++++
.../im/console/LogoutConsoleCommand.java | 19 +++++++
.../im/console/SendToUserConsoleCommand.java | 23 +++++++++
.../im/model/CreateGroupRequestPacket.java | 28 +++++++++++
.../im/model/CreateGroupResponsePacket.java | 48 ++++++++++++++++++
.../im/model/LogoutRequestPacket.java | 16 ++++++
.../im/model/LogoutResponsePacket.java | 37 ++++++++++++++
.../learnnetty/im/model/PacketCodeC.java | 4 ++
.../learnnetty/im/server/NettyServer.java | 7 ++-
.../handler/CreateGroupRequestHandler.java | 49 +++++++++++++++++++
.../server/handler/LoginRequestHandler.java | 7 +--
.../server/handler/LogoutRequestHandler.java | 23 +++++++++
.../com/bruis/learnnetty/im/util/IDUtil.java | 16 ++++++
20 files changed, 434 insertions(+), 35 deletions(-)
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/CreateGroupResponseHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LogoutResponseHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommand.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/CreateGroupConsoleCommand.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LoginConsoleCommand.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LogoutConsoleCommand.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/SendToUserConsoleCommand.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/CreateGroupRequestPacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/CreateGroupResponsePacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LogoutRequestPacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LogoutResponsePacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LogoutRequestHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/IDUtil.java
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
index e36c730..e195814 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
@@ -1,10 +1,14 @@
package com.bruis.learnnetty.im.client;
+import com.bruis.learnnetty.im.client.handler.CreateGroupResponseHandler;
import com.bruis.learnnetty.im.client.handler.LoginResponseHandler;
+import com.bruis.learnnetty.im.client.handler.LogoutResponseHandler;
import com.bruis.learnnetty.im.client.handler.MessageResponseHandler;
import com.bruis.learnnetty.im.codec.PacketDecoder;
import com.bruis.learnnetty.im.codec.PacketEncoder;
import com.bruis.learnnetty.im.codec.Spliter;
+import com.bruis.learnnetty.im.console.ConsoleCommandManager;
+import com.bruis.learnnetty.im.console.LoginConsoleCommand;
import com.bruis.learnnetty.im.model.LoginRequestPacket;
import com.bruis.learnnetty.im.model.MessageRequestPacket;
import com.bruis.learnnetty.im.util.SessionUtil;
@@ -51,8 +55,10 @@ public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new PacketDecoder());
// 登录响应
ch.pipeline().addLast(new LoginResponseHandler());
+ ch.pipeline().addLast(new LogoutResponseHandler());
// 消息返回
ch.pipeline().addLast(new MessageResponseHandler());
+ ch.pipeline().addLast(new CreateGroupResponseHandler());
// 解码
ch.pipeline().addLast(new PacketEncoder());
}
@@ -82,35 +88,18 @@ private static void connect(Bootstrap bootstrap, String host, int port, int retr
}
private static void startConsoleThread(Channel channel) {
- Scanner sc = new Scanner(System.in);
- LoginRequestPacket loginRequestPacket = new LoginRequestPacket();
+ ConsoleCommandManager consoleCommandManager = new ConsoleCommandManager();
+ LoginConsoleCommand loginConsoleCommand = new LoginConsoleCommand();
+ Scanner scanner = new Scanner(System.in);
new Thread(() -> {
while (!Thread.interrupted()) {
if (!SessionUtil.hasLogin(channel)) {
- System.out.print("输入用户名登录: ");
- String username = sc.nextLine();
- loginRequestPacket.setUserName(username);
-
- // 密码使用默认的
- loginRequestPacket.setPassword("pwd");
-
- // 发送登录数据包
- channel.writeAndFlush(loginRequestPacket);
- waitForLoginResponse();
+ loginConsoleCommand.exec(scanner, channel);
} else {
- String toUserId = sc.next();
- String message = sc.next();
- channel.writeAndFlush(new MessageRequestPacket(toUserId, message));
+ consoleCommandManager.exec(scanner, channel);
}
}
}).start();
}
-
- private static void waitForLoginResponse() {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException ignored) {
- }
- }
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/CreateGroupResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/CreateGroupResponseHandler.java
new file mode 100644
index 0000000..04125de
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/CreateGroupResponseHandler.java
@@ -0,0 +1,19 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.CreateGroupResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class CreateGroupResponseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, CreateGroupResponsePacket msg) throws Exception {
+ System.out.print("群创建成功,id 为[" + msg.getGroupId() + "], ");
+ System.out.println("群里面有:" + msg.getUserNameList());
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
index f519576..282afcc 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
@@ -6,8 +6,6 @@
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
-import java.util.Date;
-
/**
* @Description 登录响应的reponse
* @Author luohaiyang
@@ -21,10 +19,10 @@ protected void channelRead0(ChannelHandlerContext ctx, LoginResponsePacket login
String userName = loginResponsePacket.getUserName();
if (loginResponsePacket.isSuccess()) {
- System.out.println(new Date() + ": 客户端登录成功");
+ System.out.println("[" + userName + "]登录成功,userId 为: " + loginResponsePacket.getUserId());
SessionUtil.bindSession(new Session(userId, userName), ctx.channel());
} else {
- System.out.println(new Date() + ": 客户端登录失败,原因:" + loginResponsePacket.getReason());
+ System.out.println("[" + userName + "]登录失败,原因:" + loginResponsePacket.getReason());
}
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LogoutResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LogoutResponseHandler.java
new file mode 100644
index 0000000..78fd173
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LogoutResponseHandler.java
@@ -0,0 +1,19 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.LogoutResponsePacket;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class LogoutResponseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, LogoutResponsePacket logoutResponsePacket) {
+ SessionUtil.unBindSession(ctx.channel());
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommand.java
new file mode 100644
index 0000000..dd41e27
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommand.java
@@ -0,0 +1,14 @@
+package com.bruis.learnnetty.im.console;
+
+import io.netty.channel.Channel;
+
+import java.util.Scanner;
+
+/**
+ * @Description 指令接口
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public interface ConsoleCommand {
+ void exec(Scanner scanner, Channel channel);
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java
new file mode 100644
index 0000000..07a1966
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java
@@ -0,0 +1,39 @@
+package com.bruis.learnnetty.im.console;
+
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.Channel;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class ConsoleCommandManager implements ConsoleCommand {
+
+ private Map consoleCommandMap;
+
+ public ConsoleCommandManager() {
+ consoleCommandMap = new HashMap<>();
+ consoleCommandMap.put("sendToUser", new SendToUserConsoleCommand());
+ consoleCommandMap.put("logout", new LoginConsoleCommand());
+ consoleCommandMap.put("createGroup", new CreateGroupConsoleCommand());
+ }
+
+ @Override
+ public void exec(Scanner scanner, Channel channel) {
+ String command = scanner.next();
+ if (!SessionUtil.hasLogin(channel)) {
+ return;
+ }
+ ConsoleCommand consoleCommand = consoleCommandMap.get(command);
+ if (null != consoleCommand) {
+ consoleCommand.exec(scanner, channel);
+ } else {
+ System.err.println("无法识别[" + command + "]指令,请重新输入");
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/CreateGroupConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/CreateGroupConsoleCommand.java
new file mode 100644
index 0000000..db68b98
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/CreateGroupConsoleCommand.java
@@ -0,0 +1,28 @@
+package com.bruis.learnnetty.im.console;
+
+import com.bruis.learnnetty.im.model.CreateGroupRequestPacket;
+import io.netty.channel.Channel;
+
+import java.util.Arrays;
+import java.util.Scanner;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class CreateGroupConsoleCommand implements ConsoleCommand {
+
+ private static final String USER_ID_SPLITER = ",";
+
+ @Override
+ public void exec(Scanner scanner, Channel channel) {
+ CreateGroupRequestPacket createGroupRequestPacket = new CreateGroupRequestPacket();
+
+ System.out.print("【拉人群聊】输入 userId 列表,userId 之间英文逗号隔开:");
+ String userIds = scanner.next();
+ createGroupRequestPacket.setUserIdList(Arrays.asList(userIds.split(USER_ID_SPLITER)));
+ channel.writeAndFlush(createGroupRequestPacket);
+ }
+
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LoginConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LoginConsoleCommand.java
new file mode 100644
index 0000000..105cf58
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LoginConsoleCommand.java
@@ -0,0 +1,34 @@
+package com.bruis.learnnetty.im.console;
+
+import com.bruis.learnnetty.im.model.LoginRequestPacket;
+import io.netty.channel.Channel;
+
+import java.util.Scanner;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class LoginConsoleCommand implements ConsoleCommand {
+
+ @Override
+ public void exec(Scanner scanner, Channel channel) {
+ LoginRequestPacket loginRequestPacket = new LoginRequestPacket();
+
+ System.out.print("输入用户名登录: ");
+ loginRequestPacket.setUserName(scanner.nextLine());
+ loginRequestPacket.setPassword("pwd");
+
+ // 发送登录数据包
+ channel.writeAndFlush(loginRequestPacket);
+ waitForLoginResponse();
+ }
+
+ private static void waitForLoginResponse() {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ignored) {
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LogoutConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LogoutConsoleCommand.java
new file mode 100644
index 0000000..2d13370
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LogoutConsoleCommand.java
@@ -0,0 +1,19 @@
+package com.bruis.learnnetty.im.console;
+
+import com.bruis.learnnetty.im.model.LogoutRequestPacket;
+import io.netty.channel.Channel;
+
+import java.util.Scanner;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class LogoutConsoleCommand implements ConsoleCommand {
+ @Override
+ public void exec(Scanner scanner, Channel channel) {
+ LogoutRequestPacket logoutRequestPacket = new LogoutRequestPacket();
+ channel.writeAndFlush(logoutRequestPacket);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/SendToUserConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/SendToUserConsoleCommand.java
new file mode 100644
index 0000000..9ead2b5
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/SendToUserConsoleCommand.java
@@ -0,0 +1,23 @@
+package com.bruis.learnnetty.im.console;
+
+import com.bruis.learnnetty.im.model.MessageRequestPacket;
+import io.netty.channel.Channel;
+
+import java.util.Scanner;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class SendToUserConsoleCommand implements ConsoleCommand {
+
+ @Override
+ public void exec(Scanner scanner, Channel channel) {
+ System.out.print("发送消息给某个某个用户:");
+
+ String toUserId = scanner.next();
+ String message = scanner.next();
+ channel.writeAndFlush(new MessageRequestPacket(toUserId, message));
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/CreateGroupRequestPacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/CreateGroupRequestPacket.java
new file mode 100644
index 0000000..dc9ffc5
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/CreateGroupRequestPacket.java
@@ -0,0 +1,28 @@
+package com.bruis.learnnetty.im.model;
+
+import java.util.List;
+
+import static com.bruis.learnnetty.im.model.Command.CREATE_GROUP_REQUEST;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class CreateGroupRequestPacket extends Packet {
+
+ private List userIdList;
+
+ @Override
+ public Byte getCommand() {
+ return CREATE_GROUP_REQUEST;
+ }
+
+ public List getUserIdList() {
+ return userIdList;
+ }
+
+ public void setUserIdList(List userIdList) {
+ this.userIdList = userIdList;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/CreateGroupResponsePacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/CreateGroupResponsePacket.java
new file mode 100644
index 0000000..6209205
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/CreateGroupResponsePacket.java
@@ -0,0 +1,48 @@
+package com.bruis.learnnetty.im.model;
+
+import java.util.List;
+
+import static com.bruis.learnnetty.im.model.Command.CREATE_GROUP_RESPONSE;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class CreateGroupResponsePacket extends Packet {
+ private boolean success;
+
+ private String groupId;
+
+ private List userNameList;
+
+ @Override
+ public Byte getCommand() {
+
+ return CREATE_GROUP_RESPONSE;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+
+ public List getUserNameList() {
+ return userNameList;
+ }
+
+ public void setUserNameList(List userNameList) {
+ this.userNameList = userNameList;
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LogoutRequestPacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LogoutRequestPacket.java
new file mode 100644
index 0000000..c66dc68
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LogoutRequestPacket.java
@@ -0,0 +1,16 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.LOGOUT_REQUEST;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class LogoutRequestPacket extends Packet {
+ @Override
+ public Byte getCommand() {
+
+ return LOGOUT_REQUEST;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LogoutResponsePacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LogoutResponsePacket.java
new file mode 100644
index 0000000..73d2a71
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LogoutResponsePacket.java
@@ -0,0 +1,37 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.LOGOUT_RESPONSE;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class LogoutResponsePacket extends Packet {
+
+ private boolean success;
+
+ private String reason;
+
+
+ @Override
+ public Byte getCommand() {
+ return LOGOUT_RESPONSE;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java
index 7e48ff1..c5b780b 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java
@@ -28,6 +28,10 @@ private PacketCodeC() {
packetTypeMap.put(LOGIN_RESPONSE, LoginResponsePacket.class);
packetTypeMap.put(MESSAGE_REQUEST, MessageRequestPacket.class);
packetTypeMap.put(MESSAGE_RESPONSE, MessageResponsePacket.class);
+ packetTypeMap.put(LOGOUT_REQUEST, LogoutRequestPacket.class);
+ packetTypeMap.put(LOGOUT_RESPONSE, LogoutResponsePacket.class);
+ packetTypeMap.put(CREATE_GROUP_REQUEST, CreateGroupRequestPacket.class);
+ packetTypeMap.put(CREATE_GROUP_RESPONSE, CreateGroupResponsePacket.class);
serializerMap = new HashMap<>();
Serializer serializer = new JSONSerializer();
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
index 6ba48f8..e04514c 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
@@ -3,9 +3,7 @@
import com.bruis.learnnetty.im.codec.PacketDecoder;
import com.bruis.learnnetty.im.codec.PacketEncoder;
import com.bruis.learnnetty.im.codec.Spliter;
-import com.bruis.learnnetty.im.server.handler.AuthHandler;
-import com.bruis.learnnetty.im.server.handler.LoginRequestHandler;
-import com.bruis.learnnetty.im.server.handler.MessageRequestHandler;
+import com.bruis.learnnetty.im.server.handler.*;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
@@ -38,11 +36,12 @@ public static void main(String[] args) {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new Spliter());
-// ch.pipeline().addLast(new FirstServerHandler());
ch.pipeline().addLast(new PacketDecoder());
ch.pipeline().addLast(new LoginRequestHandler());
ch.pipeline().addLast(new AuthHandler());
ch.pipeline().addLast(new MessageRequestHandler());
+ ch.pipeline().addLast(new CreateGroupRequestHandler());
+ ch.pipeline().addLast(new LogoutRequestHandler());
ch.pipeline().addLast(new PacketEncoder());
}
});
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java
new file mode 100644
index 0000000..3692392
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java
@@ -0,0 +1,49 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import com.bruis.learnnetty.im.model.CreateGroupRequestPacket;
+import com.bruis.learnnetty.im.model.CreateGroupResponsePacket;
+import com.bruis.learnnetty.im.util.IDUtil;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import org.omg.PortableServer.ID_UNIQUENESS_POLICY_ID;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class CreateGroupRequestHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, CreateGroupRequestPacket msg) throws Exception {
+ List userIdList = msg.getUserIdList();
+ List userNameList = new ArrayList<>();
+
+ ChannelGroup channelGroup = new DefaultChannelGroup(ctx.executor());
+
+ for (String userId : userIdList) {
+ Channel channel = SessionUtil.getChannel(userId);
+ if (null != channel) {
+ channelGroup.add(channel);
+ userNameList.add(SessionUtil.getSession(channel).getUserName());
+ }
+ }
+
+ CreateGroupResponsePacket createGroupResponsePacket = new CreateGroupResponsePacket();
+ createGroupResponsePacket.setSuccess(true);
+ createGroupResponsePacket.setGroupId(IDUtil.randomUserId());
+ createGroupResponsePacket.setUserNameList(userNameList);
+
+ channelGroup.writeAndFlush(createGroupResponsePacket);
+
+ System.out.print("群创建成功,id 为[" + createGroupResponsePacket.getGroupId() + "], ");
+ System.out.println("群里面有:" + createGroupResponsePacket.getUserNameList());
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
index 837f2d6..6e223fd 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
@@ -3,6 +3,7 @@
import com.bruis.learnnetty.im.model.LoginRequestPacket;
import com.bruis.learnnetty.im.model.LoginResponsePacket;
import com.bruis.learnnetty.im.session.Session;
+import com.bruis.learnnetty.im.util.IDUtil;
import com.bruis.learnnetty.im.util.SessionUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
@@ -26,7 +27,7 @@ protected void channelRead0(ChannelHandlerContext ctx, LoginRequestPacket loginR
if (valid(loginRequestPacket)) {
loginResponsePacket.setSuccess(true);
- String userId = randomUserId();
+ String userId = IDUtil.randomUserId();
loginResponsePacket.setUserId(userId);
System.out.println("[" + loginRequestPacket.getUserName() + "]登录成功");
SessionUtil.bindSession(new Session(userId, loginRequestPacket.getUserName()), ctx.channel());
@@ -44,10 +45,6 @@ private boolean valid(LoginRequestPacket loginRequestPacket) {
return true;
}
- private static String randomUserId() {
- return UUID.randomUUID().toString().split("-")[0];
- }
-
@Override
public void channelInactive(ChannelHandlerContext ctx) {
SessionUtil.unBindSession(ctx.channel());
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LogoutRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LogoutRequestHandler.java
new file mode 100644
index 0000000..8ee3152
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LogoutRequestHandler.java
@@ -0,0 +1,23 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import com.bruis.learnnetty.im.model.LogoutRequestPacket;
+import com.bruis.learnnetty.im.model.LogoutResponsePacket;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class LogoutRequestHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, LogoutRequestPacket msg) {
+ SessionUtil.unBindSession(ctx.channel());
+ LogoutResponsePacket logoutResponsePacket = new LogoutResponsePacket();
+ logoutResponsePacket.setSuccess(true);
+ ctx.channel().writeAndFlush(logoutResponsePacket);
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/IDUtil.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/IDUtil.java
new file mode 100644
index 0000000..3b5403f
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/IDUtil.java
@@ -0,0 +1,16 @@
+package com.bruis.learnnetty.im.util;
+
+import java.util.UUID;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class IDUtil {
+
+ public static String randomUserId() {
+ return UUID.randomUUID().toString().split("-")[0];
+ }
+
+}
From 73e7dd2adbfcd7f488c52d9500e8d020b8ab98a1 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Fri, 25 Mar 2022 10:04:47 +0800
Subject: [PATCH 52/63] =?UTF-8?q?=E7=BE=A4=E8=81=8A=E6=B6=88=E6=81=AF?=
=?UTF-8?q?=E7=9A=84=E6=94=B6=E5=8F=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../learnnetty/im/client/NettyClient.java | 11 +++--
.../handler/GroupMessageResponseHandler.java | 20 ++++++++
.../handler/JoinGroupResponseHandler.java | 21 ++++++++
.../ListGroupMembersResponseHandler.java | 18 +++++++
.../handler/QuitGroupResponseHandler.java | 22 +++++++++
.../im/console/ConsoleCommandManager.java | 4 ++
.../im/console/JoinGroupConsoleCommand.java | 23 +++++++++
.../ListGroupMembersConsoleCommand.java | 25 ++++++++++
.../im/console/QuitGroupConsoleCommand.java | 25 ++++++++++
.../im/console/SendToGroupConsoleCommand.java | 24 +++++++++
.../im/model/GroupMessageRequestPacket.java | 39 +++++++++++++++
.../im/model/GroupMessageResponsePacket.java | 49 +++++++++++++++++++
.../im/model/JoinGroupRequestPacket.java | 26 ++++++++++
.../im/model/JoinGroupResponsePacket.java | 46 +++++++++++++++++
.../model/ListGroupMembersRequestPacket.java | 27 ++++++++++
.../model/ListGroupMembersResponsePacket.java | 41 ++++++++++++++++
.../learnnetty/im/model/PacketCodeC.java | 12 ++++-
.../im/model/QuitGroupRequestPacket.java | 26 ++++++++++
.../im/model/QuitGroupResponsePacket.java | 47 ++++++++++++++++++
.../learnnetty/im/server/NettyServer.java | 4 ++
.../handler/CreateGroupRequestHandler.java | 5 +-
.../handler/GroupMessageRequestHandler.java | 34 +++++++++++++
.../handler/JoinGroupRequestHandler.java | 33 +++++++++++++
.../ListGroupMembersRequestHandler.java | 41 ++++++++++++++++
.../handler/QuitGroupRequestHandler.java | 31 ++++++++++++
.../bruis/learnnetty/im/session/Session.java | 5 ++
.../bruis/learnnetty/im/util/SessionUtil.java | 16 +++++-
27 files changed, 666 insertions(+), 9 deletions(-)
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/GroupMessageResponseHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/JoinGroupResponseHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/ListGroupMembersResponseHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/QuitGroupResponseHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/JoinGroupConsoleCommand.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ListGroupMembersConsoleCommand.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/QuitGroupConsoleCommand.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/SendToGroupConsoleCommand.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/GroupMessageRequestPacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/GroupMessageResponsePacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/JoinGroupRequestPacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/JoinGroupResponsePacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/ListGroupMembersRequestPacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/ListGroupMembersResponsePacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/QuitGroupRequestPacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/QuitGroupResponsePacket.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/GroupMessageRequestHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/JoinGroupRequestHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/ListGroupMembersRequestHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/QuitGroupRequestHandler.java
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
index e195814..4a701b4 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
@@ -1,9 +1,6 @@
package com.bruis.learnnetty.im.client;
-import com.bruis.learnnetty.im.client.handler.CreateGroupResponseHandler;
-import com.bruis.learnnetty.im.client.handler.LoginResponseHandler;
-import com.bruis.learnnetty.im.client.handler.LogoutResponseHandler;
-import com.bruis.learnnetty.im.client.handler.MessageResponseHandler;
+import com.bruis.learnnetty.im.client.handler.*;
import com.bruis.learnnetty.im.codec.PacketDecoder;
import com.bruis.learnnetty.im.codec.PacketEncoder;
import com.bruis.learnnetty.im.codec.Spliter;
@@ -55,10 +52,14 @@ public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new PacketDecoder());
// 登录响应
ch.pipeline().addLast(new LoginResponseHandler());
- ch.pipeline().addLast(new LogoutResponseHandler());
// 消息返回
ch.pipeline().addLast(new MessageResponseHandler());
ch.pipeline().addLast(new CreateGroupResponseHandler());
+ ch.pipeline().addLast(new JoinGroupResponseHandler());
+ ch.pipeline().addLast(new QuitGroupResponseHandler());
+ ch.pipeline().addLast(new ListGroupMembersResponseHandler());
+ ch.pipeline().addLast(new GroupMessageResponseHandler());
+ ch.pipeline().addLast(new LogoutResponseHandler());
// 解码
ch.pipeline().addLast(new PacketEncoder());
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/GroupMessageResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/GroupMessageResponseHandler.java
new file mode 100644
index 0000000..3dc9921
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/GroupMessageResponseHandler.java
@@ -0,0 +1,20 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.GroupMessageResponsePacket;
+import com.bruis.learnnetty.im.session.Session;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class GroupMessageResponseHandler extends SimpleChannelInboundHandler {
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, GroupMessageResponsePacket responsePacket) {
+ String fromGroupId = responsePacket.getFromGroupId();
+ Session fromUser = responsePacket.getFromUser();
+ System.out.println("收到群[" + fromGroupId + "]中[" + fromUser + "]发来的消息:" + responsePacket.getMessage());
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/JoinGroupResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/JoinGroupResponseHandler.java
new file mode 100644
index 0000000..cc7efea
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/JoinGroupResponseHandler.java
@@ -0,0 +1,21 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.JoinGroupResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class JoinGroupResponseHandler extends SimpleChannelInboundHandler {
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, JoinGroupResponsePacket responsePacket) throws Exception {
+ if (responsePacket.isSuccess()) {
+ System.out.println("加入群[" + responsePacket.getGroupId() + "]成功!");
+ } else {
+ System.err.println("加入群[" + responsePacket.getGroupId() + "]失败,原因为:" + responsePacket.getReason());
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/ListGroupMembersResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/ListGroupMembersResponseHandler.java
new file mode 100644
index 0000000..0117eac
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/ListGroupMembersResponseHandler.java
@@ -0,0 +1,18 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.ListGroupMembersResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class ListGroupMembersResponseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, ListGroupMembersResponsePacket responsePacket) {
+ System.out.println("群[" + responsePacket.getGroupId() + "]中的人包括:" + responsePacket.getSessionList());
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/QuitGroupResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/QuitGroupResponseHandler.java
new file mode 100644
index 0000000..be82bd5
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/QuitGroupResponseHandler.java
@@ -0,0 +1,22 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.QuitGroupResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class QuitGroupResponseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, QuitGroupResponsePacket responsePacket) throws Exception {
+ if (responsePacket.isSuccess()) {
+ System.out.println("退出群聊[" + responsePacket.getGroupId() + "]成功!");
+ } else {
+ System.out.println("退出群聊[" + responsePacket.getGroupId() + "]失败!");
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java
index 07a1966..d4759d0 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java
@@ -21,6 +21,10 @@ public ConsoleCommandManager() {
consoleCommandMap.put("sendToUser", new SendToUserConsoleCommand());
consoleCommandMap.put("logout", new LoginConsoleCommand());
consoleCommandMap.put("createGroup", new CreateGroupConsoleCommand());
+ consoleCommandMap.put("joinGroup", new JoinGroupConsoleCommand());
+ consoleCommandMap.put("quitGroup", new QuitGroupConsoleCommand());
+ consoleCommandMap.put("listGroup", new ListGroupMembersConsoleCommand());
+ consoleCommandMap.put("sendToGroup", new SendToGroupConsoleCommand());
}
@Override
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/JoinGroupConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/JoinGroupConsoleCommand.java
new file mode 100644
index 0000000..c3ff3f4
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/JoinGroupConsoleCommand.java
@@ -0,0 +1,23 @@
+package com.bruis.learnnetty.im.console;
+
+import com.bruis.learnnetty.im.model.JoinGroupRequestPacket;
+import io.netty.channel.Channel;
+
+import java.util.Scanner;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class JoinGroupConsoleCommand implements ConsoleCommand{
+
+ @Override
+ public void exec(Scanner scanner, Channel channel) {
+ JoinGroupRequestPacket requestPacket = new JoinGroupRequestPacket();
+ System.out.println("输入groupId, 加入群聊:");
+ String groupId = scanner.next();
+ requestPacket.setGroupId(groupId);
+ channel.writeAndFlush(requestPacket);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ListGroupMembersConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ListGroupMembersConsoleCommand.java
new file mode 100644
index 0000000..87e79c4
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ListGroupMembersConsoleCommand.java
@@ -0,0 +1,25 @@
+package com.bruis.learnnetty.im.console;
+
+import com.bruis.learnnetty.im.model.ListGroupMembersRequestPacket;
+import io.netty.channel.Channel;
+
+import java.util.Scanner;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class ListGroupMembersConsoleCommand implements ConsoleCommand {
+
+ @Override
+ public void exec(Scanner scanner, Channel channel) {
+ ListGroupMembersRequestPacket listGroupMembersRequestPacket = new ListGroupMembersRequestPacket();
+
+ System.out.print("输入 groupId,获取群成员列表:");
+ String groupId = scanner.next();
+
+ listGroupMembersRequestPacket.setGroupId(groupId);
+ channel.writeAndFlush(listGroupMembersRequestPacket);
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/QuitGroupConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/QuitGroupConsoleCommand.java
new file mode 100644
index 0000000..4b4b284
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/QuitGroupConsoleCommand.java
@@ -0,0 +1,25 @@
+package com.bruis.learnnetty.im.console;
+
+import com.bruis.learnnetty.im.model.QuitGroupRequestPacket;
+import io.netty.channel.Channel;
+
+import java.util.Scanner;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class QuitGroupConsoleCommand implements ConsoleCommand {
+
+ @Override
+ public void exec(Scanner scanner, Channel channel) {
+ QuitGroupRequestPacket quitGroupRequestPacket = new QuitGroupRequestPacket();
+
+ System.out.print("输入 groupId,退出群聊:");
+ String groupId = scanner.next();
+
+ quitGroupRequestPacket.setGroupId(groupId);
+ channel.writeAndFlush(quitGroupRequestPacket);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/SendToGroupConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/SendToGroupConsoleCommand.java
new file mode 100644
index 0000000..90f8f90
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/SendToGroupConsoleCommand.java
@@ -0,0 +1,24 @@
+package com.bruis.learnnetty.im.console;
+
+import com.bruis.learnnetty.im.model.GroupMessageRequestPacket;
+import io.netty.channel.Channel;
+
+import java.util.Scanner;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class SendToGroupConsoleCommand implements ConsoleCommand {
+
+ @Override
+ public void exec(Scanner scanner, Channel channel) {
+ System.out.print("发送消息给某个某个群组:");
+
+ String toGroupId = scanner.next();
+ String message = scanner.next();
+ channel.writeAndFlush(new GroupMessageRequestPacket(toGroupId, message));
+
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/GroupMessageRequestPacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/GroupMessageRequestPacket.java
new file mode 100644
index 0000000..3a6f812
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/GroupMessageRequestPacket.java
@@ -0,0 +1,39 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.GROUP_MESSAGE_REQUEST;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class GroupMessageRequestPacket extends Packet {
+ private String toGroupId;
+ private String message;
+
+ public GroupMessageRequestPacket(String toGroupId, String message) {
+ this.toGroupId = toGroupId;
+ this.message = message;
+ }
+
+ @Override
+ public Byte getCommand() {
+ return GROUP_MESSAGE_REQUEST;
+ }
+
+ public String getToGroupId() {
+ return toGroupId;
+ }
+
+ public void setToGroupId(String toGroupId) {
+ this.toGroupId = toGroupId;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/GroupMessageResponsePacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/GroupMessageResponsePacket.java
new file mode 100644
index 0000000..986333b
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/GroupMessageResponsePacket.java
@@ -0,0 +1,49 @@
+package com.bruis.learnnetty.im.model;
+
+import com.bruis.learnnetty.im.session.Session;
+
+import static com.bruis.learnnetty.im.model.Command.GROUP_MESSAGE_RESPONSE;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class GroupMessageResponsePacket extends Packet {
+
+ private String fromGroupId;
+
+ private Session fromUser;
+
+ private String message;
+
+ @Override
+ public Byte getCommand() {
+
+ return GROUP_MESSAGE_RESPONSE;
+ }
+
+ public String getFromGroupId() {
+ return fromGroupId;
+ }
+
+ public void setFromGroupId(String fromGroupId) {
+ this.fromGroupId = fromGroupId;
+ }
+
+ public Session getFromUser() {
+ return fromUser;
+ }
+
+ public void setFromUser(Session fromUser) {
+ this.fromUser = fromUser;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/JoinGroupRequestPacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/JoinGroupRequestPacket.java
new file mode 100644
index 0000000..26fb73d
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/JoinGroupRequestPacket.java
@@ -0,0 +1,26 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.JOIN_GROUP_REQUEST;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class JoinGroupRequestPacket extends Packet {
+
+ private String groupId;
+
+ @Override
+ public Byte getCommand() {
+ return JOIN_GROUP_REQUEST;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/JoinGroupResponsePacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/JoinGroupResponsePacket.java
new file mode 100644
index 0000000..dce9a1d
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/JoinGroupResponsePacket.java
@@ -0,0 +1,46 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.JOIN_GROUP_RESPONSE;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class JoinGroupResponsePacket extends Packet {
+
+ private String groupId;
+
+ private boolean success;
+
+ private String reason;
+
+ @Override
+ public Byte getCommand() {
+ return JOIN_GROUP_RESPONSE;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/ListGroupMembersRequestPacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/ListGroupMembersRequestPacket.java
new file mode 100644
index 0000000..886b19c
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/ListGroupMembersRequestPacket.java
@@ -0,0 +1,27 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.LIST_GROUP_MEMBERS_REQUEST;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class ListGroupMembersRequestPacket extends Packet {
+
+ private String groupId;
+
+ @Override
+ public Byte getCommand() {
+
+ return LIST_GROUP_MEMBERS_REQUEST;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/ListGroupMembersResponsePacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/ListGroupMembersResponsePacket.java
new file mode 100644
index 0000000..dbc174e
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/ListGroupMembersResponsePacket.java
@@ -0,0 +1,41 @@
+package com.bruis.learnnetty.im.model;
+
+import com.bruis.learnnetty.im.session.Session;
+
+import java.util.List;
+
+import static com.bruis.learnnetty.im.model.Command.LIST_GROUP_MEMBERS_RESPONSE;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class ListGroupMembersResponsePacket extends Packet {
+
+ private String groupId;
+
+ private List sessionList;
+
+ @Override
+ public Byte getCommand() {
+
+ return LIST_GROUP_MEMBERS_RESPONSE;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+
+ public List getSessionList() {
+ return sessionList;
+ }
+
+ public void setSessionList(List sessionList) {
+ this.sessionList = sessionList;
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java
index c5b780b..7a81483 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java
@@ -3,9 +3,9 @@
import com.bruis.learnnetty.im.serialize.Serializer;
import com.bruis.learnnetty.im.serialize.impl.JSONSerializer;
import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufAllocator;
-import java.util.*;
+import java.util.HashMap;
+import java.util.Map;
import static com.bruis.learnnetty.im.model.Command.*;
@@ -32,6 +32,14 @@ private PacketCodeC() {
packetTypeMap.put(LOGOUT_RESPONSE, LogoutResponsePacket.class);
packetTypeMap.put(CREATE_GROUP_REQUEST, CreateGroupRequestPacket.class);
packetTypeMap.put(CREATE_GROUP_RESPONSE, CreateGroupResponsePacket.class);
+ packetTypeMap.put(JOIN_GROUP_REQUEST, JoinGroupRequestPacket.class);
+ packetTypeMap.put(JOIN_GROUP_RESPONSE, JoinGroupResponsePacket.class);
+ packetTypeMap.put(QUIT_GROUP_REQUEST, QuitGroupRequestPacket.class);
+ packetTypeMap.put(QUIT_GROUP_RESPONSE, QuitGroupResponsePacket.class);
+ packetTypeMap.put(LIST_GROUP_MEMBERS_REQUEST, ListGroupMembersRequestPacket.class);
+ packetTypeMap.put(LIST_GROUP_MEMBERS_RESPONSE, ListGroupMembersResponsePacket.class);
+ packetTypeMap.put(GROUP_MESSAGE_REQUEST, GroupMessageRequestPacket.class);
+ packetTypeMap.put(GROUP_MESSAGE_RESPONSE, GroupMessageResponsePacket.class);
serializerMap = new HashMap<>();
Serializer serializer = new JSONSerializer();
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/QuitGroupRequestPacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/QuitGroupRequestPacket.java
new file mode 100644
index 0000000..ca5342f
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/QuitGroupRequestPacket.java
@@ -0,0 +1,26 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.QUIT_GROUP_REQUEST;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class QuitGroupRequestPacket extends Packet {
+
+ private String groupId;
+
+ @Override
+ public Byte getCommand() {
+ return QUIT_GROUP_REQUEST;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/QuitGroupResponsePacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/QuitGroupResponsePacket.java
new file mode 100644
index 0000000..99529c7
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/QuitGroupResponsePacket.java
@@ -0,0 +1,47 @@
+package com.bruis.learnnetty.im.model;
+
+import static com.bruis.learnnetty.im.model.Command.QUIT_GROUP_REQUEST;
+import static com.bruis.learnnetty.im.model.Command.QUIT_GROUP_RESPONSE;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class QuitGroupResponsePacket extends Packet {
+
+ private String groupId;
+
+ private boolean success;
+
+ private String reason;
+
+ @Override
+ public Byte getCommand() {
+ return QUIT_GROUP_RESPONSE;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
index e04514c..80741f8 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
@@ -41,6 +41,10 @@ protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new AuthHandler());
ch.pipeline().addLast(new MessageRequestHandler());
ch.pipeline().addLast(new CreateGroupRequestHandler());
+ ch.pipeline().addLast(new JoinGroupRequestHandler());
+ ch.pipeline().addLast(new QuitGroupRequestHandler());
+ ch.pipeline().addLast(new ListGroupMembersRequestHandler());
+ ch.pipeline().addLast(new GroupMessageRequestHandler());
ch.pipeline().addLast(new LogoutRequestHandler());
ch.pipeline().addLast(new PacketEncoder());
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java
index 3692392..45a951d 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java
@@ -36,14 +36,17 @@ protected void channelRead0(ChannelHandlerContext ctx, CreateGroupRequestPacket
}
}
+ String groupId = IDUtil.randomUserId();
CreateGroupResponsePacket createGroupResponsePacket = new CreateGroupResponsePacket();
createGroupResponsePacket.setSuccess(true);
- createGroupResponsePacket.setGroupId(IDUtil.randomUserId());
+ createGroupResponsePacket.setGroupId(groupId);
createGroupResponsePacket.setUserNameList(userNameList);
channelGroup.writeAndFlush(createGroupResponsePacket);
System.out.print("群创建成功,id 为[" + createGroupResponsePacket.getGroupId() + "], ");
System.out.println("群里面有:" + createGroupResponsePacket.getUserNameList());
+
+ SessionUtil.bindChannelGroup(groupId, channelGroup);
}
}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/GroupMessageRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/GroupMessageRequestHandler.java
new file mode 100644
index 0000000..5507f45
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/GroupMessageRequestHandler.java
@@ -0,0 +1,34 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import com.bruis.learnnetty.im.model.GroupMessageRequestPacket;
+import com.bruis.learnnetty.im.model.GroupMessageResponsePacket;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.group.ChannelGroup;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+@ChannelHandler.Sharable
+public class GroupMessageRequestHandler extends SimpleChannelInboundHandler {
+
+ public static final GroupMessageRequestHandler INSTANCE = new GroupMessageRequestHandler();
+
+ public GroupMessageRequestHandler() {}
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, GroupMessageRequestPacket msg) throws Exception {
+ String toGroupId = msg.getToGroupId();
+ GroupMessageResponsePacket responsePacket = new GroupMessageResponsePacket();
+ responsePacket.setFromGroupId(toGroupId);
+ responsePacket.setMessage(msg.getMessage());
+ responsePacket.setFromUser(SessionUtil.getSession(ctx.channel()));
+
+ ChannelGroup channelGroup = SessionUtil.getChannelGroup(toGroupId);
+ channelGroup.writeAndFlush(responsePacket);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/JoinGroupRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/JoinGroupRequestHandler.java
new file mode 100644
index 0000000..1cf19d2
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/JoinGroupRequestHandler.java
@@ -0,0 +1,33 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import com.bruis.learnnetty.im.model.JoinGroupRequestPacket;
+import com.bruis.learnnetty.im.model.JoinGroupResponsePacket;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.group.ChannelGroup;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class JoinGroupRequestHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, JoinGroupRequestPacket msg) throws Exception {
+ // 目标群聊id
+ String groupId = msg.getGroupId();
+ ChannelGroup channelGroup = SessionUtil.getChannelGroup(groupId);
+ JoinGroupResponsePacket responsePacket = new JoinGroupResponsePacket();
+ responsePacket.setSuccess(true);
+ responsePacket.setGroupId(groupId);
+ if (null == channelGroup) {
+ responsePacket.setSuccess(false);
+ responsePacket.setReason("没有该群聊,请重试...");
+ } else {
+ channelGroup.add(ctx.channel());
+ }
+ ctx.channel().writeAndFlush(responsePacket);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/ListGroupMembersRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/ListGroupMembersRequestHandler.java
new file mode 100644
index 0000000..5418a4e
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/ListGroupMembersRequestHandler.java
@@ -0,0 +1,41 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import com.bruis.learnnetty.im.model.ListGroupMembersRequestPacket;
+import com.bruis.learnnetty.im.model.ListGroupMembersResponsePacket;
+import com.bruis.learnnetty.im.session.Session;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.group.ChannelGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class ListGroupMembersRequestHandler extends SimpleChannelInboundHandler {
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, ListGroupMembersRequestPacket requestPacket) {
+ // 1. 获取群的 ChannelGroup
+ String groupId = requestPacket.getGroupId();
+ ChannelGroup channelGroup = SessionUtil.getChannelGroup(groupId);
+
+ // 2. 遍历群成员的 channel,对应的 session,构造群成员的信息
+ List sessionList = new ArrayList<>();
+ for (Channel channel : channelGroup) {
+ Session session = SessionUtil.getSession(channel);
+ sessionList.add(session);
+ }
+
+ // 3. 构建获取成员列表响应写回到客户端
+ ListGroupMembersResponsePacket responsePacket = new ListGroupMembersResponsePacket();
+
+ responsePacket.setGroupId(groupId);
+ responsePacket.setSessionList(sessionList);
+ ctx.channel().writeAndFlush(responsePacket);
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/QuitGroupRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/QuitGroupRequestHandler.java
new file mode 100644
index 0000000..81dba9a
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/QuitGroupRequestHandler.java
@@ -0,0 +1,31 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import com.bruis.learnnetty.im.model.QuitGroupRequestPacket;
+import com.bruis.learnnetty.im.model.QuitGroupResponsePacket;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.group.ChannelGroup;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class QuitGroupRequestHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, QuitGroupRequestPacket msg) throws Exception {
+ String groupId = msg.getGroupId();
+ Channel channel = ctx.channel();
+ ChannelGroup channelGroup = SessionUtil.getChannelGroup(groupId);
+ channelGroup.remove(channel);
+
+ QuitGroupResponsePacket responsePacket = new QuitGroupResponsePacket();
+ responsePacket.setSuccess(true);
+ responsePacket.setGroupId(groupId);
+
+ channel.writeAndFlush(responsePacket);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/session/Session.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/session/Session.java
index 9779a96..7a7be2d 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/session/Session.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/session/Session.java
@@ -31,4 +31,9 @@ public String getUserName() {
public void setUserName(String userName) {
this.userName = userName;
}
+
+ @Override
+ public String toString() {
+ return userId + "->" + userName;
+ }
}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/SessionUtil.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/SessionUtil.java
index 133a38b..f4b41c3 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/SessionUtil.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/SessionUtil.java
@@ -3,6 +3,7 @@
import com.bruis.learnnetty.im.model.Attributes;
import com.bruis.learnnetty.im.session.Session;
import io.netty.channel.Channel;
+import io.netty.channel.group.ChannelGroup;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -13,8 +14,11 @@
* @Date 2022/3/23
*/
public class SessionUtil {
+
private static final Map userIdChannelMap = new ConcurrentHashMap<>();
+ private static final Map groupIdChannelGroupMap = new ConcurrentHashMap<>();
+
public static void bindSession(Session session, Channel channel) {
userIdChannelMap.put(session.getUserId(), channel);
channel.attr(Attributes.SESSION).set(session);
@@ -22,8 +26,10 @@ public static void bindSession(Session session, Channel channel) {
public static void unBindSession(Channel channel) {
if (hasLogin(channel)) {
- userIdChannelMap.remove(getSession(channel).getUserId());
+ Session session = getSession(channel);
+ userIdChannelMap.remove(session.getUserId());
channel.attr(Attributes.SESSION).set(null);
+ System.out.println(session + " 退出登录");
}
}
@@ -41,4 +47,12 @@ public static Channel getChannel(String userId) {
return userIdChannelMap.get(userId);
}
+
+ public static void bindChannelGroup(String groupId, ChannelGroup channelGroup) {
+ groupIdChannelGroupMap.put(groupId, channelGroup);
+ }
+
+ public static ChannelGroup getChannelGroup(String groupId) {
+ return groupIdChannelGroupMap.get(groupId);
+ }
}
From 43108faea552de73ea1831c5090054f816f13426 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Sat, 26 Mar 2022 10:10:01 +0800
Subject: [PATCH 53/63] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../im/codec/PacketCodecHandler.java | 35 ++++++++++++++++
.../learnnetty/im/server/NettyServer.java | 16 +++----
.../im/server/handler/AuthHandler.java | 6 +++
.../handler/CreateGroupRequestHandler.java | 6 +++
.../im/server/handler/IMHandler.java | 42 +++++++++++++++++++
.../handler/JoinGroupRequestHandler.java | 6 +++
.../ListGroupMembersRequestHandler.java | 7 ++++
.../server/handler/LoginRequestHandler.java | 18 ++++++--
.../server/handler/LogoutRequestHandler.java | 6 +++
.../server/handler/MessageRequestHandler.java | 6 +++
.../handler/QuitGroupRequestHandler.java | 6 +++
11 files changed, 139 insertions(+), 15 deletions(-)
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketCodecHandler.java
create mode 100644 Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/IMHandler.java
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketCodecHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketCodecHandler.java
new file mode 100644
index 0000000..35aa573
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketCodecHandler.java
@@ -0,0 +1,35 @@
+package com.bruis.learnnetty.im.codec;
+
+import com.bruis.learnnetty.im.model.Packet;
+import com.bruis.learnnetty.im.model.PacketCodeC;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToMessageCodec;
+
+import java.util.List;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/25
+ */
+@ChannelHandler.Sharable
+public class PacketCodecHandler extends MessageToMessageCodec {
+
+ public static final PacketCodecHandler INSTANCE = new PacketCodecHandler();
+
+ private PacketCodecHandler() {}
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, Packet msg, List out) throws Exception {
+ ByteBuf byteBuf = ctx.channel().alloc().ioBuffer();
+ PacketCodeC.INSTANCE.encode(byteBuf, msg);
+ out.add(byteBuf);
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception {
+ out.add(PacketCodeC.INSTANCE.decode(msg));
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
index 80741f8..08d3f14 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java
@@ -1,5 +1,6 @@
package com.bruis.learnnetty.im.server;
+import com.bruis.learnnetty.im.codec.PacketCodecHandler;
import com.bruis.learnnetty.im.codec.PacketDecoder;
import com.bruis.learnnetty.im.codec.PacketEncoder;
import com.bruis.learnnetty.im.codec.Spliter;
@@ -36,17 +37,10 @@ public static void main(String[] args) {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new Spliter());
- ch.pipeline().addLast(new PacketDecoder());
- ch.pipeline().addLast(new LoginRequestHandler());
- ch.pipeline().addLast(new AuthHandler());
- ch.pipeline().addLast(new MessageRequestHandler());
- ch.pipeline().addLast(new CreateGroupRequestHandler());
- ch.pipeline().addLast(new JoinGroupRequestHandler());
- ch.pipeline().addLast(new QuitGroupRequestHandler());
- ch.pipeline().addLast(new ListGroupMembersRequestHandler());
- ch.pipeline().addLast(new GroupMessageRequestHandler());
- ch.pipeline().addLast(new LogoutRequestHandler());
- ch.pipeline().addLast(new PacketEncoder());
+ ch.pipeline().addLast(PacketCodecHandler.INSTANCE);
+ ch.pipeline().addLast(LoginRequestHandler.INSTANCE);
+ ch.pipeline().addLast(AuthHandler.INSTANCE);
+ ch.pipeline().addLast(IMHandler.INSTANCE);
}
});
bind(serverBootstrap, PORT);
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/AuthHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/AuthHandler.java
index 33aad01..ad5c784 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/AuthHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/AuthHandler.java
@@ -1,6 +1,7 @@
package com.bruis.learnnetty.im.server.handler;
import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
@@ -9,8 +10,13 @@
* @Author luohaiyang
* @Date 2022/3/23
*/
+@ChannelHandler.Sharable
public class AuthHandler extends ChannelInboundHandlerAdapter {
+ public static final AuthHandler INSTANCE = new AuthHandler();
+
+ protected AuthHandler() {}
+
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (!SessionUtil.hasLogin(ctx.channel())) {
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java
index 45a951d..64c57cf 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java
@@ -5,6 +5,7 @@
import com.bruis.learnnetty.im.util.IDUtil;
import com.bruis.learnnetty.im.util.SessionUtil;
import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
@@ -19,8 +20,13 @@
* @Author luohaiyang
* @Date 2022/3/24
*/
+@ChannelHandler.Sharable
public class CreateGroupRequestHandler extends SimpleChannelInboundHandler {
+ public static final CreateGroupRequestHandler INSTANCE = new CreateGroupRequestHandler();
+
+ protected CreateGroupRequestHandler() {}
+
@Override
protected void channelRead0(ChannelHandlerContext ctx, CreateGroupRequestPacket msg) throws Exception {
List userIdList = msg.getUserIdList();
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/IMHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/IMHandler.java
new file mode 100644
index 0000000..8420f57
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/IMHandler.java
@@ -0,0 +1,42 @@
+package com.bruis.learnnetty.im.server.handler;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import com.bruis.learnnetty.im.model.Packet;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.bruis.learnnetty.im.model.Command.*;
+
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/25
+ */
+@ChannelHandler.Sharable
+public class IMHandler extends SimpleChannelInboundHandler {
+
+ public static final IMHandler INSTANCE = new IMHandler();
+
+ private Map> handlerMap;
+
+ private IMHandler() {
+ handlerMap = new HashMap<>();
+
+ handlerMap.put(MESSAGE_REQUEST, MessageRequestHandler.INSTANCE);
+ handlerMap.put(CREATE_GROUP_REQUEST, CreateGroupRequestHandler.INSTANCE);
+ handlerMap.put(JOIN_GROUP_REQUEST, JoinGroupRequestHandler.INSTANCE);
+ handlerMap.put(QUIT_GROUP_REQUEST, QuitGroupRequestHandler.INSTANCE);
+ handlerMap.put(LIST_GROUP_MEMBERS_REQUEST, ListGroupMembersRequestHandler.INSTANCE);
+ handlerMap.put(GROUP_MESSAGE_REQUEST, GroupMessageRequestHandler.INSTANCE);
+ handlerMap.put(LOGOUT_REQUEST, LogoutRequestHandler.INSTANCE);
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, Packet packet) throws Exception {
+ handlerMap.get(packet.getCommand()).channelRead(ctx, packet);
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/JoinGroupRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/JoinGroupRequestHandler.java
index 1cf19d2..1188ec6 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/JoinGroupRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/JoinGroupRequestHandler.java
@@ -3,6 +3,7 @@
import com.bruis.learnnetty.im.model.JoinGroupRequestPacket;
import com.bruis.learnnetty.im.model.JoinGroupResponsePacket;
import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
@@ -12,8 +13,13 @@
* @Author luohaiyang
* @Date 2022/3/24
*/
+@ChannelHandler.Sharable
public class JoinGroupRequestHandler extends SimpleChannelInboundHandler {
+ public static final JoinGroupRequestHandler INSTANCE = new JoinGroupRequestHandler();
+
+ protected JoinGroupRequestHandler() {}
+
@Override
protected void channelRead0(ChannelHandlerContext ctx, JoinGroupRequestPacket msg) throws Exception {
// 目标群聊id
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/ListGroupMembersRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/ListGroupMembersRequestHandler.java
index 5418a4e..8be361d 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/ListGroupMembersRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/ListGroupMembersRequestHandler.java
@@ -5,6 +5,7 @@
import com.bruis.learnnetty.im.session.Session;
import com.bruis.learnnetty.im.util.SessionUtil;
import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
@@ -17,7 +18,13 @@
* @Author luohaiyang
* @Date 2022/3/24
*/
+@ChannelHandler.Sharable
public class ListGroupMembersRequestHandler extends SimpleChannelInboundHandler {
+
+ public static final ListGroupMembersRequestHandler INSTANCE = new ListGroupMembersRequestHandler();
+
+ protected ListGroupMembersRequestHandler() {}
+
@Override
protected void channelRead0(ChannelHandlerContext ctx, ListGroupMembersRequestPacket requestPacket) {
// 1. 获取群的 ChannelGroup
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
index 6e223fd..0286a96 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java
@@ -5,19 +5,23 @@
import com.bruis.learnnetty.im.session.Session;
import com.bruis.learnnetty.im.util.IDUtil;
import com.bruis.learnnetty.im.util.SessionUtil;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.*;
+import java.util.Arrays;
import java.util.Date;
-import java.util.UUID;
/**
* @Description 接收客户端登录请求
* @Author luohaiyang
* @Date 2022/3/23
*/
+@ChannelHandler.Sharable
public class LoginRequestHandler extends SimpleChannelInboundHandler {
+ public static final LoginRequestHandler INSTANCE = new LoginRequestHandler();
+
+ protected LoginRequestHandler() {}
+
@Override
protected void channelRead0(ChannelHandlerContext ctx, LoginRequestPacket loginRequestPacket) {
// 登录校验响应
@@ -38,7 +42,13 @@ protected void channelRead0(ChannelHandlerContext ctx, LoginRequestPacket loginR
}
// 登录响应
- ctx.channel().writeAndFlush(loginResponsePacket);
+ ctx.writeAndFlush(loginResponsePacket).addListener((ChannelFutureListener) future -> {
+ // 关闭channel成功
+ Throwable cause = future.cause();
+ if (null != cause) {
+ System.out.println(Arrays.toString(cause.getStackTrace()));
+ }
+ });
}
private boolean valid(LoginRequestPacket loginRequestPacket) {
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LogoutRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LogoutRequestHandler.java
index 8ee3152..4436802 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LogoutRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LogoutRequestHandler.java
@@ -3,6 +3,7 @@
import com.bruis.learnnetty.im.model.LogoutRequestPacket;
import com.bruis.learnnetty.im.model.LogoutResponsePacket;
import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
@@ -11,8 +12,13 @@
* @Author luohaiyang
* @Date 2022/3/24
*/
+@ChannelHandler.Sharable
public class LogoutRequestHandler extends SimpleChannelInboundHandler {
+ public static final LogoutRequestHandler INSTANCE = new LogoutRequestHandler();
+
+ protected LogoutRequestHandler () {}
+
@Override
protected void channelRead0(ChannelHandlerContext ctx, LogoutRequestPacket msg) {
SessionUtil.unBindSession(ctx.channel());
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java
index 5943156..b9b83a0 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java
@@ -5,6 +5,7 @@
import com.bruis.learnnetty.im.session.Session;
import com.bruis.learnnetty.im.util.SessionUtil;
import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
@@ -13,8 +14,13 @@
* @Author luohaiyang
* @Date 2022/3/23
*/
+@ChannelHandler.Sharable
public class MessageRequestHandler extends SimpleChannelInboundHandler {
+ public static final MessageRequestHandler INSTANCE = new MessageRequestHandler();
+
+ protected MessageRequestHandler() {}
+
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageRequestPacket messageRequestPacket) throws Exception {
// 1.拿到消息发送方的会话信息
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/QuitGroupRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/QuitGroupRequestHandler.java
index 81dba9a..86455bb 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/QuitGroupRequestHandler.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/QuitGroupRequestHandler.java
@@ -4,6 +4,7 @@
import com.bruis.learnnetty.im.model.QuitGroupResponsePacket;
import com.bruis.learnnetty.im.util.SessionUtil;
import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
@@ -13,8 +14,13 @@
* @Author luohaiyang
* @Date 2022/3/24
*/
+@ChannelHandler.Sharable
public class QuitGroupRequestHandler extends SimpleChannelInboundHandler {
+ public static final QuitGroupRequestHandler INSTANCE = new QuitGroupRequestHandler();
+
+ protected QuitGroupRequestHandler() {}
+
@Override
protected void channelRead0(ChannelHandlerContext ctx, QuitGroupRequestPacket msg) throws Exception {
String groupId = msg.getGroupId();
From 75cd13ca018e7077a514463c478e9e51e0a720d8 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Wed, 30 Mar 2022 23:57:11 +0800
Subject: [PATCH 54/63] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Netty=E5=AE=9E=E6=88=98?=
=?UTF-8?q?=E6=8E=A8=E8=8D=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index e69a07d..7181972 100644
--- a/README.md
+++ b/README.md
@@ -135,6 +135,10 @@ SpringCloud源码
- Netty底层源码解析-FastThreadLocal原理分析
- Netty底层源码解析-内存分配原理分析
- Netty底层源码解析-RocketMQ底层使用到的Netty
+ - [实战+原理效果更佳!强烈推荐闪电侠大佬实战课:《Netty 入门与实战:仿写微信 IM 即时通讯系统》](https://juejin.cn/book/6844733738119593991)
+
+Netty实战课相关点位于:Spring-Netty,com/bruis/learnnetty/im包下,有需要的读者可前往查看。
+
- RocketMQ底层源码解析
- RocketMQ版本:4.9.0
@@ -146,7 +150,7 @@ SpringCloud源码
todo
-2021年年底完成了人生的两件大事,所以一直没时间持续输出源码分析,2022年开始需要继续努力,完成这个源码分析项目!
+2021年年底完成了人生的两件大事,所以一直没时间持续输出源码分析,2022年开始需要继续努力,继续完成这个源码分析项目!
- 完成Netty剩余源码分析文章
- 完成RocketMQ剩余源码分析文章
From eb95b3808d85715aa9b3f154449b9410c53162d5 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Wed, 6 Apr 2022 22:54:26 +0800
Subject: [PATCH 55/63] add LRUCache
---
.../com/learnjava/collection/LRUCache.java | 71 +++++++++++++++++++
.../reference/WeakReferenceTest.java | 2 -
2 files changed, 71 insertions(+), 2 deletions(-)
create mode 100644 JdkLearn/src/main/java/com/learnjava/collection/LRUCache.java
diff --git a/JdkLearn/src/main/java/com/learnjava/collection/LRUCache.java b/JdkLearn/src/main/java/com/learnjava/collection/LRUCache.java
new file mode 100644
index 0000000..aca54da
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/collection/LRUCache.java
@@ -0,0 +1,71 @@
+package com.learnjava.collection;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/4/6
+ */
+public class LRUCache implements Iterable {
+
+ private int MAX = 3;
+ private LinkedHashMap cache = new LinkedHashMap<>();
+
+ public void cache(K key, V value) {
+ if (cache.containsKey(key)) {
+ cache.remove(key);
+ } else if (cache.size() >= MAX) {
+ Iterator iterator = cache.keySet().iterator();
+ K first = iterator.next();
+ cache.remove(first);
+ }
+ cache.put(key, value);
+ }
+
+ public V getValue(K k) {
+ return cache.get(k);
+ }
+
+ @Override
+ public void forEach(Consumer super K> action) {
+ Iterable.super.forEach(action);
+ }
+
+ @Override
+ public Spliterator spliterator() {
+ return Iterable.super.spliterator();
+ }
+
+ @Override
+ public Iterator iterator() {
+ Iterator iterator = cache.keySet().iterator();
+ return new Iterator() {
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public K next() {
+ return iterator.next();
+ }
+ };
+ }
+
+ public static void main(String[] args) {
+ LRUCache cache = new LRUCache<>();
+ cache.cache("1", "1A");
+ cache.cache("2", "2A");
+ cache.cache("3", "3A");
+ cache.cache("1", "1A");
+
+ for (String next : cache) {
+ System.out.println(cache.getValue(next));
+ }
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/reference/WeakReferenceTest.java b/JdkLearn/src/main/java/com/learnjava/reference/WeakReferenceTest.java
index bbbb766..2e2ec2d 100644
--- a/JdkLearn/src/main/java/com/learnjava/reference/WeakReferenceTest.java
+++ b/JdkLearn/src/main/java/com/learnjava/reference/WeakReferenceTest.java
@@ -1,7 +1,5 @@
package com.learnjava.reference;
-import com.sun.webkit.graphics.Ref;
-
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
From 6cbcb877dafb2bd1ea278f2b7feedca6f56be856 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Thu, 7 Apr 2022 23:41:50 +0800
Subject: [PATCH 56/63] =?UTF-8?q?=E6=B7=BB=E5=8A=A0demo=E7=B1=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/com/learnjava/concurent/ReentrantLockDemo.java | 9 +++++++++
.../main/java/com/learnjava/concurent/SnowIdUtils.java | 2 +-
.../java/com/learnjava/concurent/SynchronizeDemo.java | 9 +++++++++
3 files changed, 19 insertions(+), 1 deletion(-)
create mode 100644 JdkLearn/src/main/java/com/learnjava/concurent/ReentrantLockDemo.java
create mode 100644 JdkLearn/src/main/java/com/learnjava/concurent/SynchronizeDemo.java
diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/ReentrantLockDemo.java b/JdkLearn/src/main/java/com/learnjava/concurent/ReentrantLockDemo.java
new file mode 100644
index 0000000..0d04fb8
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/concurent/ReentrantLockDemo.java
@@ -0,0 +1,9 @@
+package com.learnjava.concurent;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/4/7
+ */
+public class ReentrantLockDemo {
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdUtils.java b/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdUtils.java
index b07604d..384d980 100644
--- a/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdUtils.java
+++ b/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdUtils.java
@@ -58,7 +58,7 @@ private SnowFlake() {
// String ip = instance.getDockerIp().replace(".", "");
// 模拟获取机器节点ip
String ip = "127.0.0.1";
- long localIp = Long.valueOf(ip.replace(".", ""));
+ long localIp = Long.parseLong(ip.replace(".", ""));
machineIdPart = (localIp & MAX_MACHINE_ID) << SEQUENCE_BIT;
}
/**
diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/SynchronizeDemo.java b/JdkLearn/src/main/java/com/learnjava/concurent/SynchronizeDemo.java
new file mode 100644
index 0000000..4a89467
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/concurent/SynchronizeDemo.java
@@ -0,0 +1,9 @@
+package com.learnjava.concurent;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/4/7
+ */
+public class SynchronizeDemo {
+}
From 0b22c7586dd79ca3ea2c3aa3a492bd8a932b671f Mon Sep 17 00:00:00 2001
From: liuyang
Date: Mon, 11 Apr 2022 15:10:16 +0800
Subject: [PATCH 57/63] =?UTF-8?q?fix:=20=E8=B0=A2=E8=B0=A2=E6=BA=90?=
=?UTF-8?q?=E7=A0=81=E4=BE=8B=E5=AD=90=20=E6=84=9F=E8=A7=89=E5=AF=B9netty?=
=?UTF-8?q?=E7=A8=8D=E5=BE=AE=E8=83=BD=E7=90=86=E8=A7=A3=E4=B8=80=E7=82=B9?=
=?UTF-8?q?=E7=82=B9=E4=BA=86=20=E5=8F=91=E7=8E=B0=E4=B8=A4=E5=A4=84?=
=?UTF-8?q?=E7=AC=94=E8=AF=AF=20=E6=9C=9B=E9=87=87=E7=BA=B3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../bruis/learnnetty/im/console/ConsoleCommandManager.java | 2 +-
.../bruis/learnnetty/im/console/LoginConsoleCommand.java | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java
index d4759d0..8bf69f6 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java
@@ -19,7 +19,7 @@ public class ConsoleCommandManager implements ConsoleCommand {
public ConsoleCommandManager() {
consoleCommandMap = new HashMap<>();
consoleCommandMap.put("sendToUser", new SendToUserConsoleCommand());
- consoleCommandMap.put("logout", new LoginConsoleCommand());
+ consoleCommandMap.put("logout", new LogoutConsoleCommand());
consoleCommandMap.put("createGroup", new CreateGroupConsoleCommand());
consoleCommandMap.put("joinGroup", new JoinGroupConsoleCommand());
consoleCommandMap.put("quitGroup", new QuitGroupConsoleCommand());
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LoginConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LoginConsoleCommand.java
index 105cf58..3e632ca 100644
--- a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LoginConsoleCommand.java
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LoginConsoleCommand.java
@@ -17,7 +17,11 @@ public void exec(Scanner scanner, Channel channel) {
LoginRequestPacket loginRequestPacket = new LoginRequestPacket();
System.out.print("输入用户名登录: ");
- loginRequestPacket.setUserName(scanner.nextLine());
+ String userIdStr; // 在退出登录logout之后 这里会读取到最后一个回车符 用户名就是空字符串会导致无法退出登录
+ while ((userIdStr = scanner.nextLine()).isEmpty()) {
+ System.out.println("用户名异常, 请重新输入");
+ }
+ loginRequestPacket.setUserName(userIdStr);
loginRequestPacket.setPassword("pwd");
// 发送登录数据包
From 495730faa9e45dc172a4af0ed5f9551e7c79d441 Mon Sep 17 00:00:00 2001
From: luohaiyang
Date: Sat, 23 Apr 2022 12:24:07 +0800
Subject: [PATCH 58/63] =?UTF-8?q?[F]=20=E5=9B=BE=E7=89=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 ++
...ile\345\205\263\351\224\256\345\255\227.md" | 8 ++++----
note/images/JDK/volatile-01.png | Bin 0 -> 129446 bytes
note/images/JDK/volatile-02.png | Bin 0 -> 84199 bytes
note/images/JDK/volatile-03.png | Bin 0 -> 90811 bytes
5 files changed, 6 insertions(+), 4 deletions(-)
create mode 100644 note/images/JDK/volatile-01.png
create mode 100644 note/images/JDK/volatile-02.png
create mode 100644 note/images/JDK/volatile-03.png
diff --git a/README.md b/README.md
index 7181972..fee20a8 100644
--- a/README.md
+++ b/README.md
@@ -74,6 +74,7 @@ SpringCloud源码
- [一篇文章快速深入学习ThreadLocal](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/JDK/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%BF%AB%E9%80%9F%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0ThreadLocal.md)
- [深入学习Java volatile关键字](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/JDK/%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0Java%20volatile%E5%85%B3%E9%94%AE%E5%AD%97.md)
- [深入学习Thread底层原理](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/JDK/%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0Thread%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81.md)
+ - [深入学习JDK1.7、8 HashMap扩容原理]()
- Spring源码学习
- Spring版本:5.2.1.RELEASE
@@ -135,6 +136,7 @@ SpringCloud源码
- Netty底层源码解析-FastThreadLocal原理分析
- Netty底层源码解析-内存分配原理分析
- Netty底层源码解析-RocketMQ底层使用到的Netty
+ - [Netty底层的优化总结]()
- [实战+原理效果更佳!强烈推荐闪电侠大佬实战课:《Netty 入门与实战:仿写微信 IM 即时通讯系统》](https://juejin.cn/book/6844733738119593991)
Netty实战课相关点位于:Spring-Netty,com/bruis/learnnetty/im包下,有需要的读者可前往查看。
diff --git "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md" "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md"
index 79f3bfd..b90dd17 100644
--- "a/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md"
+++ "b/note/JDK/\346\267\261\345\205\245\345\255\246\344\271\240Java volatile\345\205\263\351\224\256\345\255\227.md"
@@ -51,7 +51,7 @@ Lock引起的将当前处理器缓存该变量的数据写回到系统内存中
每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是否过期,当处理器发现自己缓存行对于数据的内存地址被修改了,就会将当前缓存行设置为无效。当处理器对这个数据进行修改操作时,会重新从系统内存中读取该数据到处理器缓存中。
-[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bTawJOHf-1596181331575)(https://note.youdao.com/yws/api/personal/file/AA87E3ABBEDB4A37B69D8E75B5ED12C1?method=download&shareKey=f9788b07ab72368f3613b2744614eecf)]
+
为了实现volatile的内存语义,编译期在生成字节码时会对使用volatile关键字修饰的变量进行处理,在字节码文件里对应位置生成一个Lock前缀指令,Lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。
@@ -117,12 +117,12 @@ volatile读之后的操作不会被编译器重排序到volatile读之前。
2. 在每个volatile写操作后插入StoreLoad屏障
3. 在每个volatile读前面插入一个LoadLoad屏障
4. 在每个volatile读后面插入一个LoadStore屏障
-
-[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z1N3KBZj-1596181331583)(https://note.youdao.com/yws/api/personal/file/E11087F8FD5B4673ABD8C58F6F8DA232?method=download&shareKey=cf78d935c04cb11b039399e1d4825b74)]
+
+
- StoreStore屏障可以保证在volatile写之前,所有的普通写操作已经对所有处理器可见,StoreStore屏障保障了在volatile写之前所有的普通写操作已经刷新到主存。
- StoreLoad屏障避免volatile写与下面有可能出现的volatile读/写操作重排。因为编译器无法准确判断一个volatile写后面是否需要插入一个StoreLoad屏障(写之后直接就return了,这时其实没必要加StoreLoad屏障),为了能实现volatile的正确内存语意,JVM采取了保守的策略。在每个volatile写之后或每个volatile读之前加上一个StoreLoad屏障,而大多数场景是一个线程写volatile变量多个线程去读volatile变量,同一时刻读的线程数量其实远大于写的线程数量。选择在volatile写后面加入StoreLoad屏障将大大提升执行效率(上面已经说了StoreLoad屏障的开销是很大的)。
-[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pRcUS5Mm-1596181331589)(https://note.youdao.com/yws/api/personal/file/2A92B2D468A345F6A55C75249A89845A?method=download&shareKey=ac99a6bcd169bf4bcda8b0fbd33e0003)]
+
- LoadLoad屏障保证了volatile读不会与下面的普通读发生重排
- LoadStore屏障保证了volatile读不回与下面的普通写发生重排。
diff --git a/note/images/JDK/volatile-01.png b/note/images/JDK/volatile-01.png
new file mode 100644
index 0000000000000000000000000000000000000000..61eb672bbfddf1b3ded77cba2f2d4f5d2730a356
GIT binary patch
literal 129446
zcmbsQbyS;u^d_aZ9n{ZpA55916u!B)Ep+?i9BcibJ6UC{A#P0!2dt
zg%%5LFFnsX=lq`a{qe2!zH8lo-Pzgq%$}LOXRd2@w2qcC!Bd*27#J7?swxVvF)*;2
zF)%PkaGyN9k)Hd)i-Ey}p{gLO@0+*Z@$=gUBa_aK-F^!FG{Z-DNrbDcrI@AB;>F>5
zQN1h^wh*
z_y_K6agr0g@_&6iW@JFkr%KVnkk6#UtiTL3+S7qUK{BPxGvLh?zxh>m{
z>;6ObnBrC)9=i&3Us#{tX_aMQ{7}l`|2@CCRoO^-AI0kbrJ`azpCexHf6in5|7*I3
zb^p&1|1Z{2=l{F^{QnW6o>b=UDADIvL_s&V#dGme-poE%(N>A`~aQ5RE*
zK_8C88E@8??rVcit9%x73JUZ8dr3SHA(K6Cpa0WV@oj?pGDqUq|M3Wqz8~V38$`jr
z#~lZOZl;lI95!VS&bj!)41KY7f4i1?e_3#!jf@SH`=3w{_f&DQYO0%a{uewLqeSB*
z*B}3PJ_J+dgB34_Q!~46w#Taeg$qxAC*(v_H{ky`n}9^6er5u>4-{8{IWLSk=pWHv;H#?n1di^7`4g!A*XhZLqXEkF`GUm$))@!LEt^LvThteHNn<6L
zu$`=W`jMqB_=WL@uD!oqe=oIj9p*dE6yqhprIIe%KExg(m`?
zcmKZkLQ3hAs+2YLPSp;TO~Uijn)|zhdke@{gOh`XHYrq>M}Z=E>&vy5YnkQDT4poQ
zi)*6aBMuMFd0=uNo;V^J|B~jK`5r`BR75FDx7-pi4-=BJ3EH3x+CZGv1_us$Js2pt
za5|`5=d+yC567#-`-?=?&+3UUjEapQ`i=n}aea%O$#0sJZF02Zw@;Exse9_w>VJt&
z8yk9PM2?CcG0Y!5F~KH3)@PM+^GmE;w`@{Y6VK^1z+G^Nny&N+E5uv#Qr=$Pq{!RWZ2S`l^0NFV?#;e#^A2N;=*1S7}dG{^_E3
zL2tu(S~?9I!{xnKt^Y5$+wHuYrp-;h|KIGPJ@eGv{|cCr(s_--uysV-W{CCC!=tqG-~qK?8NRT7h84NQv5D&eF=*IgRrt>g1deV}m|FH=yvUK28}rK(
z(uN&f7n31xts{U1!dzMb9sF_bEVkNg@+c=PS074S^5G%coAggb;Mg
z2>EsLtY`4`r`w+T;>-v{dEl9_FYk8btqCYoiA^QDl5)P-L)p@+I^n!|sP&YgV?vQN
zX|OTTGtS%nFFIs~EEHTm7=GT6kayVT`ImK40lHrbFh%DLoUaS;iZA|U#Ri>o?qvYs
zX9lZZb`?EMjnn!#8R(KslYlO6{>#hd_rN(IOv~A_g+SQl=s0;I*XODSpDzz~u^G#;
zc7qh-xOG^;L9`lin11NI3Dzb5kvBsbxo0QBsrpq{?6veZl}`XGXv^0+byDls*w_i%
zWr>EjM&hk7{C;HswY@mjf;!m}8;C#XIYSB^oACH&nsJ<_u{OgjhIftvpMNt?dK))G
z@2iDO3uJCk{I2KP|&|la2
zvZ7t48>xxy&DuO&;$&VrmB-q{RVEpaJZcwkFj-wTfw96EuPjrZ<`vu@&4$euuX#!t
z)y@c&FVeHhC5a8?IOIc+G@&&x-T8}g3J7UweR|)4isR+oALkUgJ>rRy?%mcce*paw
z=jG;wN{VITV}c^#k1pzdCgn$>%#@fE@Ity}$wt;LbOhBPGipm($7+9#1*h5l(D}x%#EQXm<#D(4
z(7>Orks-Rl(goU%<{Ms0ky~Cn&(-$#X|107iTD=7t$+h{Ptf+JAZUk*#%s`wN~6Qe
ztn_4cl5g?ui1FUTuLd11)^$0?-EsEsr0;z=u%!lDdmMRb)5JyNG&0g1H%{)14)1O#b>rYSXjll3mdgzRRbg^%JVJqXF4~*n=7li*ZF5F`|U_3wG{l4
z(O7zAyQ;9ZHc{f28cYJzonx
z?jzIID3XRr1PzWv9ukTkXTH*L-R#(Grqf0@Dnj0ldv>z!9O2G}m@6o#?vzGlm(DSV
z4fIDg`a%*GW>UFT4_Ut`dA^xWa^`j(~swHm-Z;HpIhNrBRiCP$}*6skqTrZGcPocG*NWn9orFKk;
zbAh5eaIO|$ZlopDNj)>Fo+x#;C|q+{02+Qqe_HvF1BeHt&F0_%{6kyAFnkZYakLpA
zh-4>bhiPRe-?i&ipW}>gux(RuY^WJ2@orCiamp%2wBlM>MaFx$q|slnA*)rxzHbNR
zz+tPw!sv?BR+~e^eZH-ZOA9T0vQ4pPzWMD>`HEzy(k!o+7Rt%JuTHlTWu&P;Rl}}b1O!ENUT@Tf
z`PI7z*w7v3Gc__b`(~nligoO2lI^OjrmS7WuZcr?X;=Bb#~a=dI_Iqpol1%i-d->uS%wyIPkq
zQAF6IuHEh`z?qmdCFI?9TgqE?`jIW_HJ*ebu|fx8=7aMaw+o~bq$9>UVW@u-Eh+WB
z8?u~)@D9=t?8KMpw|mGT^I6F0BC!6>C1E_I%SPqeDgaO7qPVV;PX~FP5r#P
zdxNLr#Y1sc+%)7IBlCb
z+#M)T0c!L{Wn^{A>3}VL#jHc|X9OUWE))PPC76(hg|#wtO(p7%pXR?3Ff95ryw?vT
zz%y(IP9=*Fn<1pbN3B_?`DhtTT~R=TKk6d`e@q0p{I4TsBsFCdKG8)zZ|75Y8Y8Y?
zMNM#b@sj$Ha=9@{Uld4>1>hB(?l(?ofw^oc!R!O4wf9%GmO^2JIxT6-d0k4!u0?s)
zafg4IjG5T7CCt*`p!bzZM%)s@l$=gj{|`A_UG+m15P-=eDI9OIX<)
z>6g>k?#pSSse|78)Z{3LA=n&ev`$7YU
zY%1IsR^w`-mp&H+(-}eI*2GF7ZT_sYz&@HYvuD>t1Clnm*n%JIXQ;BSr|Qk6+Ok-b
zig8s0sXf&RRViY!*Nj_%2!<4HaOO*``LW2nWYTh1S)4xk0$AG4vF~vShhP_yO1_}U
zINQl3W|LXxLA9dS33WY-$|^-6y)kQn=f607Ry+m~0W*ZAtLeLbF@HIxQwznVOEU$aQf=sq
z*^vl=Jgd)tIQq?!+Ay&b54vR8XSPr^k!tSJW$y;Qw
zFEQ{mC&-jOdc{;Drus{YO)Q*dI>vxK?)8rdIE1xHasA8`
z9LF=J6B`H+LZ4mJoSxszwaTh44jYBi^eHhSZ(wokKs(^1Ia|<2;;ZEQd$(VT;7loLj8z
zA{CnHt=4Pxjty$sPuWdi^;s7e`FJU7S(TcSaNF@t&MzjrWu^1p$!gCgsMxT3rsi6t
z53F5PE_(x_0ahNkTOoJxF0v41_<&99TDIHnfnT(}v9&zs89uc1+eekqqOp}o;CSmC
zvBQ3`%%NBT!E@d-0+S7M?dOzKz^KJTSWoI*)kDdwkLU|%;+f>nocgADVkP|eB>PH)
z7R(5F&z3n*iPKB=vz49UdNeVPd#UGia_W};RgGOfZw-8tWNquK;;PyQmCh})*YRDz
z@2>`}JuGGgvq{h@vN}KfQkfV+Clz}7^NIw*j-9r(u7t_hQbA;5WS_~}El87iHHHi?
z5m{O>%i+W6+-?*_j?yXCeUI3h6#ux*VYr9ULfnQg3n41=HcIXuuMMsH0Q67KnycuFi)L1ov&dUkbcjX&L
z%#>8f8d`DCaA`9-NH-P|74R?%5u#^3X_u`pfcYj#PRKz)%#KJS4HH@au7_I5@;Y~o
zDYFY*#-|BpKjB=6)Gz5+unfM1fBm-lGG3ND&0CTp_W@#wd)tgx`1Z?dbGk?RyzzrR
zBI(g7cqyv+(Fv+4(P`;S?5^zZ&V1zdjTZ?4ka-rx>8YoBiUK|=E=9I1{=!T<*U0JO
zgGSP^&LMU?(&0|>g_vJ&0UQSQM+tzo=Luv`ry>b{E&9gaV}{W}N6jau*BbTQSrXKb
zaC>5!Xo=-`{>n|hMu*TDjw%nmyyC^mfG?>Mbwib<)>H}G1S;&jjHSrC8#tdzrBaJQ
zk8<)KaXOoSfZB#*%#_6WuUp_oBE*nJ%zVa`NQ*;3mfyG&j`GY?dq=D`7OrK}o7I&{
zqI$w`3lGOv^|M|%ct457tJ><0dwpoKG+MaIH9ivHE%7$!H^t~|YkH{gBf_-fpT2!<
z2KNG38GaTCy6sft@_sB2Zg`hj^wQ?}<)-iYje-pS0kL1W!2(rp0IJnNhP%X@hcc7E
z&X__h1Qv!Bb^+8T2a~+yFWC8(Bt5F^OSP9CnMtwwk=PyZ^tut{uHnWI2{Z)2>IMhhLfPuijCg(_#2RM^<~
zOlA$!2@h9$sm)SG;S35KtgXoGkWnGx>;O&!twnlNn{oG}6DH3xvEe+ucEDf@-;Ky<
zG~{Zxq9RNct$W2Dm?k-le9ewcKnq0s(KfcJY|*h__Myu2`^VGq>|VSLuw&Jw5ChZ6
zy_2=}&~8d~SA%^Ynu~OupsUKH0dw2ZugOk+KJd|P7AN?JnJ!A$hH0<|vOBO#v-`4Z
zu}d44X0=}aitbuu_+U`WtV{Q}!R-5`qE6>!!X*9){0NV=#T0VkSrlq-a!qQC%^xsX
zOtgcw17L?ECE^?b@9!3JV+G8A6}19vNxkr1sL>rL3bB>21roH_BCtwQv_tEch(&jrpftDA6<0>&cXTpz&1iL$leO8tU#8%voXZ5r-
zFS~J-`}|}5Q9klyNArR{=znDq{965>(eFL^$bVtT9T5Yusm4LyDnmu97$g^Q;m~_e(WAb7MIP8ud0w%xgOPDJMHva9r{+lXs00T4outKt&0raE&
z&pJvKpmbk)-|LBqb_ZBElTXeOBqA(ayKc|mLilofKOG9BAjfbH4nE#|%m{{3`e+2O
z30NKZWutrmz2
zKQNXZ;2)qGP!ScVX+u89Paqg4E?Z7?{PTh~$ik
zm7}PATXL`wAK^f;EHoi0w4G7xCO5s}Ir4(TsKzXNh!&UOBaaFrrSW*mN??IiGq{)H
z2-Xvu@-8tcka#>%=I#Ok(%cLfc(Fzn1_uLKlO0Ba{EEr3|1nMd7SvujK1fbGx7!rAKUwI}RRUf+dhR3eCcAUn5%pFwP42cg?4>wz}_LKh1A+(h>9S}
zWB+rA4X3lqz99OL*-k0r-30##dq3Y9iOse;rQJXv8Zea_+xZLfbx}0!Q&MFFMasp^
z@3|@tA}REw!t4&4N+l4!cJ!)89p4lGfTh>*MUcCStznPiYp;N+G(l-#Ev{l;YCN%K
zJ}}>mIAZ4>F}Hnr6o)s}T${_*!drNfg{lWC&5SOW6#krIVo_`Y{Q)~7kZ+DZO-9_+z!a&cdLq^^RXBtK~AtVOM`JPp_W@=EaF
zaFpo@5SQ@*l8EMK6(%Vj@ksEYoH$JCSdN+F{464$6>wM#dmUrVSkw^HZ69qdPnh@P
z;uCr9`|cPKjXWr*A=~*W%XA9}SYRjU#bcATsWj$wv8JY_A7Mp1B4)`NP-7U^@Wob_
zPVjZU_&3v+D@Q-X6|?QjjIm9r-AxK#G(CPCt2jD
zOuKEir@RuGswsDD3r}TbUWrlRagz-?>{JKhm;>Q8#fw