开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

springboot注解详解(三)容器配置注解与Spring

发表于 2021-11-22

「这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战」

一、容器配置注解

1、@Autowired

@Autowired注解用于标记Spring将要解析和注入的依赖项。此注解可以作用在构造函数、字段和setter方法上。

2、@Primary

当系统中需要配置多个具有相同类型的bean时,@Primary可以定义这些Bean的优先级。

3、@PostConstruct与@PreDestroy

值得注意的是,这两个注解不属于Spring,它们是源于JSR-250中的两个注解,位于common-annotations.jar中。@PostConstruct注解用于标注在Bean被Spring初始化之前需要执行的方法。@PreDestroy注解用于标注Bean被销毁前需要执行的方法。

4、@Qualifier

当系统中存在同一类型的多个Bean时,@Autowired在进行依赖注入的时候就不知道该选择哪一个实现类进行注入。此时,我们可以使用@Qualifier注解来微调,帮助@Autowired选择正确的依赖项。下面是一个关于此注解的代码示例:

二、Spring Boot注解

1、@SpringBootApplication

@SpringBootApplication注解是一个快捷的配置注解,在被它标注的类中,可以定义一个或多个Bean,并自动触发自动配置Bean和自动扫描组件。此注解相当于@Configuration、@EnableAutoConfiguration和@ComponentScan的组合。在Spring Boot应用程序的主类中,就使用了此注解。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
typescript复制代码@SpringBootApplication

public class Application{

 public static void main(String [] args){

 SpringApplication.run(Application.class,args);

 }

}

2、@EnableAutoConfiguration

@EnableAutoConfiguration注解用于通知Spring,根据当前类路径下引入的依赖包,自动配置与这些依赖包相关的配置项。

3、@ConditionalOnClass与@ConditionalOnMissingClass

这两个注解属于类条件注解,它们根据是否存在某个类作为判断依据来决定是否要执行某些配置。下面是一个简单的示例代码:

1
2
3
4
5
6
7
8
9
less复制代码@Configuration

@ConditionalOnClass(DataSource.class)

class MySQLAutoConfiguration {

 //...

}

4、@ConditionalOnBean与@ConditionalOnMissingBean

这两个注解属于对象条件注解,根据是否存在某个对象作为依据来决定是否要执行某些配置方法。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
less复制代码@Bean

@ConditionalOnBean(name="dataSource")

LocalContainerEntityManagerFactoryBean entityManagerFactory(){

 //...

}

@Bean

@ConditionalOnMissingBean

public MyBean myBean(){

 //...

}

5、@ConditionalOnProperty

@ConditionalOnProperty注解会根据Spring配置文件中的配置项是否满足配置要求,从而决定是否要执行被其标注的方法。示例代码如下:

1
2
3
4
5
6
7
8
9
less复制代码@Bean

@ConditionalOnProperty(name="alipay",havingValue="on")

Alipay alipay(){

 return new Alipay();

}

6、@ConditionalOnResource

此注解用于检测当某个配置文件存在使,则触发被其标注的方法,下面是使用此注解的代码示例:

1
2
3
4
5
6
7
less复制代码@ConditionalOnResource(resources = "classpath:website.properties")

Properties addWebsiteProperties(){

 //...

}

7、@ConditionalOnWebApplication与@ConditionalOnNotWebApplication

这两个注解用于判断当前的应用程序是否是Web应用程序。如果当前应用是Web应用程序,则使用Spring WebApplicationContext,并定义其会话的生命周期。下面是一个简单的示例:

1
2
3
4
5
6
7
less复制代码@ConditionalOnWebApplication

HealthCheckController healthCheckController(){

 //...

}

8、@ConditionalExpression

此注解可以让我们控制更细粒度的基于表达式的配置条件限制。当表达式满足某个条件或者表达式为真的时候,将会执行被此注解标注的方法。

1
2
3
4
5
6
7
8
9
less复制代码@Bean

@ConditionalException("${localstore} && ${local == 'true'}")

LocalFileStore store(){

 //...

}

9、@Conditional

@Conditional注解可以控制更为复杂的配置条件。在Spring内置的条件控制注解不满足应用需求的时候,可以使用此注解定义自定义的控制条件,以达到自定义的要求。下面是使用该注解的简单示例:

1
2
3
4
5
6
7
csharp复制代码@Conditioanl(CustomConditioanl.class)

CustomProperties addCustomProperties(){

 //...

}

总结

本次总结了Spring Boot中常见的各类型注解的使用方式,让大家能够统一的对Spring Boot常用注解有一个全面的了解。由于篇幅的原因,关于Spring Boot不常用的一些注解,将在下一次分享中进行补充和说明。

​

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

384 打乱数组 洗牌算法运用题

发表于 2021-11-22

「这是我参与11月更文挑战的第 22 天,活动详情查看:2021最后一次更文挑战」。

题目描述

这是 LeetCode 上的 384. 打乱数组 ,难度为 中等。

Tag : 「洗牌算法」

给你一个整数数组 nums,设计算法来打乱一个没有重复元素的数组。

实现 Solution class:

  • Solution(int[] nums) 使用整数数组 nums 初始化对象
  • int[] reset() 重设数组到它的初始状态并返回
  • int[] shuffle() 返回数组随机打乱后的结果

示例:

1
2
3
4
5
6
7
8
9
10
11
css复制代码输入
["Solution", "shuffle", "reset", "shuffle"]
[[[1, 2, 3]], [], [], []]
输出
[null, [3, 1, 2], [1, 2, 3], [1, 3, 2]]

解释
Solution solution = new Solution([1, 2, 3]);
solution.shuffle(); // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。例如,返回 [3, 1, 2]
solution.reset(); // 重设数组到它的初始状态 [1, 2, 3] 。返回 [1, 2, 3]
solution.shuffle(); // 随机返回数组 [1, 2, 3] 打乱后的结果。例如,返回 [1, 3, 2]

提示:

  • 1<=nums.length<=2001 <= nums.length <= 2001<=nums.length<=200
  • −106<=nums[i]<=106-10^6 <= nums[i] <= 10^6−106<=nums[i]<=106
  • nums 中的所有元素都是 唯一的
  • 最多可以调用 5∗1045 * 10^45∗104 次 reset 和 shuffle

洗牌算法

共有 nnn 个不同的数,根据每个位置能够选择什么数,共有 n!n!n! 种组合。

题目要求每次调用 shuffle 时等概率返回某个方案,或者说每个元素都够等概率出现在每个位置中。

我们可以使用 KnuthKnuthKnuth 洗牌算法,在 O(n)O(n)O(n) 复杂度内等概率返回某个方案。

