diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 762001e..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index c3e9e42..3540bb2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,10 @@ Spring-Boot/learnsb.iml Spring/learnspring.iml Spring-AOP/learnaop.iml Spring-Netty/Spring-Netty.iml +Spring-Netty/learnnetty.iml +Spring-Security/SpringSecurityDemo.iml +Spring-Security/springsecurity.iml +rocketmqdemo/rocketmqdemo.iml # target JdkLearn/target @@ -14,6 +18,8 @@ Spring/target Spring-AOP/target Spring-Boot/target Spring-Netty/target +Spring-Security/target +rocketmqdemo/target # .DO_Store .DS_Store diff --git a/JdkLearn/pom.xml b/JdkLearn/pom.xml index d196fc9..9fb74db 100644 --- a/JdkLearn/pom.xml +++ b/JdkLearn/pom.xml @@ -20,6 +20,18 @@ + + + com.github.ben-manes.caffeine + caffeine + + + + + io.netty + netty-all + + org.springframework.boot 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 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/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/SnowIdDemo.java b/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdDemo.java new file mode 100644 index 0000000..aa00a30 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdDemo.java @@ -0,0 +1,12 @@ +package com.learnjava.concurent; + +/** + * @author lhy + * @date 2021年7月27日 + */ +public class SnowIdDemo { + public static void main(String[] args) { + // 通过雪花秀算法获取分布式id + System.out.println(SnowIdUtils.uniqueLongHex()); + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdUtils.java b/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdUtils.java new file mode 100644 index 0000000..384d980 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdUtils.java @@ -0,0 +1,114 @@ +package com.learnjava.concurent; + +/** + * @author lhy + * @date 2021年7月27日 + */ +public class SnowIdUtils { + /** + * 私有的 静态内部类 + */ + private static class SnowFlake { + + /** + * 内部类对象(单例模式) + */ + private static final SnowIdUtils.SnowFlake SNOW_FLAKE = new SnowIdUtils.SnowFlake(); + /** + * 起始的时间戳 + */ + private final long START_TIMESTAMP = 1609464335121L; + /** + * 序列号占用位数 + */ + private final long SEQUENCE_BIT = 12; + /** + * 机器标识占用位数 + */ + private final long MACHINE_BIT = 10; + /** + * 时间戳位移位数 + */ + private final long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT; + /** + * 最大序列号 (4095) + */ + private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT); + /** + * 最大机器编号 (1023) + */ + private final long MAX_MACHINE_ID = ~(-1L << MACHINE_BIT); + /** + * 生成id机器标识部分 + */ + private long machineIdPart; + /** + * 序列号 + */ + private long sequence = 0L; + /** + * 上一次时间戳 + */ + private long lastStamp = -1L; + + /** + * 构造函数初始化机器编码 + */ + private SnowFlake() { +// String ip = instance.getDockerIp().replace(".", ""); + // 模拟获取机器节点ip + String ip = "127.0.0.1"; + long localIp = Long.parseLong(ip.replace(".", "")); + machineIdPart = (localIp & MAX_MACHINE_ID) << SEQUENCE_BIT; + } + /** + * 获取雪花ID + */ + public synchronized long nextId() { + long currentStamp = timeGen(); + while (currentStamp < lastStamp) { + throw new RuntimeException(String.format("时钟已经回拨. Refusing to generate id for %d milliseconds", lastStamp - currentStamp)); + } + if (currentStamp == lastStamp) { + sequence = (sequence + 1) & MAX_SEQUENCE; + if (sequence == 0) { + currentStamp = getNextMill(); + } + } else { + sequence = 0L; + } + lastStamp = currentStamp; + return (currentStamp - START_TIMESTAMP) << TIMESTAMP_LEFT | machineIdPart | sequence; + } + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + */ + private long getNextMill() { + long mill = timeGen(); + // + while (mill <= lastStamp) { + mill = timeGen(); + } + return mill; + } + /** + * 返回以毫秒为单位的当前时间 + */ + protected long timeGen() { + return System.currentTimeMillis(); + } + } + + /** + * 获取long类型雪花ID + */ + public static long uniqueLong() { + return SnowIdUtils.SnowFlake.SNOW_FLAKE.nextId(); + } + /** + * 获取String类型雪花ID + */ + public static String uniqueLongHex() { + return String.format("%016X", uniqueLong()); + } +} 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 { +} diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java b/JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java new file mode 100644 index 0000000..5d5b5fc --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java @@ -0,0 +1,48 @@ +package com.learnjava.concurent; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @Description + * @Author luohaiyang + * @Date 2022/4/24 + */ +public class ThreadPoolExecutorDemo { + + public static void main(String[] args) { + testThreadPoolExecutorBinaryCalc(); + } + + + /** + * 验证ThreadPoolExecutor中的二进制位运算操作 + */ + private static void testThreadPoolExecutorBinaryCalc() { +// System.out.println(ctl.get()); +// System.out.println(Integer.toBinaryString(ctlOf(RUNNING, 0))); +// System.out.println(Integer.toBinaryString(RUNNING)); + // 修改线程状态-STOP + System.out.println(Integer.toBinaryString(~runStateOf(ctlOf(STOP, 10)))); + // 修改线程状态-TERMINATED +// System.out.println(runStateOf(3)); +// System.out.println(Integer.toBinaryString(~CAPACITY)); + } + + private static final int COUNT_BITS = Integer.SIZE - 3; + + private static final int CAPACITY = (1 << COUNT_BITS) - 1; + + private static final int RUNNING = -1 << COUNT_BITS; + private static final int SHUTDOWN = 0 << COUNT_BITS; + private static final int STOP = 1 << COUNT_BITS; + private static final int TIDYING = 2 << COUNT_BITS; + private static final int TERMINATED = 3 << COUNT_BITS; + + private static AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); + + private static int runStateOf(int c) { return c & ~CAPACITY; } + + private static int workerCountOf(int c) { return c & CAPACITY; } + + private static int ctlOf(int rs, int wc) { return rs | wc; } +} diff --git a/JdkLearn/src/main/java/com/learnjava/io/bio/BIO.java b/JdkLearn/src/main/java/com/learnjava/io/bio/BIO.java new file mode 100644 index 0000000..4226be5 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/io/bio/BIO.java @@ -0,0 +1,47 @@ +package com.learnjava.io.bio; + +import java.io.InputStream; +import java.net.ServerSocket; +import java.net.Socket; + +/** + * @author lhy + * + * 在windows服务器下,可以使用telnet来合serversocket建立连接 + */ +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(); + } + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/nettysource/Client.java b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/Client.java similarity index 97% rename from JdkLearn/src/main/java/com/learnjava/nettysource/Client.java rename to JdkLearn/src/main/java/com/learnjava/io/netty/bio/Client.java index 5514bee..f84b58e 100644 --- a/JdkLearn/src/main/java/com/learnjava/nettysource/Client.java +++ b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/Client.java @@ -1,4 +1,4 @@ -package com.learnjava.nettysource; +package com.learnjava.io.netty.bio; import java.io.IOException; import java.net.Socket; diff --git a/JdkLearn/src/main/java/com/learnjava/nettysource/ClientHandler.java b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/ClientHandler.java similarity index 96% rename from JdkLearn/src/main/java/com/learnjava/nettysource/ClientHandler.java rename to JdkLearn/src/main/java/com/learnjava/io/netty/bio/ClientHandler.java index d0ddba9..d2d6b88 100644 --- a/JdkLearn/src/main/java/com/learnjava/nettysource/ClientHandler.java +++ b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/ClientHandler.java @@ -1,4 +1,4 @@ -package com.learnjava.nettysource; +package com.learnjava.io.netty.bio; import java.io.IOException; import java.io.InputStream; diff --git a/JdkLearn/src/main/java/com/learnjava/nettysource/Server.java b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/Server.java similarity index 94% rename from JdkLearn/src/main/java/com/learnjava/nettysource/Server.java rename to JdkLearn/src/main/java/com/learnjava/io/netty/bio/Server.java index 834f41b..af55558 100644 --- a/JdkLearn/src/main/java/com/learnjava/nettysource/Server.java +++ b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/Server.java @@ -1,4 +1,4 @@ -package com.learnjava.nettysource; +package com.learnjava.io.netty.bio; import java.io.IOException; import java.net.ServerSocket; @@ -30,7 +30,7 @@ public void run() { } public void doStart() { - while (true) { + for (;;) { try { Socket client = serverSocket.accept(); new ClientHandler(client).start(); diff --git a/JdkLearn/src/main/java/com/learnjava/nettysource/ServerBoot.java b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/ServerBoot.java similarity index 76% rename from JdkLearn/src/main/java/com/learnjava/nettysource/ServerBoot.java rename to JdkLearn/src/main/java/com/learnjava/io/netty/bio/ServerBoot.java index 9348e09..bbb2762 100644 --- a/JdkLearn/src/main/java/com/learnjava/nettysource/ServerBoot.java +++ b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/ServerBoot.java @@ -1,4 +1,4 @@ -package com.learnjava.nettysource; +package com.learnjava.io.netty.bio; /** @@ -9,6 +9,6 @@ public class ServerBoot { public static void main(String[] args) { Server server = new Server(PORT); - + server.start(); } } diff --git a/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/Server.java b/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/Server.java new file mode 100644 index 0000000..7c1d994 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/Server.java @@ -0,0 +1,53 @@ +package com.learnjava.io.netty.demo01; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +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.util.AttributeKey; + +/** + * @author lhy + * @date 2021/5/21 + */ +public class Server { + + public static void main(String[] args) throws Exception { + + /** + * EventLoopGroup: + * NioEventLoopGruop + * NioEventLoop + */ + + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + + try { + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childOption(ChannelOption.TCP_NODELAY, true) + .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue") + .handler(new ServerHandler()) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) { +// ch.pipeline().addLast(new AuthHandler()); + //.. + + } + }); + + ChannelFuture future = bootstrap.bind(8888).sync(); + + future.channel().closeFuture().sync(); + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/ServerHandler.java b/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/ServerHandler.java new file mode 100644 index 0000000..7123116 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/ServerHandler.java @@ -0,0 +1,74 @@ +package com.learnjava.io.netty.demo01; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * @author lhy + * @date 2021年5月22日 + */ +public class ServerHandler extends ChannelInboundHandlerAdapter { + public ServerHandler() { + super(); + } + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + super.channelRegistered(ctx); + } + + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + super.channelUnregistered(ctx); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + super.channelInactive(ctx); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + super.channelRead(ctx, msg); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + super.channelReadComplete(ctx); + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + super.userEventTriggered(ctx, evt); + } + + @Override + public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { + super.channelWritabilityChanged(ctx); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + super.exceptionCaught(ctx, cause); + } + + @Override + public boolean isSharable() { + return super.isSharable(); + } + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + super.handlerAdded(ctx); + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + super.handlerRemoved(ctx); + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/io/nio/MappedByteBufferTest.java b/JdkLearn/src/main/java/com/learnjava/io/nio/MappedByteBufferTest.java index 4097936..75cf208 100644 --- a/JdkLearn/src/main/java/com/learnjava/io/nio/MappedByteBufferTest.java +++ b/JdkLearn/src/main/java/com/learnjava/io/nio/MappedByteBufferTest.java @@ -1,5 +1,8 @@ package com.learnjava.io.nio; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -17,13 +20,55 @@ public static void main(String[] args) throws Exception { MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5); - mappedByteBuffer.put(0, (byte)'H'); - mappedByteBuffer.put(3, (byte)'9'); + mappedByteBuffer.put(0, (byte) 'H'); + mappedByteBuffer.put(3, (byte) '9'); // IndexOutOfBoundsException - mappedByteBuffer.put(5, (byte)'Y'); + mappedByteBuffer.put(5, (byte) 'Y'); randomAccessFile.close(); System.out.println("change success"); } + + /** + * + * @param from + * @param to + * @throws IOException + */ + public static void mmap4zeroCopy(String from, String to) throws IOException { + FileChannel source = null; + FileChannel destination = null; + try { + source = new RandomAccessFile(from, "r").getChannel(); + destination = new RandomAccessFile(to, "rw").getChannel(); + MappedByteBuffer inMappedBuf = + source.map(FileChannel.MapMode.READ_ONLY, 0, source.size()); + destination.write(inMappedBuf); + } finally { + if (source != null) { + source.close(); + } + if (destination != null) { + destination.close(); + } + } + } + + public static void sendfile4zeroCopy(String from, String to) throws IOException{ + FileChannel source = null; + FileChannel destination = null; + try { + source = new FileInputStream(from).getChannel(); + destination = new FileOutputStream(to).getChannel(); + source.transferTo(0, source.size(), destination); + } finally { + if (source != null) { + source.close(); + } + if (destination != null) { + destination.close(); + } + } + } } diff --git a/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioClinet.java b/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioClinet.java new file mode 100644 index 0000000..aab903b --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioClinet.java @@ -0,0 +1,40 @@ +package com.learnjava.io.nio.demo01; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.util.Date; +import java.util.Scanner; + +/** + * @author lhy + * @date 2021年5月22日 + */ +public class NioClinet { + + public static void main(String[] args) throws Exception { + + // 获取通道 + SocketChannel channel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999)); + // 切换至非阻塞模式 + channel.configureBlocking(false); + // 分配缓冲区大小 + ByteBuffer buffer = ByteBuffer.allocate(1024); + + Scanner scan = new Scanner(System.in); + + while (scan.hasNext()) { + String next = scan.next(); + // 向缓冲区里写入数据 + buffer.put( (new Date().toString() + "\n" + next).getBytes()); + buffer.flip(); + + // 向通道里写入带有数据的缓冲区对象, 表示向服务器发送数据 + channel.write( buffer); + buffer.clear(); + } + + // 关闭通道 + channel.close(); + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioServer.java b/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioServer.java new file mode 100644 index 0000000..7ea41ec --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioServer.java @@ -0,0 +1,57 @@ +package com.learnjava.io.nio.demo01; + +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Iterator; + +/** + * @author lhy + * @date 2021年5月22日 + */ +public class NioServer { + public static void main(String[] args) throws Exception { + // 获取服务端通道 + ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); + // 切换为非阻塞模式 + serverSocketChannel.configureBlocking(false); + // 绑定链接 + Selector selector = Selector.open(); + // 将通道注册在selector上,并绑定为读事件 + serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); + // 选择器轮训,阻塞 + while (selector.select()> 0) { + Iterator it = selector.selectedKeys().iterator(); + + // 判断是否有事件进来 + while (it.hasNext()) { + // 获取就绪的事件 + SelectionKey selectionKey = it.next(); + + // 读事件 + if (selectionKey.isAcceptable()) { + // 就绪的客户端连接事件 + SocketChannel acceptChannel = serverSocketChannel.accept(); + acceptChannel.configureBlocking(false); + acceptChannel.register(selector, SelectionKey.OP_READ); + } else if (selectionKey.isReadable()) { + // 读就绪事件 + SocketChannel readAcceptChannel = serverSocketChannel.accept(); + ByteBuffer allocateBuffer = ByteBuffer.allocate(1024); + + int len = 0; + while ((len = readAcceptChannel.read(allocateBuffer))> 0) { + allocateBuffer.flip(); + System.out.println(new String(allocateBuffer.array(), 0, len)); + allocateBuffer.clear(); + } + } + } + + // 取消选择键selectionKey + it.remove(); + } + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaComparatorDemo.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaComparatorDemo.java new file mode 100644 index 0000000..1db2858 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaComparatorDemo.java @@ -0,0 +1,165 @@ +package com.learnjava.lambda; + +import lombok.AllArgsConstructor; +import lombok.Data; +import sun.java2d.pipe.SpanShapeRenderer; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class LambdaComparatorDemo { + + public static String[] arrays = {"Milan", "london", "San Francisco", "Tokyo", "New Delhi"}; + + public static List employees; + + static { + Employee e1 = new Employee(1,23,"M","Rick","Beethovan", "2021-04-01"); + Employee e2 = new Employee(2,13,"F","Martina","Hengis", "2021-04-02"); + Employee e3 = new Employee(3,43,"M","Ricky","Martin","2021-04-09" ); + Employee e4 = new Employee(4,26,"M","Jon","Lowman", "2021-04-10"); + Employee e5 = new Employee(5,19,"F","Cristine","Maria", "2021-04-01"); + Employee e6 = new Employee(6,15,"M","David","Feezor", "2021-04-06"); + Employee e7 = new Employee(7,68,"F","Melissa","Roy", "2021-04-06"); + Employee e8 = new Employee(8,79,"M","Alex","Gussin", "2021-04-08"); + Employee e9 = new Employee(9,15,"F","Neetu","Singh", "2021-04-09"); + Employee e10 = new Employee(10,45,"M","Naveen","Jain", "2021-04-10"); + employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); + } + + public static void main(String[] args) { +// test01(); +// test02(); + test03(); + } + + /** + * List字符串排序 + */ + public static void test01() { + List cities = Arrays.asList(arrays); + + System.out.println(cities); + + // CASE_INSENSITIVE_ORDER 是一个排序器Comparator接口,意思是不区分大小写进行排序 + cities.sort(String.CASE_INSENSITIVE_ORDER); + System.out.println(cities); + + // 自然排序 + cities.sort(Comparator.naturalOrder()); + System.out.println(cities); + + // 可以将排序器放在Stream管道流中 + Stream.of(arrays) + .sorted(Comparator.naturalOrder()) + .forEach(System.out::println); + } + + /** + * 对整数数组进行排序 + */ + public static void test02() { + List numbers = Arrays.asList(6, 2, 4, 3, 1, 9); + System.out.println(numbers); + + // 自然排序(升序) + numbers.sort(Comparator.naturalOrder()); + System.out.println(numbers); + + // 降序 + numbers.sort(Comparator.reverseOrder()); + System.out.println(numbers); + } + + /** + * 对对象进行排序 + */ + public static void test03() { + // 根据employee的年龄进行自然排序 + employees.sort(Comparator.comparing(Employee::getAge)); + employees.forEach(System.out::println); + + System.out.println(); + + // 根据employee的年龄进行降序排序 + employees.sort(Comparator.comparing(Employee::getAge).reversed()); + employees.forEach(System.out::println); + + System.out.println(); + + // 先对性别排序,然后对年龄进行排序 + employees.sort( + Comparator.comparing(Employee::getGender) + .thenComparing(Employee::getAge) + // 性别,年龄都进行倒序排序 + .reversed() + ); + employees.forEach(System.out::println); + + // 自定义排序器 + employees.sort((em1, em2) -> { + if (em1.getAge().equals(em2.getAge())) { + return 0; + } + return em1.getAge() - em2.getAge()> 0 ? -1 : 1; + }); + employees.forEach(System.out::println); + } + + + + + + + + + + + + + + + + + + + + + + + @Data + @AllArgsConstructor + public static class Employee { + + private Integer id; + // 年龄 + private Integer age; + // 性别 + private String gender; + private String firstName; + private String lastName; + private String date; + + @Override + public String toString() { + return "Employee{" + + "id=" + id + + ", age=" + age + + ", gender='" + gender + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", date='" + date + '\'' + + '}'; + } + + // 年龄大于70的谓语 + static Predicate ageGreaterThan70 = e -> e.getAge()> 70; + + static Predicate ageLessThan18 = e -> e.getAge() < 18; + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemo01.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemo01.java new file mode 100644 index 0000000..ae7e1d4 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemo01.java @@ -0,0 +1,37 @@ +package com.learnjava.lambda; + +/** + * @author bruis + * lambda表达式 + */ +public class LambdaDemo01 { + + /** + * 打印内部类 + */ + interface Printer { + void print(String content); +// void print(String content, String operator); + } + + public static void printSomething(String content, Printer printer) { + printer.print(content); + } + + public static void main(String[] args) { +// Printer printer = (String content) -> { +// System.out.println(content); +// }; + +// 去掉参数类型,只有一个参数时可以去掉括号 +// Printer printer = (content) -> { +// System.out.println(content); +// }; + +// 只有一个参数提 +// Printer printer = val -> System.out.println(val); + + Printer printer = System.out::println; + printSomething("hello lambda", printer); + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemoForList.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemoForList.java new file mode 100644 index 0000000..e1aa8e6 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemoForList.java @@ -0,0 +1,88 @@ +package com.learnjava.lambda; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author bruis + * Lambda的List demo + */ +public class LambdaDemoForList { + + public static String[] arrays = {"Monkey", "Lion", "Giraffe", "Lemur"}; + + public static String[] arrays2 = {"Monkey", "Lion", "Giraffe", "Lemur", "Lion"}; + + public static void main(String[] args) { +// test01(); +// test02(); + test03(); + } + + /** + * 去重Distinct + 排序Sort + */ + public static void test03() { + // 去重 + List uniqueAnimals = Stream.of(arrays2) + .distinct() + .collect(Collectors.toList()); + System.out.println(uniqueAnimals); + // 排序 + List sortedAnimals = Stream.of(arrays) + // 对字母是按照自然顺序进行排序的 + .sorted() + .collect(Collectors.toList()); + System.out.println(sortedAnimals); + } + + /** + * Limit + Skip 数据截取 + */ + public static void test02() { + + /** + * 截取前两位字符串 + */ + List limitN = Stream.of(arrays) + .limit(2) + .collect(Collectors.toList()); + + /** + * 过滤掉前两位元素 + */ + List skipN = Stream.of(arrays) + .skip(2) + .collect(Collectors.toList()); + + System.out.println(limitN); + System.out.println(skipN); + } + + /** + * 过滤 + map处理 + */ + public static void test01() { + List nameStrList = Arrays.asList("abc", "efg", "hig", "hii", "klm"); + + List result = nameStrList + .stream() + .filter(s -> s.startsWith("h")) + .map(String::toUpperCase) + // 调用自定义的方法 + .map(MyStringUtils::myToUpperCase) + .collect(Collectors.toList()); + + for (String name : result) { + System.out.println(name); + } + } + + private static class MyStringUtils { + public static String myToUpperCase(String str) { + return str.toUpperCase(); + } + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapDemo.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapDemo.java new file mode 100644 index 0000000..cc03752 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapDemo.java @@ -0,0 +1,139 @@ +package com.learnjava.lambda; + +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author bruis + * 操作Map + */ +public class LambdaMapDemo { + + public static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + public static void main(String[] args) { +// test01(); +// test02(); + test03(); +// test04(); + } + + /** + * 需求,将employee集合中的元素根据date进行排序 + */ + public static void test01() { + List sortedByDate = LambdaComparatorDemo.employees + .stream() + .sorted() + .sorted((a, b) -> { + int result; + try { + result = getDate(b.getDate()).compareTo(getDate(a.getDate())); + } catch (Exception e) { + result = 0; + } + return result; + }) + .collect(Collectors.toList()); + System.out.println(sortedByDate); + } + + /** + * HashMap的merge方法,如果key相同,则通过merge来对key相同的元素进行处理 + */ + public static void test02() { + String key = "money"; + Map map = new HashMap(){{put(key, 100);}}; + + // 第三个参数时BiFunction,聚合函数(可以这么理解) + map.merge(key,100,(oldValue, newValue) -> oldValue + newValue); +// map.merge(key, 100,Integer::sum); + System.out.println(map); + } + + /** + * 对map进行排序 + */ + public static void test03() { + Map codes = new HashMap(); + codes.put("2021-03", 1); + codes.put("2021-02", 49); + codes.put("2021-05", 33); +// codes.put("2021-04-01", 1); +// codes.put("2021-04-15", 49); +// codes.put("2021-04-10", 33); +// codes.put("2021-04-05", 86); +// codes.put("2021-04-20", 92); + + // 先将Map转化为List,通过collect处理后再转为Map + Map sortedMap = codes.entrySet() + .stream() +// .sorted((c1, c2) -> c2.getValue().compareTo(c1.getValue())) +// .sorted(Map.Entry.comparingByValue()) + .sorted((c1, c2) -> c2.getKey().compareTo(c1.getKey())) + .collect( + Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (oldVal, newVal) -> oldVal, + LinkedHashMap::new + ) + ); + sortedMap.entrySet().forEach(System.out::println); + + } + + /** + * 将list转为map,并用其中一个value作为key + */ + public static void test04() { + LinkedHashMap collect = LambdaComparatorDemo.employees + .stream() + .collect( + Collectors.toMap( + LambdaComparatorDemo.Employee::getDate, + // 这样是返回本身对象的一个表达式,还可以用Function.identity() + // Function.indentity() 就是 t -> t +// employee -> employee, + Function.identity(), + (oldVal, newVal) -> { + // 重复的key就将年纪相加,然后FirstName通过--->加起来 + oldVal.setAge(oldVal.getAge() + newVal.getAge()); + oldVal.setFirstName(oldVal + .getFirstName() + .concat("--->") + .concat(newVal.getFirstName())); + return oldVal; + }, + LinkedHashMap::new + ) + ); + // 这样打印出的map元素不好观察 +// System.out.println(collect); +// collect.entrySet().forEach(System.out::println); + + LinkedHashMap sortedCollect = collect + .entrySet() + .stream() + .sorted((a, b) -> b.getKey().compareTo(a.getKey())) + .collect( + Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + // 上面已经对重复key做处理了,这里就直接范围oldVal就行 + (oldVal, newVal) -> oldVal, + LinkedHashMap::new + ) + ); + + // 根据日期排序 + sortedCollect.entrySet() + .forEach(System.out::println); + } + + public static Date getDate(String date) throws Exception { + return format.parse(date); + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapMerge.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapMerge.java new file mode 100644 index 0000000..d78e73f --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapMerge.java @@ -0,0 +1,58 @@ +package com.learnjava.lambda; + +import java.util.HashMap; +import java.util.Map; + +/** + * 关于Map的合并操作 + * + * @author lhy + * @date 2021年7月20日 + */ +public class LambdaMapMerge { + + public static void main(String[] args) { +// mapMerge(); +// mapMerge2(); + } + + /** + * value为int类型的map merge操作,将两个map,相同key merge在一起 + * + * key:string + * value:int + */ + public static void mapMerge() { + Map map1= new HashMap(); + map1.put("one",1); + map1.put("two",2); + map1.put("three",3); + Map map2= new HashMap(); + map2.put("one",1); + map2.put("two",2); + + map1.forEach((key, value) -> map2.merge(key, value, Integer::sum)); + System.out.println(map2); + } + + /** + * value为int类型的map merge操作,将两个map,相同key merge在一起 + * + * key:string + * value:String + */ + public static void mapMerge2() { + Map map1= new HashMap(); + map1.put("one","1"); + map1.put("two","2"); + map1.put("three","3"); + Map map2= new HashMap(); + map2.put("one","1"); + map2.put("two","2"); + + map1.forEach((key, value) -> map2.merge(key, value,(total, num) -> String.valueOf(Integer.parseInt(total) + Integer.parseInt(num)))); + + System.out.println(map2); + + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaReduceDemo.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaReduceDemo.java new file mode 100644 index 0000000..b656747 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaReduceDemo.java @@ -0,0 +1,104 @@ +package com.learnjava.lambda; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +/** + * @author bruis + * 累加器 + */ +public class LambdaReduceDemo { + + public static void main(String[] args) { +// test01(); +// test02(); + test03(); + } + + /** + * 对整形进行操作 + */ + public static void test01() { + List numbers = Arrays.asList(1, 2, 3, 4, 5); + Integer reduce = numbers + .stream() + .reduce(0, LambdaReduceDemo::mySum); +// .reduce(0, Integer::sum); +// .reduce(0, (total, element) -> total + element); + System.out.println(reduce); + } + + /** + * 对字符串进行操作 + */ + public static void test02() { + List letters = Arrays.asList("a", "b", "c", "d", "e"); + String reduce = letters + .stream() + .reduce("", String::concat); +// .reduce("", (totol, element) -> totol.concat(element)); + System.out.println(reduce); + } + + /** + * 操作Employee集合元素,将所有员工的年龄通过reduce累加起来 + * + * U identity, BiFunction accumulator, BinaryOperator combiner + * reduce的三个参数: + * 1)初始值; + * 2)累加器(可自己实现逻辑) + * 3) 合并器(parallelStream模式时的合并) + * + */ + public static void test03() { + // 将Employee集合元素转化为Integer集合元素(流) + // map的操作就是将a类型元素转化为b类型元素 + Stream integerStream = LambdaComparatorDemo + .employees + .stream() + .map(LambdaComparatorDemo.Employee::getAge); + + // 求所有员工的年龄 + Integer totalAge = integerStream.reduce(0, Integer::sum); + System.out.println(totalAge); + + // 数据量大的话,可以用并行计算 + // 先讲员工集合转化为"并行流" + Stream parallelStream = LambdaComparatorDemo + .employees + .parallelStream() + .map(LambdaComparatorDemo.Employee::getAge); + + // 相比于普通的单个流,parallelStream多了个合并器,将多个CPU计算的结果再合并到同一个流 + Integer reduce = parallelStream.reduce(0, Integer::sum, Integer::sum); + System.out.println(reduce); + + // 可以不用map将employee转化为Integer对象,可以直接reduce操作,最后通过Integer::sum这个合并器来将结果合并为Integer类型 + Integer total = LambdaComparatorDemo + .employees + .stream() + .reduce(0, (subTotal, emp) -> subTotal + emp.getAge(), Integer::sum); + System.out.println(total); + + Integer total2 = LambdaComparatorDemo + .employees + .stream() + .reduce(0, LambdaReduceDemo::mySum2, Integer::sum); + System.out.println(total2); + } + + /** + * 可作为BiFunction,传入到reduce作为入参 + * @param a + * @param b + * @return + */ + public static Integer mySum(int a, int b) { + return a + b; + } + + public static Integer mySum2(int a, LambdaComparatorDemo.Employee employee) { + return a + employee.getAge(); + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/StreamMatchDemo.java b/JdkLearn/src/main/java/com/learnjava/lambda/StreamMatchDemo.java new file mode 100644 index 0000000..3962b48 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/lambda/StreamMatchDemo.java @@ -0,0 +1,49 @@ +package com.learnjava.lambda; + +import java.util.Optional; + +/** + * @author bruis + */ +public class StreamMatchDemo { + + public static void main(String[] args) { +// test01(); + test02(); + } + + /** + * 判断是否有年龄大于70的员工 + */ + public static void test01() { + boolean isExistAgeThan70 = LambdaComparatorDemo.employees + .stream() + // 使用了Employee的谓语语句(这种写法方便复用) + .anyMatch(LambdaComparatorDemo.Employee.ageGreaterThan70); +// .anyMatch(e -> e.getAge()> 70); + System.out.println(isExistAgeThan70); + + boolean isExistAgeLessThan18 = LambdaComparatorDemo.employees + .stream() + .noneMatch(LambdaComparatorDemo.Employee.ageLessThan18); + + System.out.println(isExistAgeLessThan18); + } + + /** + * 元素查找与Optional + */ + public static void test02() { + + Optional employeeOptional = LambdaComparatorDemo.employees + .stream() + .filter(e -> e.getAge()> 400) + .findFirst(); + + // Optional#get 会报空 +// System.out.println(employeeOptional.get()); + LambdaComparatorDemo.Employee employee = employeeOptional.orElse(null); + System.out.println(employee == null); + } + +} diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/StreamParallelDemo.java b/JdkLearn/src/main/java/com/learnjava/lambda/StreamParallelDemo.java new file mode 100644 index 0000000..93c120c --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/lambda/StreamParallelDemo.java @@ -0,0 +1,10 @@ +package com.learnjava.lambda; + +/** + * + * 串行、并行 todo + * + * @author bruis + */ +public class StreamParallelDemo { +} diff --git a/JdkLearn/src/main/java/com/learnjava/optimization/BeanCopierDemo.java b/JdkLearn/src/main/java/com/learnjava/optimization/BeanCopierDemo.java new file mode 100644 index 0000000..5068206 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/optimization/BeanCopierDemo.java @@ -0,0 +1,170 @@ +package com.learnjava.optimization; + +import org.springframework.cglib.beans.BeanCopier; + +/** + * + * 经过测试,BeanCopier性能是BeanUtils10倍左右。 + * + * BeanCopier拷贝速度快,性能瓶颈出现在创建BeanCopier实例的过程中。 所以,把创建过的BeanCopier实例放到缓存中,下次可以直接获取,提升性能: + * + * + * @author lhy + * @date 2021年7月21日 + */ +public class BeanCopierDemo { + + private static final BeanCopier BEAN_COPIER = BeanCopier.create(Person.class, PersonVo.class, false); + + public static void main(String[] args) { + Person person = new Person("zs", "high School", 16, 177, 126); + PersonVo vo = new PersonVo(); + + BEAN_COPIER.copy(person, vo, null); + + System.out.println(vo); + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public static class PersonVo { + private String name; + private String grade; + private Integer age; + private Integer height; + private Integer weight; + + @Override + public String toString() { + return "PersonVo{" + + "name='" + name + '\'' + + ", grade='" + grade + '\'' + + ", age=" + age + + ", height=" + height + + ", weight=" + weight + + '}'; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getGrade() { + return grade; + } + + public void setGrade(String grade) { + this.grade = grade; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public Integer getHeight() { + return height; + } + + public void setHeight(Integer height) { + this.height = height; + } + + public Integer getWeight() { + return weight; + } + + public void setWeight(Integer weight) { + this.weight = weight; + } + } + + public static class Person { + private String name; + private String grade; + private Integer age; + private Integer height; + private Integer weight; + + public Person(String name, String grade, Integer age, Integer height, Integer weight) { + this.name = name; + this.grade = grade; + this.age = age; + this.height = height; + this.weight = weight; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getGrade() { + return grade; + } + + public void setGrade(String grade) { + this.grade = grade; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public Integer getHeight() { + return height; + } + + public void setHeight(Integer height) { + this.height = height; + } + + public Integer getWeight() { + return weight; + } + + public void setWeight(Integer weight) { + this.weight = weight; + } + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/optimization/CaffeineDemo.java b/JdkLearn/src/main/java/com/learnjava/optimization/CaffeineDemo.java new file mode 100644 index 0000000..88de1a0 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/optimization/CaffeineDemo.java @@ -0,0 +1,131 @@ +package com.learnjava.optimization; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.RemovalListener; + +import java.math.BigDecimal; +import java.util.concurrent.TimeUnit; + +/** + * Caffeine 代码Demo(SpringBoot自带的缓存类) + * + * + * @author lhy + * @date 2021年7月22日 + */ +public class CaffeineDemo { + + private static Cache productVoCache; + + public static RemovalListener listener = (k, v, cause) -> { + // 业务逻辑 + + + // 触发异常 + switch (cause) { + // 过期 + case EXPIRED: + break; + // 手动删除 + case EXPLICIT: + break; + // 被替换 + case REPLACED: + break; + // 垃圾回收 + case COLLECTED: + break; + // 超过数量限制 + case SIZE: + break; + default: + break; + } + }; + + public static void main(String[] args) { + // 初始化 + // afterPropertiesSet(); + } + + /** + * 模拟Spring的类初始化的时候对缓存进行初始化 + */ + public static void afterPropertiesSet() { + productVoCache = Caffeine.newBuilder() + .softValues() + .refreshAfterWrite(7200, TimeUnit.SECONDS) + .removalListener(listener) + // .build(k -> loadSync(k)) + // 非static类中,可以使用这种方式.build(this::loadSync); + .build(CaffeineDemo::loadSync); + } + + /** + * 获取对应缓存内容 + * @param key + * @return + */ + public static ProductVo getProductVo(String key) { + return productVoCache.get(key, CaffeineDemo::loadSync); + } + + /** + * 对对应商品进行缓存 + * @param key + */ + public static void putProductVo(String key) { + productVoCache.put(key, loadSync(key)); + } + + private static ProductVo loadSync(String key) { + // 业务逻辑 + return new ProductVo(); + } + + + + + + + + + + + + + + + + + + + + + + public static class ProductVo { + + private String productName; + + private BigDecimal price; + + public ProductVo() {} + + public String getProductName() { + return productName; + } + + public void setProductName(String productName) { + this.productName = productName; + } + + public BigDecimal getPrice() { + return price; + } + + public void setPrice(BigDecimal price) { + this.price = price; + } + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeDemo.java b/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeDemo.java new file mode 100644 index 0000000..fe81a19 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeDemo.java @@ -0,0 +1,70 @@ +package com.learnjava.optimization; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * + * 代码优化技巧总结 + * + * @author lhy + * @date 2021年7月19日 + */ +public class OptimizeDemo { + public static void main(String[] args) { + Map map = new HashMap(); + mergeData(map); + + Map concurrentMap = new ConcurrentHashMap(); + concurrentMergeData(concurrentMap); + } + + /** + * 对于通过map来聚合数据(非Lambda方式) + * @param map + */ + public static void mergeData(Map map) { + String key = "mapKey"; + int value = 1; + // 普通方式 + if (map.containsKey(key)) { + map.put(key, map.get(key) + value); + } else { + map.put(key,value); + } + + // 简洁方式 + Integer mapValue = map.get(key); + if (null != mapValue) { + mapValue += value; + } else { + mapValue = value; + } + map.put(key, mapValue); + } + + /** + * 针对mergeData里map的put操作,在并发情况下会存在put的时候,以及有其他线程已经put成功了,导致线程不安全, + * 所以需要使用并发集合列的putIfAbsent方法 + * @param map + */ + public static void concurrentMergeData(Map map) { + String key = "mapKey"; + int value = 1; + Integer mapValue = map.get(key); + if (null != mapValue) { + mapValue += value; + } else { + mapValue = value; + } + map.putIfAbsent(key, mapValue); + + // computeIfAbsent方法对map中的key只进行重新计算,如果不存在这个key,则添加到map中 + map.computeIfAbsent(key, (k) -> { + // 其他计算 + int a = 1, b = 2; + return a + b; + }); + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeUtilDemo.java b/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeUtilDemo.java new file mode 100644 index 0000000..6f131f6 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeUtilDemo.java @@ -0,0 +1,21 @@ +package com.learnjava.optimization; + +/** + * 优化工具类例子 + * + * @author lhy + * @date 2021年7月20日 + */ +public class OptimizeUtilDemo { + + /** + * 超快深拷贝工具类BeanCopier + */ + private BeanCopierDemo beanCopierDemo; + + /** + * Caffeine Cache + */ + private CaffeineDemo caffeineDemo; + +} diff --git a/JdkLearn/src/main/java/com/learnjava/proxy/dynamicproxy/DemoInvokerHandler.java b/JdkLearn/src/main/java/com/learnjava/proxy/dynamicproxy/DemoInvokerHandler.java new file mode 100644 index 0000000..c04119e --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/proxy/dynamicproxy/DemoInvokerHandler.java @@ -0,0 +1,53 @@ +package com.learnjava.proxy.dynamicproxy; + +import com.learnjava.proxy.staticproxy.RealSubject; +import com.learnjava.proxy.staticproxy.Subject; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * @author bruis + * + * jdk动态代理 + * + */ +public class DemoInvokerHandler implements InvocationHandler { + + // 真正的业务对象 + private Object realSubject; + + public DemoInvokerHandler(Object realSubject) { + this.realSubject = realSubject; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + System.out.println("代理预处理操作"); + + // 调用真正的代理对象逻辑 + Object result = method.invoke(realSubject, args); + + System.out.println("代理后处理操作"); + return result; + } + + /** + * 创建代理对象并返回 + * @return + */ + public Object getProxy() { + return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + realSubject.getClass().getInterfaces(), this); + } + + public static void main(String[] args) { + RealSubject realSubject = new RealSubject(); + DemoInvokerHandler invokerHandler = new DemoInvokerHandler(realSubject); + Subject proxy = (Subject) invokerHandler.getProxy(); + + // 拿到业务对象,执行业务逻辑,此时业务逻辑已经被代理对象代理了 + proxy.operation(); + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/JdkStaticProxy.java b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/JdkStaticProxy.java new file mode 100644 index 0000000..885687d --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/JdkStaticProxy.java @@ -0,0 +1,16 @@ +package com.learnjava.proxy.staticproxy; + +/** + * @author bruis + * + * JDK静态代理 + * + * 特点: + * 1. 静态代理模式需要在编译模式为每个业务类创建代理类Proxy,当需要代理的类很多时,就会出现大量的Proxy类 + * + */ +public class JdkStaticProxy { + + + +} diff --git a/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/RealSubject.java b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/RealSubject.java new file mode 100644 index 0000000..600ccc1 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/RealSubject.java @@ -0,0 +1,10 @@ +package com.learnjava.proxy.staticproxy; + +public class RealSubject implements Subject { + + @Override + public void operation() { + System.out.println("这是真正的业务类"); + } + +} diff --git a/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/Subject.java b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/Subject.java new file mode 100644 index 0000000..d48cf48 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/Subject.java @@ -0,0 +1,5 @@ +package com.learnjava.proxy.staticproxy; + +public interface Subject { + public void operation(); +} diff --git a/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/SubjectProxy.java b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/SubjectProxy.java new file mode 100644 index 0000000..f151e73 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/SubjectProxy.java @@ -0,0 +1,22 @@ +package com.learnjava.proxy.staticproxy; + +public class SubjectProxy implements Subject { + + private RealSubject realSubject; + + public SubjectProxy() { + realSubject = new RealSubject(); + } + + @Override + public void operation() { + System.out.println("代理预处理逻辑"); + realSubject.operation(); + System.out.println("代理后处理逻辑"); + } + + public static void main(String[] args) { + SubjectProxy proxy = new SubjectProxy(); + proxy.operation(); + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/reference/PhantomRefTest.java b/JdkLearn/src/main/java/com/learnjava/reference/PhantomRefTest.java new file mode 100644 index 0000000..426ef67 --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/reference/PhantomRefTest.java @@ -0,0 +1,35 @@ +package com.learnjava.reference; + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; + +public class PhantomRefTest { + + public static void main(String[] args) throws InterruptedException { + ReferenceQueue referenceQueue = new ReferenceQueue(); + + // 10mb + byte[] buffer = new byte[1024 * 1024 * 10]; + + PhantomReference phantomReference = new PhantomReference(buffer, referenceQueue); + + // 字节数组对象,失去了强引用 + buffer = null; + + Reference ref0 = referenceQueue.poll(); + + System.out.println("gc 执行之前, refQueue中是否有数据?" + (ref0 != null ? "有" : "没有")); + System.out.println("gc 执行之前, ref引用的对象:" + phantomReference.get()); + + System.gc(); + // 确保gc程序执行 + Thread.sleep(1000); + + System.out.println("gc 执行之后, ref引用的对象:" + phantomReference.get()); + + Reference ref = referenceQueue.poll(); + System.out.println("gc 执行之后, refQueue中是否有数据?" + (ref != null ? "有" : "没有")); + System.out.println("referenceQueue 中获取的 ref与 weakReference中的是否一致?" + (ref == phantomReference)); + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/reference/SoftReferenceTest.java b/JdkLearn/src/main/java/com/learnjava/reference/SoftReferenceTest.java new file mode 100644 index 0000000..38e706e --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/reference/SoftReferenceTest.java @@ -0,0 +1,35 @@ +package com.learnjava.reference; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * 软引用 + */ +public class SoftReferenceTest { + public static void main(String[] args) throws InterruptedException { + List refList = new ArrayList(); + + for (int i = 0; i < 1000; i++) { + // 10mb + byte[] buffer = new byte[1024 * 1024 * 10]; + + SoftReference softReference = new SoftReference(buffer); + + refList.add(softReference); + } + + System.gc(); + Thread.sleep(1000); + + Iterator it = refList.iterator(); + + while (it.hasNext()) { + Reference ref = it.next(); + System.out.println("当前ref引用的对象:" + ref.get()); + } + } +} diff --git a/JdkLearn/src/main/java/com/learnjava/reference/WeakReferenceTest.java b/JdkLearn/src/main/java/com/learnjava/reference/WeakReferenceTest.java new file mode 100644 index 0000000..2e2ec2d --- /dev/null +++ b/JdkLearn/src/main/java/com/learnjava/reference/WeakReferenceTest.java @@ -0,0 +1,66 @@ +package com.learnjava.reference; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * soft软引用 gc堆内存不够时,可能会回收 + * weak弱引用 gc时,内存一定会回收不可用对象 + * phantom虚引用 + */ +public class WeakReferenceTest { + public static void main(String[] args) throws Exception { +// test01(); + test02(); + } + + private static void test01() throws InterruptedException { + List refList = new ArrayList(); + + for (int i = 0; i < 1000; i++) { + // 10mb + byte[] buffer = new byte[1024 * 1024 * 10]; + + WeakReference weakReference = new WeakReference(buffer); + + refList.add(weakReference); + } + + // 将buffer直接全部回收了 + System.gc(); + Thread.sleep(1000); + Iterator iterator = refList.iterator(); + while (iterator.hasNext()) { + Reference ref = iterator.next(); + System.out.println("当前ref引用的对象:" + ref.get()); + } + } + + private static void test02() throws InterruptedException { + ReferenceQueue refQueue = new ReferenceQueue(); + + // 10 mb + byte[] buffer = new byte[1024 * 1024 * 10]; + WeakReference weakReference = new WeakReference(buffer, refQueue); + // 失去强引用关联 + buffer = null; + + Reference ref0 = refQueue.poll(); + System.out.println("gc 执行之前, refQueue中是否有数据?" + (ref0 != null ? "有" : "没有")); + System.out.println("gc 执行之前, ref引用的对象:" + weakReference.get()); + + System.gc(); + // 确保gc程序执行 + Thread.sleep(1000); + + System.out.println("gc 执行之后, ref引用的对象:" + weakReference.get()); + + Reference ref = refQueue.poll(); + System.out.println("gc 执行之后, refQueue中是否有数据?" + (ref != null ? "有" : "没有")); + System.out.println("referenceQueue 中获取的 ref与 weakReference中的是否一致?" + (ref == weakReference)); + } +} diff --git a/JdkLearn/src/test/java/com/learnjava/string/StringTest.java b/JdkLearn/src/test/java/com/learnjava/string/StringTest.java new file mode 100644 index 0000000..72e66c0 --- /dev/null +++ b/JdkLearn/src/test/java/com/learnjava/string/StringTest.java @@ -0,0 +1,49 @@ +package com.learnjava.string; + +import org.junit.Test; + +/** + * @author LuoHaiYang + */ +public class StringTest { + + @Test + public void testSplitWithSplit() { + String splitStr1 = "what,is,,,,split"; + String[] strs1 = splitStr1.split(","); + for (String s : strs1) { + System.out.println(s); + } + System.out.println(strs1.length); + } + + @Test + public void testSplitWithOutSplit() { + String splitStr1 = "what,is,,,,"; + String[] strs1 = splitStr1.split(","); + for (String s : strs1) { + System.out.println(s); + } + System.out.println(strs1.length); + } + + @Test + public void testSplitLimit() { + String splitStr1 = "what,is,,,,"; + String[] strs1 = splitStr1.split(",", -1); + for (String s : strs1) { + System.out.println(s); + } + System.out.println(strs1.length); + } + + @Test + public void testSplitLimitGreaterThanZero() { + String splitStr1 = "what,is,,,,"; + String[] strs1 = splitStr1.split(",", 3); + for (String s : strs1) { + System.out.println(s); + } + System.out.println(strs1.length); + } +} diff --git a/README.md b/README.md index d313998..fae4153 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +![JavaSourceCodeLearningImage](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/images/JavaSourceCodeLearningImage.png)

