diff --git a/01jvm/GCLearning.md b/01jvm/GCLearning.md new file mode 100644 index 00000000..f0a775a9 --- /dev/null +++ b/01jvm/GCLearning.md @@ -0,0 +1,107 @@ +## SerialGC + +配置命令:-XX:+UseSerialGC + +串行GC对年轻代使用mark-copy(标记-复制)算法,对老年代使用mark-sweep-compact(标记-清除-整理)算法。 + +特点:单线程GC,GC时会stop-the-world(STW),停止应用线程,只适合几百MB堆内存的JVM,单核CPU时比较有用。年轻代默认分配内存-1/3堆内存左右,老年代默认分配内存-2/3堆内存左右。 + +通过作业1可以看出当堆内存变大时,串行GC收集的频率下降了,但是每一次收集所用的时间有所增加,会增加STW的时间,系统延迟增加,所以串行GC不适合占用内存很大的系统。 + +-XX:+UseParNewGC改进版本的SerialGC(并行回收年轻代),可以配合CMS使用。 + +--- +## Parallel GC + +配置命令: +-XX:+UseParallelGC +-XX:+UseParallelOldGC +-XX:+UseParallelGC -XX:+UseParallelOldGC + +并行GC在年轻代使用标记-复制(mark-copy)算法,在老年代使用标记-清除-整理(mark-sweep-compact)算法。 + +特点:多线程GC(并行GC),GC同样会STW,适用于多核服务器,主要目标是增加吞吐量。 ++ 在GC期间,所有CPU内核(应该是JVM拥有的内核吧?)都在并行清理垃圾,所以总暂停时间更短; ++ 在两次GC周期的间隔期,没有GC线程在运行,不会消耗任何系统资源 + +通过作业1可以看出并行GC进行一次垃圾收集的时间要明显少于串行GC,在堆内存较大时能比串行GC条件下生成更多的对象,有更高的吞吐量。 + +--- +## CMS GC +### Mostly Concurrent Mark and Sweep Garbage Collector + +配置命令: -XX:+UseConcMarkSweepGC + +CMS GC对年轻代采用(ParNew)并行STW方式的mark-copy(标记-复制)算法,对老年代主要使用mark-sweep(标记-清除)算法。 + +目标:避免在老年代垃圾收集时出现长时间的卡顿 +特点: ++ 不对老年代进行整理,而是使用空闲列表(free-lists)来管理内存空间的回收 ++ 在mark-and-sweep(标记-清除)阶段的大部分工作和应用线程一起并发执行。 ++ 默认情况下,CMS使用的并发线程数等于CPU核心数的1/4。 ++ 进行老年代的并发回收时,可能会伴随着多次年轻代的minorGC。 + +处理过程: +1. Initial Mark(初始标记)。伴随STW暂停,初始标记的目标是标记所有的根对象,包括根对象直接引用的对象,以及被年轻代中所有存活对象所引用的对象(老年代单独回收) +2. Concurrent Mark(并发标记)。CMS GC遍历老年代,标记所有的存活对象。 +3. Concurrent Preclean(并发预清理)。如果在并发标记的过程中引用关系发生了变化,JVM会通过"Card(卡片)"的方式将发生了改变的区域标记成"脏"区,这就是所谓的卡片标记(Card Marking) +4. Final Remark(最终标记)。伴随STW,完成老年代中所有存活对象的标记。 +5. Concurrent Sweep(并发清除) +6. Concurrent Reset(并发重置) 删除不再使用的对象,并回收他们占用的内存空间 + +存在问题:老年代内存碎片问题(因为不压缩),在某些情况下GC会造成不可预测的暂停时间,特别是堆内存较大的情况下。 + +最大young区大小: +ParallelGC:1024M/3 = 341.3M +CMS: 64M\*GC线程数4\*13/10 = 332.8M + +通过作业1的观察,CMS GC在Final Remark之前可能会做几次年轻代的回收,以提高Final Remark的效率。同时如果GC无法处理产生过快的垃圾,会发生concurrent mode failure,此时所有应用线程会被暂停,CMS GC发生退化。 + +--- + +## G1 GC +### Garbage-First + +配置命令: -XX:+UseG1GC -XX:MaxGCPauseMillis=50 + +目标:将STW停顿的时间和分布,变成可预期且可配置的。(启发性配置?) + +特点: ++ G1 GC的堆内存不再分成年轻代和老年代,而是划分成多个(通常是2048个)可以存放对象的小块堆区域(smaller heap regions)。 ++ 增量收集垃圾。每次GC暂停都会收集所有年轻代的内存块,但一般只包含部分老年代的内存块。 + +重要配置参数: ++ -XX:G1NewSizePercent:初始年轻代占整个 Java Heap 的大小,默认值为 5% ++ -XX:G1MaxNewSizePercent:最大年轻代占整个 Java Heap 的大小,默认值为 60%; ++ -XX:G1HeapRegionSize:设置每个 Region 的大小,单位 MB,需要为 1、2、4、8、16、32 中的某个值,默认是堆内存的1/2000。如果这个值设置比较大,那么大对象就可以进入 Region 了; ++ -XX:ConcGCThreads:与 Java 应用一起执行的 GC 线程数量,默认是 Java 线程的 1/4,减少这个参数的数值可能会提升并行回收的效率,提高系统内部吞吐量。如果这个数值过低,参与回收垃圾的线程不足,也会导致并行回收机制耗时加长; ++ -XX:+InitiatingHeapOccupancyPercent(简称 IHOP):G1 内部并行回收循环启动的阈值,默认为 Java Heap的 45%。这个可以理解为老年代使用大于等于 45% 的时候,JVM 会启动垃圾回收。这个值非常重要,它决定了在什么时间启动老年代的并行回收; ++ -XX:G1HeapWastePercent:G1停止回收的最小内存大小,默认是堆大小的 5%。GC 会收集所有的 Region 中的对象,但是如果下降到了 5%,就会停下来不再收集了。就是说,不必每次回收就把所有的垃圾都处理完,可以遗留少量的下次处理,这样也降低了单次消耗的时间; ++ -XX:+GCTimeRatio:这个参数就是计算花在 Java 应用线程上和花在 GC 线程上的时间比率,默认是 9,跟新生代内存的分配比例一致。这个参数主要的目的是让用户可以控制花在应用上的时间,G1 的计算公式是 100/(1+GCTimeRatio)。这样如果参数设置为9,则最多 10% 的时间会花在 GC 工作上面。Parallel GC 的默认值是 99,表示 1% 的时间被用在 GC 上面,这是因为 Parallel GC 贯穿整个 GC,而 G1 则根据 Region 来进行划分,不需要全局性扫描整个内存堆. ++ -XX:MaxGCPauseMills:预期 G1 每次执行 GC 操作的暂停时间,单位是毫秒,默认值是 200 毫秒,G1 会尽量保证控制在这个范围内。 + +处理过程 +1. 年轻代模式转移暂停(Evacuation Pause): +2. 并发标记(Concurrent Marking):过程基本与CMS一样 + + Phase1:Initial Mark(初始标记) + + Phase2:Root Region Scan(Root区扫描) + + Phase3:Concurrent Mark(并发标记) + + Phase4:Remark(再次标记) + + Phase5:Cleanup(清理) +3. 转移暂停: 混合模式(Evacuation Pause (mixed)) + +G1 GC触发Full GC的三种情况(退化成Serial GC) +1. 并发模式失败。 +解决办法 + 增加堆大小,或者调整周期 +2. 晋升失败 +解决办法 ++ 增加 –XX:G1ReservePercent 选项的值(并相应增加总的堆大小)增加预留内存量。 ++ 通过减少 –XX:InitiatingHeapOccupancyPercent 提前启动标记周期。 ++ 增加 –XX:ConcGCThreads 选项的值来增加并行标记线程的数目。 +3. 巨型对象分配失败 +解决办法 增加内存或者增大 -XX:G1HeapRegionSize + +通过作业1的观察,混合模式的转移暂停不一定紧跟并发标记阶段。在并发标记与混合转移暂停之间,可能存在多次young模式的转移暂停,并且混合模式的转移暂停在一次并发标记后会发生多次。 + +--- \ No newline at end of file diff --git a/02nio/nio02/src/main/java/io/github/kimmking/gateway/filter/MyHttpRequestFilter.java b/02nio/nio02/src/main/java/io/github/kimmking/gateway/filter/MyHttpRequestFilter.java new file mode 100644 index 00000000..c5414609 --- /dev/null +++ b/02nio/nio02/src/main/java/io/github/kimmking/gateway/filter/MyHttpRequestFilter.java @@ -0,0 +1,4 @@ +package io.github.kimmking.gateway.filter; + +public class MyHttpRequestFilter { +} diff --git a/02nio/okhttpdemo/okhttpdemo/src/main/java/com/geekbang/okhttpdemo/work/OkHttpDemo.java b/02nio/okhttpdemo/okhttpdemo/src/main/java/com/geekbang/okhttpdemo/work/OkHttpDemo.java new file mode 100644 index 00000000..a635ec4d --- /dev/null +++ b/02nio/okhttpdemo/okhttpdemo/src/main/java/com/geekbang/okhttpdemo/work/OkHttpDemo.java @@ -0,0 +1,28 @@ +package com.geekbang.okhttpdemo.work; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import java.io.IOException; + +public class OkHttpDemo { + final OkHttpClient client = new OkHttpClient(); + + String getUrl(String url) throws IOException { + Request request = new Request.Builder() + .url(url) + .build(); + + try (Response response = client.newCall(request).execute()) { + return response.body().string(); + } + } + + + public static void main(String[] args) throws IOException { + OkHttpDemo okHttpDemo = new OkHttpDemo(); + String response = okHttpDemo.getUrl("http://localhost:8801"); + System.out.println(response); + } +} diff --git a/02nio/workforweek2/GCLearning.md b/02nio/workforweek2/GCLearning.md new file mode 100644 index 00000000..f0a775a9 --- /dev/null +++ b/02nio/workforweek2/GCLearning.md @@ -0,0 +1,107 @@ +## SerialGC + +配置命令:-XX:+UseSerialGC + +串行GC对年轻代使用mark-copy(标记-复制)算法,对老年代使用mark-sweep-compact(标记-清除-整理)算法。 + +特点:单线程GC,GC时会stop-the-world(STW),停止应用线程,只适合几百MB堆内存的JVM,单核CPU时比较有用。年轻代默认分配内存-1/3堆内存左右,老年代默认分配内存-2/3堆内存左右。 + +通过作业1可以看出当堆内存变大时,串行GC收集的频率下降了,但是每一次收集所用的时间有所增加,会增加STW的时间,系统延迟增加,所以串行GC不适合占用内存很大的系统。 + +-XX:+UseParNewGC改进版本的SerialGC(并行回收年轻代),可以配合CMS使用。 + +--- +## Parallel GC + +配置命令: +-XX:+UseParallelGC +-XX:+UseParallelOldGC +-XX:+UseParallelGC -XX:+UseParallelOldGC + +并行GC在年轻代使用标记-复制(mark-copy)算法,在老年代使用标记-清除-整理(mark-sweep-compact)算法。 + +特点:多线程GC(并行GC),GC同样会STW,适用于多核服务器,主要目标是增加吞吐量。 ++ 在GC期间,所有CPU内核(应该是JVM拥有的内核吧?)都在并行清理垃圾,所以总暂停时间更短; ++ 在两次GC周期的间隔期,没有GC线程在运行,不会消耗任何系统资源 + +通过作业1可以看出并行GC进行一次垃圾收集的时间要明显少于串行GC,在堆内存较大时能比串行GC条件下生成更多的对象,有更高的吞吐量。 + +--- +## CMS GC +### Mostly Concurrent Mark and Sweep Garbage Collector + +配置命令: -XX:+UseConcMarkSweepGC + +CMS GC对年轻代采用(ParNew)并行STW方式的mark-copy(标记-复制)算法,对老年代主要使用mark-sweep(标记-清除)算法。 + +目标:避免在老年代垃圾收集时出现长时间的卡顿 +特点: ++ 不对老年代进行整理,而是使用空闲列表(free-lists)来管理内存空间的回收 ++ 在mark-and-sweep(标记-清除)阶段的大部分工作和应用线程一起并发执行。 ++ 默认情况下,CMS使用的并发线程数等于CPU核心数的1/4。 ++ 进行老年代的并发回收时,可能会伴随着多次年轻代的minorGC。 + +处理过程: +1. Initial Mark(初始标记)。伴随STW暂停,初始标记的目标是标记所有的根对象,包括根对象直接引用的对象,以及被年轻代中所有存活对象所引用的对象(老年代单独回收) +2. Concurrent Mark(并发标记)。CMS GC遍历老年代,标记所有的存活对象。 +3. Concurrent Preclean(并发预清理)。如果在并发标记的过程中引用关系发生了变化,JVM会通过"Card(卡片)"的方式将发生了改变的区域标记成"脏"区,这就是所谓的卡片标记(Card Marking) +4. Final Remark(最终标记)。伴随STW,完成老年代中所有存活对象的标记。 +5. Concurrent Sweep(并发清除) +6. Concurrent Reset(并发重置) 删除不再使用的对象,并回收他们占用的内存空间 + +存在问题:老年代内存碎片问题(因为不压缩),在某些情况下GC会造成不可预测的暂停时间,特别是堆内存较大的情况下。 + +最大young区大小: +ParallelGC:1024M/3 = 341.3M +CMS: 64M\*GC线程数4\*13/10 = 332.8M + +通过作业1的观察,CMS GC在Final Remark之前可能会做几次年轻代的回收,以提高Final Remark的效率。同时如果GC无法处理产生过快的垃圾,会发生concurrent mode failure,此时所有应用线程会被暂停,CMS GC发生退化。 + +--- + +## G1 GC +### Garbage-First + +配置命令: -XX:+UseG1GC -XX:MaxGCPauseMillis=50 + +目标:将STW停顿的时间和分布,变成可预期且可配置的。(启发性配置?) + +特点: ++ G1 GC的堆内存不再分成年轻代和老年代,而是划分成多个(通常是2048个)可以存放对象的小块堆区域(smaller heap regions)。 ++ 增量收集垃圾。每次GC暂停都会收集所有年轻代的内存块,但一般只包含部分老年代的内存块。 + +重要配置参数: ++ -XX:G1NewSizePercent:初始年轻代占整个 Java Heap 的大小,默认值为 5% ++ -XX:G1MaxNewSizePercent:最大年轻代占整个 Java Heap 的大小,默认值为 60%; ++ -XX:G1HeapRegionSize:设置每个 Region 的大小,单位 MB,需要为 1、2、4、8、16、32 中的某个值,默认是堆内存的1/2000。如果这个值设置比较大,那么大对象就可以进入 Region 了; ++ -XX:ConcGCThreads:与 Java 应用一起执行的 GC 线程数量,默认是 Java 线程的 1/4,减少这个参数的数值可能会提升并行回收的效率,提高系统内部吞吐量。如果这个数值过低,参与回收垃圾的线程不足,也会导致并行回收机制耗时加长; ++ -XX:+InitiatingHeapOccupancyPercent(简称 IHOP):G1 内部并行回收循环启动的阈值,默认为 Java Heap的 45%。这个可以理解为老年代使用大于等于 45% 的时候,JVM 会启动垃圾回收。这个值非常重要,它决定了在什么时间启动老年代的并行回收; ++ -XX:G1HeapWastePercent:G1停止回收的最小内存大小,默认是堆大小的 5%。GC 会收集所有的 Region 中的对象,但是如果下降到了 5%,就会停下来不再收集了。就是说,不必每次回收就把所有的垃圾都处理完,可以遗留少量的下次处理,这样也降低了单次消耗的时间; ++ -XX:+GCTimeRatio:这个参数就是计算花在 Java 应用线程上和花在 GC 线程上的时间比率,默认是 9,跟新生代内存的分配比例一致。这个参数主要的目的是让用户可以控制花在应用上的时间,G1 的计算公式是 100/(1+GCTimeRatio)。这样如果参数设置为9,则最多 10% 的时间会花在 GC 工作上面。Parallel GC 的默认值是 99,表示 1% 的时间被用在 GC 上面,这是因为 Parallel GC 贯穿整个 GC,而 G1 则根据 Region 来进行划分,不需要全局性扫描整个内存堆. ++ -XX:MaxGCPauseMills:预期 G1 每次执行 GC 操作的暂停时间,单位是毫秒,默认值是 200 毫秒,G1 会尽量保证控制在这个范围内。 + +处理过程 +1. 年轻代模式转移暂停(Evacuation Pause): +2. 并发标记(Concurrent Marking):过程基本与CMS一样 + + Phase1:Initial Mark(初始标记) + + Phase2:Root Region Scan(Root区扫描) + + Phase3:Concurrent Mark(并发标记) + + Phase4:Remark(再次标记) + + Phase5:Cleanup(清理) +3. 转移暂停: 混合模式(Evacuation Pause (mixed)) + +G1 GC触发Full GC的三种情况(退化成Serial GC) +1. 并发模式失败。 +解决办法 + 增加堆大小,或者调整周期 +2. 晋升失败 +解决办法 ++ 增加 –XX:G1ReservePercent 选项的值(并相应增加总的堆大小)增加预留内存量。 ++ 通过减少 –XX:InitiatingHeapOccupancyPercent 提前启动标记周期。 ++ 增加 –XX:ConcGCThreads 选项的值来增加并行标记线程的数目。 +3. 巨型对象分配失败 +解决办法 增加内存或者增大 -XX:G1HeapRegionSize + +通过作业1的观察,混合模式的转移暂停不一定紧跟并发标记阶段。在并发标记与混合转移暂停之间,可能存在多次young模式的转移暂停,并且混合模式的转移暂停在一次并发标记后会发生多次。 + +--- \ No newline at end of file diff --git a/02nio/workforweek2/OkHttpDemo.java b/02nio/workforweek2/OkHttpDemo.java new file mode 100644 index 00000000..a635ec4d --- /dev/null +++ b/02nio/workforweek2/OkHttpDemo.java @@ -0,0 +1,28 @@ +package com.geekbang.okhttpdemo.work; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import java.io.IOException; + +public class OkHttpDemo { + final OkHttpClient client = new OkHttpClient(); + + String getUrl(String url) throws IOException { + Request request = new Request.Builder() + .url(url) + .build(); + + try (Response response = client.newCall(request).execute()) { + return response.body().string(); + } + } + + + public static void main(String[] args) throws IOException { + OkHttpDemo okHttpDemo = new OkHttpDemo(); + String response = okHttpDemo.getUrl("http://localhost:8801"); + System.out.println(response); + } +} diff --git a/02nio/workforweek3/work1/HttpHandler.java b/02nio/workforweek3/work1/HttpHandler.java new file mode 100644 index 00000000..a9e01f11 --- /dev/null +++ b/02nio/workforweek3/work1/HttpHandler.java @@ -0,0 +1,93 @@ +package java0.nio01.netty; + +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpUtil; +import io.netty.util.ReferenceCountUtil; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import java.io.IOException; + +import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; +import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; +import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; + +public class HttpHandler extends ChannelInboundHandlerAdapter { + + final OkHttpClient CLIENT = new OkHttpClient(); + final String TARGET_URL = "http://localhost:8801"; + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + ctx.flush(); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + try { + //logger.info("channelRead流量接口请求开始,时间为{}", startTime); + FullHttpRequest fullRequest = (FullHttpRequest) msg; + + handlerTest(fullRequest, ctx); + + } catch(Exception e) { + e.printStackTrace(); + } finally { + ReferenceCountUtil.release(msg); + } + } + + private void handlerTest(FullHttpRequest fullRequest, ChannelHandlerContext ctx) { + FullHttpResponse response = null; + try { + String value = getUrl(TARGET_URL); // 对接上次作业的httpclient或者okhttp请求另一个url的响应数据 + +// httpGet ... http://localhost:8801 +// 返回的响应,"hello,nio"; +// value = reponse.... + + response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(value.getBytes("UTF-8"))); + response.headers().set("Content-Type", "application/json"); + response.headers().setInt("Content-Length", response.content().readableBytes()); + + } catch (Exception e) { + System.out.println("处理出错:"+e.getMessage()); + response = new DefaultFullHttpResponse(HTTP_1_1, NO_CONTENT); + } finally { + if (fullRequest != null) { + if (!HttpUtil.isKeepAlive(fullRequest)) { + ctx.write(response).addListener(ChannelFutureListener.CLOSE); + } else { + response.headers().set(CONNECTION, KEEP_ALIVE); + ctx.write(response); + } + ctx.flush(); + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } + + + String getUrl(String url) throws IOException { + Request request = new Request.Builder() + .url(url) + .build(); + try (Response response = CLIENT.newCall(request).execute()) { + return response.body().string(); + } + } +} diff --git a/02nio/workforweek3/work3/HttpRequestFilter.java b/02nio/workforweek3/work3/HttpRequestFilter.java new file mode 100644 index 00000000..31253b40 --- /dev/null +++ b/02nio/workforweek3/work3/HttpRequestFilter.java @@ -0,0 +1,10 @@ +package io.github.kimmking.gateway.filter; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.FullHttpRequest; + +public interface HttpRequestFilter { + + void filter(FullHttpRequest fullRequest, ChannelHandlerContext ctx); + +} diff --git a/02nio/workforweek3/work3/MyHttpRequestFilter.java b/02nio/workforweek3/work3/MyHttpRequestFilter.java new file mode 100644 index 00000000..77998b43 --- /dev/null +++ b/02nio/workforweek3/work3/MyHttpRequestFilter.java @@ -0,0 +1,12 @@ +package io.github.kimmking.gateway.filter; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.FullHttpRequest; + +public class MyHttpRequestFilter implements HttpRequestFilter { + @Override + public void filter(FullHttpRequest fullRequest, ChannelHandlerContext ctx) { + fullRequest.headers().set("User-Agent", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36"); + } +} diff --git a/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method1.java b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method1.java new file mode 100644 index 00000000..1011919e --- /dev/null +++ b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method1.java @@ -0,0 +1,56 @@ +package java0.conc0303.homework.work2; + +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + +/** + * 本周作业:(必做)思考有多少种方式,在main函数启动一个新线程或线程池, + * 异步运行一个方法,拿到这个方法的返回值后,退出主线程? + * 写出你的方法,越多越好,提交到github。 + * + * 一个简单的代码参考: + */ +public class Method1 { + + public static void main(String[] args) { + + long start=System.currentTimeMillis(); + + // 在这里创建一个线程或线程池, + // 异步执行 下面方法 + + FutureTask task = new FutureTask(new Callable() { + @Override + public Integer call() throws Exception { + return sum(); + } + }); + + new Thread(task).start(); + try { + // 确保 拿到result 并输出 + System.out.println("异步计算结果为:"+task.get()); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + + + System.out.println("使用时间:"+ (System.currentTimeMillis()-start) + " ms"); + + // 然后退出main线程 + } + + private static int sum() { + return fibo(36); + } + + private static int fibo(int a) { + if ( a < 2) + return 1; + return fibo(a-1) + fibo(a-2); + } +} diff --git a/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method2.java b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method2.java new file mode 100644 index 00000000..31b5aa7c --- /dev/null +++ b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method2.java @@ -0,0 +1,55 @@ +package java0.conc0303.homework.work2; + +import java.util.concurrent.*; + +/** + * 本周作业:(必做)思考有多少种方式,在main函数启动一个新线程或线程池, + * 异步运行一个方法,拿到这个方法的返回值后,退出主线程? + * 写出你的方法,越多越好,提交到github。 + * + * 一个简单的代码参考: + */ +public class Method2 { + + public static void main(String[] args) { + + long start=System.currentTimeMillis(); + + // 在这里创建一个线程或线程池, + // 异步执行 下面方法 + + ExecutorService executor = Executors.newSingleThreadExecutor(); + FutureTask task = new FutureTask(new Callable() { + @Override + public Integer call() throws Exception { + return sum(); + } + }); + executor.submit(task); + + try { + // 确保 拿到result 并输出 + System.out.println("异步计算结果为:"+task.get()); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + + + System.out.println("使用时间:"+ (System.currentTimeMillis()-start) + " ms"); + + // 然后退出main线程 + executor.shutdown(); + } + + private static int sum() { + return fibo(36); + } + + private static int fibo(int a) { + if ( a < 2) + return 1; + return fibo(a-1) + fibo(a-2); + } +} diff --git a/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method3.java b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method3.java new file mode 100644 index 00000000..71d2f067 --- /dev/null +++ b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method3.java @@ -0,0 +1,41 @@ +package java0.conc0303.homework.work2; + +import java.util.concurrent.*; + +/** + * 本周作业:(必做)思考有多少种方式,在main函数启动一个新线程或线程池, + * 异步运行一个方法,拿到这个方法的返回值后,退出主线程? + * 写出你的方法,越多越好,提交到github。 + * + * 一个简单的代码参考: + */ +public class Method3 { + + public static void main(String[] args) { + + long start=System.currentTimeMillis(); + + // 在这里创建一个线程或线程池, + // 异步执行 下面方法 + + int result = CompletableFuture.supplyAsync(()->{ + return sum(); + }).join(); + + System.out.println("异步计算结果为:"+result); + System.out.println("使用时间:"+ (System.currentTimeMillis()-start) + " ms"); + + // 然后退出main线程 + + } + + private static int sum() { + return fibo(36); + } + + private static int fibo(int a) { + if ( a < 2) + return 1; + return fibo(a-1) + fibo(a-2); + } +} diff --git a/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method4.java b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method4.java new file mode 100644 index 00000000..0a99d7bb --- /dev/null +++ b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method4.java @@ -0,0 +1,48 @@ +package java0.conc0303.homework.work2; + +import java.util.concurrent.CompletableFuture; + +/** + * 本周作业:(必做)思考有多少种方式,在main函数启动一个新线程或线程池, + * 异步运行一个方法,拿到这个方法的返回值后,退出主线程? + * 写出你的方法,越多越好,提交到github。 + * + * 一个简单的代码参考: + */ +public class Method4 { + private static int result = 0; + + public static void main(String[] args) throws InterruptedException { + + long start=System.currentTimeMillis(); + + // 在这里创建一个线程或线程池, + // 异步执行 下面方法 + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + result = sum(); + } + }); + + thread.start(); + + thread.join(); + + System.out.println("异步计算结果为:"+result); + System.out.println("使用时间:"+ (System.currentTimeMillis()-start) + " ms"); + + // 然后退出main线程 + + } + + private static int sum() { + return fibo(36); + } + + private static int fibo(int a) { + if ( a < 2) + return 1; + return fibo(a-1) + fibo(a-2); + } +} diff --git a/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method5.java b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method5.java new file mode 100644 index 00000000..208e7a19 --- /dev/null +++ b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method5.java @@ -0,0 +1,55 @@ +package java0.conc0303.homework.work2; + +import java.util.concurrent.*; + +/** + * 本周作业:(必做)思考有多少种方式,在main函数启动一个新线程或线程池, + * 异步运行一个方法,拿到这个方法的返回值后,退出主线程? + * 写出你的方法,越多越好,提交到github。 + * + * 一个简单的代码参考: + */ +public class Method5 { + + public static void main(String[] args) { + + long start=System.currentTimeMillis(); + + // 在这里创建一个线程或线程池, + // 异步执行 下面方法 + + ExecutorService executor = Executors.newSingleThreadExecutor(); + CompletionService completionService = new ExecutorCompletionService<>(executor) ; + completionService.submit(new Callable() { + @Override + public Integer call() throws Exception { + return sum(); + } + }); + + try { + // 确保 拿到result 并输出 + System.out.println("异步计算结果为:"+completionService.take().get()); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + + + System.out.println("使用时间:"+ (System.currentTimeMillis()-start) + " ms"); + + // 然后退出main线程 + executor.shutdown(); + } + + private static int sum() { + return fibo(36); + } + + private static int fibo(int a) { + if ( a < 2) + return 1; + return fibo(a-1) + fibo(a-2); + } +} diff --git a/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method6.java b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method6.java new file mode 100644 index 00000000..b426a1ad --- /dev/null +++ b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method6.java @@ -0,0 +1,63 @@ +package java0.conc0303.homework.work2; + +import java.util.concurrent.ExecutionException; + +/** + * 本周作业:(必做)思考有多少种方式,在main函数启动一个新线程或线程池, + * 异步运行一个方法,拿到这个方法的返回值后,退出主线程? + * 写出你的方法,越多越好,提交到github。 + * + * 一个简单的代码参考: + */ +public class Method6 { + + public static void main(String[] args) { + + long start=System.currentTimeMillis(); + + // 在这里创建一个线程或线程池, + // 异步执行 下面方法 + + FiboRunnable fiboRunnable = new FiboRunnable(); + Thread thread = new Thread(fiboRunnable); + thread.start(); + + try { + thread.join(); + System.out.println(fiboRunnable.getResult()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + + System.out.println("使用时间:"+ (System.currentTimeMillis()-start) + " ms"); + + // 然后退出main线程 + } + + private static int sum() { + return fibo(36); + } + + private static int fibo(int a) { + if ( a < 2) + return 1; + return fibo(a-1) + fibo(a-2); + } + + public static class FiboRunnable implements Runnable{ + private int result; + @Override + public void run() { + result = sum(); + } + + public int getResult() { + return result; + } + + public void setResult(int result) { + this.result = result; + } + } +} diff --git a/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method7.java b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method7.java new file mode 100644 index 00000000..483b7bc8 --- /dev/null +++ b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method7.java @@ -0,0 +1,70 @@ +package java0.conc0303.homework.work2; + +import java.util.concurrent.CountDownLatch; + +/** + * 本周作业:(必做)思考有多少种方式,在main函数启动一个新线程或线程池, + * 异步运行一个方法,拿到这个方法的返回值后,退出主线程? + * 写出你的方法,越多越好,提交到github。 + * + * 一个简单的代码参考: + */ +public class Method7 { + + public static void main(String[] args) { + + long start=System.currentTimeMillis(); + + // 在这里创建一个线程或线程池, + // 异步执行 下面方法 + CountDownLatch countDownLatch = new CountDownLatch(1); + FiboRunnable fiboRunnable = new FiboRunnable(countDownLatch); + Thread thread = new Thread(fiboRunnable); + thread.start(); + + try { + countDownLatch.await(); + System.out.println(fiboRunnable.getResult()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + + System.out.println("使用时间:"+ (System.currentTimeMillis()-start) + " ms"); + + // 然后退出main线程 + } + + private static int sum() { + return fibo(36); + } + + private static int fibo(int a) { + if ( a < 2) + return 1; + return fibo(a-1) + fibo(a-2); + } + + public static class FiboRunnable implements Runnable{ + private int result; + private CountDownLatch countDownLatch; + + public FiboRunnable(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + @Override + public void run() { + result = sum(); + countDownLatch.countDown(); + } + + public int getResult() { + return result; + } + + public void setResult(int result) { + this.result = result; + } + } +} diff --git a/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method8.java b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method8.java new file mode 100644 index 00000000..ce7c0493 --- /dev/null +++ b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method8.java @@ -0,0 +1,80 @@ +package java0.conc0303.homework.work2; + +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +/** + * 本周作业:(必做)思考有多少种方式,在main函数启动一个新线程或线程池, + * 异步运行一个方法,拿到这个方法的返回值后,退出主线程? + * 写出你的方法,越多越好,提交到github。 + * + * 一个简单的代码参考: + */ +public class Method8 { + + public static void main(String[] args) { + + long start=System.currentTimeMillis(); + + // 在这里创建一个线程或线程池, + // 异步执行 下面方法 + CyclicBarrier cyclicBarrier = new CyclicBarrier(2); + FiboRunnable fiboRunnable = new FiboRunnable(cyclicBarrier); + Thread thread = new Thread(fiboRunnable); + thread.start(); + + try { + cyclicBarrier.await(); + System.out.println(fiboRunnable.getResult()); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (BrokenBarrierException e) { + e.printStackTrace(); + } + + + System.out.println("使用时间:"+ (System.currentTimeMillis()-start) + " ms"); + + // 然后退出main线程 + } + + private static int sum() { + return fibo(36); + } + + private static int fibo(int a) { + if ( a < 2) + return 1; + return fibo(a-1) + fibo(a-2); + } + + public static class FiboRunnable implements Runnable{ + private int result; + private CyclicBarrier cyclicBarrier; + + public FiboRunnable(CyclicBarrier cyclicBarrier) { + this.cyclicBarrier = cyclicBarrier; + } + + @Override + public void run() { + result = sum(); + try { + cyclicBarrier.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (BrokenBarrierException e) { + e.printStackTrace(); + } + } + + public int getResult() { + return result; + } + + public void setResult(int result) { + this.result = result; + } + } +} diff --git a/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method9.java b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method9.java new file mode 100644 index 00000000..701762fb --- /dev/null +++ b/03concurrency/0301/src/main/java/java0/conc0303/homework/work2/Method9.java @@ -0,0 +1,70 @@ +package java0.conc0303.homework.work2; + +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 本周作业:(必做)思考有多少种方式,在main函数启动一个新线程或线程池, + * 异步运行一个方法,拿到这个方法的返回值后,退出主线程? + * 写出你的方法,越多越好,提交到github。 + * + * 一个简单的代码参考: + */ +public class Method9 { + final static ReentrantLock lock = new ReentrantLock(); + final static Condition condition = lock.newCondition(); + static int result; + + public static void main(String[] args) { + + long start=System.currentTimeMillis(); + + // 在这里创建一个线程或线程池, + // 异步执行 下面方法 + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + lock.lock(); + result = sum(); + condition.signal(); + } catch (Exception e) { + e.printStackTrace(); + }finally { + lock.unlock(); + } + + } + }); + thread.start(); + + + try { + lock.lock(); + condition.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + }finally { + lock.unlock(); + } + System.out.println(result); + + System.out.println("使用时间:"+ (System.currentTimeMillis()-start) + " ms"); + + // 然后退出main线程 + } + + private static int sum() { + return fibo(36); + } + + private static int fibo(int a) { + if ( a < 2) + return 1; + return fibo(a-1) + fibo(a-2); + } + + +} diff --git "a/03concurrency/0301/src/main/java/java0/conc0303/homework/work6/Java\345\244\232\347\272\277\347\250\213.png" "b/03concurrency/0301/src/main/java/java0/conc0303/homework/work6/Java\345\244\232\347\272\277\347\250\213.png" new file mode 100644 index 00000000..18baf5e8 Binary files /dev/null and "b/03concurrency/0301/src/main/java/java0/conc0303/homework/work6/Java\345\244\232\347\272\277\347\250\213.png" differ diff --git a/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/HIkariDemo.java b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/HIkariDemo.java new file mode 100644 index 00000000..c2a6362d --- /dev/null +++ b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/HIkariDemo.java @@ -0,0 +1,76 @@ +package io.nononi.db.dbdemo.jdbc; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Random; + +/** + * 使用Hikati连接池 + */ +public class HIkariDemo { + private static HikariDataSource dataSource; + + static { + + //初始化HikariConfig配置 + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:mysql://localhost:3306/test"); + config.setUsername("root"); + config.setPassword("root"); + config.setDriverClassName("com.mysql.jdbc.Driver"); + config.setMaximumPoolSize(20); + config.setMinimumIdle(10); + config.setAutoCommit(false); + config.setConnectionTimeout(30000); + //初始化HikariDataSource + dataSource = new HikariDataSource(config); + } + + public static Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } + + public static void main(String[] args) throws SQLException { + Random random = new Random(); + Connection conn = null; + try { + conn = getConnection(); + conn.setAutoCommit(false); + String sql = "INSERT INTO `user` (id, name, pwd) VALUES(?, ?, ?);"; + PreparedStatement preparedStatement = conn.prepareStatement(sql); + long begin = System.currentTimeMillis(); + for (int i = 0; i < 100000; i++) { + preparedStatement.setInt(1, i + 1); + preparedStatement.setString(2, "name_" + i); + preparedStatement.setInt(3, random.nextInt(9999)); + preparedStatement.addBatch(); + if ((i + 1) % 200 == 0) { + preparedStatement.executeBatch(); + preparedStatement.clearBatch(); + } + } + if (100000 % 200 != 0) { + preparedStatement.executeBatch(); + preparedStatement.clearBatch(); + } + conn.commit(); + long end = System.currentTimeMillis(); + System.out.println("Time: " + (end - begin)); + preparedStatement.close(); + } catch (SQLException e) { + e.printStackTrace(); + try { + conn.rollback(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } finally { + conn.close(); + + } + } +} diff --git a/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcBatchDemo.java b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcBatchDemo.java new file mode 100644 index 00000000..5399761d --- /dev/null +++ b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcBatchDemo.java @@ -0,0 +1,68 @@ +package io.nononi.db.dbdemo.jdbc; + +import java.sql.*; +import java.util.Random; + +/** + * 批处理模式 + */ +public class JdbcBatchDemo { + public static void main(String[] args) throws Exception { + Random random = new Random(); + Connection conn = null; + try { + conn = getConn(); + conn.setAutoCommit(false); + String sql = "INSERT INTO `user` (id, name, pwd) VALUES(?, ?, ?);"; + PreparedStatement preparedStatement = conn.prepareStatement(sql); + long begin = System.currentTimeMillis(); + for (int i = 0; i < 100000; i++) { + preparedStatement.setInt(1, i + 1); + preparedStatement.setString(2, "name_" + i); + preparedStatement.setInt(3, random.nextInt(9999)); + preparedStatement.addBatch(); + if ((i + 1) % 200 == 0) { + preparedStatement.executeBatch(); + preparedStatement.clearBatch(); + } + } + if (100000 % 200 != 0) { + preparedStatement.executeBatch(); + preparedStatement.clearBatch(); + } + conn.commit(); + long end = System.currentTimeMillis(); + System.out.println("Time: " + (end - begin)); + preparedStatement.close(); + } catch (SQLException e) { + e.printStackTrace(); + try { + conn.rollback(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } finally { + conn.close(); + + } + + } + + private static Connection getConn() { + String driver = "com.mysql.jdbc.Driver"; + String url = "jdbc:mysql://localhost:3306/test"; + String username = "root"; + String password = "root"; + Connection conn = null; + try { + Class.forName(driver); //classLoader,加载对应驱动 + conn = (Connection) DriverManager.getConnection(url, username, password); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (SQLException e) { + e.printStackTrace(); + } + return conn; + } + +} diff --git a/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcDemo.java b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcDemo.java new file mode 100644 index 00000000..4eaa8b0e --- /dev/null +++ b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcDemo.java @@ -0,0 +1,74 @@ +package io.nononi.db.dbdemo.jdbc; + +import java.sql.*; + +/** + * JDBC增删改查,开启事务 + */ +public class JdbcDemo { + public static void main(String[] args) throws Exception { + Connection conn = getConn(); + System.out.println("开始查询:"); + select(conn); + insert(conn, 20, "Amy", 1234); + System.out.println("插入后查询:"); + select(conn); + update(conn, 20, "Bob", 1111); + System.out.println("更新后查询:"); + select(conn); + delete(conn, 20); + System.out.println("删除后查询:"); + select(conn); + conn.close(); + + + } + + private static Connection getConn() { + String driver = "com.mysql.jdbc.Driver"; + String url = "jdbc:mysql://localhost:3306/test"; + String username = "root"; + String password = "root"; + Connection conn = null; + try { + Class.forName(driver); //classLoader,加载对应驱动 + conn = (Connection) DriverManager.getConnection(url, username, password); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (SQLException e) { + e.printStackTrace(); + } + return conn; + } + + private static void select(Connection connection) throws SQLException { + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT * FROM user"); + while (resultSet.next()) { + System.out.print(resultSet.getInt("id") + " "); + System.out.print(resultSet.getString("name") + " "); + System.out.println(resultSet.getInt("pwd") + " "); + } + System.out.println(""); + } + + private static boolean insert(Connection connection, int id, String name, int pwd) throws SQLException { + Statement statement = connection.createStatement(); + String sql = "INSERT INTO `user` (id, name, pwd) VALUES(" + id + ",'" + name + "'," + pwd + ");"; + return statement.execute(sql); + + } + + private static boolean update(Connection connection, int id, String name, int pwd) throws SQLException { + Statement statement = connection.createStatement(); + String sql = "UPDATE `user` SET name ='" + name + "'," + "pwd = " + pwd + " WHERE id =" + pwd + ";"; + return statement.execute(sql); + } + + private static int delete(Connection connection, int pwd) throws SQLException { + Statement statement = connection.createStatement(); + String sql = "DELETE FROM `user` WHERE id=" + pwd + ";"; + return statement.executeUpdate(sql); + } + +} diff --git a/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcPreStmtDemo.java b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcPreStmtDemo.java new file mode 100644 index 00000000..855f42d8 --- /dev/null +++ b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcPreStmtDemo.java @@ -0,0 +1,97 @@ +package io.nononi.db.dbdemo.jdbc; + +import java.sql.*; + +/** + * PrepareStatement 方式改造 + */ +public class JdbcPreStmtDemo { + public static void main(String[] args) throws Exception { + Connection conn = null; + try { + conn = getConn(); + conn.setAutoCommit(false); + System.out.println("开始查询:"); + select(conn); + insert(conn, 20, "Amy", 1234); + System.out.println("插入后查询:"); + select(conn); + update(conn, 20, "Bob", 1111); + System.out.println("更新后查询:"); + select(conn); + delete(conn, 20); + System.out.println("删除后查询:"); + select(conn); + conn.commit(); + + } catch (SQLException e) { + e.printStackTrace(); + try { + conn.rollback(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } finally { + conn.close(); + } + + } + + private static Connection getConn() { + String driver = "com.mysql.jdbc.Driver"; + String url = "jdbc:mysql://localhost:3306/test"; + String username = "root"; + String password = "root"; + Connection conn = null; + try { + Class.forName(driver); //classLoader,加载对应驱动 + conn = (Connection) DriverManager.getConnection(url, username, password); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (SQLException e) { + e.printStackTrace(); + } + return conn; + } + + private static void select(Connection connection) throws SQLException { + String sql = "SELECT * FROM user;"; + PreparedStatement stmt = connection.prepareStatement(sql); + ResultSet resultSet = stmt.executeQuery(); + while (resultSet.next()) { + System.out.print(resultSet.getInt("id") + " "); + System.out.print(resultSet.getString("name") + " "); + System.out.println(resultSet.getInt("pwd") + " "); + } + System.out.println(""); + } + + private static int insert(Connection connection, int id, String name, int pwd) throws SQLException { + String sql = "INSERT INTO `user` (id, name, pwd) VALUES(?, ?, ?);"; + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setInt(1,id); + stmt.setString(2,name); + stmt.setInt(3,pwd); + return stmt.executeUpdate(); + + } + + private static int update(Connection connection, int id, String name, int pwd) throws SQLException { + String sql = "UPDATE `user` SET name = ?, pwd = ? WHERE id = ?;"; + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setString(1,name); + stmt.setInt(2,pwd); + stmt.setInt(3,id); + return stmt.executeUpdate(); + } + + private static int delete(Connection connection, int id) throws SQLException { + + + String sql = "DELETE FROM `user` WHERE id=?;"; + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setInt(1,id); + return stmt.executeUpdate(); + } + +} diff --git a/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcTransactionDemo.java b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcTransactionDemo.java new file mode 100644 index 00000000..f7f65e56 --- /dev/null +++ b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/JdbcTransactionDemo.java @@ -0,0 +1,87 @@ +package io.nononi.db.dbdemo.jdbc; + +import java.sql.*; + +/** + * JDBC增删改查,开启事务 + */ +public class JdbcTransactionDemo { + public static void main(String[] args) throws Exception { + Connection conn = null; + try{ + conn = getConn(); + conn.setAutoCommit(false); + System.out.println("开始查询:"); + select(conn); + insert(conn,20,"Amy",1234); + System.out.println("插入后查询:"); + select(conn); + update(conn,20,"Bob",1111); + System.out.println("更新后查询:"); + select(conn); + delete(conn,20); + System.out.println("删除后查询:"); + select(conn); + conn.commit(); + + }catch (SQLException e){ + e.printStackTrace(); + try { + conn.rollback(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + }finally { + conn.close(); + } + + } + + private static Connection getConn() { + String driver = "com.mysql.jdbc.Driver"; + String url = "jdbc:mysql://localhost:3306/test"; + String username = "root"; + String password = "root"; + Connection conn = null; + try { + Class.forName(driver); //classLoader,加载对应驱动 + conn = (Connection) DriverManager.getConnection(url, username, password); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (SQLException e) { + e.printStackTrace(); + } + return conn; + } + + private static void select(Connection connection) throws SQLException { + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT * FROM user"); + while (resultSet.next()){ + System.out.print(resultSet.getInt("id")+ " "); + System.out.print(resultSet.getString("name")+ " "); + System.out.println(resultSet.getInt("pwd")+ " "); + } + System.out.println(""); + } + + private static boolean insert(Connection connection,int id, String name, int pwd) throws SQLException { + Statement statement = connection.createStatement(); + String sql = "INSERT INTO `user` (id, name, pwd) VALUES("+ id+ ",'"+ name +"'," +pwd +");"; + return statement.execute(sql); + + } + + private static boolean update(Connection connection,int id, String name, int pwd) throws SQLException { + Statement statement = connection.createStatement(); + String sql = "UPDATE `user` SET name ='"+name+"'," +"pwd = "+ pwd+" WHERE id =" +pwd + ";"; + return statement.execute(sql); + } + + private static int delete(Connection connection, int pwd) throws SQLException { + Statement statement = connection.createStatement(); + String sql = "DELETE FROM `user` WHERE id=" +pwd + ";"; + return statement.executeUpdate(sql); + } + +} diff --git a/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/User.java b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/User.java new file mode 100644 index 00000000..f11aca4e --- /dev/null +++ b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc/User.java @@ -0,0 +1,31 @@ +package io.nononi.db.dbdemo.jdbc; + +public class User { + private Integer id; + private String name; + private Integer pwd; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getPwd() { + return pwd; + } + + public void setPwd(Integer pwd) { + this.pwd = pwd; + } +} diff --git a/04fx/homework10/src/main/java/io/nononi/db/dbdemo/shardingdb/ShardingDbDemo.java b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/shardingdb/ShardingDbDemo.java new file mode 100644 index 00000000..4e2a0129 --- /dev/null +++ b/04fx/homework10/src/main/java/io/nononi/db/dbdemo/shardingdb/ShardingDbDemo.java @@ -0,0 +1,97 @@ +package io.nononi.db.dbdemo.shardingdb; + +import java.sql.*; +import java.util.Random; + +/** + * sharding_db demo + */ +public class ShardingDbDemo { + public static void main(String[] args) throws Exception { + Connection conn = null; + Random random = new Random(); + try { + conn = getConn(); + conn.setAutoCommit(false); + System.out.println("开始查询:"); + select(conn); + insert(conn, random.nextInt(100000), "OK"); + System.out.println("插入后查询:"); + select(conn); + update(conn, 724643077406830593L, "FAIL"); + System.out.println("更新后查询:"); + select(conn); + delete(conn, 724643077406830593L); + System.out.println("删除后查询:"); + select(conn); + conn.commit(); + + } catch (SQLException e) { + e.printStackTrace(); + try { + conn.rollback(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } finally { + conn.close(); + } + + } + + private static Connection getConn() { + String driver = "com.mysql.jdbc.Driver"; + String url = "jdbc:mysql://localhost:3307/sharding_db"; + String username = "root"; + String password = "root"; + Connection conn = null; + try { + Class.forName(driver); //classLoader,加载对应驱动 + conn = (Connection) DriverManager.getConnection(url, username, password); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (SQLException e) { + e.printStackTrace(); + } + return conn; + } + + private static void select(Connection connection) throws SQLException { + String sql = "SELECT * FROM `t_order`;"; + PreparedStatement stmt = connection.prepareStatement(sql); + ResultSet resultSet = stmt.executeQuery(); + while (resultSet.next()) { + System.out.print(resultSet.getLong("order_id") + " "); + System.out.print(resultSet.getInt("user_id") + " "); + System.out.println(resultSet.getString("status") + " "); + } + System.out.println(""); + } + + private static int insert(Connection connection, int user_id, String status) throws SQLException { + String sql = "INSERT INTO `t_order` (user_id, status) VALUES(?, ?);"; + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setInt(1,user_id); + stmt.setString(2,status); + return stmt.executeUpdate(); + + } + + private static int update(Connection connection, long order_id, String status) throws SQLException { + String sql = "UPDATE `t_order` SET status = ? WHERE order_id = ?;"; + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setString(1,status); + stmt.setLong(2,order_id); + return stmt.executeUpdate(); + } + + private static int delete(Connection connection, long order_id) throws SQLException { + + + String sql = "DELETE FROM `t_order` WHERE order_id=? ;"; + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setLong(1,order_id); + return stmt.executeUpdate(); + } + +} diff --git a/04fx/homework2/src/main/java/io/nononi/week5/annotation/AnnotationConfig.java b/04fx/homework2/src/main/java/io/nononi/week5/annotation/AnnotationConfig.java new file mode 100644 index 00000000..cc97de5c --- /dev/null +++ b/04fx/homework2/src/main/java/io/nononi/week5/annotation/AnnotationConfig.java @@ -0,0 +1,12 @@ +package io.nononi.week5.annotation; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * 利用注解注入 + */ +@Configuration +@ComponentScan +public class AnnotationConfig { +} diff --git a/04fx/homework2/src/main/java/io/nononi/week5/annotation/AnnotationExample.java b/04fx/homework2/src/main/java/io/nononi/week5/annotation/AnnotationExample.java new file mode 100644 index 00000000..67f52c65 --- /dev/null +++ b/04fx/homework2/src/main/java/io/nononi/week5/annotation/AnnotationExample.java @@ -0,0 +1,13 @@ +package io.nononi.week5.annotation; + +import org.springframework.stereotype.Component; + +/** + * 自动注入样例 + */ +@Component +public class AnnotationExample { + public void printMsg(){ + System.out.println("通过注解自动注入"); + } +} diff --git a/04fx/homework2/src/main/java/io/nononi/week5/javacode/JavaCodeConfig.java b/04fx/homework2/src/main/java/io/nononi/week5/javacode/JavaCodeConfig.java new file mode 100644 index 00000000..6c6dd12e --- /dev/null +++ b/04fx/homework2/src/main/java/io/nononi/week5/javacode/JavaCodeConfig.java @@ -0,0 +1,16 @@ +package io.nononi.week5.javacode; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * java配置类 + */ +@Configuration +public class JavaCodeConfig { + + @Bean + public JavaCodeExample javaCodeExample(){ + return new JavaCodeExample(); + } +} diff --git a/04fx/homework2/src/main/java/io/nononi/week5/javacode/JavaCodeExample.java b/04fx/homework2/src/main/java/io/nononi/week5/javacode/JavaCodeExample.java new file mode 100644 index 00000000..7a062df6 --- /dev/null +++ b/04fx/homework2/src/main/java/io/nononi/week5/javacode/JavaCodeExample.java @@ -0,0 +1,13 @@ +package io.nononi.week5.javacode; + +import org.springframework.stereotype.Component; + +/** + * 待装配的Bean + */ +@Component +public class JavaCodeExample { + public void printMsg(){ + System.out.println("Java配置类注入"); + } +} diff --git a/04fx/homework2/src/main/java/io/nononi/week5/xml/XmlExample.java b/04fx/homework2/src/main/java/io/nononi/week5/xml/XmlExample.java new file mode 100644 index 00000000..52168204 --- /dev/null +++ b/04fx/homework2/src/main/java/io/nononi/week5/xml/XmlExample.java @@ -0,0 +1,7 @@ +package io.nononi.week5.xml; + +public class XmlExample { + public void printMsg(){ + System.out.println("XML装配Bean"); + } +} diff --git a/04fx/homework2/src/main/resources/xmlConfig.xml b/04fx/homework2/src/main/resources/xmlConfig.xml new file mode 100644 index 00000000..d851eaf3 --- /dev/null +++ b/04fx/homework2/src/main/resources/xmlConfig.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/04fx/homework8/src/main/java/io/nononi/starter/Klass.java b/04fx/homework8/src/main/java/io/nononi/starter/Klass.java new file mode 100644 index 00000000..74b7c844 --- /dev/null +++ b/04fx/homework8/src/main/java/io/nononi/starter/Klass.java @@ -0,0 +1,42 @@ +package io.nononi.starter; + +import java.util.ArrayList; +import java.util.List; + +public class Klass { + private int id; + List students; + + public Klass(int id) { + this.id = id; + this.students = new ArrayList<>(); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public List getStudents() { + return students; + } + + public void setStudents(List students) { + this.students = students; + } + + public void addStudent(Student student) { + students.add(student); + } + + @Override + public String toString() { + return "Klass{" + + "id=" + id + + ", students=" + students + + '}'; + } +} diff --git a/04fx/homework8/src/main/java/io/nononi/starter/School.java b/04fx/homework8/src/main/java/io/nononi/starter/School.java new file mode 100644 index 00000000..dc928853 --- /dev/null +++ b/04fx/homework8/src/main/java/io/nononi/starter/School.java @@ -0,0 +1,27 @@ +package io.nononi.starter; + +import java.util.List; + +public class School { + private List klasses; + + public School(List klasses) { + this.klasses = klasses; + } + + + public List getKlasses() { + return klasses; + } + + public void setKlasses(List klasses) { + this.klasses = klasses; + } + + @Override + public String toString() { + return "School{" + + "klasses=" + klasses + + '}'; + } +} diff --git a/04fx/homework8/src/main/java/io/nononi/starter/SchoolAutoConfiguration.java b/04fx/homework8/src/main/java/io/nononi/starter/SchoolAutoConfiguration.java new file mode 100644 index 00000000..906e0c7c --- /dev/null +++ b/04fx/homework8/src/main/java/io/nononi/starter/SchoolAutoConfiguration.java @@ -0,0 +1,51 @@ +package io.nononi.starter; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Configuration +@EnableConfigurationProperties(SchoolProperties.class) +@ConditionalOnProperty(prefix = "school", value = "enabled", havingValue = "true") +@PropertySource("classpath:application.properties") +public class SchoolAutoConfiguration { + + @Autowired + private SchoolProperties schoolProperties; + + @Bean + public School school() { + List studentIds = schoolProperties.getStudentIds(); + List studentNames = schoolProperties.getStudentNames(); + List klassIds = schoolProperties.getKlassIds(); + List> studentKlassRelation = schoolProperties.getStudentKlassRelation(); + + List students = new ArrayList<>(studentIds.size()); + for (int i = 0; i < studentIds.size(); i++) { + students.add(new Student(studentIds.get(i), studentNames.get(i))); + } + + List klasses = new ArrayList<>(); + for (int i = 0; i < klassIds.size(); i++) { + klasses.add(new Klass(klassIds.get(i))); + } + + for (Map relation : studentKlassRelation) { + klasses.get((Integer) relation.get("klassId")).addStudent(students.get((Integer) relation.get("studentId"))); + } + + System.out.println(studentIds.toString()); + System.out.println(studentNames.toString()); + System.out.println(klassIds.toString()); + System.out.println(studentKlassRelation.toString()); + + return new School(klasses); + } +} diff --git a/04fx/homework8/src/main/java/io/nononi/starter/SchoolProperties.java b/04fx/homework8/src/main/java/io/nononi/starter/SchoolProperties.java new file mode 100644 index 00000000..fd762f82 --- /dev/null +++ b/04fx/homework8/src/main/java/io/nononi/starter/SchoolProperties.java @@ -0,0 +1,46 @@ +package io.nononi.starter; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; +import java.util.Map; + +@ConfigurationProperties(prefix = "school") +public class SchoolProperties { + private List studentIds; + private List studentNames; + private List klassIds; + private List> studentKlassRelation; + + public List getStudentIds() { + return studentIds; + } + + public void setStudentIds(List studentIds) { + this.studentIds = studentIds; + } + + public List getStudentNames() { + return studentNames; + } + + public void setStudentNames(List studentNames) { + this.studentNames = studentNames; + } + + public List getKlassIds() { + return klassIds; + } + + public void setKlassIds(List klassIds) { + this.klassIds = klassIds; + } + + public List> getStudentKlassRelation() { + return studentKlassRelation; + } + + public void setStudentKlassRelation(List> studentKlassRelation) { + this.studentKlassRelation = studentKlassRelation; + } +} diff --git a/04fx/homework8/src/main/java/io/nononi/starter/Student.java b/04fx/homework8/src/main/java/io/nononi/starter/Student.java new file mode 100644 index 00000000..6a99abb4 --- /dev/null +++ b/04fx/homework8/src/main/java/io/nononi/starter/Student.java @@ -0,0 +1,35 @@ +package io.nononi.starter; + +public class Student { + private int id; + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Student(int id, String name) { + this.id = id; + this.name = name; + } + + @Override + public String toString() { + return "Student{" + + "id=" + id + + ", name='" + name + '\'' + + '}'; + } +} diff --git a/04fx/homework8/src/main/resources/META-INF/spring.factories b/04fx/homework8/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..87d8860b --- /dev/null +++ b/04fx/homework8/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.nononi.starter.SchoolAutoConfiguration diff --git a/04fx/homework8/src/main/resources/application.properties b/04fx/homework8/src/main/resources/application.properties new file mode 100644 index 00000000..3f72fc01 --- /dev/null +++ b/04fx/homework8/src/main/resources/application.properties @@ -0,0 +1,9 @@ +school.studentIds = 0,1 +school.studentNames = amy, bella +school.klassIds = 0,1 +school.studentKlassRelation[0].studentId = 0 +school.studentKlassRelation[0].klassId = 0 +school.studentKlassRelation[1].studentId = 1 +school.studentKlassRelation[1].klassId = 1 +school.enabled = true + diff --git a/04fx/homework8/src/test/java/io/nononi/starter/SchoolTest.java b/04fx/homework8/src/test/java/io/nononi/starter/SchoolTest.java new file mode 100644 index 00000000..adab26ae --- /dev/null +++ b/04fx/homework8/src/test/java/io/nononi/starter/SchoolTest.java @@ -0,0 +1,19 @@ +package io.nononi.starter; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = SchoolAutoConfiguration.class) +public class SchoolTest { + @Autowired + private School school; + + @Test + public void test() { + System.out.println(school.toString()); + } +} diff --git a/04fx/week5/README.md b/04fx/week5/README.md new file mode 100644 index 00000000..04e8e377 --- /dev/null +++ b/04fx/week5/README.md @@ -0,0 +1,19 @@ +# 第5周作业 +--- +## 作业2 + +#### 写代码实现 Spring Bean 的装配 + +实现代码:https://github.com/nononi105/JavaCourseCodes/tree/main/04fx/homework2/src/main/java/io/nononi/week5 + +--- +## 作业8 + +#### 给Student/Klass/School 实现自动配置和 Starter +实现代码:https://github.com/nononi105/JavaCourseCodes/tree/main/04fx/homework8/src/main/java/io/nononi/starter + +--- +## 作业10 + +#### 研究一下 JDBC 接口和数据库连接池 +实现代码:https://github.com/nononi105/JavaCourseCodes/tree/main/04fx/homework10/src/main/java/io/nononi/db/dbdemo/jdbc \ No newline at end of file diff --git a/04fx/work8test/src/main/java/io/nononi/starter/startertest/TestController.java b/04fx/work8test/src/main/java/io/nononi/starter/startertest/TestController.java new file mode 100644 index 00000000..b13fc8a4 --- /dev/null +++ b/04fx/work8test/src/main/java/io/nononi/starter/startertest/TestController.java @@ -0,0 +1,19 @@ +package io.nononi.starter.startertest; + +import io.nononi.starter.School; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller() +public class TestController { + @Autowired + private School school; + + @GetMapping("/test") + @ResponseBody + public String test(){ + return school.toString(); + } +} diff --git a/06db/WEEK7/README.md b/06db/WEEK7/README.md new file mode 100644 index 00000000..a326aaf6 --- /dev/null +++ b/06db/WEEK7/README.md @@ -0,0 +1,21 @@ +### 作业2 +--- +#### 按自己设计的表结构,插入 100 万订单模拟数据,测试不同方式的插入效率 + ++ 一个事务下一次性插入1000000条数据,耗时269.519秒 + ++ 一个事务下利用preparedstatement的Add Batch,200条提交一次,耗时30.657秒(坑:要添加参数rewriteBatchedStatements=true,让mysql开启批处理的支持) ++ 不开事务下利用preparedstatement的Add Batch,200条执行一次executeBatch - 218.40秒 ++ 利用load data 命令导入csv文件,耗时24.67秒 + + +### 作业9 +--- +读写分离 - 动态切换数据源版本 1.0 +实现代码:https://github.com/nononi105/JavaCourseCodes/tree/ca0240d737d54121d7e50a5edd45055c69f18a7b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb + +### 作业10 +--- +读写分离 - 数据库框架版本 2.0 +实现代码:https://github.com/nononi105/JavaCourseCodes/blob/main/06db/work10ofweek7/src/main/java/io/nononi/shardingspheredemo/datasource/DataSourceConfig.java + diff --git a/06db/WEEK8/README.md b/06db/WEEK8/README.md new file mode 100644 index 00000000..513892d1 --- /dev/null +++ b/06db/WEEK8/README.md @@ -0,0 +1,90 @@ +### 作业2 +--- +#### 设计对前面的订单表数据进行水平分库分表,拆分 2 个库,每个库 16 张表。并在新结构在演示常见的增删改查操作。 + +实现代码:https://github.com/nononi105/JavaCourseCodes/blob/main/04fx/homework10/src/main/java/io/nononi/db/dbdemo/shardingdb/ShardingDbDemo.java + +sql:CREATE TABLE t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY(order_id)); + +配置: +server.yaml +``` +authentication: + users: + root: + password: root + +props: + max-connections-size-per-query: 1 + acceptor-size: 16 # The default value is available processors count * 2. + executor-size: 16 # Infinite by default. + proxy-frontend-flush-threshold: 128 # The default value is 128. + proxy-transaction-type: LOCAL + proxy-opentracing-enabled: false + proxy-hint-enabled: false + query-with-cipher-column: true + sql-show: true + check-table-metadata-enabled: false +``` + +config-sharding.yaml + +``` +schemaName: sharding_db + +dataSourceCommon: + username: root + password: + connectionTimeoutMilliseconds: 30000 + idleTimeoutMilliseconds: 60000 + maxLifetimeMilliseconds: 1800000 + maxPoolSize: 5 + minPoolSize: 1 + maintenanceIntervalMilliseconds: 30000 + +dataSources: + ds_0: + url: jdbc:mysql://127.0.0.1:3316/demo_ds_0?serverTimezone=UTC&useSSL=false + ds_1: + url: jdbc:mysql://127.0.0.1:3326/demo_ds_1?serverTimezone=UTC&useSSL=false + +rules: +- !SHARDING + tables: + t_order: + actualDataNodes: ds_${0..1}.t_order_${0..15} + tableStrategy: + standard: + shardingColumn: order_id + shardingAlgorithmName: t_order_inline + keyGenerateStrategy: + column: order_id + keyGeneratorName: snowflake + defaultDatabaseStrategy: + standard: + shardingColumn: user_id + shardingAlgorithmName: database_inline + defaultTableStrategy: + none: + + shardingAlgorithms: + database_inline: + type: INLINE + props: + algorithm-expression: ds_${user_id % 2} + t_order_inline: + type: INLINE + props: + algorithm-expression: t_order_${order_id % 16} + keyGenerators: + snowflake: + type: SNOWFLAKE + props: + worker-id: 123 +``` + + +### 作业6 基于ShardingSphere 的 Atomikos XA 实现一个简单的分布式事务应用 demo +--- +实现代码:https://github.com/nononi105/JavaCourseCodes/tree/main/06db/work6ofweek8/src/main + diff --git a/06db/work10ofweek7/src/main/java/io/nononi/shardingspheredemo/datasource/DataSourceConfig.java b/06db/work10ofweek7/src/main/java/io/nononi/shardingspheredemo/datasource/DataSourceConfig.java new file mode 100644 index 00000000..ef7d73e4 --- /dev/null +++ b/06db/work10ofweek7/src/main/java/io/nononi/shardingspheredemo/datasource/DataSourceConfig.java @@ -0,0 +1,49 @@ +package io.nononi.shardingspheredemo.datasource; + +import org.apache.commons.dbcp2.BasicDataSource; + +import org.apache.shardingsphere.api.config.masterslave.MasterSlaveRuleConfiguration; +import org.apache.shardingsphere.shardingjdbc.api.MasterSlaveDataSourceFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.sql.DataSource; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +@Configuration +public class DataSourceConfig { + + @Bean + public DataSource dataSource() throws SQLException { + // Configure actual data sources + Map dataSourceMap = new HashMap<>(); + + // Configure master data source + BasicDataSource masterDataSource = new BasicDataSource(); + masterDataSource.setDriverClassName("com.mysql.jdbc.Driver"); + masterDataSource.setUrl("jdbc:mysql://localhost:3316/db"); + masterDataSource.setUsername("root"); + masterDataSource.setPassword(""); + dataSourceMap.put("ds_master", masterDataSource); + + // Configure the first slave data source + BasicDataSource slaveDataSource1 = new BasicDataSource(); + slaveDataSource1.setDriverClassName("com.mysql.jdbc.Driver"); + slaveDataSource1.setUrl("jdbc:mysql://localhost:3326/db"); + slaveDataSource1.setUsername("root"); + slaveDataSource1.setPassword(""); + dataSourceMap.put("ds_slave", slaveDataSource1); + + + // Configure read-write split rule + MasterSlaveRuleConfiguration masterSlaveRuleConfig = new MasterSlaveRuleConfiguration("ds_master_slave", "ds_master", Arrays.asList("ds_slave0")); + + // Get data source + DataSource dataSource = MasterSlaveDataSourceFactory.createDataSource(dataSourceMap, masterSlaveRuleConfig, new Properties()); + return dataSource; + } +} diff --git a/06db/work10ofweek7/src/main/java/io/nononi/shardingspheredemo/service/OrderService.java b/06db/work10ofweek7/src/main/java/io/nononi/shardingspheredemo/service/OrderService.java new file mode 100644 index 00000000..3fc924b8 --- /dev/null +++ b/06db/work10ofweek7/src/main/java/io/nononi/shardingspheredemo/service/OrderService.java @@ -0,0 +1,6 @@ +package io.nononi.shardingspheredemo.service; + +public interface OrderService { + void insert(int id, String name); + void query(); +} diff --git a/06db/work10ofweek7/src/main/java/io/nononi/shardingspheredemo/service/impl/OrderServiceImpl.java b/06db/work10ofweek7/src/main/java/io/nononi/shardingspheredemo/service/impl/OrderServiceImpl.java new file mode 100644 index 00000000..b2db3ad7 --- /dev/null +++ b/06db/work10ofweek7/src/main/java/io/nononi/shardingspheredemo/service/impl/OrderServiceImpl.java @@ -0,0 +1,51 @@ +package io.nononi.shardingspheredemo.service.impl; + +import io.nononi.shardingspheredemo.service.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class OrderServiceImpl implements OrderService { + + @Autowired + private DataSource dataSource; + + @Override + public void insert(int id, String name) { + try { + Connection conn = dataSource.getConnection(); + String sql = "INSERT INTO `order` (id,name) VALUES (?,?);"; + PreparedStatement stmt = conn.prepareStatement(sql); + stmt.setInt(1,id); + stmt.setString(2,name); + stmt.executeUpdate(); + + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + @Override + public void query() { + String sql = "SELECT * FROM `order`;"; + try { + Connection conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet resultSet = stmt.executeQuery(); + while (resultSet.next()) { + System.out.print(resultSet.getInt("id") + " "); + System.out.println(resultSet.getString("name") + " "); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/06db/work6ofweek8/src/main/java/io/nononi/xademo/XaDemo.java b/06db/work6ofweek8/src/main/java/io/nononi/xademo/XaDemo.java new file mode 100644 index 00000000..9ddf4e1d --- /dev/null +++ b/06db/work6ofweek8/src/main/java/io/nononi/xademo/XaDemo.java @@ -0,0 +1,78 @@ +package io.nononi.xademo; + +import org.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory; +import org.apache.shardingsphere.transaction.core.TransactionType; +import org.apache.shardingsphere.transaction.core.TransactionTypeHolder; + +import javax.sql.DataSource; +import java.io.File; +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; + +public class XaDemo { + public static void main(String[] args) throws IOException, SQLException { + DataSource dataSource = getShardingDatasource(); + cleanupData(dataSource); + + TransactionTypeHolder.set(TransactionType.XA); + + Connection conn = dataSource.getConnection(); + String sql = "insert into t_order (user_id, order_id) VALUES (?, ?);"; + + System.out.println("First XA Start insert data"); + try (PreparedStatement statement = conn.prepareStatement(sql)) { + conn.setAutoCommit(false); + //会插入到ds1的t_order_1; + statement.setLong(1, 1); + statement.setLong(2, 1); + statement.executeUpdate(); + //会插入到ds0的t_order_0; + statement.setLong(1, 2); + statement.setLong(2, 2); + statement.executeUpdate(); + + conn.commit(); + } + + System.out.println("First XA insert successful"); + + System.out.println("Second XA Start insert data"); + try (PreparedStatement statement = conn.prepareStatement(sql)) { + conn.setAutoCommit(false); + //会插入到ds1的t_order_1; + //回滚后应该两个数据库中都没有插入 + statement.setLong(1, 3); + statement.setLong(2, 3); + statement.executeUpdate(); + //会插入到ds0的t_order_0; + statement.setLong(1, 4); + statement.setLong(2, 4); + statement.executeUpdate(); + conn.rollback(); + + } finally { + conn.close(); + } + System.out.println("Second XA insert rollback"); + } + + private static void cleanupData(DataSource dataSource) { + System.out.println("Delete all Data"); + try (Connection conn = dataSource.getConnection(); Statement statement = conn.createStatement()) { + statement.execute("delete from t_order;"); + conn.commit(); + } catch (SQLException e) { + e.printStackTrace(); + } + System.out.println("Delete all Data successful"); + } + + static private DataSource getShardingDatasource() throws IOException, SQLException { + String fileName = "C:\\Users\\NoNo_Ni\\Desktop\\Java进阶训练营\\javacourse\\06db\\work6ofweek8\\src\\main\\resources\\sharding-databases-tables.yaml"; + File yamlFile = new File(fileName); + return YamlShardingSphereDataSourceFactory.createDataSource(yamlFile); + } +} diff --git a/06db/work6ofweek8/src/main/resources/sharding-databases-tables.yaml b/06db/work6ofweek8/src/main/resources/sharding-databases-tables.yaml new file mode 100644 index 00000000..d5a42c8f --- /dev/null +++ b/06db/work6ofweek8/src/main/resources/sharding-databases-tables.yaml @@ -0,0 +1,42 @@ +dataSources: + ds_0: !!com.zaxxer.hikari.HikariDataSource + driverClassName: com.mysql.jdbc.Driver + jdbcUrl: jdbc:mysql://127.0.0.1:3316/demo_ds_xa_0?serverTimezone=UTC&useSSL=false + username: root + password: + autoCommit: false + ds_1: !!com.zaxxer.hikari.HikariDataSource + driverClassName: com.mysql.jdbc.Driver + jdbcUrl: jdbc:mysql://127.0.0.1:3326/demo_ds_xa_1?serverTimezone=UTC&useSSL=false + username: root + password: + autoCommit: false + +rules: + - !SHARDING + tables: + t_order: + actualDataNodes: ds_${0..1}.t_order_${0..1} + databaseStrategy: + standard: + shardingColumn: user_id + shardingAlgorithmName: database_inline + tableStrategy: + standard: + shardingColumn: order_id + shardingAlgorithmName: t_order_inline + bindingTables: + - t_order + + shardingAlgorithms: + database_inline: + type: INLINE + props: + algorithm-expression: ds_${user_id % 2} + t_order_inline: + type: INLINE + props: + algorithm-expression: t_order_${order_id % 2} + +props: + sql-show: true \ No newline at end of file diff --git a/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/annotation/ReadOnly.java b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/annotation/ReadOnly.java new file mode 100644 index 00000000..5141d344 --- /dev/null +++ b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/annotation/ReadOnly.java @@ -0,0 +1,15 @@ +package io.nononi.dynamicdb.annotation; + +import org.springframework.stereotype.Component; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Component +public @interface ReadOnly { + +} diff --git a/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/aspect/ReadOnlyAspect.java b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/aspect/ReadOnlyAspect.java new file mode 100644 index 00000000..3d05a66b --- /dev/null +++ b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/aspect/ReadOnlyAspect.java @@ -0,0 +1,44 @@ +package io.nononi.dynamicdb.aspect; + +import io.nononi.dynamicdb.annotation.ReadOnly; +import io.nononi.dynamicdb.datasource.DynamicDataSource; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +@Aspect +@Component +public class ReadOnlyAspect { + + @Pointcut("@annotation(io.nononi.dynamicdb.annotation.ReadOnly)") + public void readOnly(){}; + + + @Around("readOnly()") + public Object around(ProceedingJoinPoint point) throws Throwable{ + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + + ReadOnly readOnly = method.getAnnotation(ReadOnly.class); + if(readOnly == null){ + DynamicDataSource.setDataSource("master"); + System.out.println("datasource is :" + "master"); + } else { + DynamicDataSource.setDataSource("slave"); + System.out.println("datasource is :" + "slave"); + } + + try { + return point.proceed(); + }finally { + DynamicDataSource.clearDataSource(); + System.out.println("clean datasource"); + } + } + +} diff --git a/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/datasource/DynamicDataSource.java b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/datasource/DynamicDataSource.java new file mode 100644 index 00000000..aa54e862 --- /dev/null +++ b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/datasource/DynamicDataSource.java @@ -0,0 +1,39 @@ +package io.nononi.dynamicdb.datasource; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import javax.sql.DataSource; +import java.util.Map; + +public class DynamicDataSource extends AbstractRoutingDataSource { + + + /** + * threadLocal 设置数据源 + */ + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources){ + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() { + return getDataSource(); + } + + public static void setDataSource(String dataSource) { + CONTEXT_HOLDER.set(dataSource); + } + + public static String getDataSource() { + return CONTEXT_HOLDER.get(); + } + + public static void clearDataSource() { + CONTEXT_HOLDER.remove(); + } + +} diff --git a/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/datasource/DynamicDataSourceConfig.java b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/datasource/DynamicDataSourceConfig.java new file mode 100644 index 00000000..914e4191 --- /dev/null +++ b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/datasource/DynamicDataSourceConfig.java @@ -0,0 +1,44 @@ +package io.nononi.dynamicdb.datasource; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class DynamicDataSourceConfig { + + @Bean(name = "master") + public DataSource masterDatasource(){ + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName("com.mysql.jdbc.Driver"); + dataSource.setUrl("jdbc:mysql://localhost:3316/db"); + dataSource.setUsername("root"); + dataSource.setPassword(""); + return dataSource; + } + + @Bean(name = "slave") + public DataSource slaveDatasource(){ + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName("com.mysql.jdbc.Driver"); + dataSource.setUrl("jdbc:mysql://localhost:3326/db"); + dataSource.setUsername("root"); + dataSource.setPassword(""); + return dataSource; + } + + @Bean + @Primary + public DynamicDataSource dataSource(@Qualifier("master") DataSource masterDatasource, @Qualifier("slave") DataSource slaveDatasource){ + Map targetDataSources = new HashMap<>(); + targetDataSources.put("master", masterDatasource); + targetDataSources.put("slave",slaveDatasource); + return new DynamicDataSource(masterDatasource,targetDataSources); + } +} diff --git a/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/service/OrderService.java b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/service/OrderService.java new file mode 100644 index 00000000..0e954fce --- /dev/null +++ b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/service/OrderService.java @@ -0,0 +1,6 @@ +package io.nononi.dynamicdb.service; + +public interface OrderService { + void insert(int id ,String name); + void query(); +} diff --git a/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/service/impl/OrderServiceImpl.java b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/service/impl/OrderServiceImpl.java new file mode 100644 index 00000000..e30f4c83 --- /dev/null +++ b/06db/work9ofweek7/src/main/java/io/nononi/dynamicdb/service/impl/OrderServiceImpl.java @@ -0,0 +1,55 @@ +package io.nononi.dynamicdb.service.impl; + +import io.nononi.dynamicdb.annotation.ReadOnly; +import io.nononi.dynamicdb.datasource.DynamicDataSource; +import io.nononi.dynamicdb.service.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Component +public class OrderServiceImpl implements OrderService { + + @Autowired + private DynamicDataSource dataSource; + + @Override + public void insert(int id, String name) { + try { + Connection conn = dataSource.getConnection(); + System.out.println(conn.getMetaData().getURL()); + String sql = "INSERT INTO `order` (id,name) VALUES (?,?);"; + PreparedStatement stmt = conn.prepareStatement(sql); + stmt.setInt(1,id); + stmt.setString(2,name); + stmt.executeUpdate(); + + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + @Override + @ReadOnly + public void query() { + String sql = "SELECT * FROM `order`;"; + try { + Connection conn = dataSource.getConnection(); + System.out.println(conn.getMetaData().getURL()); + PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet resultSet = stmt.executeQuery(); + while (resultSet.next()) { + System.out.print(resultSet.getInt("id") + " "); + System.out.println(resultSet.getString("name") + " "); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/06db/work9ofweek7/src/test/java/io/nononi/dynamicdb/DynamicdbApplicationTests.java b/06db/work9ofweek7/src/test/java/io/nononi/dynamicdb/DynamicdbApplicationTests.java new file mode 100644 index 00000000..431a4b80 --- /dev/null +++ b/06db/work9ofweek7/src/test/java/io/nononi/dynamicdb/DynamicdbApplicationTests.java @@ -0,0 +1,22 @@ +package io.nononi.dynamicdb; + +import io.nononi.dynamicdb.service.impl.OrderServiceImpl; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@SpringBootTest +@ExtendWith(SpringExtension.class) +class DynamicdbApplicationTests { + @Autowired + private OrderServiceImpl orderService; + + @Test + public void testDynamicDb(){ + orderService.insert(4,"ohhhhhh"); + orderService.query(); + } + +} diff --git a/06db/workofweek6/work6.sql b/06db/workofweek6/work6.sql new file mode 100644 index 00000000..4fef162d --- /dev/null +++ b/06db/workofweek6/work6.sql @@ -0,0 +1,106 @@ +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户表id', + `username` varchar(50) NOT NULL COMMENT '用户名', + `password` varchar(50) NOT NULL COMMENT '用户密码', + `email` varchar(50) DEFAULT NULL, + `phone` varchar(20) DEFAULT NULL, + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_time` datetime NOT NULL COMMENT '最后一次更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `user_name_unique` (`username`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '用户表'; + +DROP TABLE IF EXISTS `product`; +CREATE TABLE `product` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品id', + `category_id` int(11) NOT NULL COMMENT '分类id,对应category表的主键', + `name` varchar(100) NOT NULL COMMENT '商品名称', + `price` decimal(20,2) NOT NULL COMMENT '价格,单位-元保留两位小数', + `stock` int(11) NOT NULL COMMENT '库存数量', + `status` int(6) DEFAULT '1' COMMENT '商品状态.1-在售 2-下架 3-删除', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '商品表'; + +DROP TABLE IF EXISTS `category`; +CREATE TABLE `category` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '类别Id', + `parent_id` int(11) DEFAULT NULL COMMENT '父类别id当id=0时说明是根节点,一级类别', + `name` varchar(50) DEFAULT NULL COMMENT '类别名称', + `status` tinyint(1) DEFAULT '1' COMMENT '类别状态1-正常,2-已废弃', + `sort_order` int(4) DEFAULT NULL COMMENT '排序编号,同类展示顺序,数值相等则自然排序', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '商品类别表'; + +DROP TABLE IF EXISTS `order`; +CREATE TABLE `order` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单id', + `order_no` bigint(20) DEFAULT NULL COMMENT '订单号', + `user_id` int(11) DEFAULT NULL COMMENT '用户id', + `shipping_id` int(11) DEFAULT NULL, + `payment` decimal(20,2) DEFAULT NULL COMMENT '实际付款金额,单位是元,保留两位小数', + `payment_type` int(4) DEFAULT NULL COMMENT '支付类型,1-在线支付', + `postage` int(10) DEFAULT NULL COMMENT '运费,单位是元', + `status` int(10) DEFAULT NULL COMMENT '订单状态:0-已取消-10-未付款,20-已付款,40-已发货,50-交易成功,60-交易关闭', + `payment_time` datetime DEFAULT NULL COMMENT '支付时间', + `send_time` datetime DEFAULT NULL COMMENT '发货时间', + `end_time` datetime DEFAULT NULL COMMENT '交易完成时间', + `close_time` datetime DEFAULT NULL COMMENT '交易关闭时间', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `order_no_index` (`order_no`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '订单表'; + + +DROP TABLE IF EXISTS `order_item`; +CREATE TABLE `order_item` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单子表id', + `user_id` int(11) DEFAULT NULL, + `order_no` bigint(20) DEFAULT NULL, + `product_id` int(11) DEFAULT NULL COMMENT '商品id', + `product_name` varchar(100) DEFAULT NULL COMMENT '商品名称', + `current_unit_price` decimal(20,2) DEFAULT NULL COMMENT '生成订单时的商品单价,单位是元,保留两位小数', + `quantity` int(10) DEFAULT NULL COMMENT '商品数量', + `total_price` decimal(20,2) DEFAULT NULL COMMENT '商品总价,单位是元,保留两位小数', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `order_no_index` (`order_no`) USING BTREE, + KEY `order_no_user_id_index` (`user_id`,`order_no`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '订单子表'; + + +DROP TABLE IF EXISTS `shipping`; +CREATE TABLE `shipping` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) DEFAULT NULL COMMENT '用户id', + `receiver_name` varchar(20) DEFAULT NULL COMMENT '收货姓名', + `receiver_phone` varchar(20) DEFAULT NULL COMMENT '收货固定电话', + `receiver_mobile` varchar(20) DEFAULT NULL COMMENT '收货移动电话', + `receiver_province` varchar(20) DEFAULT NULL COMMENT '省份', + `receiver_city` varchar(20) DEFAULT NULL COMMENT '城市', + `receiver_district` varchar(20) DEFAULT NULL COMMENT '区/县', + `receiver_address` varchar(200) DEFAULT NULL COMMENT '详细地址', + `receiver_zip` varchar(6) DEFAULT NULL COMMENT '邮编', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '收货信息表'; + +DROP TABLE IF EXISTS `cart`; +CREATE TABLE `cart` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `product_id` int(11) DEFAULT NULL COMMENT '商品id', + `quantity` int(11) DEFAULT NULL COMMENT '数量', + `checked` int(11) DEFAULT NULL COMMENT '是否选择,1=已勾选,0=未勾选', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `user_id_index` (`user_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '购物车表'; \ No newline at end of file diff --git a/07rpc/rpc01/rpcfx-core/pom.xml b/07rpc/rpc01/rpcfx-core/pom.xml index 4570a59d..07f54b14 100644 --- a/07rpc/rpc01/rpcfx-core/pom.xml +++ b/07rpc/rpc01/rpcfx-core/pom.xml @@ -59,6 +59,17 @@ spring-boot-starter-web + + org.aspectj + aspectjweaver + + + + io.netty + netty-all + 4.1.45.Final + + org.springframework.boot spring-boot-starter-test diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java index 5d1ae517..87c36664 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/Rpcfx.java @@ -43,7 +43,7 @@ public static T create(final Class serviceClass, final String url, Filter // 0. 替换动态代理 -> 字节码生成 return (T) Proxy.newProxyInstance(Rpcfx.class.getClassLoader(), new Class[]{serviceClass}, new RpcfxInvocationHandler(serviceClass, url, filters)); - + // } public static class RpcfxInvocationHandler implements InvocationHandler { diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/aop/RpcService.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/aop/RpcService.java new file mode 100644 index 00000000..e9fc175d --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/aop/RpcService.java @@ -0,0 +1,10 @@ +package io.kimmking.rpcfx.client.aop; + +import java.lang.annotation.*; + + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RpcService { +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/aop/RpcServiceAspect.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/aop/RpcServiceAspect.java new file mode 100644 index 00000000..28d55a20 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/aop/RpcServiceAspect.java @@ -0,0 +1,84 @@ +package io.kimmking.rpcfx.client.aop; + +import com.alibaba.fastjson.JSON; +import io.kimmking.rpcfx.api.RpcfxRequest; +import io.kimmking.rpcfx.api.RpcfxResponse; +import io.kimmking.rpcfx.client.netty4.NettyHttpClient; +import io.kimmking.rpcfx.exception.RpcfxException; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + + +import java.io.IOException; + +/** + * 定义切面,切点为标记@RpcSerivice注解的方法,环绕型通知 + */ +@Aspect +@Component +public class RpcServiceAspect { + private static final MediaType JSONTYPE = MediaType.get("application/json; charset=utf-8"); + + @Pointcut(value = "@annotation(io.kimmking.rpcfx.client.aop.RpcService)") + public void pointcut(){ + + } + + + @Around(value = "pointcut()") + public Object around(ProceedingJoinPoint point) { + String methodName = point.getSignature().getName(); + Object[] methodArgs = point.getArgs(); + RpcfxRequest request = new RpcfxRequest(); + request.setMethod(methodName); + request.setParams(methodArgs); + request.setServiceClass(point.getTarget().getClass().getInterfaces()[0].getName()); + + RpcfxResponse response; + try { + response = post(request); + } catch (IOException e) { + e.printStackTrace(); + throw new RpcfxException(e.getMessage()); + } + + return JSON.parse(response.getResult().toString()); + + } + + private RpcfxResponse post(RpcfxRequest req) throws IOException { + String reqJson = JSON.toJSONString(req); + System.out.println("req json: "+reqJson); + + // 1.可以复用client + // 2.尝试使用httpclient或者netty client +// OkHttpClient client = new OkHttpClient(); +// final Request request = new Request.Builder() +// .url("http://127.0.0.1:8080/") +// .post(RequestBody.create(JSONTYPE, reqJson)) +// .build(); +// String respJson = client.newCall(request).execute().body().string(); +// System.out.println("resp json: "+respJson); +// return JSON.parseObject(respJson, RpcfxResponse.class); + + try { + NettyHttpClient nettyHttpClient = new NettyHttpClient("127.0.0.1", 8080); + return nettyHttpClient.send(req); + + } catch (Exception e) { + e.printStackTrace(); + RpcfxResponse response = new RpcfxResponse(); + response.setStatus(false); + response.setException(new RpcfxException(e.getMessage())); + response.setResult(null); + return response; + } + } +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/netty4/HttpClientHandler.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/netty4/HttpClientHandler.java new file mode 100644 index 00000000..c9311678 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/netty4/HttpClientHandler.java @@ -0,0 +1,51 @@ +package io.kimmking.rpcfx.client.netty4; + +import com.alibaba.fastjson.JSON; +import io.kimmking.rpcfx.api.RpcfxResponse; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.util.CharsetUtil; + + +public class HttpClientHandler extends ChannelInboundHandlerAdapter { + private ChannelHandlerContext ctx; + private ChannelPromise promise; + private volatile RpcfxResponse rpcfxResponse; + + public ChannelPromise flushMessage(FullHttpRequest request) { + if (ctx == null) + throw new IllegalStateException(); + + System.out.println("flush flushMessage"); + promise = ctx.writeAndFlush(request).channel().newPromise(); + return promise; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + this.ctx = ctx; + System.out.println("已连接"); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + + if(msg instanceof FullHttpResponse){ + FullHttpResponse response = (FullHttpResponse)msg; + ByteBuf buf = response.content(); + String result = buf.toString(CharsetUtil.UTF_8); + this.rpcfxResponse = JSON.parseObject(result, RpcfxResponse.class); + this.promise.setSuccess(); //任务完成 + } + } + + public RpcfxResponse getRpcfxResponse() throws InterruptedException { + return this.rpcfxResponse; + } + +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/netty4/NettyHttpClient.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/netty4/NettyHttpClient.java new file mode 100644 index 00000000..458e2fb0 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/client/netty4/NettyHttpClient.java @@ -0,0 +1,88 @@ +package io.kimmking.rpcfx.client.netty4;//package io.github.kimmking.gateway.outbound; + +import com.alibaba.fastjson.JSON; +import io.kimmking.rpcfx.api.RpcfxRequest; +import io.kimmking.rpcfx.api.RpcfxResponse; +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.Unpooled; +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.handler.codec.http.*; + +import java.net.InetSocketAddress; + +public class NettyHttpClient { + private HttpClientHandler clientHandler = new HttpClientHandler(); + private final String host; + private final int port; + + public NettyHttpClient(String host, int port) { + this.host = host; + this.port = port; + } + + public RpcfxResponse send(RpcfxRequest rpcfxRequest) throws Exception { + EventLoopGroup workerGroup = new NioEventLoopGroup(); + + try { + Bootstrap b = new Bootstrap(); + b.group(workerGroup) + .channel(NioSocketChannel.class) + .remoteAddress(new InetSocketAddress(host,port)) + .option(ChannelOption.SO_KEEPALIVE, true) + .handler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + // 客户端接收到的是httpResponse响应,所以要使用HttpResponseDecoder进行解码 + ch.pipeline().addLast(new HttpResponseDecoder()); + // 客户端发送的是httprequest,所以要使用HttpRequestEncoder进行编码 + ch.pipeline().addLast(new HttpRequestEncoder()); + ch.pipeline().addLast(new HttpObjectAggregator(1024*1024)); + ch.pipeline().addLast(new HttpServerExpectContinueHandler()); + ch.pipeline().addLast(clientHandler); + } + }); + + // Start the client. + ChannelFuture f = b.connect().sync(); + + + RpcfxResponse response = this.post(rpcfxRequest); + f.channel().closeFuture().sync(); + + return response; + } finally { + workerGroup.shutdownGracefully(); + } + + } + + private RpcfxResponse post(RpcfxRequest rpcfxRequest) throws InterruptedException { + byte[] bytes = JSON.toJSONBytes(rpcfxRequest); + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST, "/", + Unpooled.wrappedBuffer(bytes)); + request.headers().add(HttpHeaderNames.CONNECTION,HttpHeaderValues.KEEP_ALIVE); + request.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON); + request.headers().add(HttpHeaderNames.CONTENT_LENGTH,request.content().readableBytes()); + ChannelPromise channelPromise = clientHandler.flushMessage(request); + channelPromise.await(); + return clientHandler.getRpcfxResponse(); + } + + public static void main(String[] args) throws Exception { + String host = "127.0.0.1"; + int port = 8080; + + NettyHttpClient nettyHttpClient = new NettyHttpClient(host, port); + + RpcfxRequest rpcfxRequest = new RpcfxRequest(); + rpcfxRequest.setServiceClass("io.kimmking.rpcfx.demo.api.UserService"); + rpcfxRequest.setParams(new Integer[]{1}); + rpcfxRequest.setMethod("findById"); + RpcfxResponse response = nettyHttpClient.send(rpcfxRequest); + System.out.println(response); +// System.out.println(nettyHttpClient.post(request)); + } +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/exception/RpcfxException.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/exception/RpcfxException.java new file mode 100644 index 00000000..155e0770 --- /dev/null +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/exception/RpcfxException.java @@ -0,0 +1,12 @@ +package io.kimmking.rpcfx.exception; + + +public class RpcfxException extends RuntimeException { + public RpcfxException(String message) { + super(message); + } + + public RpcfxException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java index a6f77dac..b47820cc 100644 --- a/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java +++ b/07rpc/rpc01/rpcfx-core/src/main/java/io/kimmking/rpcfx/server/RpcfxInvoker.java @@ -9,30 +9,38 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.HashMap; public class RpcfxInvoker { private RpcfxResolver resolver; + private HashMap> serviceContext; + public RpcfxInvoker(RpcfxResolver resolver){ this.resolver = resolver; } + public RpcfxInvoker(HashMap> serviceContext) { + this.serviceContext = serviceContext; + } + public RpcfxResponse invoke(RpcfxRequest request) { RpcfxResponse response = new RpcfxResponse(); String serviceClass = request.getServiceClass(); // 作业1:改成泛型和反射 - Object service = resolver.resolve(serviceClass);//this.applicationContext.getBean(serviceClass); +// Object service = resolver.resolve(serviceClass);//this.applicationContext.getBean(serviceClass); + Class service = this.serviceContext.get(serviceClass); try { - Method method = resolveMethodFromClass(service.getClass(), request.getMethod()); - Object result = method.invoke(service, request.getParams()); // dubbo, fastjson, + Method method = resolveMethodFromClass(service, request.getMethod()); + Object result = method.invoke(service.newInstance(), request.getParams()); // dubbo, fastjson, // 两次json序列化能否合并成一个 response.setResult(JSON.toJSONString(result, SerializerFeature.WriteClassName)); response.setStatus(true); return response; - } catch ( IllegalAccessException | InvocationTargetException e) { + } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { // 3.Xstream diff --git a/07rpc/rpc01/rpcfx-demo-api/src/main/java/io/kimmking/rpcfx/demo/api/OrderService.java b/07rpc/rpc01/rpcfx-demo-api/src/main/java/io/kimmking/rpcfx/demo/api/OrderService.java index 1884fedb..51b7a08d 100644 --- a/07rpc/rpc01/rpcfx-demo-api/src/main/java/io/kimmking/rpcfx/demo/api/OrderService.java +++ b/07rpc/rpc01/rpcfx-demo-api/src/main/java/io/kimmking/rpcfx/demo/api/OrderService.java @@ -1,5 +1,6 @@ package io.kimmking.rpcfx.demo.api; + public interface OrderService { Order findOrderById(int id); diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java index a4187d14..8fb4a4b0 100644 --- a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/RpcfxClientApplication.java @@ -1,19 +1,10 @@ package io.kimmking.rpcfx.demo.consumer; -import io.kimmking.rpcfx.api.Filter; -import io.kimmking.rpcfx.api.LoadBalancer; -import io.kimmking.rpcfx.api.Router; -import io.kimmking.rpcfx.api.RpcfxRequest; import io.kimmking.rpcfx.client.Rpcfx; -import io.kimmking.rpcfx.demo.api.Order; -import io.kimmking.rpcfx.demo.api.OrderService; import io.kimmking.rpcfx.demo.api.User; import io.kimmking.rpcfx.demo.api.UserService; -import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.SpringBootApplication; -import java.util.List; -import java.util.Random; @SpringBootApplication public class RpcfxClientApplication { @@ -23,11 +14,14 @@ public class RpcfxClientApplication { // nexus, userserivce -> userdao -> user // + + + public static void main(String[] args) { // UserService service = new xxx(); // service.findById - +// SpringApplication.run(RpcfxClientApplication.class, args); UserService userService = Rpcfx.create(UserService.class, "http://localhost:8080/"); User user = userService.findById(1); System.out.println("find user id=1 from server: " + user.getName()); @@ -39,31 +33,31 @@ public static void main(String[] args) { // // // UserService userService2 = Rpcfx.createFromRegistry(UserService.class, "localhost:2181", new TagRouter(), new RandomLoadBalancer(), new CuicuiFilter()); -// SpringApplication.run(RpcfxClientApplication.class, args); - } - - private static class TagRouter implements Router { - @Override - public List route(List urls) { - return urls; - } - } - private static class RandomLoadBalancer implements LoadBalancer { - @Override - public String select(List urls) { - return urls.get(0); - } } - @Slf4j - private static class CuicuiFilter implements Filter { - @Override - public boolean filter(RpcfxRequest request) { - log.info("filter {} -> {}", this.getClass().getName(), request.toString()); - return true; - } - } +// private static class TagRouter implements Router { +// @Override +// public List route(List urls) { +// return urls; +// } +// } +// +// private static class RandomLoadBalancer implements LoadBalancer { +// @Override +// public String select(List urls) { +// return urls.get(0); +// } +// } +// +// @Slf4j +// private static class CuicuiFilter implements Filter { +// @Override +// public boolean filter(RpcfxRequest request) { +// log.info("filter {} -> {}", this.getClass().getName(), request.toString()); +// return true; +// } +// } } diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/config/AnnotationConfig.java b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/config/AnnotationConfig.java new file mode 100644 index 00000000..4fdb4326 --- /dev/null +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/config/AnnotationConfig.java @@ -0,0 +1,16 @@ +package io.kimmking.rpcfx.demo.consumer.config; + + +import com.alibaba.fastjson.parser.ParserConfig; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +@Configuration +@EnableAspectJAutoProxy +@ComponentScan(basePackages = {"io.kimmking.rpcfx.demo.consumer.service","io.kimmking.rpcfx.client.aop"}) +public class AnnotationConfig { + static { + ParserConfig.getGlobalInstance().addAccept("io.kimmking"); + } +} \ No newline at end of file diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/service/OrderServiceImpl.java b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/service/OrderServiceImpl.java new file mode 100644 index 00000000..e38e3926 --- /dev/null +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/service/OrderServiceImpl.java @@ -0,0 +1,14 @@ +package io.kimmking.rpcfx.demo.consumer.service; + +import io.kimmking.rpcfx.client.aop.RpcService; +import io.kimmking.rpcfx.demo.api.Order; +import io.kimmking.rpcfx.demo.api.OrderService; + +public class OrderServiceImpl implements OrderService { + + @RpcService + @Override + public Order findOrderById(int id) { + return null; + } +} diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/service/UserServiceImpl.java b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/service/UserServiceImpl.java new file mode 100644 index 00000000..7ef2065a --- /dev/null +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/main/java/io/kimmking/rpcfx/demo/consumer/service/UserServiceImpl.java @@ -0,0 +1,16 @@ +package io.kimmking.rpcfx.demo.consumer.service; + +import io.kimmking.rpcfx.client.aop.RpcService; +import io.kimmking.rpcfx.demo.api.User; +import io.kimmking.rpcfx.demo.api.UserService; +import org.springframework.stereotype.Component; + +@Component +public class UserServiceImpl implements UserService { + + @RpcService + @Override + public User findById(int id) { + return null; + } +} diff --git a/07rpc/rpc01/rpcfx-demo-consumer/src/test/io/kimmking/rpcfx/demo/consumer/AopTest.java b/07rpc/rpc01/rpcfx-demo-consumer/src/test/io/kimmking/rpcfx/demo/consumer/AopTest.java new file mode 100644 index 00000000..a49f5b79 --- /dev/null +++ b/07rpc/rpc01/rpcfx-demo-consumer/src/test/io/kimmking/rpcfx/demo/consumer/AopTest.java @@ -0,0 +1,27 @@ +package io.kimmking.rpcfx.demo.consumer; + + +import io.kimmking.rpcfx.demo.api.User; +import io.kimmking.rpcfx.demo.api.UserService; +import io.kimmking.rpcfx.demo.consumer.config.AnnotationConfig; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = AnnotationConfig.class) +public class AopTest { + + + @Autowired + private UserService userService; + + @Test + public void testAop() { + User user = userService.findById(1); + System.out.println("find user id=1 from server: " + user.getName()); + + } +} diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/ClassUtil.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/ClassUtil.java new file mode 100644 index 00000000..759f745b --- /dev/null +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/ClassUtil.java @@ -0,0 +1,162 @@ +package io.kimmking.rpcfx.demo.provider; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Stream; + + +public class ClassUtil { + + + /** + * 从包package中获取所有的Class + * + * @param pack + * @return + */ + public static Set> getClasses(String pack) { + + // 第一个class类的集合 + Set> classes = new LinkedHashSet>(); + // 是否循环迭代 + boolean recursive = true; + // 获取包的名字 并进行替换 + String packageName = pack; + String packageDirName = packageName.replace('.', '/'); + // 定义一个枚举的集合 并进行循环来处理这个目录下的things + Enumeration dirs; + try { + dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); + // 循环迭代下去 + while (dirs.hasMoreElements()) { + // 获取下一个元素 + URL url = dirs.nextElement(); + // 得到协议的名称 + String protocol = url.getProtocol(); + // 如果是以文件的形式保存在服务器上 + if ("file".equals(protocol)) { + // System.err.println("file类型的扫描"); + // 获取包的物理路径 + String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); + // 以文件的方式扫描整个包下的文件 并添加到集合中 + findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes); + } else if ("jar".equals(protocol)) { + // 如果是jar包文件 + // 定义一个JarFile + // System.err.println("jar类型的扫描"); + JarFile jar; + try { + // 获取jar + jar = ((JarURLConnection) url.openConnection()).getJarFile(); + // 从此jar包 得到一个枚举类 + Enumeration entries = jar.entries(); + // 同样的进行循环迭代 + while (entries.hasMoreElements()) { + // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件 + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + // 如果是以/开头的 + if (name.charAt(0) == '/') { + // 获取后面的字符串 + name = name.substring(1); + } + // 如果前半部分和定义的包名相同 + if (name.startsWith(packageDirName)) { + int idx = name.lastIndexOf('/'); + // 如果以"/"结尾 是一个包 + if (idx != -1) { + // 获取包名 把"/"替换成"." + packageName = name.substring(0, idx).replace('/', '.'); + } + // 如果可以迭代下去 并且是一个包 + if ((idx != -1) || recursive) { + // 如果是一个.class文件 而且不是目录 + if (name.endsWith(".class") && !entry.isDirectory()) { + // 去掉后面的".class" 获取真正的类名 + String className = name.substring(packageName.length() + 1, name.length() - 6); + try { + // 添加到classes + classes.add(Class.forName(packageName + '.' + className)); + } catch (ClassNotFoundException e) { + // log + // .error("添加用户自定义视图类错误 + // 找不到此类的.class文件"); + e.printStackTrace(); + } + } + } + } + } + } catch (IOException e) { + // log.error("在扫描用户定义视图时从jar包获取文件出错"); + e.printStackTrace(); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + + return classes; + } + + /** + * 以文件的形式来获取包下的所有Class + * + * @param packageName + * @param packagePath + * @param recursive + * @param classes + */ + public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, + Set> classes) { + // 获取此包的目录 建立一个File + File dir = new File(packagePath); + // 如果不存在或者 也不是目录就直接返回 + if (!dir.exists() || !dir.isDirectory()) { + // log.warn("用户定义包名 " + packageName + " 下没有任何文件"); + return; + } + // 如果存在 就获取包下的所有文件 包括目录 + File[] dirfiles = dir.listFiles(new FileFilter() { + // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件) + public boolean accept(File file) { + return (recursive && file.isDirectory()) || (file.getName().endsWith(".class")); + } + }); + // 循环所有文件 + for (File file : dirfiles) { + // 如果是目录 则继续扫描 + if (file.isDirectory()) { + findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, + classes); + } else { + // 如果是java类文件 去掉后面的.class 只留下类名 + String className = file.getName().substring(0, file.getName().length() - 6); + try { + // 添加到集合中去 + // classes.add(Class.forName(packageName + '.' + + // className)); + classes.add( + Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className)); + } catch (ClassNotFoundException e) { + // log.error("添加用户自定义视图类错误 找不到此类的.class文件"); + e.printStackTrace(); + } + } + } + } + + +} diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcServiceRegister.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcServiceRegister.java new file mode 100644 index 00000000..b94a3f1d --- /dev/null +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcServiceRegister.java @@ -0,0 +1,32 @@ +package io.kimmking.rpcfx.demo.provider; + +import java.util.HashMap; +import java.util.Set; + + +public class RpcServiceRegister { + + public HashMap> serviceContext = new HashMap<>(); + + + /*** + * 获取所有service目录下的实现类 + * @param servicePath + */ + public RpcServiceRegister(String servicePath) { + Set> implServiceList = ClassUtil.getClasses(servicePath); + + for (Class clz : implServiceList) { + Class[] interfaces = clz.getInterfaces(); + for (Class inf: interfaces) { + if (clz.getSimpleName().startsWith(inf.getSimpleName())) { + serviceContext.put(inf.getName(), clz); + } + } + } + } + + public HashMap> getServiceContext() { + return serviceContext; + } +} diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java index d79e422e..5f33ee7e 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/RpcfxServerApplication.java @@ -1,17 +1,15 @@ package io.kimmking.rpcfx.demo.provider; -import com.alibaba.fastjson.JSON; import io.kimmking.rpcfx.api.RpcfxRequest; import io.kimmking.rpcfx.api.RpcfxResolver; import io.kimmking.rpcfx.api.RpcfxResponse; import io.kimmking.rpcfx.api.ServiceProviderDesc; import io.kimmking.rpcfx.demo.api.OrderService; import io.kimmking.rpcfx.demo.api.UserService; +import io.kimmking.rpcfx.demo.provider.service.OrderServiceImpl; +import io.kimmking.rpcfx.demo.provider.service.UserServiceImpl; import io.kimmking.rpcfx.server.RpcfxInvoker; -import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.CuratorFrameworkFactory; -import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.zookeeper.CreateMode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; @@ -22,8 +20,6 @@ import org.springframework.web.bind.annotation.RestController; import java.net.InetAddress; -import java.net.InterfaceAddress; -import java.net.UnknownHostException; @SpringBootApplication @RestController @@ -78,8 +74,8 @@ public RpcfxResponse invoke(@RequestBody RpcfxRequest request) { } @Bean - public RpcfxInvoker createInvoker(@Autowired RpcfxResolver resolver){ - return new RpcfxInvoker(resolver); + public RpcfxInvoker createInvoker(@Autowired RpcServiceRegister register){ + return new RpcfxInvoker(register.getServiceContext()); } @Bean @@ -92,15 +88,20 @@ public RpcfxResolver createResolver(){ // annotation +// +// @Bean(name = "io.kimmking.rpcfx.demo.api.UserService") +// public UserService createUserService(){ +// return new UserServiceImpl(); +// } +// +// @Bean(name = "io.kimmking.rpcfx.demo.api.OrderService") +// public OrderService createOrderService(){ +// return new OrderServiceImpl(); +// } - @Bean(name = "io.kimmking.rpcfx.demo.api.UserService") - public UserService createUserService(){ - return new UserServiceImpl(); - } - - @Bean(name = "io.kimmking.rpcfx.demo.api.OrderService") - public OrderService createOrderService(){ - return new OrderServiceImpl(); + @Bean + public RpcServiceRegister rpcServiceRegister(){ + return new RpcServiceRegister("io.kimmking.rpcfx.demo.provider.service"); } } diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/OrderServiceImpl.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/service/OrderServiceImpl.java similarity index 85% rename from 07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/OrderServiceImpl.java rename to 07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/service/OrderServiceImpl.java index 39821952..6bf797ef 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/OrderServiceImpl.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/service/OrderServiceImpl.java @@ -1,4 +1,4 @@ -package io.kimmking.rpcfx.demo.provider; +package io.kimmking.rpcfx.demo.provider.service; import io.kimmking.rpcfx.demo.api.Order; import io.kimmking.rpcfx.demo.api.OrderService; diff --git a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/service/UserServiceImpl.java similarity index 84% rename from 07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java rename to 07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/service/UserServiceImpl.java index 5c37d60a..c2f0421d 100644 --- a/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/UserServiceImpl.java +++ b/07rpc/rpc01/rpcfx-demo-provider/src/main/java/io/kimmking/rpcfx/demo/provider/service/UserServiceImpl.java @@ -1,4 +1,4 @@ -package io.kimmking.rpcfx.demo.provider; +package io.kimmking.rpcfx.demo.provider.service; import io.kimmking.rpcfx.demo.api.User; import io.kimmking.rpcfx.demo.api.UserService;