Enda Lin

keep foolish, keep sharp


  • Home

  • Categories

  • About

  • Archives

  • Tags

Kubernetes

Posted on 2019-06-06 |
Words count in article 111 | Reading time 1

简介(容器编排工具)

Kubernetes 是Google创建管理的容器集群关系系统,开源,开源实现容器集群的自动化部署、自动扩缩容、维护等功能,其目标是促进完善组件和工具的生态系统,以减轻应用程序在公有云或私有云中运行的负担。

安装kubeadm

kubeadm是kubernetes的集群安装工具,能够快速安装kubernetes集群。

待定

数据结构-树相关

Posted on 2019-06-05 |
Words count in article 514 | Reading time 2

堆排序

堆分为最大堆和最小堆

最大堆

定义:设数组a存放了n个数据元素,数组下标从0开始,如果当数组下标2i+1<n时存在a[i].key>a[2i+1].key>=a[i].key,当数组下标2i+2<n时,有a[i].key>=a[2i+2].key,这样的数据结构称为最大堆。

最大堆的根节点是堆值中最大的数据元素。

对于最大堆,从根结点到每个叶结点的路径,数组元素组成的序列都是递减有序的

通常把堆的根节点称为堆顶元素。

最小堆(与最大堆类似,这里不再累赘)

创建堆

在完全二叉树中,第一个非叶子结点a[i] (i = (n - 2) / 2)

创建思路:从第一个非叶子结点开始,找出a[2i+1]和a[2i+2]的最大值,并与a[i]进行比对,若比a[i]小,则说明以a[i]为更结点的堆已经是最大堆,否则,二者交换。以此类推,直到调整到根节点。

值得注意的是:若左右子节点并非叶子结点,与a[i]的调换可能会引起子节点的一连串调整,这也是值得我们注意的地方

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
/**
* @param data 待排序的数组
* @param n 元素的总个数
* @param h 当前的根节点
*/
void createHeap(int[] data, int n, int h) {
int i = h;
int j = 2 * i + 1;
int temp = data[i];

while (j < n) {

// 寻找左右子节点的最大值
if (j + 1 < n && data[j] < data[j + 1]) j++;

// 对比根节点与左右子节点的最大值
if (temp > data[j]) {
break;
} else {
data[i] = data[j];
i = j;
j = 2 * i + 1;
}
}
data[i] = temp;
}

void initHeap(int[] data, int n) {
for (int i = (n - 2) / 2; i > -1; i--) {
createHeap(data, n, i);
}
}

利用最大堆来进行排序

1
2
3
4
5
6
7
8
9
10
void heapSort(int[] data, int n) {
initHeap(data, n);

for (int i = n - 1;i > -1;i--) {
int temp = data[i];
data[i] = data[0];
data[0] = temp;
createHeap(data, i, 0);
}
}

Nginx

Posted on 2019-06-05 |
Words count in article 976 | Reading time 4

Nginx简介

Nginx是一款高性能Http服务器、反向代理服务器以及电子邮件(IMAP、POP3)代理服务器,能支撑5万并发,CPU、内存等资源消耗非常低,运行稳定。

Nginx应用场景

  • HTTP服务器:Nginx是一个HTTP服务,可以独立提供HTTP服务,可以做网页静态服务器。
  • 虚拟主机:可以实在一台服务器虚拟出多个网站。
  • 反向代理、负载均衡:可以使用Nginx反向代理到多个集群。

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
version: '3.1'
services:
tomcat9090:
image: tomcat
container_name: tomcat9090
restart: always
ports:
- 9090:8080
tomcat9091:
image: tomcat
container_name: tomcat9091
restart: always
ports:
- 9091:8080
nginx:
image: nginx
container_name: nginx
restart: always
ports:
- 80:80
- 9000:9000
volumes:
- ./conf/nginx.conf:/etc/nginx/nginx.conf
- ./web:/usr/share/nginx/wwwroot

虚拟主机

虚拟主机是一种特殊的软硬件技术,它可以将网络上的每一台计算机分成多个虚拟主机,每个虚拟主机可以独立对外提供www服务,这样就实现一台主机对外提供web服务。

通过Nginx可以实现虚拟主机的配置,Nginx支持三种类型的虚拟主机的配置

  • 基于IP的虚拟主机
  • 基于域名的虚拟主机
  • 基于端口的虚拟主机

