常用垃圾回收器

  • CMS
  • G1
  • ZGC

通过如下命令,可以查看垃圾回收器的使用情况

1
$ java -XX:+PrintCommandLineFlags -version

jdk1.8 如下:

1
2
3
4
5
6
7
8
9
10
-XX:InitialHeapSize=534944128
-XX:MaxHeapSize=8559106048
-XX:+PrintCommandLineFlags
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:-UseLargePagesIndividualAllocation
-XX:+UseParallelGC
openjdk version "1.8.0_181-1-redhat"
OpenJDK Runtime Environment (build 1.8.0_181-1-redhat-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)

jdk1.8 -XX:+UseParallelGC 默认使用的是 Parallel 垃圾回收器

jdk11 如下:

1
2
3
4
-XX:G1ConcRefinementThreads=10 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=534944128 -XX:MaxHeapSize=8559106048 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC -XX:-UseLargePagesIndividualAllocation
openjdk version "11.0.6" 2020-01-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.6+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.6+10, mixed mode)

jdk11 -XX:+UseG1GC默认使用的垃圾回收器是G1

理解 JVM 垃圾回收有用的资源

  1. 新一代垃圾回收器 ZGC 的探索与实践
  2. JVM 系列–还不会选择合适的垃圾收集器?
  3. Java——七种垃圾收集器+JDK11 最新 ZGC
  4. JVM 调优

jvm 启动参数

java 启动参数共分为三类;
其一是标准参数(-),所有的 JVM 实现都必须实现这些参数的功能,而且向后兼容;
其二是非标准参数(-X),默认 jvm 实现这些参数的功能,但是并不保证所有 jvm 实现都满足,且不保证向后兼容;
其三是非 Stable 参数(-XX),此类参数各个 jvm 实现会有所不同,将来可能会随时取消,需要慎重使用;

常用非标准参数

通过 java -X 可以查看支持的非标准参数

1
2
3
4
-Xms6g //最小堆
-Xmx6g //最大堆
-Xss1m //线程栈的大小
-Xmn200m //设置年轻代大小为200M

常用非 Stable 参数

通过java -XX:+PrintFlagsFinal -version可以查看支持的非 Stable 参数

  • -XX:+<option>:代表启用 true
  • -XX:-<option>:代表禁用 false
1
2
-XX:+PrintGC
-XX:+PrintGCDetails

G1

G1 全称为 Garbage-First,意为垃圾优先,哪一块的垃圾最多就优先清理它。

常用参数

1
2
-XX:+UseG1GC //使用G1回收器
-XX:MaxGCPauseMillis=20 // 最大暂停时间为20ms 越小意味着堆小,回收频繁

ZGC

ZGC(The Z Garbage Collector)是 JDK 11 中推出的一款低延迟垃圾回收器,它的设计目标包括:

  • 停顿时间不超过 10ms;
  • 停顿时间不会随着堆的大小,或者活跃对象的大小而增加;
  • 支持 8MB~4TB 级别的堆(未来支持 16TB)。
1
2
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
-XX:ConcGCThreads=2 -XX:ParallelGCThreads=6

超低延迟(TP999 < 20ms)和高延迟(TP999 > 200ms)服务收益不大,吞吐量优先的场景,ZGC 可能并不适合。

更多信息参考:新一代垃圾回收器 ZGC 的探索与实践

jvm 性能查看工具

jstat

利用 jvm 内建的指令对 java 应用程序的资源和性能进行实时的命令行监控,包括针对 heap size 和垃圾回收情况的监控

1
2
3
4
$ jstat -gc 14724 1000 2 //间隔1s,统计两次
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
19968.0 22528.0 0.0 0.0 429056.0 185319.3 395264.0 33163.2 59160.0 55725.5 7808.0 7049.8 8 0.063 3 0.156 0.219
19968.0 22528.0 0.0 0.0 429056.0 185855.6 395264.0 33163.2 59160.0 55725.5 7808.0 7049.8 8 0.063 3 0.156 0.219