具体的,我们从前往后尝试填充 [0,n−1][0, n - 1][0,n−1] 该填入什么数时,通过随机当前下标与(剩余的)哪个下标进行值交换来实现。

对于下标 xxx 而言,我们从 [x,n−1][x, n - 1][x,n−1] 中随机出一个位置与 xxx 进行值交换,当所有位置都进行这样的处理后,我们便得到了一个公平的洗牌方案。

对于下标为 000 位置,从 [0,n−1][0, n - 1][0,n−1] 随机一个位置进行交换,共有 nnn 种选择;下标为 111 的位置,从 [1,n−1][1, n - 1][1,n−1] 随机一个位置进行交换,共有 n−1n - 1n−1 种选择 … 且每个位置的随机位置交换过程相互独立。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Java复制代码class Solution {
int[] nums;
int n;
Random random = new Random();
public Solution(int[] _nums) {
nums = _nums;
n = nums.length;
}
public int[] reset() {
return nums;
}
public int[] shuffle() {
int[] ans = nums.clone();
for (int i = 0; i < n; i++) {
swap(ans, i, i + random.nextInt(n - i));
}
return ans;
}
void swap(int[] arr, int i, int j) {
int c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
}
  • 时间复杂度:O(n)O(n)O(n)
  • 空间复杂度:O(n)O(n)O(n)

最后

这是我们「刷穿 LeetCode」系列文章的第 No.384 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour… 。

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

「Linux 奏章 14」服务管理 (daemon)

发表于 2021-11-22

「这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战」。

服务 (service) 本质就是进程,但是运行在后台,通常都会监听某个端口,等待其他程序的请求,比如 (mysql、sshd、防火墙等),因此又称为守护进程 (daemon).

  1. service

service 管理指令:

  • ⭐service <服务名> [start | stop | restart | reload | status]

  • service 指令管理的服务在 /etc/init.d 中查看

    • img
  • 🔥 在 CentOS 7.0 以后很多服务不再使用 service,而是 systemctl 指令。

  • 常用:service network status、service network start、service network stop

  • ⭐修改系统自启动服务

    • 输入指令: setup ==> 选择 System services

      img

    • 修改自启动服务 ( 按下Space 修改 ):

      img

  1. runlevel

  • 重申服务的运行级别(runlevel)
    • 0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动
    • 1:单用户工作状态,root权限,用于权限维护,禁止远程登录
    • 2:多用户状态(无NFS),不支持网络
    • 3:完全的多用户状态(有NFS),登陆后进入控制台命令行模式
    • 4:系统未使用,保留
    • 5:X11控制台,登陆后进入图形GUI界面
    • 6:系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动(循环开关机)
  • ⭐开机流程:开机 => BIOS => /boot => systemd (pid=1) => 运行级别 => 运行级别对应的服务
  • ⭐修改和查看默认运行级别
    • systemctl get-default
    • systemctl set-default multi-user.target
    • systemctl set-default graphical.target
  1. chkconfig

chkconfig 指令:

  • 通过 chkconfig 命令可以给服务的各个运行级别设置自启动/自关闭(某个服务对于各个运行级别不一定一样,是独立的)
  • ⭐chkconfig 基本语法:
    • chkconfig --list [| grep xxx] : 查看服务
    • chkconfig <服务名> --list
    • chkconfig --level 5 <服务名> on/off : 启停服务(对于某个运行级别)
  • 案例演示:对 network 服务在 3 运行级别开启自启动
    • chkconfig --level 3 network on
    • 注意 : chkconfig 重新设置服务的自启动/自关闭,需要 reboot 才能生效
    • img
  1. systemctl

🔥 systemctl 指令详解:

  • 使用 netstat -anp 可以监控网络状态!
    • img
  • CentOS 7 以后运行级别简化为 3 和 5,所以 systemctl 不再区分运行级别,执行同时会设置 3 和 5 的运行级别。
  • ⭐基本语法 : systemctl [start|stop|enable|disable|is-enabled|restart|status] <服务名>
  • ⭐systemctl 指令管理的服务在 /usr/lib/systemd/system 查看(service 指令管理的服务在 /etc/init.d 中)
    • ll /usr/lib/systemd/system:
    • img
  • ⭐systemctl 设置服务自启动状态【常用命令】
    • systemctl list-unit-files [| grep <服务名>] : 查看服务
    • systemctl enable <服务名> : 设置服务开机启动
    • systemctl disable <服务名> : 关闭服务开机启动
    • systemctl is-enabled <服务名> : 查询某个服务是否自启动
  • ⭐systemctl 设置当前(临时)服务状态——并不影响自启动状态
    • systemctl status <服务名> : 查看当前服务状态
    • systemctl stop <服务名> : 关闭当前服务(临时,与自启动无关)
    • systemctl start <服务名> : 开启当前服务(临时,与自启动无关)
    • …
  • systemctl list-unit-files:

  • systemctl status:

  1. firewall-cmd

  • 查看当前防火墙状态等一系列操作:systemctl status firewalld

img

  • 注意

    • stop|start 这种操作是立即生效的,当重启系统后,还是回归以前对服务的自启动设置(不同于自启动)【telnet 测试某个端口即可验证】
    • 如果希望某个服务自启动或者自关闭并永久生效,使用 systemctl [enable|disable] <服务名>
  • 防火墙 firewall & 打开或关闭指定端口 ❗

    • 在真正的生产环境,往往需要将防火墙打开,但问题是:如果将防火墙打开,那么外部请求数据包就不能跟服务器监听端口通讯。这时,就需要打开指定的端口,比如 80、22、8080…

      如下介绍如何实现:

    • ⭐查询端口是否开放 : firewall-cmd --query-port=端口号/协议

    • ⭐打开端口 : firewall-cmd --permanent --add-port=端口号/协议

    • ⭐关闭端口 : firewall-cmd --permanent --remove-port=端口号/协议

    • ⭐无论打开/关闭,只有重新载入才能生效 : firewall-cmd --reload

    • ⭐查看当前网络状态(可以用来查端口号对应的协议) : netstat -anp | more

  • 应用实例

    • ① 启用防火墙

      img

    • ② 开放111端口

      img

    • ③ 测试111端口是否能 telnet

      物理机 telnet 192.168.10.131 111

    • ④ 再次关闭111端口

      img

希望本文对你有所帮助🧠

欢迎在评论区留下你的看法🌊,我们一起讨论与分享🔥

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring cloud微服务安全实战 学习分享 downl

发表于 2021-11-22

download: Spring cloud微服务安全实战

我们将java程序写好并运行没有问题之后,会将java程序打包成exe,好方便随便使用,可是对于新手java人员来说不清楚java程序怎么打包成exe?下面来我们就来给大家讲解一下java程序打包成exe方法。

第一步:

① 指定要进行转化的JAR或class文件.

  1. 可以将class文件事先打包成jar包,然后浏览,定位到这个jar包
  2. 可以直接选择包含所有class文件的目录

② 选定java运行时最低要求的JRE版本.

java程序怎么打包成exe?java程序打包成exe方法.jpg

第二步: 选择要生成的应用程序类型,包括三种:

  1. 控制台程序: 创建一个控制台类型的程序,运行时控制台是可见的.
  2. Windows窗口程序: 创建一个窗口类型程序,运行时控制台不可见.这个是最主要的类型.
  3. Windows NT服务: 创建一个Windows NT服务类型程序,使用/test可以作为控制台程序测试运行.这个我不太了解.

1.jpg

第三步: 根据第二步选择不同,第三步操作不同.本人以选择Windows窗口程序为例.

① 输入开始运行的主启动类,即main函数所在的类.

② 选择一张程序运行欢迎画面.

2.jpg

第四步: 对生成的exe进行一些保护性的设置

3.jpg

第五步: 添加程序运行时所依赖的jar文件.比如: 操作mysql数据库所需要的mysql.jar,生成日志文件需要的log4j.jar

另外,可以将程序运行时依赖的jre包文件(对其进行精简)添加进行,使程序能在不带有jre的windows系统环境中运行.

4.jpg

第六步: 设置生成的exe文件信息

5.jpg

  1. 指定exe文件名
  2. 选择exe显示的图标文件
  3. 设置exe文件版本信息

6.jpg

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

那些你学了又忘的Java IO(二):数据资源浅析

发表于 2021-11-22

人生苦短,不如养狗

一、概要

  在上一章节中我们了解到Java I/O在进行数据传输的过程中,其传输的一端必定为数据资源,下面我们就来具体了解一下能够被Java I/O操作的常见的数据资源。

二、数据资源

  在Java I/O当中根据存储空间和功能可以将数据资源大致分为以下几类:

  • 字节/字符数组
  • 文件
  • 管道
  • 网络资源
  • 系统输入/输出(来自外设的输入/输出)

1. 字节/字符数组

  字节/字符数据是存在于程序内部的临时存储数据,这类数据会存储在应用程序的内存当中,一般用于程序运行时进行使用,而字节/字符数组则是其对应的存储空间。

  在使用字节或者字符数组时需要注意,这里存储的数据应当保证是 纯文本的 、 与文件属性无关的 数据内容,如果数据内容存在依赖文件属性的情况,那么即时可以存储到字节/字符数组当中,当将数据还原的时候也会存在文件损坏或者乱码的情况。除此以外,由于是存储在内存当中的临时数据,当应用程序发生了故障中断或者重启时,这部分数据会发生丢失的情况。

  在Java I/O当中通过使用 ByteArrayInputStream/CharArrayReader 和 ByteArrayOutputStream/CharArrayWriter 来对字节/字符数组进行读取或者写入操作。

2. 文件

  文件可以说是除了字节/字符数组以外Java当中最常见的数据存储空间,是一种存储在磁盘当中的持久化数据,即时当你的应用程序发生故障中断或者重启,文件本身会依然存在。需要注意的是,和字节/字符数据相比,除了存储位置不同,文件还包含了文件数据本身的属性信息,并且不同类型的文件其存储时的数据结构还存在着较大差异。

  为了方便Java I/O对于文件的操作,JDK在io类库当中提供了 File 和 FileDescriptor 两个类对文件进行抽象表示。这里可能有些同学要迷惑了,为什么一个概念要用两个不同的类的抽象表示,这不是增加学习成本吗?下面我们来具体分析一下这两个类。

File类

  从源码中可以看到,File类描述了一个文件/目录的基本情况,诸如文件路径、是否存在、是否为隐藏文件、是否可读、是否可写、列出当前目录下的文件、文件大小等信息。除了描述文件的基本信息,File类还提供了根据指定路径创建文件、删除文件、获取父文件、获取绝对路径等操作。可以看到这里提供的能力是将 文件属性信息 作为一个 对象 而进行的对应操作,并不涉及到文件内容的操作。

FileDescriptor类

  对操作系统熟悉的同学一定听过文件描述符这样一个概念,其实FileDescriptor类的功能和操作系统当中的文件描述符类似,只不过是在Java这个平台当中。FileDescriptor类实际上是用于表示一个指向已经被打开的文件的指针,当然,在Java当中应当描述为引用。需要注意的是,这里仅提供了充当底层机器特定结构的不透明句柄的作用,并没有File类的功能,也没有操作文件内容的能力。

  从上面的分析我们可以找到一些关于Java中分为两个类去描述文件的思路,File类在进行文件处理时需要依赖文件路径,而FileDescriptor类的操作则与文件路径无关而与底层机器有关,只需要起到句柄作用即可,可以看到这里包含着 单一职责 的思想,前者处理了文件存在性和权限等问题,后者则实现了对于文件在底层机器中的定位问题。但是这两者都无法直接进行文件内容的操作,需要通过 FileInputStream/FileOutputStream 来进行文件的读写操作。

3. 管道

  其实这又是一个来源于操作系统的概念,就如同管道在操作系统中提供进程间通信的能力一样,Java I/O中的管道提供了在 同一个JVM当中两个线程进行通信 的能力。需要注意,虽然功能看起来类似,但是后者提供的通信能力是针对 同一个进程 当中的两个线程,而非不同地址空间的两个进程。

4. 网络资源

  网络资源实际上是存储在远程机器上的数据资源,其在远程机器上的存在形式可能是字符/字节数组,也可能是文件。要想访问网络资源,就需要通过网络连接进行通信。由于网络通信的复杂性和不确定性,导致在进行数据资源处理时,I/O模型不能简单的复用读取本地资源的I/O模型。在Java I/O类库当中并没有很好的给出对应的模型方案,而在后续的Java NIO中给出了解决方案。

5. 系统输入/输出

  简单来说就是通过外设向程序进行输入或者展示程序输出给外设的结果。在Java中有如下三个流提供系统输入/输出:

  • Systen.in : 只有指向Java应用程序的键盘输入才能进行读取;
  • Systen.out
  • Systen.err : 将错误信息输出到控制台中(对,就是你平时在ide中看到的红色的堆栈信息);

三、总结

  本章主要讲解了Java I/O中的操作对应——数据资源,也即所操作数据的来源和目的地。以上几种数据资源是根据io类库中的类进行的简单分类,并没有完全包含所有的类型,有兴趣的同学可以继续拓展研究一下。下一章我们将进行 流 这一概念的学习。

扫码_搜索联合传播样式-标准色版.jpg

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

大数据Hive学习之旅第三篇 一、DML 数据操作 二、查询

发表于 2021-11-22

「这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战」。

一、DML 数据操作

1、数据导入

1.1、向表中装载数据(Load)

  • 语法
1
hive复制代码hive> load data [local] inpath '数据的 path' [overwrite] into table student [partition (partcol1=val1,…)];

(1)load data:表示加载数据

(2)local:表示从本地加载数据到 hive 表;否则从 HDFS 加载数据到 hive 表

(3)inpath:表示加载数据的路径

(4)overwrite:表示覆盖表中已有数据,否则表示追加

(5)into table:表示加载到哪张表

(6)student:表示具体的表

(7)partition:表示上传到指定分区

  • 实操案例
+ 创建一张表



1
2
hive复制代码hive (default)> create table student(id string, name string)
> row format delimited fields terminated by '\t';
+ 加载本地文件到 hive
1
2
hive复制代码hive (default)> load data local inpath '/opt/module/hive-3.1.2/datas/student' into table student;
Loading data to table default.student
+ 加载 HDFS 文件到 hive 中 上传文件到 HDFS
1
hive复制代码hive (default)> dfs -put /opt/module/hive-3.1.2/datas/student.txt /user/moe/hive/;
加载 HDFS 上数据
1
2
hive复制代码hive (default)> load data inpath '/user/moe/hive/student.txt' into table student;
Loading data to table default.student
+ 加载数据覆盖表中已有的数据
1
2
hive复制代码hive (default)> load data inpath '/user/moe/hive/student.txt' overwrite into table student;
Loading data to table default.student

1.2、通过查询语句向表中插入数据(Insert)

  • 创建一张表
1
hive复制代码hive (default)> create table student_par(id string, name string) row format delimited fields terminated by '\t';
  • 基本插入数据
1
hive复制代码hive (default)> insert into table student_par values('1','wangwu'),('2','zhaoliu');
  • 基本模式插入(根据单张表查询结果)
1
hive复制代码hive (default)> insert overwrite table student_par select id,name from student;

insert into:以追加数据的方式插入到表或分区,原有数据不会删除

insert overwrite:会覆盖表中已存在的数据

注意:insert 不支持插入部分字段

  • 多表(多分区)插入模式(根据多张表查询结果)

1.3、查询语句中创建表并加载数据(As Select)

根据查询结果创建表(查询的结果会添加到新创建的表中)

1
hive复制代码create table if not exists student3 as select id, name from student;

1.4、创建表时通过 Location 指定加载数据路径

  • 上传数据到 hdfs 上
1
2
hive复制代码hive (default)> dfs -mkdir /student;
hive (default)> dfs -put /opt/module/hive-3.1.2/datas/student.txt /student;
  • 创建表,并指定在 hdfs 上的位置
1
2
3
hive复制代码hive (default)> create external table if not exists student3(id int, name string)
> row format delimited fields terminated by '\t'
> location '/student';
  • 查询数据
1
hive复制代码hive (default)> select * from student3;

1.5、Import 数据到指定 Hive 表中

注意:先用 export 导出后,再将数据导入。

1
hive复制代码hive (default)> import table student2 from '/user/hive/warehouse/export/student';

2、数据导出

2.1、Insert 导出

  • 将查询的结果导出到本地
1
hive复制代码hive (default)> insert overwrite local directory '/opt/module/hive-3.1.2/datas/student01' select * from student;
  • 将查询的结果格式化导出到本地
1
hive复制代码hive (default)> insert overwrite local directory '/opt/module/hive-3.1.2/datas/student02' row format delimited fields terminated by '\t' select * from student;
  • 将查询的结果导出到 HDFS 上(没有 local)
1
hive复制代码hive (default)> insert overwrite directory '/student03' row format delimited fields terminated by '\t' select * from student;

2.2、Hadoop 命令导出到本地

1
hive复制代码hive (default)> dfs -get /user/hive/warehouse/student/student.txt /opt/module/data/export/student3.txt;

2.3、Hive Shell 命令导出

基本语法:(hive -f/-e 执行语句或者脚本 > file)

1
shell复制代码[moe@hadoop102 datas]$ hive -e 'select * from student;' > ./student4.txt

2.4、Export 导出到 HDFS 上

1
hive复制代码hive (default)> export table student to '/student5';

export 和 import 主要用于两个 Hadoop 平台集群之间 Hive 表迁移。

2.5、Sqoop 导出

2.6、清除表中数据(Truncate)

注意:Truncate 只能删除管理表,不能删除外部表中数据

1
hive复制代码hive (default)> truncate table student;

二、查询

1、基本查询(Select…From)

cwiki.apache.org/confluence/…

查询语句语法:

1
2
3
4
5
6
7
8
9
txt复制代码SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BY col_list]
[ORDER BY col_list]
[CLUSTER BY col_list
| [DISTRIBUTE BY col_list] [SORT BY col_list]
]
[LIMIT number]

