• [java] 一文带你了解J.U.C的FutureTask、Fork/Join框架和BlockingQueue
    >摘要: J.U.C是Java并发编程中非常重要的工具包,今天,我们就来着重讲讲J.U.C里面的FutureTask、Fork/Join框架和BlockingQueue。 本文分享自华为云社区《[【高并发】J.U.C组件扩展](https://bbs.huaweicloud.com/blogs/353871?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=other&utm_content=content)》,作者: 冰 河 。 # FutureTask FutureTask是J.U.C(java.util.concurrent)下的,但不是AQS(AbstractQueuedSynchronizer)的子类。其对线程结果的处理值得借鉴和在项目中使用。 Thread和Runnable执行完任务无法获取执行结果。Java1.5开始提供了Callable和Future,通过它们可以在任务执行完毕之后,得到任务执行的结果。 # Callable与Runnable接口对比 Callable:泛型接口,提供一个call()方法,支持抛出异常,并且执行后有返回值 Runnable:接口,提供一个run()方法,不支持抛出异常,执行后无返回值 # Future接口 对于具体的Callable和Runnable任务,可以进行取消,查询任务是否被取消,查询是否完成以及获取结果等。 Future可以监视目标线程调用call()的情况,当调用Future的get()方法时,就可以获得结果。此时,执行任务的线程可能不会直接完成,当前线程就开始阻塞,直到call()方法结束返回结果,当前线程才会继续执行。总之,Future可以得到别的线程任务方法的返回值。 # FutureTask类 实现的接口为RunnableFuture,而RunnableFuture接口继承了Runnable和Future两个接口,所以FutureTask类最终也是执行Callable类型的任务。如果FutureTask类的构造方法参数是Runnable的话,会转换成Callable类型。 类实现了两个接口:Runnable和Future。所以,它即可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,这样设计的好处如下: 假设有一个很费时的逻辑,需要计算并且返回这个值,同时,这个值又不是马上需要,则可以使用Runnable和Future的组合,用另外一个线程去计算返回值,而当前线程在使用这个返回值之前,可以做其他的操作,等到需要这个返回值时,再通过Future得到。 Future示例代码如下: ``` package io.binghe.concurrency.example.aqs; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @Slf4j public class FutureExample { static class MyCallable implements Callable{ @Override public String call() throws Exception { log.info("do something in callable"); Thread.sleep(5000); return "Done"; } } public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); Future future = executorService.submit(new MyCallable()); log.info("do something in main"); Thread.sleep(1000); String result = future.get(); log.info("result: {}", result); executorService.shutdown(); } } ``` FutureTask示例代码如下: ``` package io.binghe.concurrency.example.aqs; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; @Slf4j public class FutureTaskExample { public static void main(String[] args) throws Exception{ FutureTask futureTask = new FutureTask(new Callable() { @Override public String call() throws Exception { log.info("do something in callable"); Thread.sleep(5000); return "Done"; } }); new Thread(futureTask).start(); log.info("do something in main"); Thread.sleep(1000); String result = futureTask.get(); log.info("result: {}", result); } } ``` # Fork/Join框架 位于J.U.C(java.util.concurrent)中,是Java7中提供的用于执行并行任务的框架,其可以将大任务分割成若干个小任务,最终汇总每个小任务的结果后得到最终结果。基本思想和Hadoop的MapReduce思想类似。 主要采用的是工作窃取算法(某个线程从其他队列里窃取任务来执行),并行分治计算中的一种Work-stealing策略 ## 为什么需要使用工作窃取算法呢? 假如我们需要做一个比较大的任务,我们可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,于是把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程负责处理A队列里的任务。但是有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。 ## 工作窃取算法的优点: 充分利用线程进行并行计算,并减少了线程间的竞争 ## 工作窃取算法的缺点: 在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并且该算法会消耗更多的系统资源,比如创建多个线程和多个双端队列。 对于Fork/Join框架而言,当一个任务正在等待它使用Join操作创建的子任务结束时,执行这个任务的工作线程查找其他未被执行的任务,并开始执行这些未被执行的任务,通过这种方式,线程充分利用它们的运行时间来提高应用程序的性能。为了实现这个目标,Fork/Join框架执行的任务有一些局限性。 ## Fork/Join框架局限性: (1)任务只能使用Fork和Join操作来进行同步机制,如果使用了其他同步机制,则在同步操作时,工作线程就不能执行其他任务了。比如,在Fork/Join框架中,使任务进行了睡眠,那么,在睡眠期间内,正在执行这个任务的工作线程将不会执行其他任务了。 (2)在Fork/Join框架中,所拆分的任务不应该去执行IO操作,比如:读写数据文件 (3)任务不能抛出检查异常,必须通过必要的代码来出来这些异常 Fork/Join框架的核心类 Fork/Join框架的核心是两个类:ForkJoinPool和ForkJoinTask。ForkJoinPool负责实现工作窃取算法、管理工作线程、提供关于任务的状态以及执行信息。ForkJoinTask主要提供在任务中执行Fork和Join操作的机制。 示例代码如下: ``` package io.binghe.concurrency.example.aqs; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.concurrent.RecursiveTask; @Slf4j public class ForkJoinTaskExample extends RecursiveTask { public static final int threshold = 2; private int start; private int end; public ForkJoinTaskExample(int start, int end) { this.start = start; this.end = end; } @Override protected Integer compute() { int sum = 0; //如果任务足够小就计算任务 boolean canCompute = (end - start) = threshold; if (canCompute) { for (int i = start; i = end; i++) { sum += i; } } else { // 如果任务大于阈值,就分裂成两个子任务计算 int middle = (start + end) / 2; ForkJoinTaskExample leftTask = new ForkJoinTaskExample(start, middle); ForkJoinTaskExample rightTask = new ForkJoinTaskExample(middle + 1, end); // 执行子任务 leftTask.fork(); rightTask.fork(); // 等待任务执行结束合并其结果 int leftResult = leftTask.join(); int rightResult = rightTask.join(); // 合并子任务 sum = leftResult + rightResult; } return sum; } public static void main(String[] args) { ForkJoinPool forkjoinPool = new ForkJoinPool(); //生成一个计算任务,计算1+2+3+4 ForkJoinTaskExample task = new ForkJoinTaskExample(1, 100); //执行一个任务 Future result = forkjoinPool.submit(task); try { log.info("result:{}", result.get()); } catch (Exception e) { log.error("exception", e); } } } ``` # BlockingQueue 阻塞队列,是线程安全的。 ## 被阻塞的情况如下: (1)当队列满时,进行入队列操作 (2)当队列空时,进行出队列操作 ## 使用场景如下: 主要在生产者和消费者场景 ## BlockingQueue的方法 BlockingQueue 具有 4 组不同的方法用于插入、移除以及对队列中的元素进行检查。如果请求的操作不能得到立即执行的话,每个方法的表现也不同。这些方法如下: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/17/1655430690326993865.png) 四组不同的行为方式解释: - 抛出异常 如果试图的操作无法立即执行,抛一个异常。 - 特殊值 如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。 - 阻塞 如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。 - 超时 如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是 true / false)。 ## BlockingQueue的实现类如下: - ArrayBlockingQueue:有界的阻塞队列(容量有限,必须在初始化的时候指定容量大小,容量大小指定后就不能再变化),内部实现是一个数组,以FIFO的方式存储数据,最新插入的对象是尾部,最新移除的对象是头部。 - DelayQueue:阻塞的是内部元素,DelayQueue中的元素必须实现一个接口——Delayed(存在于J.U.C下)。Delayed接口继承了Comparable接口,这是因为Delayed接口中的元素需要进行排序,一般情况下,都是按照Delayed接口中的元素过期时间的优先级进行排序。应用场景主要有:定时关闭连接、缓存对象、超时处理等。内部实现使用PriorityQueue和ReentrantLock。 - LinkedBlockingQueue:大小配置是可选的,如果初始化时指定了大小,则是有边界的;如果初始化时未指定大小,则是无边界的(其实默认大小是Integer类型的最大值)。内部实现时一个链表,以FIFO的方式存储数据,最新插入的对象是尾部,最新移除的对象是头部。 - PriorityBlockingQueue:带优先级的阻塞队列,无边界,但是有排序规则,允许插入空对象(也就是null)。所有插入的对象必须实现Comparable接口,队列优先级的排序规则就是按照对Comparable接口的实现来定义的。可以从PriorityBlockingQueue中获得一个迭代器Iterator,但这个迭代器并不保证按照优先级的顺序进行迭代。 - SynchronousQueue:队列内部仅允许容纳一个元素,当一个线程插入一个元素后,就会被阻塞,除非这个元素被另一个线程消费。因此,也称SynchronousQueue为同步队列。SynchronousQueue是一个无界非缓存的队列。准确的说,它不存储元素,放入元素只有等待取走元素之后,才能再次放入元素
  • [新手课堂] 关于Java数组的那些事
    数组是相同数据类型的元素的有序集合。为什么说是有序的呢?访问数组元素是通过数组下标进行的,下标从0开始,依次递增直到 length - 1(length为数组长度),所以可以理解为有序的。首先,来谈谈如何初始化数组?两种初始化方法:动态初始化、静态初始化。(这里以一维数组为例)//动态初始化int[] arr1 = new int[9];//声明了一个数组长度为9的一维数组//除了上面的初始化方式,还可以拆分为两步int[] arr2;arr2 = new int [9];//这两种都属于动态初始化,只是方式存在差异//静态初始化,声明数组长度为5的一维数组int[] arr3 = new int[]{1,2,3,4,5};//静态初始化的标准格式//也可以分为两步初始化//int[] arr3;//arr3 = new int[]{1,2,3,4,5};int[] arr4 = {1,2,3,4},5;//静态初始化的省略格式,不能分为两步既然声明了一个数组,那么数组是储存在哪里的呢?当使用"new"关键字创建数组对象后,会在堆中为数组分配相应的内存空间。总的来说,就是当你使用"new"关键字创建一个对象时,就会在堆中为该对象分配相应的内存空间。数组初始化,那么如何访问数组元素进行赋值和获取元素值呢?int[] arr = new int[5];//数组元素的值默认为0//数组元素赋值for(int i = 0; i < 5; i++){arr = 1;//这里就将数组下标为i的元素赋值为1}//既然对数组赋值了,那么如何获取数组元素的值System.out.println(arr);//这样是否会输出数组元素的值呢//答案是不会,这样输出的是数组的内存地址哈希值[I@#########]//方括号代表是数组,大写字母I代表为整型,后面的#则是十六进制数//这里介绍一种循环:for each循环for(int x:arr){System.out.println(x);//这样就能输出数组元素的值了,当然你也可以使用普通的for循环来进行访问//for(int i = 0; i < 5; i++){//System.out.println(arr);//}}以上就是Java数组中关于一维数组的简单介绍。
  • [交流吐槽] java基本数据类型
    package base;public class demo02 {    public static void main(String[] args) {        //八大基本数据类型        //整型        int num1 = 10;  //最常用        //范围在100多//        byte num2 = 200;        //short        short num3 = 20;        //long        long num4 = 40L;    //Long类型在数字后面加个L表示是long类型        //float 浮点数:小数        float num5 = 12.3F; //float类型加F,否则就报错        double num6 = 3.14159;        //字符        char name1 = 'a';//        char name2 = 'as';  //字符是一个        //字符串        //String 不是关键字,是一个类        String num7 = "asd";        //布尔值        boolean flag = true;    //真        boolean fla = false;    //假    }}
  • [技术干货] API工具--Apifox和Postman对比(区别)
    ## 前言 Postman和Apifox有什么区别?他们之间分别有什么优势,感兴趣的同学可以继续往下看。 不吹不黑,只列功能,纯客观比对。 ## 一.功能列表对比 ### (一)接口设计与文档管理功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282565040530404.png) 1.导入功能对比 Apifox的导入功能除了支持OpenApi之外,还支持yapi,RAP2,postman等国内用得比较多的接口文档导入,而Postman支持的格式相对较少。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282576741711183.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282585176936279.png) 2.在线分享功能对比 Postman的在线分享功能,付费版支持“只读”功能,Apifox分享功能支持选择过期日期、设置密码,选择分享内容的范围,选择环境等功能。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282596041219729.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282605326923851.png) 3.编辑接口文档对比 接口文档既可以纯粹的MD格式文档对接口做整体说明,也可以在单个接口内部对单个接口进行说明注释。Apifox会增加创建时间、负责人、所属业务分组等业务和协同层面的注释信息。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282614774579220.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282622401738551.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282631475197465.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282640297691879.png) 4.生成代码功能对比 Postman支持将接口生成代码,postman支持的接口和框架为4种,Apifox支持130多种语言和框架 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282649937591303.4) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282657274516776.png) 5.数据模型功能对比 在postman中没有这个功能,在Apifox中,由于本身具备接口设计的功能,因此会将实体类的相关参数封装成一个数据模型,供不同的接口调用,提高数据复用的效率,提高接口封装的程度,减少重复的工作。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282667876270264.png) ### (二)接口调试功能对比 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282676280468705.png) 对比了下,Postman基本依赖于JS脚本,通过编写脚本对接口进行调试。 Apifox则是可视化调试界面为主,自定义脚本编辑为辅。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282684951710175.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282692278583614.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282700429181459.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282708291131952.png) 两者对比,在postman中需要写脚本才能实现的接口断言和提取变量、等待时间,在这里都能直接通过填写参数来完成、不需要写脚本。 而操作数据库这个功能postman则不支持。postman只支持js脚本,Apifox目前支持调用其他语言的外部函数和脚本,不过需要先安装相关的Python、java等环境。 ### (三)接口mock功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282717724329514.png) Postman也有mock功能,但它的mock服务需要自己搭建而且mock功能并不强。 在Postman上执行API mock 需要经过3步: 第一步:创建 mock服务器,获得mock url 第二步:逐个编写并添加 mock 示例,供执行mock时返回对应的接口响应 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282725490242234.png) 也就是说接口mock 出来的响应来源于先前调试已经有的,或者直接自己编辑一个响应进去,才能得到一个返回。 mock server 只能返回自己手动添加进去的几条响应,而无法自己无限制创建出mock 数据。 第三步: 将mock url 复制到接口里进行调试。 而想要在 Apifox 内做接口 mock 只需要在`环境`中选择mock服务 在响应参数中选择mock规则,点击发送请求,则mock服务会返回与实际业务返回高度相似的接口响应。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282738275949352.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282746608606303.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282754904742773.png) ### (四)接口测试功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282765832339857.png) 在Postman里写测试脚本,使用动态参数,接口响应断言,参数传递都通过写脚本来实现。 如果要作业务接口测试,需要写各种场景下的用例,同样是通过写脚本来修改参数用例的执行顺序和设置循环次数的。使用postman至少需要掌握基础的js语言。 Apifox里面做自动化测试可视化程度相对较高一些,创建用例的时候可以在接口设计面板修改参数然后保存,场景用例可以添加不同的参数用例作为步骤,通过拖曳来选择用例的执行顺序。 右侧的面板可以填写循环次数,接口间的参数传递和断言也可以在可视化面板提取出来。完成单个接口测试或者场景测试,都不需要写代码。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282780666517472.png) ## 二.团队协作功能 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282790439655374.png) Postman的团队协作功能是付费的,3人以下团队可使用免费版协作,3人以上根据可用功能和人数有不同的价格版本。 但通常一个团队不可能只有3个人,也就是说,有限开放的那点协同功能是无法支持正常的团队协作需求的。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282798642373509.png) Apifox的协同功能是免费的,团队成员的权限管理,接口数据同步、在线分享都没有障碍。 本身Apifox的定位和Postman就不一样,它一出生就是定位在API管理和协作上。 所以除了协作功能必须的权限管理和数据同步上,它也最大程度地做数据复用,尽量减少不必要的工作量。 比如说接口调试的参数用例可以直接导入来做自动化测试,一个数据模型可以给多个接口使用,一套接口数据可以给后端做调试、前端做mock、测试做自动化。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282806721395621.png) ## 三.Apifox 没有的功能 Postman支持fork GitHub上的代码,以及API 网关。这两块在Apifox上均没有相关的功能。 两个工具的功能有相同的地方,但本质上各自的市场定位还是不同的,Postman打通了接口调试、测试、到线上监测,代码生成。 而Apifox始终立身于前端、后端测试间基于接口的设计、调试、测试、文档管理等一系列接口的生命周期管理来发力。 在相同的功能点上,Apifox基于本土互联网团队的协作模式和痛点,基本做到了人无我有,人有我优 的程度。 因此如果基于各种原因,寻找Postman替代的开发们,不妨体验一下Apifox。 ## 四.产品价格 从收费模式上看,postman是基础功能不收费,协作功能收费;Apifox是公网版本不收费,私有化部署收费。 Apifox的SaaS版本也没有什么功能和团队人数的限制,对于我们常规的项目开发来说,免费版本就够用了。 公网的SaaS版本,数据的确是放在他们服务器上的,但这点Postman其实也一样,而且postman的服务器可是放在国外的。 如果大家的项目安全保密级别较高,想要做私有化部署,可以去他们官网咨询,这方面我没咨询过就不对比了。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/15/1655282816062828538.png) ### 下载地址 **Apifox官网**:[www.apifox.cn](https://www.apifox.cn/?utm_source=liam)
  • [最佳实践] 使用IntelliJ IDEA Java创建和使用DLI表
    操作场景DLI支持用户编写代码创建Spark作业来创建数据库、创建DLI表或OBS表和插入表数据等操作。本示例完整的演示通过编写java代码、使用Spark作业创建数据库、创建表和插入表数据的详细操作,帮助您在DLI上进行作业开发。约束限制不支持的场景:在SQL作业中创建了数据库(database),编写程序代码指定在该数据库下创建表。例如在DLI的SQL编辑器中的某SQL队列下,创建了数据库testdb。后续通过编写程序代码在testdb下创建表testTable,编译打包后提交的Spark Jar作业则会运行失败。不支持创建加密的DLI表,即不支持创建DLI表时设置encryption=true。例如,如下建表语句不支持:CREATE TABLE tb1(id int) using parquet options(encryption=true)支持的场景在SQL作业中创建数据库(database),表(table) , 通过SQL或Spark程序作业读取插入数据。在Spark程序作业中创建数据库(database),表(table), 通过SQL或Spark程序作业读取插入数据。环境准备在进行Spark 作业访问DLI元数据开发前,请准备以下开发环境。表1 Spark Jar作业开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI进行Spark作业访问DLI元数据开发流程参考如下:图1 Spark作业访问DLI元数据开发流程表2 开发流程说明序号阶段操作界面说明1创建DLI通用队列DLI控制台创建作业运行的DLI队列。2OBS桶文件配置OBS控制台如果是创建OBS表,则需要上传文件数据到OBS桶下。配置Spark创建表的元数据信息“spark.sql.warehouse.dir”的存储路径。3新建Maven工程,配置pom文件IntelliJ IDEA参考样例代码说明,编写程序代码创建DLI表或OBS表。4编写程序代码5调试,编译代码并导出Jar包6上传Jar包到OBS和DLIOBS控制台将生成的Spark Jar包文件上传到OBS目录下和DLI程序包中。7创建Spark Jar作业DLI控制台在DLI控制台创建Spark Jar作业并提交运行作业。8查看作业运行结果DLI控制台查看作业运行状态和作业运行日志。步骤1:创建DLI通用队列第一次提交Spark作业,需要先创建队列,例如创建名为“sparktest”的队列,队列类型选择为“通用队列”。在DLI管理控制台的左侧导航栏中,选择“队列管理”。单击“队列管理”页面右上角“购买队列”进行创建队列。创建名为“sparktest”的队列,队列类型选择为“通用队列”。创建队列详细介绍请参考创建队列。单击“立即购买”,确认配置。配置确认无误,单击“提交”完成队列创建。步骤2:OBS桶文件配置如果需要创建OBS表,则需要先上传数据到OBS桶目录下。本次演示的样例代码创建了OBS表,测试数据内容参考如下示例,创建名为的testdata.csv文件。12,Michael 27,Andy 30,Justin进入OBS管理控制台,在“桶列表”下,单击已创建的OBS桶名称,本示例桶名为“dli-test-obs01”,进入“概览”页面。单击左侧列表中的“对象”,选择“上传对象”,将testdata.csv文件上传到OBS桶根目录下。在OBS桶根目录下,单击“新建文件夹”,创建名为“warehousepath”的文件夹。该文件夹路径用来存储Spark创建表的元数据信息“spark.sql.warehouse.dir”。步骤3:新建Maven工程,配置pom依赖以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。如上图所示,本示例创建Maven工程名为:SparkJarMetadata,Maven工程路径为:“D:\DLITest\SparkJarMetadata”。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.11</artifactId> <version>2.3.2</version> </dependency> </dependencies>图3 修改pom.xml文件在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.dli.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:DliCatalogTest。步骤4:编写代码编写DliCatalogTest程序创建数据库、DLI表和OBS表。完整的样例请参考Java样例代码,样例代码分段说明如下:导入依赖的包。import org.apache.spark.sql.SparkSession;创建SparkSession会话。创建SparkSession会话时需要指定Spark参数:"spark.sql.session.state.builder"、"spark.sql.catalog.class"和"spark.sql.extensions",按照样例配置即可。SparkSession spark = SparkSession .builder() .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") .config("spark.sql.extensions","org.apache.spark.sql.CarbonInternalExtensions"+","+"org.apache.spark.sql.DliSparkExtension") .appName("java_spark_demo") .getOrCreate();创建数据库。如下样例代码演示,创建名为test_sparkapp的数据库。spark.sql("create database if not exists test_sparkapp").collect();创建DLI表并插入测试数据。spark.sql("drop table if exists test_sparkapp.dli_testtable").collect(); spark.sql("create table test_sparkapp.dli_testtable(id INT, name STRING)").collect(); spark.sql("insert into test_sparkapp.dli_testtable VALUES (123,'jason')").collect(); spark.sql("insert into test_sparkapp.dli_testtable VALUES (456,'merry')").collect();创建OBS表。如下示例中的OBS路径需要根据步骤2:OBS桶文件配置中的实际数据路径修改。spark.sql("drop table if exists test_sparkapp.dli_testobstable").collect(); spark.sql("create table test_sparkapp.dli_testobstable(age INT, name STRING) using csv options (path 'obs://dli-test-obs01/testdata.csv')").collect();关闭SparkSession会话spark。spark.stop();步骤5:调试、编译代码并导出Jar包单击IntelliJ IDEA工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\DLITest\SparkJarMetadata\target”下名为“SparkJarMetadata-1.0-SNAPSHOT.jar”。步骤6:上传Jar包到OBS和DLI下登录OBS控制台,将生成的“SparkJarMetadata-1.0-SNAPSHOT.jar”Jar包文件上传到OBS路径下。将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。图4 创建程序包单击“确定”,完成创建程序包。步骤7:创建Spark Jar作业登录DLI控制台,单击“作业管理 > Spark作业”。在“Spark作业”管理界面,单击“创建作业”。在作业创建界面,配置对应作业运行参数。具体说明如下:表3 Spark Jar作业参数填写参数名参数值所属队列选择已创建的DLI通用队列。例如当前选择步骤1:创建DLI通用队列创建的通用队列“sparktest”。作业名称(--name)自定义Spark Jar作业运行的名称。当前定义为:SparkTestMeta。应用程序选择步骤6:上传Jar包到OBS和DLI下中上传到DLI程序包。例如当前选择为:“SparkJarObs-1.0-SNAPSHOT.jar”。主类格式为:程序包名+类名。例如当前为:com.huawei.dli.demo.DliCatalogTest。Spark参数(--conf)spark.dli.metaAccess.enable=truespark.sql.warehouse.dir=obs://dli-test-obs01/warehousepath说明:spark.sql.warehouse.dir参数的OBS路径为步骤2:OBS桶文件配置中配置创建。访问元数据选择:是其他参数保持默认值即可。图5 创建Spark Jar作业单击“执行”,提交该Spark Jar作业。在Spark作业管理界面显示已提交的作业运行状态。查看作业运行结果在Spark作业管理界面显示已提交的作业运行状态。初始状态显示为“启动中”。如果作业运行成功则作业状态显示为“已成功”,通过以下操作查看创建的数据库和表。可以在DLI控制台,左侧导航栏,单击“SQL编辑器”。在“数据库”中已显示创建的数据库“test_sparkapp”。图6 查看创建的数据库双击数据库名,可以在数据库下查看已创建成功的DLI和OBS表。图7 查看表双击DLI表名dli_testtable,单击“执行”查询DLI表数据。图8 查询DLI表数据注释掉DLI表查询语句,双击OBS表名dli_testobstable,单击“执行”查询OBS表数据。图9 查询OBS表数据如果作业运行失败则作业状态显示为“已失败”,单击“操作”列“更多”下的“Driver日志”,显示当前作业运行的日志,分析报错原因。图10 查看Driver日志原因定位解决后,可以在作业“操作”列,单击“编辑”,修改作业相关参数后,单击“执行”重新运行该作业即可。后续指引如果您想通过Spark Jar作业访问其他数据源,请参考《使用Spark作业跨源访问数据源》。创建DLI表的语法请参考创建DLI表,创建OBS表的语法请参考创建OBS表。如果是通过API接口调用提交该作业请参考以下操作说明:调用创建批处理作业接口,参考以下请求参数说明。详细的API参数说明请参考《数据湖探索API参考》>《创建批处理作业》。将请求参数中的“catalog_name”参数设置为“dli”。conf 中需要增加"spark.dli.metaAccess.enable":"true"。如果需要执行DDL,则还要在conf中配置"spark.sql.warehouse.dir": "obs://bucket/warehousepath"。完整的API请求参数可以参考如下示例说明。{ "queue":"citest", "file":"SparkJarMetadata-1.0-SNAPSHOT.jar", "className":"DliCatalogTest", "conf":{"spark.sql.warehouse.dir": "obs://bucket/warehousepath", "spark.dli.metaAccess.enable":"true"}, "sc_type":"A", "executorCores":1, "numExecutors":6, "executorMemory":"4G", "driverCores":2, "driverMemory":"7G", "catalog_name": "dli" }Java样例代码本示例操作步骤采用Java进行编码,具体完整的样例代码参考如下:package com.huawei.dli.demo; import org.apache.spark.sql.SparkSession; public class DliCatalogTest { public static void main(String[] args) { SparkSession spark = SparkSession .builder() .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") .config("spark.sql.extensions","org.apache.spark.sql.CarbonInternalExtensions"+","+"org.apache.spark.sql.DliSparkExtension") .appName("java_spark_demo") .getOrCreate(); spark.sql("create database if not exists test_sparkapp").collect(); spark.sql("drop table if exists test_sparkapp.dli_testtable").collect(); spark.sql("create table test_sparkapp.dli_testtable(id INT, name STRING)").collect(); spark.sql("insert into test_sparkapp.dli_testtable VALUES (123,'jason')").collect(); spark.sql("insert into test_sparkapp.dli_testtable VALUES (456,'merry')").collect(); spark.sql("drop table if exists test_sparkapp.dli_testobstable").collect(); spark.sql("create table test_sparkapp.dli_testobstable(age INT, name STRING) using csv options (path 'obs://dli-test-obs01/testdata.csv')").collect(); spark.stop(); } }scala样例代码scala样例代码object DliCatalogTest { def main(args:Array[String]): Unit = { val sql = args(0) val runDdl = Try(args(1).toBoolean).getOrElse(true) System.out.println(s"sql is $sql runDdl is $runDdl") val sparkConf = new SparkConf(true) sparkConf .set("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") .set("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") sparkConf.setAppName("dlicatalogtester") val spark = SparkSession.builder .config(sparkConf) .enableHiveSupport() .config("spark.sql.extensions", Seq("org.apache.spark.sql.CarbonInternalExtensions", "org.apache.spark.sql.DliSparkExtension").mkString(",")) .appName("SparkTest") .getOrCreate() System.out.println("catalog is " + spark.sessionState.catalog.toString) if (runDdl) { val df = spark.sql(sql).collect() } else { spark.sql(sql).show() } spark.close() } }Python样例代码Python样例代码#!/usr/bin/python # -*- coding: UTF-8 -*- from __future__ import print_function import sys from pyspark.sql import SparkSession if __name__ == "__main__": url = sys.argv[1] creatTbl = "CREATE TABLE test_sparkapp.dli_rds USING JDBC OPTIONS ('url'='jdbc:mysql://%s'," \ "'driver'='com.mysql.jdbc.Driver','dbtable'='test.test'," \ " 'passwdauth' = 'DatasourceRDSTest_pwd','encryption' = 'true')" % url spark = SparkSession \ .builder \ .enableHiveSupport() \ .config("spark.sql.session.state.builder","org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") \ .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") \ .config("spark.sql.extensions",','.join(["org.apache.spark.sql.CarbonInternalExtensions","org.apache.spark.sql.DliSparkExtension"])) \ .appName("python Spark test catalog") \ .getOrCreate() spark.sql("CREATE database if not exists test_sparkapp").collect() spark.sql("drop table if exists test_sparkapp.dli_rds").collect() spark.sql(creatTbl).collect() spark.sql("select * from test_sparkapp.dli_rds").show() spark.sql("insert into table test_sparkapp.dli_rds select 12,'aaa'").collect() spark.sql("select * from test_sparkapp.dli_rds").show() spark.sql("insert overwrite table test_sparkapp.dli_rds select 1111,'asasasa'").collect() spark.sql("select * from test_sparkapp.dli_rds").show() spark.sql("drop table test_sparkapp.dli_rds").collect() spark.stop()
  • [最佳实践] 使用IntelliJ IDEA Java创建和使用Spark UDTF
    操作场景DLI支持用户使用Hive UDTF(User-Defined Table-Generating Functions)自定义表值函数,UDTF用于解决一进多出业务场景,即其输入与输出是一对多的关系,读入一行数据,输出多个值。约束限制在DLI Console上执行UDTF相关操作时,需要使用自建的SQL队列。跨账号使用UDTF时,除了创建UDTF函数的用户,其他用户如果需要使用时,需要先进行授权才可使用对应的UDTF函数。授权操作参考如下:登录DLI管理控制台,选择“ 数据管理 > 程序包管理”页面,选择对应的UDTF Jar包,单击“操作”列中的“权限管理”,进入权限管理页面,单击右上角“授权”,勾选对应权限。自定义函数中引用static类或接口时,必须要加上“try catch”异常捕获,否则可能会造成包冲突,导致函数功能异常。环境准备在进行UDTF开发前,请准备以下开发环境。表1 UDTF开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI下UDTF函数开发流程参考如下:图1 UDTF开发流程表2 开发流程说明序号阶段操作界面说明1新建Maven工程,配置pom文件IntelliJ IDEA参考操作步骤说明,编写UDTF函数代码。2编写UDTF函数代码3调试,编译代码并导出Jar包4上传Jar包到OBSOBS控制台将生成的UDTF函数Jar包文件上传到OBS目录下。5创建DLI的UDTF函数DLI控制台在DLI控制台的SQL作业管理界面创建使用的UDTF函数。6验证和使用DLI的UDTF函数DLI控制台在DLI作业中使用创建的UDTF函数。操作步骤新建Maven工程,配置pom文件。以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>1.2.1</version> </dependency> </dependencies>图3 pom文件中添加配置在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:UDTFSplit。编写UDTF函数代码。完整样例代码请参考样例代码。UDTF的类需要继承“org.apache.hadoop.hive.ql.udf.generic.GenericUDTF”,实现initialize,process,close三个方法。UDTF首先会调用initialize方法,此方法返回UDTF的返回行的信息,如,返回个数,类型等。初始化完成后,会调用process方法,真正处理在process函数中,在process中,每一次forward()调用产生一行。如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数。public void process(Object[] args) throws HiveException { // TODO Auto-generated method stub if(args.length == 0){ return; } String input = args[0].toString(); if(StringUtils.isEmpty(input)){ return; } String[] test = input.split(";"); for (int i = 0; i < test.length; i++) { try { String[] result = test[i].split(":"); forward(result); } catch (Exception e) { continue; } } }最后调用close方法,对需要清理的方法进行清理。编写调试完成代码后,通过IntelliJ IDEA工具编译代码并导出Jar包。单击工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。图4 编译打包打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\MyUDTF\target”下名为“MyUDTF-1.0-SNAPSHOT.jar”。登录OBS控制台,将生成的Jar包文件上传到OBS路径下。说明:Jar包文件上传的OBS桶所在的区域需与DLI的队列区域相同,不可跨区域执行操作。(可选)可以将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。单击“确定”,完成创建程序包。创建DLI的UDTF函数。登录DLI管理控制台,单击“SQL编辑器”,执行引擎选择“spark”,选择已创建的SQL队列和数据库。图5 选择队列和数据库在SQL编辑区域输入下列命令创建UDTF函数,单击“执行”提交创建。CREATE FUNCTION mytestsplit AS 'com.huawei.demo.UDTFSplit' using jar 'obs://dli-test-obs01/MyUDTF-1.0-SNAPSHOT.jar';重启原有SQL队列,使得创建的UDTF函数生效。登录数据湖探索管理控制台,选择“队列管理”,在对应“SQL队列”类型作业的“操作”列,单击“重启”。在“重启队列”界面,选择“确定”完成队列重启。验证和使用创建的UDTF函数。在查询语句中使用6中创建的UDTF函数,如:select mytestsplit('abc:123\;efd:567\;utf:890');图6 执行结果(可选)删除UDTF函数。如果不再使用该Function,可执行以下语句删除UDTF函数:Drop FUNCTION mytestsplit;样例代码UDTFSplit.java完整的样例代码参考如下所示:package com.huawei.demo; import java.util.ArrayList; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.hive.ql.exec.UDFArgumentException; import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException; import org.apache.hadoop.hive.ql.metadata.HiveException; import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory; import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; public class UDTFSplit extends GenericUDTF { @Override public void close() throws HiveException { // TODO Auto-generated method stub } @Override public void process(Object[] args) throws HiveException { // TODO Auto-generated method stub if(args.length == 0){ return; } String input = args[0].toString(); if(StringUtils.isEmpty(input)){ return; } String[] test = input.split(";"); for (int i = 0; i < test.length; i++) { try { String[] result = test[i].split(":"); forward(result); } catch (Exception e) { continue; } } } @Override public StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException { if (args.length != 1) { throw new UDFArgumentLengthException("ExplodeMap takes only one argument"); } if (args[0].getCategory() != ObjectInspector.Category.PRIMITIVE) { throw new UDFArgumentException("ExplodeMap takes string as a parameter"); } ArrayList<String> fieldNames = new ArrayList<String>(); ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(); fieldNames.add("col1"); fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); fieldNames.add("col2"); fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs); } }
  • [最佳实践] 使用IntelliJ IDEA Java创建和使用Spark UDF
    操作场景DLI支持用户使用Hive UDF(User Defined Function,用户定义函数)进行数据查询等操作,UDF只对单行数据产生作用,适用于一进一出的场景。约束限制在DLI Console上执行UDF相关操作时,需要使用自建的SQL队列。跨账号使用UDF时,除了创建UDF函数的用户,其他用户如果需要使用时,需要先进行授权才可使用对应的UDF函数。授权操作参考如下:登录DLI管理控制台,选择“ 数据管理 > 程序包管理”页面,选择对应的UDF Jar包,单击“操作”列中的“权限管理”,进入权限管理页面,单击右上角“授权”,勾选对应权限。自定义函数中引用static类或接口时,必须要加上“try catch”异常捕获,否则可能会造成包冲突,导致函数功能异常。环境准备在进行UDF开发前,请准备以下开发环境。表1 UDF开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI下UDF函数开发流程参考如下:图1 开发流程表2 开发流程说明序号阶段操作界面说明1新建Maven工程,配置pom文件IntelliJ IDEA参考操作步骤说明,编写UDF函数代码。2编写UDF函数代码3调试,编译代码并导出Jar包4上传Jar包到OBSOBS控制台将生成的UDF函数Jar包文件上传到OBS目录下。5创建DLI的UDF函数DLI控制台在DLI控制台的SQL作业管理界面创建使用的UDF函数。6验证和使用DLI的UDF函数DLI控制台在DLI作业中使用创建的UDF函数。操作步骤新建Maven工程,配置pom文件。以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>1.2.1</version> </dependency> </dependencies>图3 pom文件中添加配置在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:SumUdfDemo。编写UDF函数代码。UDF函数实现,主要注意以下几点:自定义UDF需要继承org.apache.hadoop.hive.ql.UDF。需要实现evaluate函数,evaluate函数支持重载。详细UDF函数实现,可以参考如下样例代码:package com.huawei.demo; import org.apache.hadoop.hive.ql.exec.UDF; public class SumUdfDemo extends UDF { public int evaluate(int a, int b) { return a + b; } }编写调试完成代码后,通过IntelliJ IDEA工具编译代码并导出Jar包。单击工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\DLITest\MyUDF\target”下名为“MyUDF-1.0-SNAPSHOT.jar”。登录OBS控制台,将生成的Jar包文件上传到OBS路径下。说明:Jar包文件上传的OBS桶所在的区域需与DLI的队列区域相同,不可跨区域执行操作。(可选)可以将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。单击“确定”,完成创建程序包。创建UDF函数。登录DLI管理控制台,单击“SQL编辑器”,执行引擎选择“spark”,选择已创建的SQL队列和数据库。图4 选择队列和数据库在SQL编辑区域输入下列命令创建UDF函数,单击“执行”提交创建。CREATE FUNCTION TestSumUDF AS 'com.huawei.demo.SumUdfDemo' using jar 'obs://dli-test-obs01/MyUDF-1.0-SNAPSHOT.jar';重启原有SQL队列,使得创建的Function生效。登录数据湖探索管理控制台,选择“队列管理”,在对应“SQL队列”类型作业的“操作”列,单击“重启”。在“重启队列”界面,选择“确定”完成队列重启。使用UDF函数。在查询语句中使用6中创建的UDF函数:select TestSumUDF(1,2);图5 执行结果(可选)删除UDF函数。如果不再使用UDF函数,可执行以下语句删除该函数:Drop FUNCTION TestSumUDF;
  • [技术干货] JAVA反射获取Constructor、Field、Method对象[转载]
    在实际开发中,通过反射可以得到一个类的完整结构,包括类的构造方法、类的属性、类的方法,这就需要使用到java.lang.reflect包中的以下几个类:1、Constructor:表示类中的构造方法2、Field:表示类中的属性3、Method:表示类中的方法目录1、使用反射技术获取构造器对象并使用2、使用反射技术获取成员变量对象并使用3、使用反射技术获取方法对象并使用使用反射技术获取构造器对象并使用实验类:public class 反射机制_2实验类 {    private String name;    private int age;     private 反射机制_2实验类() {        System.out.println("无参构造器执行!");    }     public 反射机制_2实验类(String name, int age) {        System.out.println("有参构造器执行!");        this.name = name;        this.age = age;    }     public String getName() {        return name;    }     public void setName(String name) {        this.name = name;    }     public int getAge() {        return age;    }     public void setAge(int age) {        this.age = age;    }     @Override    public String toString() {        return "反射机制_2实验类{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }} 获取构造器对象:import org.junit.Test; import java.lang.reflect.Constructor; public class 反射机制_2构造器获取对象 {    //1、getConstructors    //获取全部的构造器,只能获取public修饰的构造器    @Test    public void getConstructors(){        //获取类对象        Class c=反射机制_2实验类.class;        //提取类中的全部的构造器对象(这里只能拿public修饰的构造器)        Constructor[] constructors=c.getConstructors();        //遍历构造器        for (Constructor constructor : constructors) {            System.out.println(constructor.getName()+"\t构造器参数个数为:"+constructor.getParameterCount()+"个");        }        //运行结果:IT2.反射机制_2实验类    构造器参数个数为:2个    }     //2、getDeclaredConstructors    //获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及    @Test    public void getDeclaredConstructors(){        //获取类对象        Class c=反射机制_2实验类.class;        //提取类中的全部的构造器对象        Constructor[] constructors=c.getDeclaredConstructors();        //遍历构造器        for (Constructor constructor : constructors) {            System.out.println(constructor.getName()+"\t构造器参数个数为:"+constructor.getParameterCount()+"个");        }        //运行结果:        // IT2.反射机制_2实验类    构造器参数个数为:0个        // IT2.反射机制_2实验类    构造器参数个数为:2个    }     //3、getConstructor    //获取某个构造器:只能拿Public修饰的某个构造器    @Test    public void getConstructor() throws Exception {        //获取类对象        Class c=反射机制_2实验类.class;        //定位单个构造器对象(按照参数,这里定位的是有参的构造器)        Constructor cons=c.getConstructor(String.class,int.class);       // Constructor cons=c.getConstructor();//如果获取无参构造器,但因为我设置的是private私有的,权限不足无法获取,便会报错        System.out.println(cons.getName()+"\t构造器参数个数为:"+cons.getParameterCount()+"个");        //运行结果:IT2.反射机制_2实验类    构造器参数个数为:2个    }    //4、getDeclaredConstructor    //获取某个构造器:只要你敢写,这里就能拿到,无所谓权限是否可及    //一般是用这个,什么都可以获取,并且是根据自己需要什么而获取    @Test    public void getDeclaredConstructor() throws Exception {        //获取类对象        Class c=反射机制_2实验类.class;        //定位单个构造器对象(按照参数,这里定位的是有参的构造器)        Constructor cons=c.getDeclaredConstructor(String.class,int.class);        System.out.println(cons.getName()+"\t构造器参数个数为:"+cons.getParameterCount()+"个");        //运行结果:IT2.反射机制_2实验类    构造器参数个数为:2个         //获取类对象        Class c2=反射机制_2实验类.class;        //定位单个构造器对象(按照参数定位无参构造器)        Constructor cons2=c2.getDeclaredConstructor();        System.out.println(cons2.getName()+"\t构造器参数个数为:"+cons2.getParameterCount()+"个");        //运行结果:IT2.反射机制_2实验类    构造器参数个数为:0个    }}Class在开发中最常见的用法就是将Class类对象实例化为自定义类的对象,即可通过一个给定的字符串(类的全限定类名)实例化一个本类的对象。将Class对象实例化为本类对象时,可以通过无参构造完成,也可以通过有参构造完成。 创建对象:import org.junit.Test;import java.lang.reflect.Constructor; //反射可以破坏封装性,私有的也可以执行了public class 反射机制_2创建对象 {    @Test    public void getDeclaredConstructor() throws Exception {        //获取类对象        Class c=反射机制_2实验类.class;        //定位单个构造器对象(按照参数,这里定位的是有参的构造器)        Constructor cons=c.getDeclaredConstructor(String.class,int.class);        System.out.println(cons.getName()+"\t构造器参数个数为:"+cons.getParameterCount()+"个");        反射机制_2实验类 s1= (反射机制_2实验类) cons.newInstance("狗蛋",18);        System.out.println(s1);        System.out.println();         //获取类对象        Class c2=反射机制_2实验类.class;        //定位单个构造器对象(按照参数定位无参构造器)        Constructor cons2=c2.getDeclaredConstructor();        System.out.println(cons2.getName()+"\t构造器参数个数为:"+cons2.getParameterCount()+"个");         //如果遇到了私有的构造器,可以暴力反射        cons2.setAccessible(true);//权限打开(只是这一次有效,并不是一直打开)         反射机制_2实验类 s2= (反射机制_2实验类) cons2.newInstance();        System.out.println(s2);         //运行结果:        //IT2.反射机制_2实验类    构造器参数个数为:2个        //有参构造器执行!        //反射机制_2实验类{name='狗蛋', age=18}        //        //IT2.反射机制_2实验类    构造器参数个数为:0个        //无参构造器执行!        //反射机制_2实验类{name='null', age=0}    }}使用反射技术获取成员变量对象并使用实验类:public class 反射机制_2实验类2 {    private String name;    private int age;    public static String schoolName;    public static final String Name="遇安";     public 反射机制_2实验类2() {        System.out.println("无参构造器执行!");    }     public 反射机制_2实验类2(String name, int age) {        System.out.println("有参构造器执行!");        this.name = name;        this.age = age;    }     public String getName() {        return name;    }     public void setName(String name) {        this.name = name;    }     public int getAge() {        return age;    }     public void setAge(int age) {        this.age = age;    }     public static String getSchoolName() {        return schoolName;    }     public static void setSchoolName(String schoolName) {        反射机制_2实验类2.schoolName = schoolName;    }     @Override    public String toString() {        return "反射机制_2实验类2{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }} 获取成员变量:在反射操作中可以获取一个类中的全部属性,但是类中的属性包括两部分,即从父类继承的属性和本类定义的属性。因此,在获取类的属性时也有以下两种不同的方式:1、获取实现的接口或父类中的公共属性:public Field [] getFields throws SecurityException2、获取本类中的全部属性:public Field [] getDeclaredFields throws Exception上述两种方法返回的都是Field数组,每一个Field对象表示类中的一个属性。如果要获取属性中的详细信息,就需要调用Field类的方法。import org.junit.Test; import java.lang.reflect.Field; public class 反射机制_2获取成员变量 {    //1、获取全部的成员变量  //  Field[] getDeclaredFields    //获得所有的成员变量对应的Field对象,只要申明了就可以得到    @Test    public void getDeclaredFields(){        //定位Class对象        Class c=反射机制_2实验类2.class;        //定位全部成员变量        Field [] fields=c.getDeclaredFields();        //遍历获取,常量也会被当做成员变量        for (Field field : fields) {            System.out.println(field.getName()+"的类型是:"+field.getType());        }    }     //2、获取某个成员变量对象    //Field getDeclaredField(String name)    @Test    public void getDeclaredField() throws Exception {        //定位Class对象        Class c=反射机制_2实验类2.class;        //根据名称定位某个成员变量        Field f=c.getDeclaredField("age");        System.out.println(f);        System.out.println(f.getName()+"的类型是:"+f.getType());    }}运行结果:Test1、name的类型是:class java.lang.Stringage的类型是:intschoolName的类型是:class java.lang.StringCOUNTTRY的类型是:class java.lang.String Test2、private int IT2.反射机制_2实验类2.ageage的类型是:int 获取了成员变量有什么用呢?import org.junit.Test; import java.lang.reflect.Field; public class 反射机制_2获取成员变量 {    //获取了成员变量有什么用呢?    @Test    public void demo() throws Exception {        //反射第一步获取Class对象        Class c=反射机制_2实验类2.class;        //提取某个成员变量        Field f=c.getDeclaredField("age");         f.setAccessible(true);//因为我的age成员变量是用private修饰的,所以需要暴力打开权限         //作用一:赋值        反射机制_2实验类2 s=new 反射机制_2实验类2();        f.set(s,18);//s.setAge(18);        System.out.println(s);        //运行结果:        // 无参构造器执行!        //反射机制_2实验类2{name='null', age=18}这里可以看出,成员变量被赋值成功         //作用二:取值        int age = (int) f.get(s);        System.out.println(age);//18    }}使用反射技术获取方法对象并使用实验类:public class 反射机制_2实验类3 {    private String name;     public 反射机制_2实验类3() {     }     public 反射机制_2实验类3(String name) {        this.name = name;    }    public void run(){        System.out.println("跑起来了。。");    }     private void eat(){        System.out.println("累了,该吃饭了。。");    }    private String eat(String name){        System.out.println("那就浅吃一下"+name+"吧");        return "针不戳";    }    public static void ind(){        System.out.println("欢迎来到遇安的博客!");    }     public String getName() {        return name;    }     public void setName(String name) {        this.name = name;    }} 获取成员方法:import org.junit.Test; import java.lang.reflect.Method; public class 反射机制_2获取成员方法 {    //获得类中的所有成员方法对象    @Test    public void getDeclaredMethods(){        //获取类对象        Class c=反射机制_2实验类3.class;        //提取全部方法,包括私有的        Method [] methods=c.getDeclaredMethods();        //遍历全部方法        for (Method method : methods) {            System.out.println(method.getName()+"返回值类型:"+method.getReturnType()+"参数个数:"+method.getParameterCount());        }    }    //提取某个方法对象    @Test    public void getDeclaredMethod() throws Exception {        //获取类对象        Class c=反射机制_2实验类3.class;        //提取单个方法对象        Method m1=c.getDeclaredMethod("eat");        Method m2=c.getDeclaredMethod("eat",String.class);         //暴力打开权限        m1.setAccessible(true);        m2.setAccessible(true);         //触发方法的执行        反射机制_2实验类3 s=new 反射机制_2实验类3();        //注意:如果方法是没有结果返回的,那么返回的是Null        Object result =m1.invoke(s);        System.out.println(result);         Object result2=m2.invoke(s,"海鲜大餐");        System.out.println(result2);     }}运行结果:累了,该吃饭了。。null那就浅吃一下海鲜大餐原文链接:https://blog.csdn.net/qq_62731133/article/details/125089941
  • [技术干货] 超详细带你用Java实现QQ的聊天功能[转载]
    第一步:完成界面搭建要求:创建两个窗口:一个客户端,一个服务端,完成代码书写步骤:1.定义JFrame窗体中的组件:文本域,滚动条,面板,文本框,按钮 //属性    //文本域    private JTextArea jta;    //滚动条    private JScrollPane jsp;    //面板    private JPanel jp;    //文本框    private JTextField jtf;    //按钮    private JButton jb; 2.在构造方法中初始化窗口的组件: //构造方法    public ServerChatMain(){        //初始化组件        jta = new JTextArea();        //设置文本域默认不可编辑        jta.setEditable(false);        //注意:需要将文本域添加到滚动条中,实现滚动效果        jsp =new JScrollPane(jta);        //初始化面板        jp = new JPanel();        jtf = new JTextField(10);        jb=new JButton("发送");        //注意:需要将文本框与按钮添加到面板中        jp.add(jtf);        jp.add(jb); 注意:需要将滚动条与面板全部添加到窗体中,继承了窗体的属性,这里this就是窗体 this.add(jsp, BorderLayout.CENTER);//BorderLayout--边框布局        this.add(jp,BorderLayout.SOUTH);4.设置设置”标题“,大小,位置,关闭,是否可见   //注意:需要设置”标题“,大小,位置,关闭,是否可见        this.setTitle("QQ聊天服务端");        this.setSize(400,300);        this.setLocation(700,300);        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序就退出        this.setVisible(true);//设置窗体可见    }这样我们就完成了服务端的QQ聊天界面窗口搭建:注意:JTextArea文本域是不可以书写的客户端与服务端代码类似,这里就不一一展示了书写完毕代码,运行效果如下图:第二步:TPC通信的思路与步骤‍❄️使用网络编程完成数据点的传输(TCP,UDP协议)TCP协议TCP 是面向连接的运输层协议。应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的(一对一)TCP 提供可靠交付的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达TCP 提供全双工通信。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据面向字节流。TCP 中的“流”指的是流入到进程或从进程流出的字节序列TCP 服务端 具体步骤(客户端类似)具体步骤:   1.创建一个服务端的套接字    2.等待客户端连接   3.获取socket通道的输入流(输入六是实现读取数据的,一行一行读取)BufferedReader->readLine();    4.获取socket 通道的输出流(输出流实现写出数据,也是写一行换一行,刷新)BufferedWriter->newLine();    5.关闭socket 通道TCP通信步骤代码实现: try {            //1.创建一个服务端的套接字            ServerSocket serverSocket = new ServerSocket(8888);             //2.等待客户端连接           Socket socket = serverSocket.accept();             //3.获取socket通道的输入流(输入六是实现读取数据的,一行一行读取)BufferedReader->readLine();            //InputStream in = socket.getInputStream();           BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));            //4.获取socket 通道的输出流(输出流实现写出数据,也是写一行换一行,刷新)BufferedWriter->newLine();            //当用户点击发送按钮的时候写出数据            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));             //循环读取数据并拼接到文本域            String line = null;           while((line = br.readLine())!=null){               //将读取的数据拼接到文本域中显示               jta.append(line+System.lineSeparator());           }              //5.关闭socket 通道            serverSocket.close();         }catch (IOException e) {            e.printStackTrace();        } 点击发送按钮实现数据的传输 @Override    public void actionPerformed(ActionEvent actionEvent) {        System.out.println("发送按钮被点击了");    }步骤:1.获取文本框中发送的内容2.拼接需要发送的数据内容3.自己也要显示4.发送5.清空文本框内容@Override    public void actionPerformed(ActionEvent actionEvent) {        //System.out.println("发送按钮被点击了");        //1.获取文本框中发送的内容        String text = jtf.getText();        //2.拼接需要发送的数据内容        text = "服务端对客户端说:"+text;        //3.自己也要显示        jta.append(text);        //4.发送        try {            bw.write(text);            bw.newLine();//换行刷新            bw.flush();            //5.清空文本框内容            jtf.setText("");        } catch (IOException e) {            e.printStackTrace();        }      } 第三步:实现回车键发送数据(客户端类似)首先要实现一个接口public class ClientChatMain extends JFrame implements ActionListener, KeyListener {@Override    public void keyPressed(KeyEvent e) {         //回车键        // System.out.println(e);        //发送数据到socket 同道中        if(e.getKeyCode()==KeyEvent.VK_ENTER) {        sendDataToSocket();    }全部代码:服务端:package com.ithmm.chat; import javax.swing.*;import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.util.Properties; //说明:如果一个类,需要有界面的显示,那么这个类就需要继承JFrame,此时,该类可以被成为一个窗体类/*步骤:     1.定义JFrame窗体中的组件     2.在构造方法中初始化窗口的组件     3.使用网络编程完成数据的链接(TPC,UDP  协议)     4.实现"发送”按钮的监听点击事件     5.实现“回车键”发送数据 */public class ServerChatMain extends JFrame implements ActionListener, KeyListener {    public static void main(String[] args) {         //调用构造方法        new ServerChatMain();    }     //属性    //文本域    private JTextArea jta;    //滚动条    private JScrollPane jsp;    //面板    private JPanel jp;    //文本框    private JTextField jtf;    //按钮    private JButton jb;    //输出流(成员变量)    private BufferedWriter bw = null;    //服务端的端口号    //private static int serverPort;     //使用static静态方法读取外部京台文件    //static代码块特点:1.在类加载的时候自动执行    //特点2:一个类会被加载一次,因此静态代码块在程序中仅会被执行一次     /*static{        Properties prop = new Properties();        try {            //加载            prop.load(new FileReader("chat.properties"));            //给属性赋值            Integer.parseInt(prop.getProperty("serverPort"));        } catch (IOException e) {            e.printStackTrace();        }    }*/    //构造方法    public ServerChatMain(){        //初始化组件        jta = new JTextArea();        //设置文本域默认不可编辑        jta.setEditable(false);        //注意:需要将文本域添加到滚动条中,实现滚动效果        jsp =new JScrollPane(jta);        //初始化面板        jp = new JPanel();        jtf = new JTextField(10);        jb=new JButton("发送");        //注意:需要将文本框与按钮添加到面板中        jp.add(jtf);        jp.add(jb);         //注意:需要将滚动条与面板全部添加到窗体中,继承了窗体的属性,这里this就是窗体        this.add(jsp, BorderLayout.CENTER);//BorderLayout--边框布局        this.add(jp,BorderLayout.SOUTH);         //注意:需要设置”标题“,大小,位置,关闭,是否可见        this.setTitle("QQ聊天服务端");        this.setSize(400,300);        this.setLocation(700,300);        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序就退出        this.setVisible(true);//设置窗体可见         /**************** TCP   服务端    Start *********************/        //给发送按钮绑定一个监听点击事件        jb.addActionListener(this);        //给文本框绑定一个键盘点击事件        jtf.addKeyListener(this);         try {            //1.创建一个服务端的套接字            ServerSocket serverSocket = new ServerSocket(8888);             //2.等待客户端连接           Socket socket = serverSocket.accept();             //3.获取socket通道的输入流(输入六是实现读取数据的,一行一行读取)BufferedReader->readLine();            //InputStream in = socket.getInputStream();           BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));            //4.获取socket 通道的输出流(输出流实现写出数据,也是写一行换一行,刷新)BufferedWriter->newLine();            //当用户点击发送按钮的时候写出数据            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));             //循环读取数据并拼接到文本域            String line = null;           while((line = br.readLine())!=null){               //将读取的数据拼接到文本域中显示               jta.append(line+System.lineSeparator());           }              //5.关闭socket 通道            serverSocket.close();         }catch (IOException e) {            e.printStackTrace();        }         /**************** TCP   服务端    end  *********************/    }     @Override    public void actionPerformed(ActionEvent actionEvent) {        //System.out.println("发送按钮被点击了");       sendDataToSocket();    }    //行为    @Override    public void keyPressed(KeyEvent e) {         //回车键        // System.out.println(e);        //发送数据到socket 同道中        if(e.getKeyCode()==KeyEvent.VK_ENTER) {            sendDataToSocket();        }    }    //定义一个方法,实现将数据发送到socket通道中    private void sendDataToSocket(){        //1.获取文本框中发送的内容        String text = jtf.getText();        //2.拼接需要发送的数据内容        text = "服务端对客户端说:"+text;        //3.自己也要显示        jta.append(text+System.lineSeparator());        //4.发送        try {            bw.write(text);            bw.newLine();//换行刷新            bw.flush();            //5.清空文本框内容            jtf.setText("");        } catch (IOException e) {            e.printStackTrace();        }     }     @Override    public void keyTyped(KeyEvent keyEvent) {     }       @Override    public void keyReleased(KeyEvent keyEvent) {     }   } 客户端: import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JPanel;import javax.swing.JScrollPane;import javax.swing.JTextArea;import javax.swing.JTextField;import java.awt.BorderLayout;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.Socket;import java.util.Properties; //说明:如果一个类,需要有界面的显示,那么这个类就需要继承JFrame,此时,该类可以被成为一个窗体类/*步骤:     1.定义JFrame窗体中的组件     2.在构造方法中初始化窗口的组件 */ public class ClientChatMain extends JFrame implements ActionListener, KeyListener {    public static void main(String[] args) {         //调用构造方法        new ClientChatMain();    }     //属性    //文本域    private JTextArea jta;    //滚动条    private JScrollPane jsp;    //面板    private JPanel jp;    //文本框    private JTextField jtf;    //按钮    private JButton jb;    //s输出流    private BufferedWriter bw =null;    //客户端的IP地址   // private static String clientIp;    //客户端的port端口号    // private static int clientPort;     //静态代码块加载外部配置文件   /* static{      Properties prop = new Properties();        try {            prop.load(new FileReader("chat.properties"));            clientIp = prop.getProperty("clientIp");            clientPort =Integer.parseInt( prop.getProperty("clientPort"));        } catch (IOException e) {            e.printStackTrace();        }    }*/    //构造方法    public ClientChatMain(){        //初始化组件        jta = new JTextArea();        //设置文本域默认不可编辑        jta.setEditable(false);        //注意:需要将文本域添加到滚动条中,实现滚动效果        jsp =new JScrollPane(jta);        //初始化面板        jp = new JPanel();        jtf = new JTextField(10);        jb=new JButton("发送");        //注意:需要将文本框与按钮添加到面板中        jp.add(jtf);        jp.add(jb);         //注意:需要将滚动条与面板全部添加到窗体中,继承了窗体的属性,这里this就是窗体        this.add(jsp, BorderLayout.CENTER);//BorderLayout--边框布局        this.add(jp,BorderLayout.SOUTH);         //注意:需要设置”标题“,大小,位置,关闭,是否可见        this.setTitle("QQ聊天客户端");        this.setSize(400,300);        this.setLocation(700,300);        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序就退出        this.setVisible(true);//设置窗体可见         /**************** TCP   客户端    Start *********************/         //给发送按钮绑定一个监听点击事件        jb.addActionListener(this);        //给文本框绑定一个键盘键        jtf.addKeyListener(this);        try {            //1.创建一个客户端的套接字(尝试连接)             Socket socket = new Socket("127.0.0.1",8888);             //2.获取socket通道的输入流           BufferedReader br =  new BufferedReader(new InputStreamReader(socket.getInputStream()));            //3.获取socket 通道的输出流          bw =  new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));            //循环读取数据,并拼接到文本域           String line = null;           while((line = br.readLine())!=null){               jta.append(line + System.lineSeparator());           }             //4.关闭socket 通道            socket.close();        }catch (Exception e){            e.printStackTrace();        }         /**************** TCP   客户端    end  *********************/    }     @Override    public void actionPerformed(ActionEvent e) {       sendDataToSocket();    }    //行为    @Override    public void keyPressed(KeyEvent e) {        //回车键        if(e.getKeyCode() == KeyEvent.VK_ENTER){            //发送数据            sendDataToSocket();        }     }    private void sendDataToSocket(){        //1.获取文本框需要发送内容        String text = jtf.getText();         //2.拼接内容         text = "客户端对服务端说"+text;         //3.自己显示        jta.append(text+System.lineSeparator());         try {            //4.发送            bw.write(text);            bw.newLine();//换行加刷新            bw.flush();             bw.write(text);            //5.清空            jtf.setText("");        } catch (IOException e) {            e.printStackTrace();        }     }    @Override    public void keyTyped(KeyEvent e) {     }     @Override    public void keyReleased(KeyEvent e) {     } } 接口类:public interface addActistener {}public interface addActionListener {原文链接:https://blog.csdn.net/m0_68089732/article/details/124897209
  • [沙箱实验室] 我申请了 通过鲲鹏开发套件实现Java代码迁移 实验
    我申请了 通过鲲鹏开发套件实现Java代码迁移 实验但是我登录华为云控制台后发现没有ECS服务区当我以为是区域错误,我切换其他区域直接提示没有权限,导致我无法继续实验
  • [技术干货] 通过鲲鹏开发套件实现Java代码迁移
    1、概述本实验指导用户使用鲲鹏分析扫描工具识别java软件中的依赖库,并在鲲鹏平台完成java代码的编译迁移。2、准备环境2.1、预置环境预置实验环境需要等待【1-3分钟】后预置成功。环境预置会生成名称为“ecs-netty”的弹性云服务器ECS,创建配置相关的VPC、弹性公网IP、安全组。预置成功后ECS资源用户、密码信息可点击预置环境信息查看。 2.2、 配置环境查看云主机公网IP地址2.3、 安装依赖包远程登录创建好的弹性云服务器ECS,操作如下:①双击桌面的“Xfce终端”打开Terminal,输入以下命令登录云服务器;注意:请使用云服务器的公网IP替换命令中的【EIP】。LANG=en_us.UTF-8 ssh root@EIP 【EIP为云主机公网IP地址】说明:云服务器的弹性公网IP地址可以通过点击“控制台”->“服务列表”->“计算”->“弹性云服务器ECS”进入服务器列表,进行查看并复制。②接受秘钥输入“yes”,回车;③输入密码:使用预置环境信息中云服务器名称为ecs-netty的用户密码(输入密码时,命令行窗口不会显示密码,输完之后直接键入回车)。成功登录云服务器之后如下图所示(实验过程中请勿关闭该Terminal窗口,否则需要重复此步骤重新建立连接):登录成功后显示如下登录成功,使用“dependency advisor”工具分析jar依赖。执行以下命令下载分析工具:Wget https://sandbox-experiment-resource-east-1.obs.myhuaweicloud.com/netty-praxis/Dependency-advisor-Kunpeng-linux-1.1.3.tar.gztar zxvf Dependency-advisor-Kunpeng-linux-1.1.3.tar.gz && cd Dependency-advisor-Kunpeng-linux-1.1.3 && bash install.sh web约5分钟安装成功后显示如下图:安装完成,在实验环境浏览器新建tab页面,输入地址“https://EIP:8082”访问已安装的分析工具。操作如下:① 使用弹性云服务器ECS的公网IP替换链接中的【EIP】;② 【https】访问浏览器警告不安全,点击“高级”-> “添加例外”-> “确认安全例外”如下图所示:切换到“Xfce终端”执行以下命令下载需要分析的源码包“netty-all-4.1.34.Final.jar”:cd ~ && wget https://sandbox-experiment-resource-north-4.obs.cn-north-4.myhuaweicloud.com/netty-praxis/netty-all-4.1.34.Final.jar mkdir /opt/depadv/depadmin/netty && mkdir /opt/depadv/depadmin/netty/netty-all-4.1.34.Final mv netty-all-4.1.34.Final.jar /opt/depadv/depadmin/netty/netty-all-4.1.34.Final && cd /opt/depadv/depadmin/netty/netty-all-4.1.34.Final && jar -xvf netty-all-4.1.34.Final.jar && rm -rf netty-all-4.1.34.Final.jar && cd ~ 2.4、 数据分析切换到实验桌面浏览器已成功登录分析工具的页面,在网页上选择“分析软件安装包”,然后路径输入框输入netty,如下图所示:点击“分析”,分析完成结果如下图所示:2.5、 安装OpenJDK执行以下命令创建一个文件夹并下载OpenJDK安装包:mkdir netty-4.1.34 && cd netty-4.1.34 && wget https://sandbox-experiment-resource-north-4.obs.cn-north-4.myhuaweicloud.com/netty-praxis/OpenJDK8U-jdk_aarch64_linux_hotspot_jdk8u242-b08.tar.gz tar -zxf OpenJDK8U-jdk_aarch64_linux_hotspot_jdk8u242-b08.tar.gz mkdir -pv /opt/tools/installed/ mv jdk8u242-b08 /opt/tools/installed/执行以下命令编辑配置文件:vim /etc/profile键入“Shift+g”进入文件末尾,键入“i”进入文本编辑模式,在最后一行之后新起一行,复制粘贴以下配置:export JAVA_HOME=/opt/tools/installed/jdk8u242-b08export PATH=$JAVA_HOME/bin:$PATH2.6、 安装Maven执行以下命令下载Maven安装包:wget https://sandbox-experiment-resource-north-4.obs.cn-north-4.myhuaweicloud.com/netty-praxis/apache-maven-3.6.3-bin.tar.gz tar -zxf apache-maven-3.6.3-bin.tar.gz mv apache-maven-3.6.3 /opt/tools/installed/ wget https://sandbox-experiment-resource-north-4.obs.cn-north-4.myhuaweicloud.com/netty-praxis/settings.xml && rm -rf /opt/tools/installed/apache-maven-3.6.3/conf/settings.xml && cp settings.xml /opt/tools/installed/apache-maven-3.6.3/conf/ 2.7、编译环境2.7.1、配置编译环境执行以下命令下载自动化配置脚本:wget https://sandbox-experiment-resource-north-4.obs.cn-north-4.myhuaweicloud.com/netty-praxis/apache-maven-3.6.3-bin.tar.gz tar -zxf apache-maven-3.6.3-bin.tar.gz mv apache-maven-3.6.3 /opt/tools/installed/ wget https://sandbox-experiment-resource-north-4.obs.cn-north-4.myhuaweicloud.com/netty-praxis/settings.xml && rm -rf /opt/tools/installed/apache-maven-3.6.3/conf/settings.xml && cp settings.xml /opt/tools/installed/apache-maven-3.6.3/conf/2.7.2、 编译netty-tcnativecd /root/netty-4.1.34/netty-tcnative-netty-tcnative-parent-2.0.22.Final/ mvn install –DskipTests cd .. && tar -zxvf netty-netty-4.1.34.Final.tar.gz && cd netty-netty-4.1.34.Final mvn install -DskipTests【约需5分钟】编译成功如下图所示:2.8、 验证结果执行以下命令切换到编译结果文件夹。cd /root/.m2/repository/io/netty/netty-all/ jar -xvf 4.1.34.Final/netty-all-4.1.34.Final.jar && ls cd META-INF/ && tree最终显示如下
  • [技术干货] 一个毕业两年JAVA程序员的迷思[转载]
    首先,介绍一下我自己,从普通二本毕业,到上海工作,不知不觉,已经两年时间了。说实话,自己还是一个菜鸟,总是在迷茫,从未去努力。刚毕业的时候,想着自己还年轻,菜就菜吧,工作一年就好了,毕竟工作的环境几乎都是比自己大,或者和自己同龄的年轻人,说不上当一天和尚撞一天钟吧,偶尔会去学习一些东西,但是努力确实谈不上。很多时候,也能发现自己和同事的差距,但是很少去思考为什么。在此还是要感叹一句C+V大法确实牛逼啊。目前,在这家公司已经呆了两年的时间了,期间也从未想过换工作,因为公司的氛围我还是蛮喜欢的,同事们都很友好,很少加班,即使加班了也可以换调休,在这里确实也学到了很多东西,但是我觉得差的很远。平时的工作怎么说呢,让我写代码还行,但是让我干别的,不不不,我真不会。我真的很害怕经理又让我建表,然后把我怼一顿,建的是个什么玩意(有些夸张,经理还是蛮好的,教了我很多东西);让我写一些项目里没有样例的代码,写完之后,把我叫过去,指着我的代码,删掉,然后敲一遍给我看,简直是折磨我啊(我还是挺佩服经理的,在我和他的多次博弈中,我好像没赢过)。我对我的总结就是,干啥啥不行,CV第一名。其实,公司用的技术还是蛮新的,springboot微服务+hibernate+vue,至少在现在还算是比较新的技术,我也很庆幸用的不是jsp(并没有看不起的意思,谁还没用过jsp是不是,手动狗头),哈哈哈。之前也有想过写博客,也确实写了两篇,但是那时候确实还是很太菜了,所以后来文章被我删掉了,因为当我回头去看的时候,我想说:写的是个什么玩意啊,连si都不如。。。我想可能有很多人和我一样吧,摸鱼的日子确实还是可以的,但是总觉得少了点什么,没有核心竞争力是件可怕的事情,短时间还看不出来,时间长了,你总是要被时代所抛弃的。以后的日子里,争取每周两篇博客,忙的话至少也要一篇,有关于自己的心得和一些技术的(大家千万别喷我)。这篇文章就当成是我csdn的开山之作吧,我并没有想要赞和评论的意思,但是那也算是对我的一种鼓励和认可吧(狗头奉上),谢谢大家原文链接:https://blog.csdn.net/qq_42281590/article/details/106183225
  • [问题求助] 【鲲鹏】【弹性云服务器】java web打包的war文件在华为云端运行不了
    【功能模块】弹性云服务器【操作步骤&问题现象】1、java web打包的war文件在华为云端运行不了,网页打不开2、华为云端tomcat可以正常打开,端口正常3.代码在本地可以运行,但请求云资源运行不了【截图信息】在WinSCP的文档目录页面弹性公网IP麻烦哪位大神帮忙解答,谢谢!
  • [问题求助] 【鲲鹏】【弹性ECS云服务器】java代码无法运行
    【功能模块】java代码放到云端服务器无法运行,系统找不到java.util等等包,但已经配置了linux的jdk版本【操作步骤&问题现象】1、执行操作文档的时候不能运行java代码,显示java.util does not exists2、系统已经有java版本,java -version已有为jdk1.8.0_242,自己下载了linux的java版本并配置了JAVA_HOME目录【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] java
    课程质量真的是好得没话说,我看过很多前端的课程,但从没有哪家课程能将前端的知识体系划分的如此全面细致,还能保证每一个知识点还都能讲得如此透彻,在讲知识点的基础上还能开篇幅去讲思想,更是难得。比如下面的函数式编程,这种编程范式我之前从来都没使用过,更不知道柯里化、函数组合为何物。直到在拉钩大前端课程中,每一个知识点的学习,都让我有种重获新生的感觉,仿佛以前学习的东西都白学了,只知道简单的用法,不了解核心原理,更不会用高级特性。现在每学习完一个模块,就期待着解锁下一个模块,迫不及待地想去知道下一个模块可以让自己get到哪些技能。
总条数:949 到第
上滑加载中