参数含义:
S0C:年轻代的第一个 survicor(幸存区)的容量(字节)
S1C:年轻代的第二个 survicor(幸存区)的容量(字节)
S0U:年轻代的第一个 survicor(幸存区)已经使用的空间(字节)
S1U:年轻代的第二个 survicor(幸存区)已经使用的空间(字节)
EC:年轻代中的 Eden(伊甸园)的容量
EU:年轻代中的 Een(伊甸园)已经使用的容量
OC:年老代的容量
OU:年老代已经使用的空间
MC:metadata 的容量
MU:metadata 已经使用的空间
CCSC:压缩类空间的大小
CCSU:压缩类空间的使用大小
YG:从应用程序启动到采样时年轻代中 gc 次数
YGCT:从应用程序启动到采样时年轻代中 gc 所用时间(s)
FGC:从应用程序启动到采样时年老代 gc 次数
FGCT:从应用程序启动到采样时年老代(全 gc)gc 所用时间(s)
GC: 从应用程序启动到采样时 gc 用的总时间(s)

jmap

查看 jvm 的内存分配情况

1
2
3
$ jmap -heap 14724
$ jmap -dump:format=b,file=D:\test\heap.hprof 14724
$ jmap -histo:live 14724 //只统计活的对象数量

jstack

查看 java 程序的线程运行情况

1
$ jstack 14724

jinfo

1
2
$ jinfo 14724 //查看jvm系统详细参数及环境变量
$ jinfo -flag MaxHeapSize 14724 //查看最大堆内存

最佳实践

输出 GC 日志

jdk11

1
java -Xlog:gc*:file=gc.log,filecount=10,filesize=10m

jdk8

1
-XX:+PrintGCDetails -Xloggc:<PATH_TO_GC_LOG_FILE>

dump

1
2
3
#出现 OOM 时生成堆 dump:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/jvmlogs/

手动 dump,拷贝压缩文件,使用 MAT 来进行分析

1
jmap -dump:format=b,file=/temp/kafka-streams/logs/dump.log 1

《如何成为学习高手》笔记

作者从5个方面分享了如何更好的学习,成为学习高手,花费一天时间走马观花,只是在个别章节里收益匪浅,按照文章的划分总结一下自己的收获。

底层思维

告别伪勤奋

1、花大量时间做容易的环节。

解决思路是:在你“踮一踮脚能够够得到”的层面去学习、去做事,只有去完成那些让你感到需要思考、需要克服困难的任务,才称得上“真勤奋”。

2、从来不去检测自己获得了什么。

解决思路是:学的东西,要立刻检测,检测自己有没有记住,检测自己是不是会做题。对于真正的勤奋来说,再功利主义也不为过。你要看到反馈,你要立刻看到反馈。一切不以检测和输出为目的的努力,都是耍流氓。

3、只是埋头苦学,从来不总结规律。

解决思路是:学习这件事,不仅包括对知识的学习,还包括对学习方法的学习。你要学会观察、分析、总结自己是如何学习的,对方法进行针对性的调整和完善,并且有意识地指导自己接下来的努力方向。

4、道德许可效应让你原地徘徊,沉迷于已经努力的幻觉之中。

解决思路是:将你想做的事情最简化,去掉所有花里胡哨的,不搞形式主义,直接开始。

掌握考试思维:成为一个特别会考试的人

1、考试,最重要的是“输出”,需要主动回想知识点,把它们从脑袋里调取出来,针对问题给出答案。你要学会用考试的思维去学习。

2、你要去研究考试题,包括它的出题范围和出题思路,去分析考试时,题目要求你怎样回忆知识点、怎样运用知识点。然后,据此决定学习的时候,如何学,以及学什么。

3、在学习的过程中,做到即用即学和即学即用,才能对知识点有透彻的理解和记忆,并且在考试中,做到更为高效准确地调取,以及运用。

4、检索式练习”是最为有效的学习方式,它是指通过提问和主动回想的方式,去巩固知识点。你在回想知识点的时候,所付出的认知努力越大,学习和记忆的效果就越好。

阅读全文 »

Elasticsearch在EventHub项目中的实战应用

前言

Event Hub是一个高度可缩放、分布式、基于时间序列的事件中心,能够实时的处理流式事件并进行告警和提醒。

Event Hub作为Newegg事件信息中枢,产品化新蛋各产品资源及平台底层基础设施服务生命周期与运转中的重要事件信息,并构建完善的事件消费渠道与流程,支撑线上监控与运维。

Event Hub产品化提供的事件信息,由Newegg内部各产品模块与底层基础设施服务获取,经过聚合,判定和收敛再最终呈现。信息源来自各模块底层的系统日志与监控项,保障客户透传客户的信息准确性与价值。关于Event Hub更详细介绍,请查看Newegg的事件中心之Event Hub