1.1、全表和特定列查询

  • 数据准备

原始数据

dept:

1
2
3
4
txt复制代码10	ACCOUNTING	1700
20 RESEARCH 1800
30 SALES 1900
40 OPERATIONS 1700

emp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
txt复制代码7369	SMITH	CLERK	7902	1980-12-17	800.00		20
7499 ALLEN SALESMAN 7698 1981-2-20 1600.00 300.00 30
7521 WARD SALESMAN 7698 1981-2-22 1250.00 500.00 30
7566 JONES MANAGER 7839 1981-4-2 2975.00 20
7654 MARTIN SALESMAN 7698 1981-9-28 1250.00 1400.00 30
7698 BLAKE MANAGER 7839 1981-5-1 2850.00 30
7782 CLARK MANAGER 7839 1981-6-9 2450.00 10
7788 SCOTT ANALYST 7566 1987-4-19 3000.00 20
7839 KING PRESIDENT 1981-11-17 5000.00 10
7844 TURNER SALESMAN 7698 1981-9-8 1500.00 0.00 30
7876 ADAMS CLERK 7788 1987-5-23 1100.00 20
7900 JAMES CLERK 7698 1981-12-3 950.00 30
7902 FORD ANALYST 7566 1981-12-3 3000.00 20
7934 MILLER CLERK 7782 1982-1-23 1300.00 10
+ 创建部门表