配置文件

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
# CPU多少核就填多少核,充分利用CPU资源
worker_processes 1;

events {
worker_connections 1024;
}

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

sendfile on;

keepalive_timeout 65;

server {
listen 80;
# 基于 IP
server_name 172.28.7.36;

# 基于域名
# server_name endalin.com;
location / {
root /usr/share/nginx/wwwroot/welcome;
index index.html;
}
}

server {
# 基于端口
# listen 81;
listen 80;
server_name 172.28.7.36;

# 基于域名
# server_name oj.endalin.com;
location / {
root /usr/share/nginx/wwwroot/welcome81;
index index.html;
}
}
}

正向代理

在客户端(浏览器)配置代理服务器,通过代理服务器访问指定网址

反向代理、负载均衡

反向代理服务器架设在服务器端,通过缓存经常被请求的页面来缓解服务器的工作量,将客户机请求转发给内部网络上的目标服务器,并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器和目标主机对外表现为一个服务器。

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
version: '3.1'
services:
tomcat9090:
image: tomcat
container_name: tomcat9090
restart: always
ports:
- 9090:8080
tomcat9091:
image: tomcat
container_name: tomcat9091
restart: always
ports:
- 9091:8080
nginx:
image: nginx
container_name: nginx
restart: always
ports:
- 80:80
- 9000:9000
volumes:
- ./conf/nginx.conf:/etc/nginx/nginx.conf
- ./web:/usr/share/nginx/wwwroot

配置文件

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
worker_processes 1;

events {
worker_connections 1024;
}

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

sendfile on;

keepalive_timeout 65;

upstream tomcatServer{
server 10.42.29.120:9000 weight=10;
server 10.42.29.120:9091 weight=10;
}

server {
listen 80;
server_name 10.42.29.120;
location / {
proxy_pass http://tomcatServer;
index index.html index.jsp;
}
}
}

动静分离

通过location 指定不同的后缀名实现不同的请求转发,通过expires 参数设置,可以使浏览器缓存过期时间,减少与服务器之间的请求和流量。

具体Expires 定义:给一个资源设定一个过期时间,无需服务器去验证,直接通过浏览器自身确定是否过期,不会产生额外的流量。这种方式适合不经常变动的资源。假如我设置了3d,即三天,表示在三天之内访问这个URL,发送一个请求给服务器,对比服务器该文件最后的更新时间,如果更新时间没有发生变化,则不会从服务器中抓取,返回状态码304,如果有修改,则直接从服务器中直接下载,返回状态200

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
worker_processes 1;

events {
worker_connections 1024;
}

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

sendfile on;

keepalive_timeout 65;

upstream tomcatServer{
server 10.60.2.128:9000 weight=10;
server 10.60.2.128:9091 weight=10;
}

server {
listen 80;
server_name 10.60.2.128;

location /welcome81 {
root /usr/share/nginx/wwwroot/;
index index.html;
}

location /image {
root /usr/share/nginx/wwwroot/;
# 列出文件目录
autoindex on;
}
}
}

RabbitMQ

Posted on 2019-06-05 |
Words count in article 564 | Reading time 2

RabbitMQ相关概念

生产者和消费者

  • producer:消息的生产者
  • consumer:消息的消费者

Queue

  • 消息队列:提供了FIFO的处理机制,具有缓存数据的能力
  • 在RabbitMQ中,队列消息可以设置为持久化、临时或自动删除
    • 持久化队列:队列中的消息会在Server本地磁盘存储一份,防止系统挂掉,导致数据丢失。
    • 临时队列:队列中的数据在系统重启后就会丢失。
    • 自动删除的队列:当不存在用户连接到Server,队列中的数据就会被自动删除。

ExChange

类似于交换机,提供消息路由策略。在RabbitMQ中,Producer不是通过信道直接将消息发送给Queue的,而是先发给ExChange,一个ExChange与多个Queue绑定,Producer在传递消息的时候,会传递一个ROUTING_KEY,ExChange会根据这个值按照特定的路由算法,将消息分配给指定的Queue,与Queue一样,ExChange也有持久、临时和自动删除的。

Binding

所谓绑定就是一个特定的Exchange与一个特定的Queue绑定起来。ExChange和Queue的绑定可以是多对多的关系。

Virtual