为了实现事件的多维度查询,事件的追溯性,我们将事件存储在elasticsearch中。我们设计了两个index:event_hub_currentevent_hub_history

事件是具有时间序列特征的,我们会在event_hub_historyindex中写入每条事件信息,通过事件唯一ID,在event_hub_current中更新事件信息,随着时间的流逝,大多数情况我们关注的结果。如果你了解过实时计算,可以参照Stream和Table。一个是流式的,一个是结果。

阅读全文 »

Elasticsearch速览学习笔记

声明:本来是打算春节期间在官网学习,温故一下相关知识,。无意间发现铭毅天下Elasticsearch文章很全,对快速了解一些知识点,很有帮助。尤其是知识星球里的内容,奈何收费。别人辛勤劳动成果,当然无可厚非。我就借鉴了他的知识图谱,确定自己的学习点,再结合官网文档和他的公众号。引用的地方已经标注,特此声明。

如果没钱没时间,收藏我这边篇笔记就好。如果你舍得花钱,有充足的时间,推荐去购买一下他的知识星球。

基本知识点

分词必知

当字段类型为text的时候会进行分词,默认分词器是standard

两个地方会出现分词,一个是indexing,一个是search。文档索引的时候肯定会分词,search时候针对search查询语句内容分析。默认的话是两者保持一致。某些场景下可以在search中设置分词

分词器分为三个部分:Tokenizers (分词)、Token filters(修改分词例如小写,删除分词,增加分词)、Character filters(用在分词前去除字符)

Test分词器

1
2
3
4
5
POST _analyze
{
"analyzer":"standard",
"text": "The quick brown fox. 1"
}

排查当前index分词的结果

1
2
3
4
5
GET kibana_sample_data_logs/_analyze
{
"field": "my_text",
"text": "Is this déjà vu?"
}