1
2
3
4
5
6
hive复制代码create table if not exists dept(
deptno int,
dname string,
loc int
)
row format delimited fields terminated by '\t';
+ 创建员工表
1
2
3
4
5
6
7
8
9
10
hive复制代码create table if not exists emp(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int)
row format delimited fields terminated by '\t';
+ 导入数据
1
2
3
4
5
6
hive复制代码hive (default)> load data local inpath '/opt/module/hive-3.1.2/datas/dept.txt' into table dept;
Loading data to table default.dept
OK
Time taken: 0.266 seconds
hive (default)> load data local inpath '/opt/module/hive-3.1.2/datas/emp.txt' into table emp;
Loading data to table default.emp
  • 全表查询
1
2
hive复制代码hive (default)> select * from emp;
hive (default)> select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp ;
  • 选择特定列查询
1
hive复制代码hive (default)> select empno, ename from emp;

注意:

(1)SQL 语言大小写不敏感。

(2)SQL 可以写在一行或者多行

(3)关键字不能被缩写也不能分行

(4)各子句一般要分行写。

(5)使用缩进提高语句的可读性。

1.2、列别名

  • 重命名一个列
  • 便于计算
  • 紧跟列名,也可以在列名和别名之间加入关键字‘AS’
  • 案例实操

查询名称和部门

