文章字数:52,阅读全文大约需要1分钟
相比于
@Value
这个方法更加简便一点
person.properties
1 | person: |
1 | // 1. 引入配置文件 |
文章字数:52,阅读全文大约需要1分钟
相比于
@Value
这个方法更加简便一点
person.properties
1 | person: |
1 | // 1. 引入配置文件 |
文章字数:548,阅读全文大约需要2分钟
归纳自原文。
从文件读取信息并发送到其它服务器的过程中普通文件读取和零拷贝的区别
File.read(file, buf, len)
读取文件内容到缓冲区buf
里Socket.send(socket, buf, len)
发送数据看上去数据只经过两次复制: 文件->buf->socket的缓冲区
其实底层步骤是
read()
方法,DMA(direct memory access 直接内存存取)
会现将数据存储到内核空间的读取缓冲区。read()
方法调用返回时,因为应用程序需要操作此数据(即赋值给buf
),因此触发了一次上下文切换(内核态->用户态)。数据被拷贝到了用户地址空间。(cpu
需要参与操作)send()
方法,此时又触发了一次上下文切换(用户态->内核态)。buf
里的数据被拷贝到与目标套接字相关的内核空间缓冲区,此操作需要cpu
参与send()
方法调用返回前会进行最后一次拷贝,由DMA(direct memory access 直接内存存取)
将数据从缓冲区传到协议引擎进行发送。也就是总共进行了四次拷贝操作
dma->cpu->cpu->dma
传统方式中cpu
的两次操作其实是多余的。我们只是发送数据,不需要对于数据进行操作。所以无需进入用户态,直接在内核态进行数据转移即可。
FileChannel
类的transferTo()
可以实现将在两个内核缓冲区中搭建一个传递通道,可以将传统方式的两次cpu
操作转换成一次cpu
操作,即transferTo()
。
从操作系统角度来看,数据只在内核空间内传递就已经算是零拷贝。
内核需要复制的原因是因为通用硬件DMA
访问需要连续的内存空间(因此需要缓冲区)。 但是,如果硬件支持scatter-and-gather
,这是可以避免的。
即内部会自动将内核的数据之间发送给套接字引擎。即内核区域只存在一份数据。
文章字数:2051,阅读全文大约需要8分钟
Java.Util.Concurrent是JDK1.5之后推出的java并发工具包。其中有很多多线程工具类,能有效减少竞争条件,减少死锁进程。
线程池是一组可复用的线程集合。由处理线程+任务队列组成。
涉及对象信息
Executor
: 线程池中Runnable
任务执行者。ExecutorService
: 管理线程池对象,能够把Runnable
和Callable
交到线程池中执行Executors
: 此类的静态方法能够生成不同的线程池,并返回ExecutorService
用于管理。常用程池类型
可以使用ThreadPoolExecutor 的构造函数创建属性细节不同的线程池,一下为已经定义好的线程池
newFixedThreadPool
: 固定大小的线程池,共享的无界队列管理任务。关闭前发生异常导致线程终止,则会使用新线程代替。(如果需要)
newCachedThreadPool
: 无界线程池,当需要线程使用但是线程池内无线程,则会新建一个线程并加入线程池。60s未用的线程会被移除。
newSingleThreadExecutor
: 无界队列,单个执行线程。如果关闭前因为异常线程被迫结束,后续需要线程时会创建新的线程并替换不可用的。
ThreadPoolExecutor
: 这个类的构造方法可以生成自定义配置的线程池,可以设置最大线程数
,最小线程数
,空闲线程keepAlive时间
。
以上线程池生成后返回的都是ExecutorService
对象
ExecutorService.submit()
: 方法可以提交任务给线程池
ExecutorService.shutdown()
: 此方法可以结束线程池
可以初始化任意数量的许可。
acquire()
方法会拿走一个许可,如果没有许可了则阻塞。release()
方法可以释放当前线程拿走的许可。限制同一时间可以同时执行的线程数量
10个人抢2个位置
1 | import java.util.concurrent.ExecutorService; |
一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。
ReentrantLock
将由最近成功获得锁定,并且还没有释放该锁定的线程所拥有。
当锁定没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁定并返回。
如果当前线程已经拥有该锁定,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。
此类的构造方法接受一个可选的公平参数。当设置为 true时,在多个线程的争用下,这些锁定倾向于将访问权授予等待时间最长的线程。否则此锁定将无法保证任何特定访问顺序。
与采用默认设置(使用不公平锁定)相比,使用公平锁定的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁定和保证锁定分配的均衡性时差异较小。
不过要注意的是,公平锁定不能保证线程调度的公平性。因此,使用公平锁定的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁定时。还要注意的是,未定时的 tryLock 方法并没有使用公平设置。因为即使其他线程正在等待,只要该锁定是可用的,此方法就可以获得成功。
基本使用
1 | class X { |
初始化锁,指定一个数量。
begin.await()
会阻塞线程,begin.countDown();
每次计数-1
,直到为0阻塞的线程才会释放
锁初始化时指定几个
await()
之后await()
方法才会释放当前线程
例子
1 | import java.text.SimpleDateFormat; |
支持两个附加操作的 Queue,这两个操作是:检索元素时等待队列变为非空,以及存储元素时等待空间变得可用。
支持两个附加操作的 Queue,这两个操作是:检索元素时等待队列变为非空,以及存储元素时等待空间变得可用。
BlockingQueue 不接受 null 元素。试图 add
、put
或 offer
一个 null 元素时,某些实现会抛出 NullPointerException。null 被用作指示 poll 操作失败的警戒值。
BlockingQueue 可以是限定容量的。它在任意给定时间都可以有一个 remainingCapacity,超出此容量,便无法无阻塞地 put 额外的元素。
没有任何内部容量约束的 BlockingQueue 总是报告 Integer.MAX_VALUE 的剩余容量。
5.BlockingQueue 实现主要用于生产者-使用者队列,但它另外还支持 Collection 接口。因此,举例来说,使用 remove(x) 从队列中移除任意一个元素是有可能的。(最好不要使用这些操作)
然而,这种操作通常不会有效执行,只能有计划地偶尔使用
,比如在取消排队信息时。
BlockingQueue 实现是线程安全的。所有排队方法都可以使用内部锁定或其他形式的并发控制来自动达到它们的目的。
然而,大量的 Collection 操作(addAll、containsAll、retainAll 和 removeAll)没有必要自动执行,除非在实现中特别说明。
因此,举例来说,在只添加了 c 中的一些元素后,addAll(c) 有可能失败(抛出一个异常)。
BlockingQueue 实质上不 支持使用任何一种“close”或“shutdown”操作来指示不再添加任何项。
这种功能的需求和使用有依赖于实现的倾向。例如,一种常用的策略是:对于生产者,插入特殊的 end-of-stream 或 poison 对象,并根据使用者获取这些对象的时间来对它们进行解释。
1 | import java.util.concurrent.BlockingQueue; |
相比于一个个去获取线程的结果
Future.get()
会造成线程阻塞,消耗时间。循环CompletionService
就是一个保存已经完成的线程的结果Future
的BlockingQueue
,take()
方法就会从中取出一个。
循环executorCompletionService.take().get()
就能得到结果
1 | import java.util.concurrent.Callable; |
执行线程后返回的
Future
代表线程返回的结果,用于检测异步线程是否结束,如果结束获取返回值,没结束可以阻塞线程直到有结果。
1 | import java.util.concurrent.Callable; |
创建延时任务,取消任务
1 | import static java.util.concurrent.TimeUnit.SECONDS; |
文章字数:827,阅读全文大约需要3分钟
1 | <dependency> |
1 | package org.example.util; |
文章字数:1467,阅读全文大约需要5分钟
java
用ssl
加密方式连接mqtt
服务器。其它ssl
加密的也可以参考,SSLSocketFactory
获取部分都是一样的。踩了很多坑,根据生成工具不同(openssl
和keytool
)以及秘钥文件编码不同有若干种方法。这里把自己遇到的所有情况都统一记录一下。
不加密的连接方式之前有写过,就不赘述了,这里列出不同的地方
1 | mqttClient = new MqttClient(host, clientId, new MemoryPersistence()); |
SSLSocketFactory获取方式有两种:
CA
证书、客户端证书、客户端私钥、私钥密码 获取(使用openssl
生成的,keytool
能生成证书,但是不能直接导出秘钥文件)keystore
和truststore
获取(通过keytool
生成的)读取证书和秘钥也有两种方式(证书获取的方式)
bcpkix-jdk15on
包提供的方法,需要引包pem
秘钥文件,需要先把文件PKCS8
编码一下。(编码方法在openssl
的文章里)稍微解释一下上面的两种方式
CA
证书是用来验证服务端发过来的证书,因为这里是双向认证,所以需要CA
证书来认证服务端发过来的是否是合法证书。CA
证书签发,这样服务端那边才能用CA
证书验证合法)openssl
生成私钥的时候设置的密码(具体生成方式之前的文章有)keystore
和truststore
keystore
是用jdk
自带的工具keytool
生成的秘钥和证书管理库,用来保存自己的秘钥和证书。需要用keytool
生成并导入客户端的证书和秘钥。具体使用之前有文章可以参考。truststore
本质也是keystore
,只是里面存的是受信的证书。用来验证服务端证书是否可信,将CA
导入即可keystore
和truststore
验证,只不过导入的步骤用代码实现了,第二种方式使用命令实现的。1 | <dependency> |
1 |
|
pem
秘钥文件pkcs8
编码1 | openssl pkcs8 -topk8 -in client.private.pem -out pkcs8.client.private.pem -nocrypt |
1 | /** |
发现项目中有生成好的p12
证书,可以直接使用。这里再追加一种p12
证书和CA
证书验证的方式
1 | /** |
1 | // 自定义一个不验证的TrustManager 即可 |
1 | final TrustManager trustManager = new X509ExtendedTrustManager(){ |
文章字数:492,阅读全文大约需要1分钟
1 | import java.awt.Color; |
1 | response.setHeader("Pragma", "No-cache"); |
文章字数:252,阅读全文大约需要1分钟
1 |
|
1 |
|
1 | import java.io.BufferedReader; |
1 |
|
文章字数:1192,阅读全文大约需要4分钟
Logback是由log4j创始人设计的另一个开源日志。
springBoot
自带logback的依赖1 | # 控制台日志打印debug级别的日志(默认不打印) |
1 | <?xml version="1.0" encoding="UTF-8"?> |
1 | private static Logger logger = LoggerFactory.getLogger(demo.class);//当前类的class,方便定位 |
文章字数:1197,阅读全文大约需要4分钟
常用插件有三种,对应三个配置
maven-jar-plugin
: maven 默认打包插件,用来创建 project jarmaven-shade-plugin
: 打可执行包,executable(fat) jarmaven-assembly-plugin
: 支持自定义打包方式打包前需要先clean
一下,重新加载依赖
maven-jar-plugin
配置
1 | <plugin> |
maven-assembly-plugin
1 | <plugin> |
双击idea maven
菜单栏的package
或者直接命令执行mvn:package
生成两个包(可执行jar和项目压缩包)
包含两个文件:
pom.xml整体的配置
package.xml包含在pom.xml中,用于指定assembly装配时的配置
pom.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
package.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
文章字数:2100,阅读全文大约需要8分钟
nginx
由maxter
和worker
线程组成,master
接受外部信号,并发送给各个worker
线程。监控worker
线程的状态,重启异常结束的worker
./nginx -s stop
停止./nginx -s quit
退出./nginx -s reload
重新加载nginx.conf
1 | main //全局设置 |
1 | #user www; //work进程的用户权限 |
名称 | 含义 |
---|---|
$remote_addr | 客户端ip |
$remote_user | 远程客户端的用户名(一般是‘-’) |
$time_local | 访问时间和时区 |
$request | url及请求方法 |
$status | 响应码 |
$body_bytes_sent | 给客户的发送的文件主题内容字节数 |
$http_user_agent | 用户使用的代理(一般是浏览器) |
$http_x_forwarded_for | 可以记录客户端ip |
$http_referer | 用户从那个链接过来的 |
优先级 | 表达式 | 说明 |
---|---|---|
1 | location =/ | 精准匹配/ |
2 | location /xxx | 开头是/xxx,正常匹配(^~) |
2 | location ^~/static/ | ^~ 正常匹配,以/static/ 开头 |
3 | location ~ /a | 正则匹配,严格匹配,区分大小写 |
3 | location ~* /a | 不区分大小写正则 |
root
转,整个path1+path2alias
只转path2
proxy_pass
,如果port后面有/则只传path2,没有/则传path1 + path2response
根据传过来的信息,生成内容,返回页面echo
内容生成的命令,echo信息到response (需要echo-nginx-module第三方插件)rewrite regex replacement [flag]
regex
正则,^/
代表总是匹配replacement
替换值,新值flag
处理标志,可选break
内部转发,替换新值,然后走后续的执行阶段last
内部转发,用替换值重新走location,警惕死循环redirect
301重定向permanent
302重定向flag
的时候最后一个rewrite会覆盖之前的,有flag
则第一个生效location [=|~|~*|^~] /uri/ { … }
用法1 | server{ |
1 | server { |
1 | server{ |
1 | location /xxx.png { |
1 | location xxx { |
Accept-Encoding: gzip, deflate
Content-Encoding: gzip
的返回给浏览器,表示当前是用什么方式压缩的。1 | location xx { |
conf文件
1 | ssl_certificate /xxx/xxx/xx.crt; |
1 | server { |
keepalive
解决高可用问题lvs
思想多个Nginx配置keepalive,就会生成一个虚拟网关(固定ip),虚拟网关按照策略将流量分发给各个Nginx,虚拟网关不是真实网关所以不会挂掉。
./configure --prefix=/data/program/keepalived --sysconf=/etc/
指定文件位置(prefix)和配置文件位置(sysconf)make
执行安装keepalived.conf
是配置文件1 | global_defs { |
chk_http_port.sh内容
1 | #!/bin/bash |