配置一个分析器,去掉英文修饰词

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
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"std_english": {
"type": "standard",
"stopwords": "_english_"
}
}
}
},
"mappings": {
"properties": {
"my_text": {
"type": "text",
"analyzer": "standard",
"fields": {
"english": {
"type": "text",
"analyzer": "std_english"
}
}
}
}
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
POST my-index-000001/_analyze
{
"field": "my_text",
"text": "The old brown cow"
}
// [ the, old, brown, cow ]
POST my-index-000001/_analyze
{
"field": "my_text.english",
"text": "The old brown cow"
}
// [ old, brown, cow ]
阅读全文 »

春雷行动-前端技术之CSS必备知识

前言

这篇文章不是教程,也不是让读者能够学会和掌握CSS,而是我的学习笔记。文章的目的是让我实践,系统的了解一下一些必备知识,仅此而已。

如果你需要这方面的知识详细学习的话,推荐你看参考的链接。内容全部来源于此。

正文

五种经典布局

1.空间居中

1
2
3
4
.parent{
display: grid;
place-items: center;
}

核心代码是place-items属性,那个是它的简写形式place-items: <align-items> <justify-items>; 两者相同的话可以省略。

左上角 place-items: start;

右下角place-items: end;

2.并列式布局

并列式布局就是多个项目并列。容器设置成 Flex 布局,内容居中(justify-content)可换行(flex-wrap

1
2
3
4
5
.container{
display: flex;
justify-content: center;
flex-wrap: wrap;
}

项目上面只用一行flex

1
2
3
.col{
flex: 1 1 300px;
}

flex属性是flex-growflex-shrinkflex-basis这三个属性的简写形式 flex: <flex-grow> <flex-shrink> <flex-basis>;

  • flex-basis:项目的初始宽度。
  • flex-grow:指定如果有多余宽度,项目是否可以扩大。
  • flex-shrink:指定如果宽度不足,项目是否可以缩小。

flex: 0 1 300px; 表示项目初始宽度是300,宽度不够的话可以缩小,但是不可以扩大。flex: 1 1 300px;,表示项目始终会占满所有宽度。

阅读全文 »

Newegg的事件中心之Event Hub

万事皆事件

事件是信息的一种承载媒介,描述特定对象某一瞬间的非持续性变化,与唯一时刻和唯一对象关联。例如:某台计算机从运行状态变更为关机,程序运行开始和结束,办公大楼停电等。事件是对象在两个不同状态中的变更瞬间的记录。

对于事件,我们关注时间点,什么事件,什么状态。在企业中存在大量的事件,系统事件,监控事件,业务事件等,通过对事件的治理和挖掘,能够发现很多价值,解决切实的痛点。基于以上思考,我们构建了Event Hub。

Event Hub简介

Event Hub是一个高度可缩放、分布式、基于时间序列的事件中心,能够实时的处理流式事件并进行告警和提醒。

Event Hub作为Newegg事件信息中枢,产品化新蛋各产品资源及平台底层基础设施服务生命周期与运转中的重要事件信息,并构建完善的事件消费渠道与流程,支撑线上监控与运维。

Event Hub产品化提供的事件信息,由Newegg内部各产品模块与底层基础设施服务获取,经过聚合,判定和收敛再最终呈现。信息源来自各模块底层的系统日志与监控项,保障客户透传客户的信息准确性与价值。

目前应用场景

企业级监控/告警平台

在Event Hub之前公司监控存在一些问题:

  • 告警不可追溯
  • 告警不可指派
  • 状态可变更很弱
  • 监控信息可视化很弱
  • 没有更好的统计报表

为了解决以上问题,治理企业级监控问题,我们在Event Hub中基于现存的问题,构建了企业级监控平台。俗话说,先挑软柿子捏。

作为企业级监控平台,Event Hub 立足于能够助力发现、定位、解决问题,保障系统与服务整体的稳定与性能。引入事件作为监控的信息载体,能更准确与直接描述资源底层基础设施服务的运行状态,助力更高效发现、定位从而解决问题。致力于提交信息描述准确性,减少延迟,传递更多的信息,完善监控信息维度,使用通用事件引擎对告警类信息加工处理,尽而告警。

1611048694361

阅读全文 »

Java NIO扫盲篇

概述

对于java世界,想了解高性能网络编程。那么就必须了解NIO,和常用的网络编程框架,以及高性能的网络编程模式。这里面缺一不可,本篇只是管中窥豹,将自己学习的过程和笔记记录下来,希望给工作中很难接触这方面的同学带来一点点帮助!

Java NIO是什么?

NIO 是一种同步非阻塞的 I/O 模型,在 Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 ChannelSelectorBuffer 等抽象。

NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它支持面向缓冲的,基于通道的 I/O 操作方法。 NIO 提供了与传统 BIO 模型中的 SocketServerSocket 相对应的 SocketChannelServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。

NIO 的基本流程

通常来说 NIO 中的所有 IO 都是从 Channel(通道) 开始的。

  • 从通道进行数据读取 :创建一个缓冲区,然后请求通道读取数据。
  • 从通道进行数据写入 :创建一个缓冲区,填充数据,并要求通道写入数据。

NIO 核心组件

NIO 包含下面几个核心的组件:

  • Channel(通道)
  • Buffer(缓冲区)
  • Selector(选择器)

通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类), 而 通道 可以用于读、写或者同时用于读写。

如何使用?

内存映射文件 I/O

内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。

下面代码行将文件的前 1024 个字节映射到内存中,map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。因此,可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行映射。

1
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);

这里多说一句,在Kafka源码中索引就大量使用了内存映射文件。

文件NIO

读文件

读取文件涉及三个步骤:(1) 从 FileInputStream 获取 Channel,(2) 创建 Buffer,(3) 将数据从 Channel 读到 Buffer

第一步:获取通道

1
2
FileInputStream fis = new FileInputStream("C:\\demo.txt");
FileChannel fileChannel = fis.getChannel();

然后创建Buffer,这里选择的间接缓冲

1
2
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//ByteBuffer buffer = ByteBuffer.allocateDirect( 1024 ); 直接缓存,速度更快,JVM层面尽肯能避免多次拷贝

最后将数据从 Channel 读到 Buffer

1
fileChannel.read(byteBuffer)

写文件

几乎和读文件一样。

首先从 FileOutputStream 获取一个通道

1
2
FileOutputStream fos = new FileOutputStream("C:\\demo.txt");
FileChannel channel = fos.getChannel();

下一步是创建一个缓冲区并在其中放入一些数据

1
2
3
4
5
6
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
for (int i=0; i<byteBuffer.limit(); ++i) {
buffer.put( byteBuffer.get(i));
}

buffer.flip();

最后一步是写入缓冲区中:

1
channel.write(buffer);

套接字NIO