1
hive复制代码hive (default)> select ename AS name, deptno dn from emp;

1.3、算术运算符

image.png

案例实操:查询出所有员工的薪水后加 1 显示。

1
hive复制代码hive (default)> select sal + 1 from emp;

1.4、常用函数

  • 求总行数(count)
1
hive复制代码hive (default)> select count(*) cnt from emp;
  • 求工资的最大值(max)
1
hive复制代码hive (default)> select max(sal) max_sal from emp;
  • 求工资的最小值(min)
1
hive复制代码hive (default)> select min(sal) min_sal from emp;
  • 求工资的总和(sum)
1
hive复制代码hive (default)> select sum(sal) sum_sal from emp;
  • 求工资的平均值(avg)
1
hive复制代码hive (default)> select avg(sal) avg_sal from emp;

1.5、Limit 语句

1
hive复制代码hive (default)> select * from emp limit 5;

1.6、Where 语句

  • 使用 WHERE 子句,将不满足条件的行过滤掉
  • WHERE 子句紧随 FROM 子句
  • 案例实操

查询出薪水大于 1000 的所有员工

1
hive复制代码hive (default)> select * from emp where sal >1000;

注意:where 子句中不能使用字段别名。

1.7、比较运算符(Between/In/ Is Null)

image.png

  • 查询出薪水等于 5000 的所有员工
1
hive复制代码hive (default)> select * from emp where sal = 5000;
  • 查询工资在 500 到 1000 的员工信息
1
hive复制代码hive (default)> select * from emp where sal between 500 and 1000;
  • 查询 comm 为空的所有员工信息
1
hive复制代码hive (default)> select * from emp where comm is null;
  • 查询工资是 1500 或 5000 的员工信息
1
hive复制代码hive (default)> select * from emp where sal IN (1500, 5000);

1.8、Like 和 RLike

  • 使用 LIKE 运算选择类似的值
  • 选择条件可以包含字符或数字

% 代表零个或多个字符(任意个字符)。

_ 代表一个字符。

  • RLIKE 子句

RLIKE 子句是 Hive 中这个功能的一个扩展,其可以通过 Java 的正则表达式这个更强大的语言来指定匹配条件。

  • 案例实操
+ 查找名字以 A 开头的员工信息



1
hive复制代码hive (default)> select * from emp where ename LIKE 'A%';
+ 查找名字中第二个字母为 A 的员工信息
1
hive复制代码hive (default)> select * from emp where ename LIKE '_A%';
+ 查找名字中带有 A 的员工信息
1
hive复制代码hive (default)> select * from emp where ename RLIKE '[A]';

1.9、逻辑运算符(And/Or/Not)

image.png

  • 查询薪水大于 1000,部门是 30
1
hive复制代码hive (default)> select * from emp where sal>1000 and deptno=30;
  • 查询薪水大于 1000,或者部门是 30
1
hive复制代码hive (default)> select * from emp where sal>1000 or deptno=30;
  • 查询除了 20 部门和 30 部门以外的员工信息
1
hive复制代码hive (default)> select * from emp where deptno not IN(30, 20);

2、分组

2.1、Group By 语句

GROUP BY 语句通常会和聚合函数一起使用,按照一个或者多个列队结果进行分组,然后对每个组执行聚合操作。

  • 计算 emp 表每个部门的平均工资
1
hive复制代码hive (default)> select t.deptno, avg(t.sal) avg_sal from emp t group by t.deptno;
  • 计算 emp 每个部门中每个岗位的最高薪水
1
hive复制代码hive (default)> select t.deptno, t.job, max(t.sal) max_sal from emp t group by t.deptno, t.job;

2.2、Having 语句

having 与 where 不同点

  • where 后面不能写分组函数,而 having 后面可以使用分组函数。
  • having 只用于 group by 分组统计语句。

求每个部门的平均工资

1
hive复制代码hive (default)> select deptno, avg(sal) from emp group by deptno;

求每个部门的平均薪水大于 2000 的部门

1
hive复制代码hive (default)> select deptno, avg(sal) avg_sal from emp group by deptno having avg_sal > 2000;

3、Join 语句

3.1、等值 Join

Hive 支持通常的 SQL JOIN 语句。

根据员工表和部门表中的部门编号相等,查询员工编号、员工名称和部门名称;

1
hive复制代码hive (default)> select e.empno, e.ename, d.deptno, d.dname from emp e inner join dept d on e.deptno = d.deptno;

3.2、表的别名

  • 好处
+ 使用别名可以简化查询。
+ 使用表名前缀可以提高执行效率。
  • 案例实操

合并员工表和部门表

1
hive复制代码hive (default)> select e.empno, e.ename, d.deptno from emp e join dept d on e.deptno = d.deptno;

3.3、内连接

内连接:只有进行连接的两个表中都存在与连接条件相匹配的数据才会被保留下来。

1
hive复制代码hive (default)> select e.empno, e.ename, d.deptno from emp e join dept d on e.deptno = d.deptno;

3.4、左外连接

左外连接:JOIN 操作符左边表中符合 WHERE 子句的所有记录将会被返回。

1
hive复制代码hive (default)> select e.empno, e.ename, d.deptno from emp e left join dept d on e.deptno = d.deptno;

3.5、右外连接

右外连接:JOIN 操作符右边表中符合 WHERE 子句的所有记录将会被返回。

1
hive复制代码hive (default)> select e.empno, e.ename, d.deptno from emp e right join dept d on e.deptno = d.deptno;

3.6、满外连接

满外连接:将会返回所有表中符合 WHERE 语句条件的所有记录。如果任一表的指定字段没有符合条件的值的话,那么就使用 NULL 值替代。

1
hive复制代码hive (default)> select e.empno, e.ename, d.deptno from emp e full join dept d on e.deptno = d.deptno;

3.7、多表连接

注意:连接 n 个表,至少需要 n-1 个连接条件。例如:连接三个表,至少需要两个连接条件。

  • 数据准备