RabbitMQ的使用过程

  • 客户端连接到消息队列服务器,打开一个Channel
  • 客户端声明一个ExChange,并设置相关属性
  • 客户端声明一个Queue,并设置相关属性
  • 客户端使用Routing Key,在ExChange与Queue之间建立好绑定关系
  • 客户端投递消息到ExChange
  • ExChange接收到消息之后,就根据消息的key和已经绑定好的binging,进行消息路由,将消息投递到一个或多个队列里

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: '3.1'
services:
rabbitmq:
restart: always
image: rabbitmq:management
container_name: rabbitmq
ports:
- 5672:5672
- 15672:15672
environment:
RABBITMQ_DEFAULT_USER: rabbit
RABBITMQ_DEFAULT_PASS: 123456
volumes:
- ./data:/var/lib/rabbitmq

消息提供者

1
2
3
4
5
6
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: rabbit
password: 123456
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

@Configuration
public class RabbitConfiguration {
@Bean
public Queue queue() {
return new Queue("helloRabbit");
}
}

@Component
public class RabbitProvider {

@Autowired
private AmqpTemplate amqpTemplate;

public void send() {
String content = "Hello World " + new Date();
System.out.println("Provider: " + content);
amqpTemplate.convertAndSend("helloRabbit", content);
}
}

消息消费者

1
2
3
4
5
6
7
8
9
@Component
@RabbitListener(queues = "helloRabbit")
public class RabbitConsumer {

@RabbitHandler
public void process(String content) {
System.out.println("Consumer:" + content);
}
}

问题

  • 如何保证消息队列中的数据百分之一百被消费掉

计算机网络基础知识之运输层

Posted on 2019-06-03 |
Words count in article 2k | Reading time 6

OSI 七层模型

TCP/IP 五层模型

应用层

状态码

  • 1XX Informational(信息性状态码) 接收的请求正在处理
  • 2XX Success(成功状态码) 请求正常处理完毕
  • 3XX Redirection(重定向状态码) 需要进行附加操作以完成请求
  • 4XX Client Error(客户端错误状态码) 服务器无法处理请求
  • 5XX Server Error(服务器错误状态码) 服务器处理请求出错

运输层

基本术语

  • 进程:指计算机正在运行的实体
  • 应用进程的相互通信: 一台主机的进程和另一台主机的一个进程交换数据的过程
  • 传输层的复用与分用:复用是指发送方不同的进程可以通过一个传输层协议传送数据, 分用指的是接收方的传输层在剥去报文的首部后能把这些数据正确地交到目的应用进程中。
  • TCP:传输控制协议
  • UDP:用户数据报协议
  • 端口:为的是确认对方机器是哪一个进程在和自己交互
  • 停止等待协议:指发送方每发送完一个分组就停止发送,等待对方确认,在收到确认之后再发送下一个分组。
  • 流量控制:控制对方的发送速率,既要让接收方来得及接收,也不要使网络发送拥塞
  • 拥塞控制:防止过多数据注入网络中,防止路由器或者链路过载。
Read more »

SpringCloud微服务架构实战读书笔记②

Posted on 2019-06-03 |
Words count in article 301 | Reading time 1

使用Feign实现声明式REST调用

为服务消费者整合Feign

  • 添加依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    </dependency>
  • 创建一个Feign接口,并添加@FeignClient注解

    1
    2
    3
    4
    5
    @FeignClient(name="applicationName")
    public interface UserFeignClient {
    @RequestMapping(value = "/{id}")
    public User findById(@PathVatiable("id")Long id);
    }
  • @FeignClient注解是任意一个服务提供方的名称,用以创建Ribbon负载均衡器。

自定义Feign配置

修改接口

1
@FeignClient(name="applicationName", configuration = FeignConfiguration.class)

有一点值得注意的是,本例中的FeignConfiguration类不能包括在主程序上下文的@ComponentScan中,否则该类中的配置信息就会被所有的@FeignClient共享, 如果只想定义某个FeignClient客户端的配置,该点需要特别注意。此处可以配置@ComponentScan不扫描配置类的所在包

手动创建Feign

Feign对继承的支持

Feign对压缩的支持

Feign的日志

使用Feign构造多参数请求