@@ -16,25 +17,42 @@ Spring-Security-OAuth2 - Netty + Netty + + + Netty

-Java流行框架源码分析,学习以及总结。 +Java流行框架源码分析,学习以及总结。项目持续更新中,不建议直接Fork,欢迎star、watch。 + +对于框架底层源码的学习,需要反复、认真思考,并做到温故而知新,这样才能将底层原理吸收得更加牢固。 + 框架包括: -- [x] Spring源码 -- [x] SpringBoot源码 -- [x] SpringAOP源码 -- [x] SpringSecurity源码 -- [x] SpringSecurity OAuth2源码 -- [x] JDK源码 -- [ ] Netty源码 -- [ ] MyBatis源码 -- [x] Dubbo源码 -- [ ] SpringCloud源码 +✅ Spring源码 + +✅ SpringBoot源码 + +✅ SpringAOP源码 + +✅ SpringSecurity源码 + +✅ SpringSecurity OAuth2源码 + +✅ JDK源码 + +✅ Dubbo源码 + +✅ Netty源码 + +✅ RocketMQ源码 + +MyBatis源码 + +SpringCloud源码 > 为什么要分析、学习源码? @@ -56,6 +74,8 @@ Java流行框架源码分析,学习以及总结。 - [一篇文章快速深入学习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扩容原理]() + - [开源项目里那些看不懂的位运算分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/JDK/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E9%87%8C%E9%82%A3%E4%BA%9B%E7%9C%8B%E4%B8%8D%E6%87%82%E7%9A%84%E4%BD%8D%E8%BF%90%E7%AE%97%E5%88%86%E6%9E%90.md) - Spring源码学习 - Spring版本:5.2.1.RELEASE @@ -76,39 +96,74 @@ Java流行框架源码分析,学习以及总结。 - SpringBoot源码学习 - SpringBoot版本:2.2.1.RELEASE - - [深入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%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) + - [深入浅出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/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源码学习 - 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) - - [从零开始系统学习SpringSecurity和OAuth2(二)—— 安全过滤器FilterChainProxy](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%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20%E5%AE%89%E5%85%A8%E8%BF%87%E6%BB%A4%E5%99%A8FilterChainProxy.md) - - [从零开始系统学习SpringSecurity和OAuth2(三)—— WebSecurity建造核心逻辑](https://github.com/coderbruis/JavaSourceCodeLearning/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%89%EF%BC%89%E2%80%94%E2%80%94%20WebSecurity%E5%BB%BA%E9%80%A0%E6%A0%B8%E5%BF%83%E9%80%BB%E8%BE%91.md) - - [从零开始系统学习SpringSecurity和OAuth2(四)—— FilterChainProxy过滤器链中的几个重要的过滤器](https://github.com/coderbruis/JavaSourceCodeLearning/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%E5%9B%9B%EF%BC%89%E2%80%94%E2%80%94%20FilterChainProxy%E8%BF%87%E6%BB%A4%E5%99%A8%E9%93%BE%E4%B8%AD%E7%9A%84%E5%87%A0%E4%B8%AA%E9%87%8D%E8%A6%81%E7%9A%84%E8%BF%87%E6%BB%A4%E5%99%A8.md) + - [深入浅出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) + - [深入浅出SpringSecurity和OAuth2(二)—— 安全过滤器FilterChainProxy](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%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20%E5%AE%89%E5%85%A8%E8%BF%87%E6%BB%A4%E5%99%A8FilterChainProxy.md) + - [深入浅出SpringSecurity和OAuth2(三)—— WebSecurity建造核心逻辑](https://github.com/coderbruis/JavaSourceCodeLearning/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%89%EF%BC%89%E2%80%94%E2%80%94%20WebSecurity%E5%BB%BA%E9%80%A0%E6%A0%B8%E5%BF%83%E9%80%BB%E8%BE%91.md) + - [深入浅出SpringSecurity和OAuth2(四)—— FilterChainProxy过滤器链中的几个重要的过滤器](https://github.com/coderbruis/JavaSourceCodeLearning/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%E5%9B%9B%EF%BC%89%E2%80%94%E2%80%94%20FilterChainProxy%E8%BF%87%E6%BB%A4%E5%99%A8%E9%93%BE%E4%B8%AD%E7%9A%84%E5%87%A0%E4%B8%AA%E9%87%8D%E8%A6%81%E7%9A%84%E8%BF%87%E6%BB%A4%E5%99%A8.md) - Dubbo底层源码解析 - Dubbo底层源码版本:2.7.8 + - [Dubbo底层源码学习—— 源码搭建](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E6%BA%90%E7%A0%81%E6%90%AD%E5%BB%BA.md) - [Dubbo底层源码学习(一)—— Dubbo的URL](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%20Dubbo%E7%9A%84URL.md) - - Dubbo底层源码学习(二)—— Dubbo的SPI + - [Dubbo底层源码学习(二)—— Dubbo的SPI机制(上)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20Dubbo%E7%9A%84SPI%E6%9C%BA%E5%88%B6%EF%BC%88%E4%B8%8A%EF%BC%89.md) + - [Dubbo底层源码学习(二)—— Dubbo的SPI机制(中)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo底层源码学习%EF%BC%88二%EF%BC%89——%20Dubbo的SPI机制%EF%BC%88中%EF%BC%89.md +) + - [Dubbo底层源码学习(二)—— Dubbo的SPI机制(下)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20Dubbo%E7%9A%84SPI%E6%9C%BA%E5%88%B6%EF%BC%88%E4%B8%8B%EF%BC%89.md) - Dubbo底层源码学习(三)—— Dubbo的注册中心 - Dubbo底层源码学习(四)—— Dubbo的注册中心缓存机制 - Dubbo底层源码学习(五)—— Dubbo的注册中心重试机制 + - [Dubbo底层源码学习(六)—— Dubbo的服务暴露](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94%E6%9C%8D%E5%8A%A1%E6%9A%B4%E9%9C%B2.md) + - Dubbo底层源码学习(七)—— Dubbo的服务消费 + +- Netty底层源码解析 + - 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底层源码解析-初始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底层源码解析-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接受数据原理分析](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底层源码解析-内存分配原理分析 + - Netty底层源码解析-RocketMQ底层使用到的Netty + - [Netty底层的优化总结]() + - [实战+原理效果更佳!强烈推荐闪电侠大佬实战课:《Netty 入门与实战:仿写微信 IM 即时通讯系统》](https://juejin.cn/book/6844733738119593991) + +Netty实战课相关点位于:Spring-Netty,com/bruis/learnnetty/im包下,有需要的读者可前往查看。 + + +- RocketMQ底层源码解析 + - RocketMQ版本:4.9.0 + - RocketMQ底层源码解析-RocketMQ环境搭建 + - RocketMQ底层源码解析-本地调试RocketMQ源码 + - RocketMQ底层源码解析-NameServer分析 持续更新中... - + +todo + +2021年年底完成了人生的两件大事,所以一直没时间持续输出源码分析,2022年开始需要继续努力,继续完成这个源码分析项目! + +- 完成Netty剩余源码分析文章 +- 完成RocketMQ剩余源码分析文章 +- 完成Dubbo剩余源码分析文章 +- C语言基础学习(为Redis底层源码学习做准备) +- Redis底层源码分析 +- JUC底层源码分析 # 支持 - ![给个赞吧](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/images/zan.jpg) - 原创不易,各位帅哥美女star支持下... diff --git a/Spring-Netty/pom.xml b/Spring-Netty/pom.xml index 14ee73a..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,12 @@ io.netty netty-all - ${netty-all.version} + + + + com.alibaba + fastjson + 1.2.76 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..4a701b4 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java @@ -0,0 +1,106 @@ +package com.bruis.learnnetty.im.client; + +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; +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; +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 PacketDecoder()); + // 登录响应 + ch.pipeline().addLast(new LoginResponseHandler()); + // 消息返回 + 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()); + } + }); + + 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) { + ConsoleCommandManager consoleCommandManager = new ConsoleCommandManager(); + LoginConsoleCommand loginConsoleCommand = new LoginConsoleCommand(); + Scanner scanner = new Scanner(System.in); + + new Thread(() -> { + while (!Thread.interrupted()) { + if (!SessionUtil.hasLogin(channel)) { + loginConsoleCommand.exec(scanner, channel); + } else { + consoleCommandManager.exec(scanner, channel); + } + } + }).start(); + } +} 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/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/LoginResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java new file mode 100644 index 0000000..282afcc --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java @@ -0,0 +1,33 @@ +package com.bruis.learnnetty.im.client.handler; + +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; + +/** + * @Description 登录响应的reponse + * @Author luohaiyang + * @Date 2022年3月23日 + */ +public class LoginResponseHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, LoginResponsePacket loginResponsePacket) throws Exception { + String userId = loginResponsePacket.getUserId(); + String userName = loginResponsePacket.getUserName(); + + if (loginResponsePacket.isSuccess()) { + System.out.println("[" + userName + "]登录成功,userId 为: " + loginResponsePacket.getUserId()); + SessionUtil.bindSession(new Session(userId, userName), ctx.channel()); + } else { + System.out.println("[" + userName + "]登录失败,原因:" + 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/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/client/handler/MessageResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java new file mode 100644 index 0000000..ea6fac1 --- /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; + +/** + * @Description + * @Author luohaiyang + * @Date 2022年3月23日 + */ +public class MessageResponseHandler extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageResponsePacket messageResponsePacket) throws Exception { + 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/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/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/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..c09b096 --- /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/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..8bf69f6 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/ConsoleCommandManager.java @@ -0,0 +1,43 @@ +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 LogoutConsoleCommand()); + 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 + 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/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/LoginConsoleCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LoginConsoleCommand.java new file mode 100644 index 0000000..3e632ca --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/console/LoginConsoleCommand.java @@ -0,0 +1,38 @@ +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("输入用户名登录: "); + String userIdStr; // 在退出登录logout之后 这里会读取到最后一个回车符 用户名就是空字符串会导致无法退出登录 + while ((userIdStr = scanner.nextLine()).isEmpty()) { + System.out.println("用户名异常, 请重新输入"); + } + loginRequestPacket.setUserName(userIdStr); + 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/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/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/Attributes.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Attributes.java new file mode 100644 index 0000000..fe72853 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/Attributes.java @@ -0,0 +1,13 @@ +package com.bruis.learnnetty.im.model; + +import com.bruis.learnnetty.im.session.Session; +import io.netty.util.AttributeKey; + +/** + * @Description Netty 属性集 + * @Author haiyangluo + * @Date 2022年3月22日 + */ +public interface Attributes { + AttributeKey SESSION = AttributeKey.newInstance("session"); +} 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/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/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/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..32599fd --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/LoginResponsePacket.java @@ -0,0 +1,57 @@ +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 String userId; + + private String userName; + + 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; + } + + 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/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/MessageRequestPacket.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageRequestPacket.java new file mode 100644 index 0000000..4dae50c --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageRequestPacket.java @@ -0,0 +1,43 @@ +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 { + + private String toUserId; + + private String message; + + public MessageRequestPacket(){} + + public MessageRequestPacket(String toUserId, String message) { + this.toUserId = toUserId; + this.message = message; + } + + @Override + public Byte getCommand() { + return MESSAGE_REQUEST; + } + + public String getMessage() { + return message; + } + + 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 new file mode 100644 index 0000000..372a33b --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/MessageResponsePacket.java @@ -0,0 +1,47 @@ +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 fromUserId; + + private String fromUserName; + + private String message; + + @Override + public Byte getCommand() { + + return MESSAGE_RESPONSE; + } + + public String getMessage() { + return message; + } + + 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/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..7a81483 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/model/PacketCodeC.java @@ -0,0 +1,102 @@ +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 java.util.HashMap; +import java.util.Map; + +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); + 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); + 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(); + 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 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 getRequestType(byte command) { + + return packetTypeMap.get(command); + } +} 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/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..08d3f14 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/NettyServer.java @@ -0,0 +1,58 @@ +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; +import com.bruis.learnnetty.im.server.handler.*; +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(PacketCodecHandler.INSTANCE); + ch.pipeline().addLast(LoginRequestHandler.INSTANCE); + ch.pipeline().addLast(AuthHandler.INSTANCE); + ch.pipeline().addLast(IMHandler.INSTANCE); + } + }); + 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/AuthHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/AuthHandler.java new file mode 100644 index 0000000..ad5c784 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/AuthHandler.java @@ -0,0 +1,29 @@ +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; + +/** + * @Description + * @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())) { + 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/CreateGroupRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java new file mode 100644 index 0000000..64c57cf --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/CreateGroupRequestHandler.java @@ -0,0 +1,58 @@ +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.ChannelHandler; +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日 + */ +@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(); + 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()); + } + } + + String groupId = IDUtil.randomUserId(); + CreateGroupResponsePacket createGroupResponsePacket = new CreateGroupResponsePacket(); + createGroupResponsePacket.setSuccess(true); + 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/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 new file mode 100644 index 0000000..1188ec6 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/JoinGroupRequestHandler.java @@ -0,0 +1,39 @@ +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.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 JoinGroupRequestHandler extends SimpleChannelInboundHandler { + + public static final JoinGroupRequestHandler INSTANCE = new JoinGroupRequestHandler(); + + protected JoinGroupRequestHandler() {} + + @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..8be361d --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/ListGroupMembersRequestHandler.java @@ -0,0 +1,48 @@ +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.ChannelHandler; +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日 + */ +@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 + 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/LoginRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java new file mode 100644 index 0000000..0286a96 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LoginRequestHandler.java @@ -0,0 +1,62 @@ +package com.bruis.learnnetty.im.server.handler; + +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.*; + +import java.util.Arrays; +import java.util.Date; + +/** + * @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) { + // 登录校验响应 + LoginResponsePacket loginResponsePacket = new LoginResponsePacket(); + loginResponsePacket.setVersion(loginRequestPacket.getVersion()); + loginResponsePacket.setUserName(loginRequestPacket.getUserName()); + + if (valid(loginRequestPacket)) { + loginResponsePacket.setSuccess(true); + String userId = IDUtil.randomUserId(); + loginResponsePacket.setUserId(userId); + System.out.println("[" + loginRequestPacket.getUserName() + "]登录成功"); + SessionUtil.bindSession(new Session(userId, loginRequestPacket.getUserName()), ctx.channel()); + } else { + loginResponsePacket.setReason("账号密码校验失败"); + loginResponsePacket.setSuccess(false); + System.out.println(new Date() + ": 登录失败!"); + } + + // 登录响应 + 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) { + return true; + } + + @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..4436802 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/LogoutRequestHandler.java @@ -0,0 +1,29 @@ +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.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +/** + * @Description + * @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()); + 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/server/handler/MessageRequestHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java new file mode 100644 index 0000000..b9b83a0 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/MessageRequestHandler.java @@ -0,0 +1,45 @@ +package com.bruis.learnnetty.im.server.handler; + +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.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +/** + * @Description + * @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.拿到消息发送方的会话信息 + Session session = SessionUtil.getSession(channelHandlerContext.channel()); + + // 2.通过消息发送方的会话信息构造要发送的消息 + MessageResponsePacket messageResponsePacket = new MessageResponsePacket(); + messageResponsePacket.setFromUserId(session.getUserId()); + messageResponsePacket.setFromUserName(session.getUserName()); + messageResponsePacket.setMessage(messageRequestPacket.getMessage()); + + // 3.拿到消息接收方的 channel + Channel toUserChannel = SessionUtil.getChannel(messageRequestPacket.getToUserId()); + + // 4.将消息发送给消息接收方 + if (toUserChannel != null && SessionUtil.hasLogin(toUserChannel)) { + toUserChannel.writeAndFlush(messageResponsePacket); + } else { + System.err.println("[" + messageRequestPacket.getToUserId() + "] 不在线,发送失败!"); + } + } +} 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..86455bb --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/server/handler/QuitGroupRequestHandler.java @@ -0,0 +1,37 @@ +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.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 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(); + 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 new file mode 100644 index 0000000..7a7be2d --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/session/Session.java @@ -0,0 +1,39 @@ +package com.bruis.learnnetty.im.session; + +/** + * @Description + * @Author luohaiyang + * @Date 2022年3月23日 + */ +public class Session { + + private String userId; + + private String userName; + + public Session(String userId, String userName) { + this.userId = userId; + this.userName = userName; + } + + 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; + } + + @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/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]; + } + +} 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..f4b41c3 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/util/SessionUtil.java @@ -0,0 +1,58 @@ +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 io.netty.channel.group.ChannelGroup; + +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(); + + 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); + } + + public static void unBindSession(Channel channel) { + if (hasLogin(channel)) { + Session session = getSession(channel); + userIdChannelMap.remove(session.getUserId()); + channel.attr(Attributes.SESSION).set(null); + System.out.println(session + " 退出登录"); + } + } + + 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); + } + + public static void bindChannelGroup(String groupId, ChannelGroup channelGroup) { + groupIdChannelGroupMap.put(groupId, channelGroup); + } + + public static ChannelGroup getChannelGroup(String groupId) { + return groupIdChannelGroupMap.get(groupId); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/BusinessException.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/BusinessException.java new file mode 100644 index 0000000..e9e5caf --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/BusinessException.java @@ -0,0 +1,8 @@ +package com.bruis.learnnetty.netty.channelpipeline; + +public class BusinessException extends Exception { + + public BusinessException(String message) { + super(message); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/InBoundHandlerA.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/InBoundHandlerA.java new file mode 100644 index 0000000..31004a3 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/InBoundHandlerA.java @@ -0,0 +1,16 @@ +package com.bruis.learnnetty.netty.channelpipeline; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * @author + */ +public class InBoundHandlerA extends ChannelInboundHandlerAdapter { + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + System.out.println("InBoundHandlerA: " + msg); + ctx.fireChannelRead(msg); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/InBoundHandlerB.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/InBoundHandlerB.java new file mode 100644 index 0000000..270d84f --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/InBoundHandlerB.java @@ -0,0 +1,20 @@ +package com.bruis.learnnetty.netty.channelpipeline; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * @author + */ +public class InBoundHandlerB extends ChannelInboundHandlerAdapter { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + System.out.println("InBoundHandlerB: " + msg); + ctx.fireChannelRead(msg); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + ctx.channel().pipeline().fireChannelRead("hello world"); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/InBoundHandlerC.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/InBoundHandlerC.java new file mode 100644 index 0000000..e4dcd7e --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/InBoundHandlerC.java @@ -0,0 +1,16 @@ +package com.bruis.learnnetty.netty.channelpipeline; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.util.ReferenceCountUtil; + +/** + * @author + */ +public class InBoundHandlerC extends ChannelInboundHandlerAdapter { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + System.out.println("InBoundHandlerC: " + msg); + ctx.fireChannelRead(msg); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/OutBoundHandlerA.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/OutBoundHandlerA.java new file mode 100644 index 0000000..4711fd5 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/OutBoundHandlerA.java @@ -0,0 +1,17 @@ +package com.bruis.learnnetty.netty.channelpipeline; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; + +/** + * @author + */ +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); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/OutBoundHandlerB.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/OutBoundHandlerB.java new file mode 100644 index 0000000..05a74fd --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/OutBoundHandlerB.java @@ -0,0 +1,27 @@ +package com.bruis.learnnetty.netty.channelpipeline; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; + +import java.util.concurrent.TimeUnit; + +/** + * @author + */ +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); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/OutBoundHandlerC.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/OutBoundHandlerC.java new file mode 100644 index 0000000..6b49e11 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/OutBoundHandlerC.java @@ -0,0 +1,18 @@ +package com.bruis.learnnetty.netty.channelpipeline; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; + +/** + * @author + */ +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); +// throw new BusinessException("from OutBoundHandlerC"); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/Server.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/Server.java new file mode 100644 index 0000000..dbcd6f7 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/channelpipeline/Server.java @@ -0,0 +1,48 @@ +package com.bruis.learnnetty.netty.channelpipeline; + +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.util.AttributeKey; + +/** + * @author + */ +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()); +// 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(); + } + } +} \ No newline at end of file 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)); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/fastthreadlocal/FastThreadLocalTest.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/fastthreadlocal/FastThreadLocalTest.java new file mode 100644 index 0000000..ec00911 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/fastthreadlocal/FastThreadLocalTest.java @@ -0,0 +1,62 @@ +package com.bruis.learnnetty.netty.fastthreadlocal; + +import io.netty.util.concurrent.FastThreadLocal; + +/** + * FastThreadLocal测试类 + * + * @author lhy + * @date 2021年7月13日 + */ +public class FastThreadLocalTest { + + /** + * FastThreadLocal对象1 + */ + private static FastThreadLocal threadLocal0 = new FastThreadLocal() { + @Override + protected Object initialValue() throws Exception { + Object o = new Object(); + System.out.println("threadLocal0 initialValue: " + o); + return o; + } + + @Override + protected void onRemoval(Object value) throws Exception { + System.out.println("onRemoval"); + } + }; + + private static FastThreadLocal threadLocal1 = new FastThreadLocal() { + @Override + protected Object initialValue() throws Exception { + Object o = new Object(); + System.out.println("threadLocal1 initialValue: " + o); + return o; + } + }; + + public static void main(String[] args) { + new Thread(() -> { + Object object0 = threadLocal0.get(); + System.out.println(Thread.currentThread().getName() + "---> " + object0); + + threadLocal0.set(new Object()); + }) .start(); + + new Thread(() -> { + Object object0 = threadLocal0.get(); + System.out.println(Thread.currentThread().getName() + "---> " + object0); + + System.out.println(Thread.currentThread().getName() + "---> " + (threadLocal0.get() == object0)); +// while (true) { +// System.out.println(Thread.currentThread().getName() + "---> " + (threadLocal0.get() == object0)); +// try { +// Thread.sleep(1000); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } + }).start(); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/fastthreadlocal/ThreadLocalTest.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/fastthreadlocal/ThreadLocalTest.java new file mode 100644 index 0000000..11b1b33 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/fastthreadlocal/ThreadLocalTest.java @@ -0,0 +1,38 @@ +package com.bruis.learnnetty.netty.fastthreadlocal; + +/** + * @author lhy + * @date 2021年7月14日 + */ +public class ThreadLocalTest { + + private static ThreadLocal threadLocal0 = new ThreadLocal(); + + private static ThreadLocal threadLocal1 = new ThreadLocal(); + + public static void main(String[] args) { + // 线程外 + System.out.println("main线程1: " + threadLocal0.get()); + Object o = new Object(); + threadLocal0.set(o); + + new Thread(() -> { + Object threadObject = threadLocal0.get(); + System.out.println("线程内: " + threadObject); + if (threadObject == null) { + Object newObject = new Object(); + System.out.println("新new一个对象:" + newObject); + threadLocal0.set(newObject); + } + try { + Thread.sleep(1000); + System.out.println("休眠了一秒"); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println("线程内,从ThreadLocal获取:" + threadLocal0.get()); + }).start(); + + System.out.println("main线程2: " + threadLocal0.get()); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/ClientTest.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/ClientTest.java new file mode 100644 index 0000000..cd1d1d8 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/ClientTest.java @@ -0,0 +1,100 @@ +package com.bruis.learnnetty.netty.heartbeat; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.concurrent.DefaultEventExecutorGroup; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author lhy + * @date 2021年8月19日 + */ +public class ClientTest { + + public static final EventLoopGroup myEventLoopGroup = new NioEventLoopGroup(1, new ThreadFactory() { + + private AtomicInteger threadIndex = new AtomicInteger(0); + + @Override + public Thread newThread(Runnable r) { + return new Thread(r, String.format("MyNettyClientSelector_%d", this.threadIndex.incrementAndGet())); + } + }); + + public static final DefaultEventExecutorGroup nettyHandlerExecutorGroup = new DefaultEventExecutorGroup(1, + new ThreadFactory() { + private AtomicInteger threadIndex = new AtomicInteger(0); + @Override + public Thread newThread(Runnable r) { + return new Thread(r, "nettyHandlerThread_" + this.threadIndex.incrementAndGet()); + } + }); + + public static final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "scheduledThread_"); + thread.setDaemon(false); + return thread; + } + }); + + public static void main(String[] args) { + + Bootstrap bootstrap = new Bootstrap() + .group(myEventLoopGroup) + .channel(NioSocketChannel.class) + .option(ChannelOption.TCP_NODELAY, true) + .option(ChannelOption.SO_KEEPALIVE, false) + .option(ChannelOption.SO_SNDBUF, 65535) + .option(ChannelOption.SO_RCVBUF, 65535) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast(nettyHandlerExecutorGroup, + new NettyEncoder(), + new NettyDecoder(), + new ConnectResponseHandler()); + } + }); + + InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9090); + + final ChannelFuture channelFuture = bootstrap.connect(inetSocketAddress); + + if (channelFuture.awaitUninterruptibly(2, TimeUnit.MINUTES)) { +// heartBeat(channelFuture.channel()); + scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + heartBeat(channelFuture.channel()); + } + }, 1000, 30 * 1000, TimeUnit.MILLISECONDS); + } + } + + public static void heartBeat(Channel channel) { + String request = "客户端发起了心跳请求"; + RemotingCommand command= new RemotingCommand(); + command.setBody(request.getBytes()); + command.setCode(1); + channel.writeAndFlush(command); + } + + public static class ConnectResponseHandler extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception { + System.out.println("服务端返回消息了:" + new String(msg.getBody())); + } + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/NettyDecoder.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/NettyDecoder.java new file mode 100644 index 0000000..81be993 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/NettyDecoder.java @@ -0,0 +1,40 @@ +package com.bruis.learnnetty.netty.heartbeat; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; + +import java.nio.ByteBuffer; + +/** + * @author lhy + * @date 2021年8月19日 + */ +public class NettyDecoder extends LengthFieldBasedFrameDecoder { + + public NettyDecoder() { + super(16777216, 0, 4, 0, 4); + } + + @Override + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + ByteBuf frame = null; + + try { + frame = (ByteBuf) super.decode(ctx, in); + if (null == frame) { + return null; + } + ByteBuffer byteBuffer = frame.nioBuffer(); + return RemotingCommand.decode(byteBuffer); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (null != frame) { + frame.release(); + } + } + + return null; + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/NettyEncoder.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/NettyEncoder.java new file mode 100644 index 0000000..1a7fd50 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/NettyEncoder.java @@ -0,0 +1,39 @@ +package com.bruis.learnnetty.netty.heartbeat; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +import java.nio.ByteBuffer; + +/** + * @author lhy + * @date 2021年8月19日 + */ +@ChannelHandler.Sharable +public class NettyEncoder extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, RemotingCommand remotingCommand, ByteBuf out) throws Exception { + try { + ByteBuffer header = remotingCommand.encodeHeader(); + out.writeBytes(header); + byte[] body = remotingCommand.getBody(); + if (null != body) { + out.writeBytes(body); + } +// out.writeBytes(remotingCommand.getBody()); + } catch (Exception e) { + e.printStackTrace(); + ctx.channel().close().addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + // 关闭channel成功 + } + }); + } + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/RemotingCommand.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/RemotingCommand.java new file mode 100644 index 0000000..6bf9aa9 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/RemotingCommand.java @@ -0,0 +1,87 @@ +package com.bruis.learnnetty.netty.heartbeat; + +import java.nio.ByteBuffer; + +/** + * @author lhy + * @date 2021年8月19日 + */ +public class RemotingCommand { + + private Integer code; // 请求码 + + private byte[] body; // 请求内容 + + public static RemotingCommand decode(final ByteBuffer byteBuffer) { + int limit = byteBuffer.limit(); + + int oriHeaderLen = byteBuffer.getInt(); + int headerLength = getHeaderLength(oriHeaderLen); + + byte[] headerData = new byte[headerLength]; + byteBuffer.get(headerData); + + int bodyLength = limit - 4 - headerLength; + + byte[] body = new byte[bodyLength]; + byteBuffer.get(body); + RemotingCommand remotingCommand = new RemotingCommand(); + remotingCommand.setBody(body); + return remotingCommand; + } + + public ByteBuffer encodeHeader() { + return encodeHeader(this.body.length); + } + + public ByteBuffer encodeHeader(final int bodyLength) { + int length = 4; + + byte[] headerData; + headerData = this.headerEncode(); + length += headerData.length; // 头 + length += bodyLength; // 请求/响应体 + + ByteBuffer result = ByteBuffer.allocate(4 + length - bodyLength); // 分配header + result.putInt(length); + result.put(markProtocolType(headerData.length, SerializeType.JSON)); + result.put(headerData); // 添加头 + result.flip(); + + return result; + } + + public static byte[] markProtocolType(int source, SerializeType type) { + byte[] result = new byte[4]; + + result[0] = type.getCode(); + result[1] = (byte) ((source>> 16) & 0xFF); + result[2] = (byte) ((source>> 8) & 0xFF); + result[3] = (byte) (source & 0xFF); + return result; + } + + private byte[] headerEncode() { + return RemotingSerializable.encode(this); + } + + public static int getHeaderLength(int length) { + return length & 0xFFFFFF; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public byte[] getBody() { + return body; + } + + public void setBody(byte[] body) { + this.body = body; + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/RemotingSerializable.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/RemotingSerializable.java new file mode 100644 index 0000000..2dca4f3 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/RemotingSerializable.java @@ -0,0 +1,50 @@ +package com.bruis.learnnetty.netty.heartbeat; + +import com.alibaba.fastjson.JSON; + +import java.nio.charset.Charset; + +/** + * @author lhy + * @date 2021年8月19日 + */ +public abstract class RemotingSerializable { + private final static Charset CHARSET_UTF8 = Charset.forName("UTF-8"); + + public static byte[] encode(final Object obj) { + final String json = toJson(obj, false); + if (json != null) { + return json.getBytes(CHARSET_UTF8); + } + return null; + } + + public static String toJson(final Object obj, boolean prettyFormat) { + return JSON.toJSONString(obj, prettyFormat); + } + + public static T decode(final byte[] data, Class classOfT) { + final String json = new String(data, CHARSET_UTF8); + return fromJson(json, classOfT); + } + + public static T fromJson(String json, Class classOfT) { + return JSON.parseObject(json, classOfT); + } + + public byte[] encode() { + final String json = this.toJson(); + if (json != null) { + return json.getBytes(CHARSET_UTF8); + } + return null; + } + + public String toJson() { + return toJson(false); + } + + public String toJson(final boolean prettyFormat) { + return toJson(this, prettyFormat); + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/SerializeType.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/SerializeType.java new file mode 100644 index 0000000..6187252 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/SerializeType.java @@ -0,0 +1,28 @@ +package com.bruis.learnnetty.netty.heartbeat; + +/** + * @author lhy + * @date 2021年8月20日 + */ +public enum SerializeType { + JSON((byte) 0); + + private byte code; + + SerializeType(byte code) { + this.code = code; + } + + public static SerializeType valueOf(byte code) { + for (SerializeType serializeType : SerializeType.values()) { + if (serializeType.getCode() == code) { + return serializeType; + } + } + return null; + } + + public byte getCode() { + return code; + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/ServerTest.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/ServerTest.java new file mode 100644 index 0000000..4753a2e --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/heartbeat/ServerTest.java @@ -0,0 +1,55 @@ +package com.bruis.learnnetty.netty.heartbeat; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; + +/** + * @author lhy + * @date 2021年8月19日 + */ +public class ServerTest { + public static void main(String[] args) throws InterruptedException { + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup=new NioEventLoopGroup(); + ServerBootstrap serverBootstrap=new ServerBootstrap(); + serverBootstrap.group(bossGroup,workerGroup) + .channel(NioServerSocketChannel.class) + //设置线程队列中等待连接的个数 + .option(ChannelOption.SO_BACKLOG,128) + .option(ChannelOption.SO_REUSEADDR, true) + .childOption(ChannelOption.TCP_NODELAY, true) + .childOption(ChannelOption.SO_SNDBUF, 65535) + .childOption(ChannelOption.SO_RCVBUF, 65535) + .childOption(ChannelOption.SO_KEEPALIVE,true) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception { + nioSocketChannel.pipeline() + .addLast(new NettyEncoder(), + new NettyDecoder(), + new ConnectServerHandler()); + + } + }); + + ChannelFuture future = serverBootstrap.bind(9090).sync(); + future.channel().closeFuture().sync(); + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + + public static class ConnectServerHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception { + System.out.println("Server服务端:" + new String(msg.getBody())); + RemotingCommand response = new RemotingCommand(); + response.setBody("接受到请求了".getBytes()); + response.setCode(0); + ctx.writeAndFlush(response); + } + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/NettyClient.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/NettyClient.java similarity index 97% rename from Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/NettyClient.java rename to Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/NettyClient.java index f429e99..db6994b 100644 --- a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/NettyClient.java +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/NettyClient.java @@ -1,4 +1,4 @@ -package com.bruis.learnnetty.netty.demo01; +package com.bruis.learnnetty.netty.quickstart; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/NettyClientHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/NettyClientHandler.java similarity index 98% rename from Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/NettyClientHandler.java rename to Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/NettyClientHandler.java index f777499..976fdda 100644 --- a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/NettyClientHandler.java +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/NettyClientHandler.java @@ -1,4 +1,4 @@ -package com.bruis.learnnetty.netty.demo01; +package com.bruis.learnnetty.netty.quickstart; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/NettyServerHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/NettyServerHandler.java similarity index 96% rename from Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/NettyServerHandler.java rename to Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/NettyServerHandler.java index 9d131fa..b7a9d04 100644 --- a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/NettyServerHandler.java +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/NettyServerHandler.java @@ -1,11 +1,8 @@ -package com.bruis.learnnetty.netty.demo01; +package com.bruis.learnnetty.netty.quickstart; -import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.ChannelPipeline; import io.netty.util.CharsetUtil; import java.util.concurrent.TimeUnit; diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/Server.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/Server.java similarity index 98% rename from Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/Server.java rename to Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/Server.java index a3f99d2..3adeabc 100644 --- a/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/demo01/Server.java +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/netty/quickstart/Server.java @@ -1,4 +1,4 @@ -package com.bruis.learnnetty.netty.demo01; +package com.bruis.learnnetty.netty.quickstart; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; 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; + } +} diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/FutureMain.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/FutureMain.java new file mode 100644 index 0000000..d0de69c --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/FutureMain.java @@ -0,0 +1,64 @@ +package com.bruis.learnnetty.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/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/RequestFuture.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/RequestFuture.java new file mode 100644 index 0000000..1fd7dec --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/RequestFuture.java @@ -0,0 +1,123 @@ +package com.bruis.learnnetty.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/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/Response.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/Response.java new file mode 100644 index 0000000..ae1852c --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/Response.java @@ -0,0 +1,28 @@ +package com.bruis.learnnetty.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/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/SubThread.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/SubThread.java new file mode 100644 index 0000000..9101e54 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/reentranlock/SubThread.java @@ -0,0 +1,30 @@ +package com.bruis.learnnetty.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/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/FutureMain.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/FutureMain.java new file mode 100644 index 0000000..bd4ee93 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/FutureMain.java @@ -0,0 +1,64 @@ +package com.bruis.learnnetty.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/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/RequestFuture.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/RequestFuture.java new file mode 100644 index 0000000..f1dca80 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/RequestFuture.java @@ -0,0 +1,103 @@ +package com.bruis.learnnetty.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/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/Response.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/Response.java new file mode 100644 index 0000000..c5dfc05 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/Response.java @@ -0,0 +1,28 @@ +package com.bruis.learnnetty.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/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/SubThread.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/SubThread.java new file mode 100644 index 0000000..16e2a57 --- /dev/null +++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/thread/synchronize/SubThread.java @@ -0,0 +1,31 @@ +package com.bruis.learnnetty.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); + } +} diff --git a/Spring-Security/SpringSecurityDemo.iml b/Spring-Security/SpringSecurityDemo.iml new file mode 100644 index 0000000..3a7ce81 --- /dev/null +++ b/Spring-Security/SpringSecurityDemo.iml @@ -0,0 +1,91 @@ + + + +