1
2
3
txt复制代码1700	Beijing
1800 London
1900 Tokyo
  • 创建位置表
1
2
3
4
5
hive复制代码create table if not exists location(
loc int,
loc_name string
)
row format delimited fields terminated by '\t';
  • 导入数据
1
hive复制代码hive (default)> load data local inpath '/opt/module/hive-3.1.2/datas/location.txt' into table location;
  • 多表连接查询
1
hive复制代码hive (default)>SELECT e.ename, d.dname, l.loc_name FROM emp e JOIN dept d ON d.deptno = e.deptno JOIN location l ON d.loc = l.loc;

大多数情况下,Hive 会对每对 JOIN 连接对象启动一个 MapReduce 任务。本例中会首先启动一个 MapReduce job 对表 e 和表 d 进行连接操作,然后会再启动一个 MapReduce job 将第一个 MapReduce job 的输出和表 l; 进行连接操作。

注意:为什么不是表 d 和表 l 先进行连接操作呢?这是因为 Hive 总是按照从左到右的顺序执行的。

优化:当对 3 个或者更多表进行 join 连接时,如果每个 on 子句都使用相同的连接键的话,那么只会产生一个 MapReduce job。

3.8、笛卡尔积

  • 笛卡尔集会在下面条件下产生
+ 省略连接条件
+ 连接条件无效
+ 所有表中的所有行互相连接
  • 案例实操
1
hive复制代码hive (default)> select empno, dname from emp, dept;

4、排序

4.1、全局排序(Order By)

Order By:全局排序,只有一个 Reducer

  • 使用 ORDER BY 子句排序

ASC(ascend): 升序(默认)

DESC(descend): 降序

  • ORDER BY 子句在 SELECT 语句的结尾
  • 案例实操
+ 查询员工信息按工资升序排列



1
hive复制代码hive (default)> select * from emp order by sal;
+ 查询员工信息按工资降序排列
1
hive复制代码hive (default)> select * from emp order by sal desc;

4.2、按照别名排序

按照员工薪水的 2 倍排序

1
hive复制代码hive (default)> select ename, sal*2 twosal from emp order by twosal;

4.3、多个列排序

按照部门和工资升序排序

1
hive复制代码hive (default)> select ename, deptno, sal from emp order by deptno, sal;

4.4、每个 Reduce 内部排序(Sort By)

Sort By:对于大规模的数据集 order by 的效率非常低。在很多情况下,并不需要全局排序,此时可以使用 sort by。

Sort by 为每个 reducer 产生一个排序文件。每个 Reducer 内部进行排序,对全局结果集来说不是排序。

  • 设置 reduce 个数
1
hive复制代码hive (default)> set mapreduce.job.reduces=3;
  • 查看设置 reduce 个数
1
hive复制代码hive (default)> set mapreduce.job.reduces;
  • 根据部门编号降序查看员工信息
1
hive复制代码hive (default)> select * from emp sort by deptno desc;
  • 将查询结果导入到文件中(按照部门编号降序排序)
1
hive复制代码hive (default)> insert overwrite local directory '/opt/module/data/sortby-result' select * from emp sort by deptno desc;

4.5、分区(Distribute By)

Distribute By: 在有些情况下,我们需要控制某个特定行应该到哪个 reducer,通常是为了进行后续的聚集操作。distribute by 子句可以做这件事。distribute by 类似 MR 中 partition(自定义分区),进行分区,结合 sort by 使用。对于 distribute by 进行测试,一定要分配多 reduce 进行处理,否则无法看到 distribute by 的效果。

先按照部门编号分区,再按照员工编号降序排序。

1
2
hive复制代码hive (default)> set mapreduce.job.reduces=3;
hive (default)> insert overwrite local directory '/opt/module/data/distribute-result' select * from emp distribute by deptno sort by empno desc;

distribute by 的分区规则是根据分区字段的 hash 码与 reduce 的个数进行模除后,余数相同的分到一个区。

Hive 要求 DISTRIBUTE BY 语句要写在 SORT BY 语句之前。

4.6、Cluster By

当 distribute by 和 sorts by 字段相同时,可以使用 cluster by 方式。

cluster by 除了具有 distribute by 的功能外还兼具 sort by 的功能。但是排序只能是升序排序,不能指定排序规则为 ASC 或者 DESC。

以下两种写法等价

1
2
hive复制代码hive (default)> select * from emp cluster by deptno;
hive (default)> select * from emp distribute by deptno sort by deptno;

注意:按照部门编号分区,不一定就是固定死的数值,可以是 20 号和 30 号部门分到一个分区里面去。

三、友情链接

大数据Hive学习之旅第二篇

大数据Hive学习之旅第一篇

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

图解对称与非对称加密算法,轻松学会HTTPS原理 对称加密

发表于 2021-11-21

对称加密

先说一下对称加密,顾名思义,对称加密就是加解密双方的加解密过程是对称的。也就是说加密方用一个秘钥进行加密,解密方也通同一个实现商定好的key取进行解密,加解密过程中,使用同一个秘钥称为对称加密,常见的对称加密算法有:DES、3DES(TripleDES)、AES、RC2、RC4、RC5和Blowfish等

Image.png
这么做看上去没什么问题,只要秘钥不会泄露似乎就不会有安全问题,但是怎么保证秘钥不会被泄露出去?这是一个问题,还有秘钥又要怎么传输给对方?这也是一个问题,线面看三张图,由于通信双反个都需要保存秘钥,当通信网络很大的时候,梅朵一个用户就需要保存很多额外的秘钥。
Image [10].png

Image [11].png

Image [12].png
最致命的是,这些秘钥也只能通过网络进行传输,不可能用过面对面的方式交换秘钥。秘钥的保存也会带来额外的负担

Image [13].png
而且秘钥的传输过程需要进行加密,那么这样便进入了一个死循环。传输信息需要秘钥进行加密,传输秘钥也需要秘钥加密……因此就引出了非对称加密算法,只用对称加密看样子是无法解决这个问题了。

非对称加密

上面对称加密说的是加解密通信双方使用同一个秘钥加解密叫做对称,那么加解密方双使用不同的秘钥进行加解密则为非对称加密。如图,非对称加密过程中,存在一对公钥 (publickey) 和私钥 (privatekey)。

Image [2].png

其中,对数据进行加密的秘钥是对所有人公开的,而对数据解密的秘钥却仅为数据的接收者持有,如图:

Image [3].png
非对称加密算法的保密性好,它消除了最终用户交换密钥的需要。但是加解密速度要远远慢于对称加密,在某些极端情况下,甚至能比对称加密慢上1000倍。有得必有舍。常用的非对称加密算法有:RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)。