Spring Cloud为Feign添加了Spring MVC的注解支持。

  • 例子一

    1
    2
    3
    4
    5
    @FeignClient(name="applicationName")
    public interface UserFeignClient {
    @GetMapping(value = "/{id}")
    public User findById(@RequestParam Map<String, Object> map);
    }
  • 例子二

    1
    2
    3
    4
    5
    @FeignClient(name="applicationName")
    public interface UserFeignClient {
    @PostMapping(value = "/{id}")
    public User findById(User user);
    }

MySQL索引与锁

Posted on 2019-06-03 |
Words count in article 2k | Reading time 6

MySQL存储引擎的基础知识

  • MySQL的基本存储结构是页,所有记录都存储在页里面。

  • 每个页之间组成的是一个双向链表。
  • 每个数据页里面的记录可以组成一个单向链表。
    • 每个数据页都会为存储在本页里面的数据生成一个页目录,在通过主键查找某条记录时可以使用二分法快速定位
    • 而根据其它列进行检索时,就只能通过遍历的手段

在没有任何索引的的表中,select语句的执行会进行如下两次遍历

  • 遍历双向链表,找到所在页
  • 遍历页内的单链表,找到所在的记录
Read more »

《JAVA并发编程的艺术》读书笔记④

Posted on 2019-06-03 |
Words count in article 792 | Reading time 2

数据依赖性

image.png

如果两个操作之间访问同一个变量,且这两个操作有一个为写操作,此时这两个操作就存在数据依赖性。

编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。

这里所说的数据依赖性针对单个处理器中的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。

as-if-serial语义

as-if-serial意思就是说:不管怎么重排序(编译器和处理器为了提高并行度),单线程程序的执行结果不能改变。

同步程序的顺序一致性效果

image.png

JMM在具体实现的基本方针是:在不改变正确同步的程序执行结果的前提下,尽可能地为编译器和处理器的优化打开方便之门。

JMM不保证对64位的long型和double型变量的写操作具有原子性。因为当JVM在一些32位的处理器上运行的时候,如果对64为数据的写操作要求原子性,会有比较大的开销,所以JVM可能会把一个64位的long/double写操作拆分成两个32位的写操作的来执行,这两个32位的写操作可能会被分配到不同的总线事务中,此时对这个64位的变量的写操作不具有原子性。

未同步程序的执行特性

对于未同步或未正确同步的多线程程序,JMM只提供最小安全性: 线程执行时读取到的值, 要么是之前某个线程写入的值, 要么是默认值(0, NULL, false), JMM保证线程读操作读到的值不会无中生有.

为了实现最小安全性, JVM在堆上分配对象时,首先会对内存空间进行清零, 然后才会在上面分配对象(JVM会同步这两个操作), 因此, 在已清零的内存空间分配对象是,域的默认初始化已经完成了.

锁的内存语义

锁的释放和获取的内存语义

当线程获取锁的时候,JMM会把该线程对应的本地内存置为无效,从而是的被监视器保护的临界区代码必须从主内存中读取共享变量.

对锁释放和锁获取的内存含义做个总结:

  • 线程A释放一个锁, 实质上是线程A向接下来将要获取这个锁的某个线程发出了线程A对共享变量所做出了修改的消息
  • 线程B获取一个锁,实质上是线程B接收了之前某个线程(即A线程)对这个变量所作出的修改的消息
  • 线程A释放锁, 然后线程B获取锁, 这个过程实质上是线程A通过主内存向线程B发送消息.

《JAVA并发编程的艺术》读书笔记③

Posted on 2019-06-02 |
Words count in article 991 | Reading time 3

JAVA内存模型的基础

在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。

  • 在共享内存的并发模型中,线程之间共享程序的公共状态,通过读写内存中的公共状态来进行隐式通信。
  • 在消息传递的并发模型中,线程之间没有公共状态,线程之间必须通过发送消息来显式进行通信。

JAVA的并发采用的是共享内存模型。

JAVA内存模型的抽象结构

  • 在JAVA中,所有实例域、静态域和数组元素都在堆内存中,堆内存在线程之间共享。
  • 局部变量、方法定义参数和异常处理函数,在栈内存里面,不会在线程之间共享。

image.png

线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存存储了该内存以读写共享变量的副本。
A与B之间的通信,需要通过以下2个步骤

  • 线程A把本地内存A中更新过的共享变量刷新的主内存中。
  • 线程B到主内存中去读取线程A之前已经更新过的共享变量。