NIO 实现了 IO 多路复用中的 Reactor 模型

  • 一个线程(Thread)使用一个选择器 Selector 通过轮询的方式去监听多个通道 Channel 上的事件,从而让一个线程就可以处理多个事件。
  • 通过配置监听的通道 Channel 为非阻塞,那么当 Channel 上的 IO 事件还未到达时,就不会进入阻塞状态一直等待,而是继续轮询其它 Channel,找到 IO 事件已经到达的 Channel 执行。

创建选择器

1
Selector selector = Selector.open();

将通道注册到选择器上

1
2
3
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_ACCEPT);

注册的具体事件,主要有以下几类:

  • SelectionKey.OP_CONNECT
  • SelectionKey.OP_ACCEPT
  • SelectionKey.OP_READ
  • SelectionKey.OP_WRITE

监听事件

1
int num = selector.select();

使用 select() 来监听到达的事件,它会一直阻塞直到有至少一个事件到达。

处理事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
while (true) {
int num = selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// ...
} else if (key.isReadable()) {
// ...
}
keyIterator.remove();
}
}

高性能网络编程

Reactor模式

Reactor模式+任务池模式

多Reactor模式

参考

  1. Java NIO

  2. Scalable IO in Java

  3. 《Scalable IO in Java》笔记

只是记录自己不知道的知识点,或者经常容易忘的内容,想知道更多的信息建议看课程或者找我聊天交换技能。在课程的内容基础之上,补充了一些自己知道的知识点。

Kafka的认知

  • 分布式消息引擎平台
  • 分布式实时流式处理平台

早期Kafka社区对Kafka的定位为⼀个分布式、分区化且带备份功能的提交⽇志(Commit Log)服务,近期在官网彻底更改为分布式实时流式处理平台。

Kafka流式处理框架的优势

  • 更容易实现端到端的正确性(Correctness)
  • 轻量型,嵌入式流式计算的定位

避免不必要的Rebalance

  • session.timeout.ms
  • heartbeat.interval.ms
  • max.poll.interval.ms
  • GC参数

session.timout.ms决定了Consumer存活性的时间间隔

heartbeat.interval.ms决定存活心跳发送间隔。

max.poll.interval.ms 它限定了Consumer端应⽤程序两次调⽤poll⽅法的最⼤时间间隔。

消费者TCP管理

消费者实例在KafkaConsumer.poll建立TCP连接,主要分为3类连接:

  1. 确定协调者和获取集群元数据。
  2. 连接协调者,令其执⾏组成员管理操作。
  3. 执⾏实际的消息获取。

第一类连接仅在开始前建立,稍后(第三类创建成功)就会销毁,consumer实例会长期保留2,3类连接。

Consumer实例会长期建立broker数量(分区所在broker数量)+1个连接。

TCP连接的三个时机:

  1. 发起FindCoordinator请求时
  2. 连接协调者时
  3. 消费数据时

何时关闭TCP连接:

  1. ⼿动调⽤KafkaConsumer.close()⽅法
  2. 执⾏Kill命令
  3. Kafka⾃动关闭(是由消费者端参数connection.max.idle.ms控制的,该参数现在的默认值是9分钟)
阅读全文 »

概要

记录线上Zookeeper集群和Kafka集群部署过程,操作系统配置,以及一些参数的设置。给大家部署提供一些宝贵意见和参考。部署一个集群,按照官方社区的文档,很容易就搭建一个集群,但是为了更好的发挥集群的性能,有很多设置是可以避免产生不必要的问题,都是在惨痛的教训中产生的经验。

本文内容来自NeweggConfluent 产线上Kafka cluster运维经验,仅供参考。

Newegg 产线Kafka版本选择Confluent发行版本,版本对照表:

Confluent Platform Apache Kafka
5.5.x 2.5.x

Docker镜像列表:

Service Info
Zookeeper confluentinc/cp-zookeeper:5.5.1
Kafka confluentinc/cp-kafka:5.5.1

Zookeeper Cluster

硬件

内存至少4GB,Zookeeper对swap敏感,应当避免swap。

集群配置

zookeeper节点数据应该为2n+1,n大于0,保持集群中超过一半节点存活。

Confluent docker 镜像参数设置是在ENV(环境变量)中设置,以ZOOKEEPER开头,以_替换.

参数清单

1
2
3
4
5
6
7
8
9
10