数学原理——模运算

大家可能想不到,这个精密的加密算法,其背后的数学原理竟然是模运算,不过和我们日常开发中的模运算还是不太相同哦。这里要理解他的数学原理可能需要一定的数学基础,看不懂的话知道过程和相应的原理就行了,下面举个例子:

33mod73^3 mod 733mod7

计算3的3次方对7取模运算很容易,但是反过来,要计算

3xmod=63^x mod= 63xmod=6
在已经知道结果是6的情况下,要反过来求x的值,就很困难了

Image [17].png

如果数字非常大,再代进去计算就非常困难。因此模运算被称为单向函数(One-Way Function)
公钥加密正是运用了这一特性:

Image [18].png
正向计算密文C很简单

Image [19].png
反向计算出原始数据m却很难

Image [20].png
对这个公式进行变换得到:
Image [21].png
因此,如何选择e和d方便成了加密中关键的问题
著名的欧拉定理:

Image [22].png

Image [23].png
这个算法也正是数学与计算机的一次美妙结合。

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring配置数据源

发表于 2021-11-21

「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」

1.1数据源(连接池)使用

数据源为提高程序性能出现

实例化数据源,初始化连接资源

使用资源时从数据源中获取

使用完毕将连接资源归还给数据源

常见数据源:DBCP C3P0 BoneCP Druid

1.1数据源开发步骤

1导入数据源的坐标和数据库驱动坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
xml复制代码 <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.47</version>
      </dependency>
      <dependency>
          <groupId>c3p0</groupId>
          <artifactId>c3p0</artifactId>
          <version>0.9.1.2</version>
      </dependency>
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.1.10</version>
      </dependency>
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
      </dependency>

2 创建数据源对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
java复制代码   @Test
//测试手动创建 c3p0数据源
   public void dataSourceTest() throws Exception {
       ComboPooledDataSource dataSource = new ComboPooledDataSource();
       dataSource.setDriverClass("com.mysql.jdbc.Driver");
       dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
       dataSource.setUser("root");
       dataSource.setPassword("root");
       Connection connection = dataSource.getConnection();
       System.out.println(connection);
       connection.close();
  }
   @Test
   public void druidTest() throws Exception{
       DruidDataSource druidDataSource = new DruidDataSource();
       druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
       druidDataSource.setUrl("jdbc:mysql://localhost:3306/test");
       druidDataSource.setUsername("root");
       druidDataSource.setPassword("root");
       DruidPooledConnection connection = druidDataSource.getConnection();
       System.out.println(connection);
       connection.close();
  }

2 设置数据源基本的连接数据

1.2 抽取jdbc.properties配置文件

1.在resources下新建jdbc.properties配置文件

2.配置

1
2
3
4
properties复制代码jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.user=root
jdbc.password=root

3.读取properties配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码@Test  //测试c3p0数据源(使用properties配置文件)
   public void c3p0Test() throws Exception {
       ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
       String driver = jdbc.getString("jdbc.driver");
       String url = jdbc.getString("jdbc.url");
       String user = jdbc.getString("jdbc.user");
       String password = jdbc.getString("jdbc.password");
       ComboPooledDataSource dataSource = new ComboPooledDataSource();
       dataSource.setDriverClass(driver);
       dataSource.setJdbcUrl(url);
       dataSource.setUser(user);
       dataSource.setPassword(password);
       Connection connection = dataSource.getConnection();
       System.out.println(connection);
       connection.close();
  }

1.3 Spring配置数据源

可以将dataSource的创建权交给Spring容器

1.导入Spring 坐标

2.创建配置文件

1
2
3
4
5
6
xml复制代码      <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
         <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
         <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
         <property name="username" value="root"></property>
         <property name="password" value="root"></property>
     </bean>

3.从Spring容器中获取bean

1
2
3
4
5
6
7
8
9
java复制代码    @Test
   public void testSpringContext() throws Exception {
       ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
       DruidDataSource druidDataSource = app.getBean(DruidDataSource.class);
       DruidPooledConnection connection = druidDataSource.getConnection();
       System.out.println(connection);
       connection.close();
​
  }

1.4 Spring加载properties配置文件

1.配置.xml

image-20210331224846684.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
xml复制代码<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder location="classpath:jdbc.properties"/>
   <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
         <property name="driverClassName" value="${jdbc.driver}"></property>
         <property name="url" value="${jdbc.url}"></property>
         <property name="username" value="${jdbc.user}"></property>
         <property name="password" value="${jdbc.password}"></property>
     </bean>
</beans>

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Python的线程02 多线程展示 什么多线程?

发表于 2021-11-21

「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」

正式的Python专栏第41篇,同学站住,别错过这个从0开始的文章!

前面文章写线程的基础知识,这次我们看看多线程!

什么多线程?

多线程,就是多个独立的运行单位,同时执行同样的事情。

想想一下,文章发布后同时被很多读者阅读,这些读者在做的事情‘阅读’就是一个一个的线程。

多线程就是多个读者同时阅读这篇文章。重点是:同时有多个读者在做阅读这件事情。

如果是多个读者,分时间阅读,最后任意时刻只有一个读者在阅读,虽然是多个读者,但还是单线程。

我们再拿前面分享的代码:关注和点赞。

1
2
3
4
5
6
7
8
python复制代码def dianzan_guanzhu():
now = datetime.datetime.now()
name = "python萌新"
print("%s name:%s" % (now, name))
time.sleep(1)
result = "好棒!" + name + " 关注雷学委,学会了开发知识!"
print("%s result:%s" % (now, result))
return result

我们看看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
python复制代码#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/11/21 12:02 上午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : __init__.py.py
# @Project : hello

import threading
import datetime
import time


def dianzan_guanzhu():
now = datetime.datetime.now()
name = "python萌新"
print("%s name:%s" % (now, name))
time.sleep(1)
result = "好棒!" + name + " 关注雷学委,学会了开发知识!"
print("%s result:%s" % (now, result))
return result


for i in range(3):
mythread = threading.Thread(name="t-" + str(i), target=dianzan_guanzhu)
print("mythread:", mythread)
print("is_alive:", mythread.is_alive())
mythread.start()
print("is_alive:", mythread.is_alive())

Thread类可以传入name指定线程名字。

直接复制运行,这里我们创建了3个线程。

它们依次调用了dianzan_guanzhu函数

下面是运行结果:

屏幕快照 2021-11-23 上午12.24.41.png

这3个线程不同时间打印完成了,但是内容打印乱序了,甚至还串行了。

读者同学可以多运行几次。

获取活跃线程相关数据

threading.active_count函数: 可以获取活跃线程数。

threading.current_thread函数:可以获取活跃线程对象,这样我们可以获取这样获取线程名称:threading.current_thread().getName()。