从源代码到指令序列的重排序

在执行程序时,为了提高性能,编译器和处理器通常会对指令做重排序,重排序分为三种:

  • 编译器优化的重排序:编译器在不改变单线程语义的前提下,可以重新安排语句的执行顺序。
  • 指令级并行的重排序:现代处理器采用了指令级并行技术来将多条指令重叠执行,如果不存在数据依赖性,处理器可以改变语句对应的机器指令的执行顺序。
  • 内存系统的重排序:由于处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是乱序执行。

image.png

对于指令重排,JAVA内存模型的处理器重排序规则可以要求JAVA编译器在生成指令序列时,插入特定类型的内存屏障指令,通过内存屏障指令来禁止特定类型的处理器重排序。

内存屏障:指的是重排序时不能把后面的指令重排序到内存屏障之前的位置。

展示一个由于内存操作重排带来问题的例子(经典)

在展示之前,首先补充一点知识:现代处理器使用写缓存区临时保存向内存下入的数据,以保证指令流水线持续运行,避免处理器停顿下来等待向内存写入数据而产生延迟,同时,以批处理的方式刷新缓存区,以及合并写缓存区对统一内存地址的多次写,减少堆内存总线的占用。

这个特性会对内存操作的顺序产生影响:处理器堆内存的读写操作的执行顺序,不一定与内存实际发生的读写操作顺序一致(因为内存操作重排)

image.png

如果AB同时执行,可能会得到读取到脏数据,原因如下:

image.png

此处由于指令重排,导致A1->A2的顺序变成了A2->A1,所以读取了脏数据。

一般情况下,处理器都不允许对存在数据依赖的操作做重排序。

happens-before

在JMM中,如果一个操作的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。

A happens-before B, JMM 并不要求A一定要在B之前执行, JMM仅仅要求前一个操作的结果对后一个操作课件,且前一个操作按顺序排在第二个操作之前.

一条SQL语句在MySQL中的执行流程

Posted on 2019-06-02 |
Words count in article 568 | Reading time 2

MySQL 架构分析

首先,我们需要了解MySQL的一个简要的架构

  • 连接器: 身份验证与权限相关
  • 查询缓存:执行查询语句时,会先查询缓存(失效率过高,不推荐用)
  • 分析器:分析SQL语句
  • 优化器:优化SQL语句
  • 执行器:执行SQL语句,并从存储引擎用返回数据

image.png

值得注意的是,Mysql分为了Server层和存储引擎层:

  • Server层: 主要包括连接器、查询缓存、分析器、优化器和执行器等组件,很多功能都是在这一层实现的,如存储过程、触发器、视图、函数等等,还有一个日志模块binglog
  • 存储引擎模块:支持InnoDB、MyISAM等等,InnoDB为默认的存储模块。

MySQL的日志模块

这里,我们需要特别主义redo log 和 binlog这两个日志模块,前者是InnoDB自带的日志模块,后者是MySQL自带的日志模式。

SQL 语句的类型

SQL语句的类型主要分为两种

  • 查询: select
  • 更新:update、delete

执行更新语句时,该如何操作这两个日志模块

1
update tb_student set name = 'wt' where name = 'name'
  • 首先,要根据where查询到对应那一条数据
  • 根据查询到的数据,先进行修改,然后调用引擎接口,写入这一行数据,InnoDB会把数据保存在缓存中,同时记录redo log,此时redo log 进入prepare状态,然后告诉执行器,执行结束
  • 执行器收到通知后记录binlog,然后调用引擎接口,提交redo log为提交状态
  • 更新完成

小结

  • MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redolog 只有 InnoDB 有。
  • 引擎层是插件式的,目前主要包括,MyISAM,InnoDB,Memory 等。
  • 查询语句的执行流程如下:权限校验(如果命中缓存)—》查询缓存—》分析器—》优化器—》权限校验—》执行器—》引擎
  • 更新语句执行流程如下:分析器—-》权限校验—-》执行器—》引擎—redo log(prepare 状态—》binlog—》redo log(commit状态)
1…5678
Enda Lin

Enda Lin

所有的伟大都是从零开始

79 posts
35 tags
GitHub E-Mail
© 2019 Enda Lin
您是博主的第 位小伙伴 总访问量 次