- ZOOKEEPER_SERVER_ID=1
- ZOOKEEPER_TICK_TIME=2000
- ZOOKEEPER_CLIENT_PORT=2181
- ZOOKEEPER_INIT_LIMIT=10
- ZOOKEEPER_SYNC_LIMIT=5
- ZOOKEEPER_SERVERS=192.168.0.1:2888:3888;192.168.0.2:2888:3888;192.168.0.3:2888:3888
- ZOOKEEPER_AUTOPURGE_SNAP_RETAIN_COUNT=5
- ZOOKEEPER_AUTOPURGE_PURGE_INTERVAL=10
- KAFKA_HEAP_OPTS=-Xmx4G -Xms4G

ZOOKEEPER_AUTOPURGE_SNAP_RETAIN_COUNT控制快照的数量,ZOOKEEPER_AUTOPURGE_PURGE_INTERVAL控制清除快照的时间间隔,默认不清除,这个很重要。

文件映射

Host volume Container volume
/opt/app/cp-zookeeper-5.5.1/data /var/lib/zookeeper/data
/opt/app/cp-zookeeper-5.5.1/log /var/lib/zookeeper/log
/etc/localtime /etc/localtime
阅读全文 »

前言

本篇文章介绍生成一个自签名SSL证书以及使用Nginx docker 代理一个https服务。

SSL证书验证安全连接,有两种验证模式:

  1. 仅客户端验证服务器的证书,客户端自己不提供证书;
  2. 客户端和服务器都互相验证对方的证书。

显然第二种更安全,一般web采用第一种,比较简单。

创建自签名证书

创建步骤

  1. 创建Key;
  2. 创建签名请求;
  3. 将Key的口令移除;
  4. 用Key签名证书。

创建脚本

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
34
35
#!/bin/sh

# create self-signed server certificate:

read -p "Enter your domain [www.example.com]: " DOMAIN

echo "Create server key..."

openssl genrsa -des3 -out $DOMAIN.key 2048

echo "Create server certificate signing request..."

SUBJECT="/C=US/ST=Mars/L=iTranswarp/O=iTranswarp/OU=iTranswarp/CN=$DOMAIN"

openssl req -new -subj $SUBJECT -key $DOMAIN.key -out $DOMAIN.csr

echo "Remove password..."

mv $DOMAIN.key $DOMAIN.origin.key
openssl rsa -in $DOMAIN.origin.key -out $DOMAIN.key

echo "Sign SSL certificate..."

openssl x509 -req -days 3650 -in $DOMAIN.csr -signkey $DOMAIN.key -out $DOMAIN.crt

echo "TODO:"
echo "Copy $DOMAIN.crt to /etc/nginx/ssl/$DOMAIN.crt"
echo "Copy $DOMAIN.key to /etc/nginx/ssl/$DOMAIN.key"
echo "Add configuration in nginx:"
echo "server {"
echo " ..."
echo " listen 443 ssl;"
echo " ssl_certificate /etc/nginx/ssl/$DOMAIN.crt;"
echo " ssl_certificate_key /etc/nginx/ssl/$DOMAIN.key;"
echo "}"

执行以上脚本会生成4个文件:

核心配置

1
2
3
4
5
6
7
8
9
10
11
 server {
listen 443 ssl;
server_name localhost;

ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;

location / {
proxy_pass http://localhost:8866/;
}
}

完整配置

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
34
35
36
37
38
39
40
41
42
43
events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;

sendfile on;
tcp_nopush on;
keepalive_timeout 65;
gzip on;
include /etc/nginx/conf.d/*.conf;
index index.html index.htm;

server {
listen 80;
server_name localhost;

location / {
proxy_pass http://localhost:8866/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

server {
listen 443 ssl;
server_name localhost;

ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;

location / {
proxy_pass http://localhost:8866/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_cookie_path / "/; httponly; secure; SameSite=NONE";
}
}
}

启动nginx容器

1
docker run --name my-custom-nginx-container -p 443:443 -v "F:\docker-workspace\nginx-demo\nginx.conf:/etc/nginx/nginx.conf:ro" -v "F:\docker-workspace\nginx-demo\www.example.com.crt:/etc/nginx/www.example.com.crt" -v "F:\docker-workspace\nginx-demo\www.example.com.key:/etc/nginx/www.example.com.key" -d nginx

参考

  1. 给Nginx配置一个自签名的SSL证书
0%