前文说过了,加上主线程,一共是4个线程。

运行下面代码看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
python复制代码#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/11/21 12:02 上午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : __init__.py.py
# @Project : hello
import random
import threading
import datetime
import time


def dianzan_guanzhu():
thread_name = threading.current_thread().getName()
now = datetime.datetime.now()
print("线程启动了:", thread_name)
name = "python萌新"+thread_name
print("%s - %s name:%s" % (thread_name, now, name))
time.sleep(1)
result = "好棒!" + name + " 关注雷学委,学会了开发知识!"
print("%s - %s result:%s" % (thread_name, now, result))
return result


for i in range(3):
mythread = threading.Thread(name="t-" + str(i), target=dianzan_guanzhu)
print("mythread:", mythread)
print("is_alive:", mythread.is_alive())
mythread.start()
ac = threading.active_count()
print("active_count:", ac)

如果我们把活跃线程数打印,那么等3个线程都start调用了。

加上主线程,最多是4个活跃线程。

屏幕快照 2021-11-23 上午12.26.44.png

今天先展示一下多个线程执行同个任务的代码实现。

明天再细致分享多线程的协调,这个才是噩梦的开始,算是劝退系列了。

对了,喜欢Python的朋友,请关注学委的 Python基础专栏 or Python入门到精通大专栏

持续学习持续开发,我是雷学委!

编程很有趣,关键是把技术搞透彻讲明白。

欢迎关注微信,点赞支持收藏!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring Cloud / Alibaba 微服务架构

发表于 2021-11-21

这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战。

上篇文章创建了网关子模块并介绍了Predicate的test方法,本篇文章将继续举例说明一下Predicate中and、negate、or、isEqual方法,简略解读一下PathRoutePredicateFactory.apply方法的源码。

Predicate 的 and 方法

and方法等同于我们的逻辑与(&&),存在短路特性,需要所有的条件都满足,代码如下:

1
2
3
4
5
6
7
8
9
10
ini复制代码@Test
public void testPredicateAnd() {

Predicate<String> letterLengthLimit = s -> s.length() > 5;
Predicate<String> letterStartWith = s -> s.startsWith("gate");

MICRO_SERVICE.stream().filter(
letterLengthLimit.and(letterStartWith)
).forEach(System.out::println);
}

Predicate 的 or 方法

or等同于我们的逻辑或(||),多个条件只要一个满足即可。 代码如下:

1
2
3
4
5
6
7
8
9
10
ini复制代码@Test
public void testPredicateOr() {

Predicate<String> letterLengthLimit = s -> s.length() > 5;
Predicate<String> letterStartWith = s -> s.startsWith("gate");

MICRO_SERVICE.stream().filter(
letterLengthLimit.or(letterStartWith)
).forEach(System.out::println);
}

Predicate 的 negate 方法

negate 等同于我们的逻辑非(!)。 代码如下:

1
2
3
4
5
6
typescript复制代码@Test
public void testPredicateNegate() {

Predicate<String> letterStartWith = s -> s.startsWith("gate");
MICRO_SERVICE.stream().filter(letterStartWith.negate()).forEach(System.out::println);
}

Predicate 的 isEqual 方法

isEqual 类似于 equals(),区别在于,isEqual 会先判断对象是否为 NULL,不为 NULL 再使用 equals 进行比较。 代码如下:

1
2
3
4
5
scss复制代码@Test
public void testPredicateIsEqual() {
Predicate<String> equalGateway = s -> Predicate.isEqual("gateway").test(s);
MICRO_SERVICE.stream().filter(equalGateway).forEach(System.out::println);
}

PathRoutePredicateFactory.apply方法的源码

以上就大体介绍完了Predicate的方法,可以查看PathRoutePredicateFactory.apply方法的源码,apply方法最终是返回了一个new GatewayPredicate(),它继承了java8的Predicate,里面就有实现test方法。

先通过parsePath方法去获取到当前请求的路径信息,然后再去判断一下这个路径信息与我们传进来的配置是否相关,通过filter进行一个matches正则表达式的判断是否匹配,然后用findFirst返回第一个满足条件的。isPresent方法是判断查询的类对象是否存在。大家也可以去了解一下Java8引入的一个类————Optional,它的存在很好地解决了空指针异常的问题。

以下是两个比较常用的方法:

1、ofNullable

1
2
3
4
5
6
7
8
9
10
11
12
php复制代码/**
* Returns an {@code Optional} describing the specified value, if non-null,
* otherwise returns an empty {@code Optional}.
*
* @param <T> the class of the value
* @param value the possibly-null value to describe
* @return an {@code Optional} with a present value if the specified value
* is non-null, otherwise an empty {@code Optional}
*/
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}

2、orElse

1
2
3
4
5
6
7
8
9
10
typescript复制代码/**
* Return the value if present, otherwise return {@code other}.
*
* @param other the value to be returned if there is no value present, may
* be null
* @return the value, if present, otherwise {@code other}
*/
public T orElse(T other) {
return value != null ? value : other;
}

匹配就代表当前的请求被路由到当前定义的route中,否则就没有匹配再去寻找完成其他匹配。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
arduino复制代码public Predicate<ServerWebExchange> apply(PathRoutePredicateFactory.Config config) {
final ArrayList<PathPattern> pathPatterns = new ArrayList();
synchronized(this.pathPatternParser) {
this.pathPatternParser.setMatchOptionalTrailingSeparator(config.isMatchOptionalTrailingSeparator());
config.getPatterns().forEach((pattern) -> {
PathPattern pathPattern = this.pathPatternParser.parse(pattern);
pathPatterns.add(pathPattern);
});
}

return new GatewayPredicate() {
public boolean test(ServerWebExchange exchange) {
PathContainer path = PathContainer.parsePath(exchange.getRequest().getURI().getRawPath());
Optional<PathPattern> optionalPathPattern = pathPatterns.stream().filter((pattern) -> {
return pattern.matches(path);
}).findFirst();
if (optionalPathPattern.isPresent()) {
PathPattern pathPattern = (PathPattern)optionalPathPattern.get();
PathRoutePredicateFactory.traceMatch("Pattern", pathPattern.getPatternString(), path, true);
PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);
ServerWebExchangeUtils.putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
return true;
} else {
PathRoutePredicateFactory.traceMatch("Pattern", config.getPatterns(), path, false);
return false;
}
}

public String toString() {
return String.format("Paths: %s, match trailing slash: %b", config.getPatterns(), config.isMatchOptionalTrailingSeparator());
}
};
}

那么对于Predicate我们就介绍到这边,下一篇文章我们将介绍一下集成Alibaba Nacos实现动态路由配置。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…244245246…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%