PyPI (Python Package Index) 是 Python 编程语言的软件存储库。开发者可以通过 PyPI 查找和安装由 Python 社区开发和共享的软件,也可以将自己开发的库上传至 PyPI 。

由于国外访问比较慢,我们可以通过配置国内的镜像来进行加速访问。

配置 Aliyun 的镜像

Linux 平台

  1. 找到下列文件
1
~/.pip/pip.conf
  1. 在上述文件中添加或修改:
1
2
3
4
5
[global]
index-url = https://mirrors.aliyun.com/pypi/simple/

[install]
trusted-host=mirrors.aliyun.com

Windows 平台

exporler 地址栏输入 %AppData%,进入文件夹后,在 pip 文件夹下新建 pip.ini 文件并写入以下配置。

阅读全文 »

Gradle 是什么

Gradle is an open-source build automation tool focused on flexibility and performance. Gradle build scripts are written using a Groovy or Kotlin DSL. Read about Gradle features to learn what is possible with Gradle.

Gradle 是一个专注于灵活性和性能的开源自动化构建工具,其脚本由 GroovyKotlin DSL 编写。Gradle 有以下特性:

  • 高度可定制:Gradle 采用可以高度自定义的方式进行建模。
  • 快速:Gradle 通过重用旧的输出、仅变更的输入以及并行的方式来快速完成任务。
  • 功能强大:Gradle 是 Android 的官方构建工具,并附带对许多流行语言和技术的支持。

安装

安装之前,请确保已经正确安装 JDK8 或更高版本

使用包管理器安装

针对类 Unix 系统(macOSLinuxCygwinSolarisFreeBSD)可以采用以下两种包管理的方式安装。

  • SDKMAN 包管理器
1
sdk install gradle
阅读全文 »

Jenkins 是常见的 CI 平台

安装

Windows 平台

CentOS 平台

更为详细的步骤及说明可查看官网

添加 jenkins 源

命令如下:

1
2
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key

yum 安装

阅读全文 »

本文介绍几个非常有用的 Arthas 命令,熟练使用这几个命令,可以让我们在排查线上问题的时候更加轻松。

关于 Arthas 的设计思想,美团技术团队的文章Java 动态追踪技术探究一文中做了详细的说明,有兴趣可以去阅读一下。

trace

trace 命令见名知意,可以用于跟踪方法内部的调用路径,渲染整个调用链路上所有的性能开销和跟踪调用链路。

例如,我们提供了一个 controller,用来保存一笔订单数据,如下:

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
package com.goku.order.rest;

import com.goku.order.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;

/**
* @author fuyongde
* @date 2020/1/18 15:36
*/
@RestController
@RequestMapping("/api/orders")
@Slf4j
public class OrderRestController {

private static Random random = new Random();

@PostMapping
public Map<String, Object> save(@RequestBody Order order) {
long id = random.nextLong();
order.setId(id);
Date now = new Date();
if (Objects.nonNull(order.getFlag()) && order.getFlag()) {
log.info("调用路径1");
} else {
log.warn("调用路径2");
}
order.setCreateTime(now);
order.setUpdateTime(now);
log.info("request body : {}", order);
Map<String, Object> map = new HashMap<>(16);
map.put("order", order);
return map;
}
}

现在项目在运行中,我们要查看 save 方法的链路,打开 arthas 并选择我们的工程,使用 trace com.goku.order.rest.OrderRestController save 命令并触发一次调用

1
2
3
4
5
6
7
8
9
Affect(class-cnt:1 , method-cnt:1) cost in 69 ms.
`---ts=2020-01-18 22:40:56;thread_name=http-nio-8081-exec-1;id=14;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@4f824872
`---[3.6313ms] com.goku.order.rest.OrderRestController:save()
+---[0.0164ms] com.goku.order.entity.Order:setId() #26
+---[0.0047ms] com.goku.order.entity.Order:getFlag() #28
+---[0.1695ms] org.slf4j.Logger:warn() #31
+---[0.0103ms] com.goku.order.entity.Order:setCreateTime() #33
+---[0.0062ms] com.goku.order.entity.Order:setUpdateTime() #34
`---[2.6388ms] org.slf4j.Logger:info() #35

从输出上来看,我们可以很清楚看到代码的 26、28、31、33、34、35 行被执行了,同样的,如果 order 对象的 flag 属性如果为 true,则第 31 行的调用会变成第 29 行,如下:

1
2
3
4
5
6
7
8
`---ts=2020-01-18 22:56:00;thread_name=http-nio-8081-exec-4;id=17;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@4f824872
`---[2.1072ms] com.goku.order.rest.OrderRestController:save()
+---[0.0051ms] com.goku.order.entity.Order:setId() #26
+---[min=0.0023ms,max=0.0023ms,total=0.0046ms,count=2] com.goku.order.entity.Order:getFlag() #28
+---[0.6911ms] org.slf4j.Logger:info() #29
+---[0.0186ms] com.goku.order.entity.Order:setCreateTime() #33
+---[0.0032ms] com.goku.order.entity.Order:setUpdateTime() #34
`---[0.312ms] org.slf4j.Logger:info() #35
阅读全文 »

本文探讨一下 MySQL5.7 版本中 优化器的代价模型。相关内容也可查看官网

MySQL 优化器成本模型由两个表组成,分别位于 mysql.server_cost 和 mysql.engine_cost 中。

  • server_cost: 针对服务器操作的优化器成本估算
  • engine_cose: 针对特定存储引擎的特定操作的优化器成本估算

server_cost

server_cost 包含以下值:

  • disk_temptable_create_cost:缺省值 40.0,该字段标识基于磁盘的存储引擎(InnoDB 和 MyISAM)中创建临时表的成本估算。从缺省值也可以看出,默认情况下创建临时表的代价是非常昂贵的。
  • disk_temptable_row_cost:缺省值 1.0,该字段标识基于磁盘的存储引擎(InnoDB 和 MyISAM)中创建临时行的成本估算。
  • key_compare_cost:缺省值 0.1,该字段表示比较记录的成本。
  • memory_temptable_create_cost:缺省值 2.0,MEMORY 存储引擎创建临时表的成本,从与 disk_temptable_create_cost 的对比中可以看出,默认情况下 MEMORY 存储引擎创建临时表的成本是远小于 InnoDB 和 MyISAM 存储引擎的。
  • memory_temptable_row_cost:缺省值 0.2,MEMORY 存储引擎创建临时行的成本。
  • row_evaluate_cost:缺省值 0.2,行评估成本,即扫描了多少行的成本。

engine_cost

  • io_block_read_cost:缺省值 1.0,从磁盘读取索引或数据块的成本。
  • memory_block_read_cost:缺省值 1.0,从内存数据库缓冲区读取索引说数据块的成本。

修改成本模型

1
2
3
4
5
6
7
8
9
10
11
-- 修改所有的 io_block_read_cost 成本
UPDATE mysql.engine_cost
SET cost_value = 2.0
WHERE cost_name = 'io_block_read_cost';
FLUSH OPTIMIZER_COSTS;

-- 仅修改 InnoDB 存储引擎的成本
INSERT INTO mysql.engine_cost
VALUES ('InnoDB', 0, 'io_block_read_cost', 3.0,
CURRENT_TIMESTAMP, 'Using a slower disk for InnoDB');
FLUSH OPTIMIZER_COSTS;
阅读全文 »

本文粗略说以下 MySQL 中常见的索引分类。

物理存储角度

  • 聚簇索引
  • 非聚簇索引

聚簇索引将数据存储与索引放到了一起,找到索引也就找到了数据。非聚簇索引将数据存储与索引分开,索引结构的叶子结点指向了数据对应的行。

InnoDB 中,在聚簇索引之上创建的索引,称之为辅助索引,辅助索引叶子结点存储的不是行的物理位置,而是主键值,因此辅助索引访问数据总是需要二次查找的。

一图顶千言,如下图所示:

由于聚簇索引是将数据跟索引结构放在一起的,因此一个表仅有一个聚簇索引。

聚簇索引默认是主键,如果表中没有定义主键,InnoDB 会选择一个唯一非空索引,如果没有这样的索引,InnoDB 会隐式定义一个主键来作为聚簇索引。

数据结构角度

阅读全文 »

本文描述一个 Java 多线程死锁问题,并演示使用 jstackarthas 工具来发现应用程序中的线程死锁。

先编写一段死锁的代码

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
public class App {

public static void main(String[] args) {
String x = "x";
String y = "y";
Thread a = new Thread(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
synchronized (x) {
System.out.println(String.format("Thread %s Start %s", threadName, "------"));
System.out.println(String.format("Thread %s, x = %s", threadName, x));
synchronized (y) {
System.out.println(String.format("Thread %s, y = %s", threadName, y));
}
System.out.println(String.format("Thread %s End %s", threadName, "------"));
}
}
});

Thread b = new Thread(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
synchronized (y) {
System.out.println(String.format("Thread %s Start %s", threadName, "------"));
System.out.println(String.format("Thread %s, y = %s", threadName, y));
synchronized (x) {
System.out.println(String.format("Thread %s, x = %s", threadName, x));
}
System.out.println(String.format("Thread %s End %s", threadName, "------"));
}
}
});

a.start();
b.start();
}
}

运行以上代码,发现如下输出:

1
2
3
4
Thread Thread-0 Start ------
Thread Thread-0, x = x
Thread Thread-1 Start ------
Thread Thread-1, y = y

由于 Thread-0 和 Thread-1 均没有输出 End 信息,可知线程陷入了死锁。

jstack 分析

jstack 是 jdk 自带的线程堆栈分析工具,使用该命令可以查看或导出 Java 应用程序中线程堆栈信息。

Useage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
(to connect to a hung process)
jstack [-m] [-l] <executable> <core>
(to connect to a core file)
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
(to connect to a remote debug server)

Options:
-F 强制转储线程信息,当 jstack 不响应时使用
-m 打印Java和本机框架的所有栈信息(混合模式)
-l 长列表,答应更多关于锁的附加信息
-h or -help 打印帮住信息
阅读全文 »

Dubbo 内置了 4 种负载均衡策略

  • RandomLoadBalance:随机负载均衡,随机选择一个服务结点,跟权重相关。该策略是 Dubbo默认负载均衡策略。
  • RoundRobinLoadBalance:轮询负载均衡,轮询选择一个服务结点,跟权重相关。
  • LeastActiveLoadBalance:最少活跃调用数,相同的活跃数随机选择一个结点,跟权重相关。实现方式是对活跃数值前后计数差。使得慢的结点收到更少的请求,因为越慢的结点前后计数差越大。
  • ConsistentHashLoadBalance:一致性哈希负载均衡,即相同的参数的请求总是落在同一个服务结点上。

通过观察类之间依赖图,可以快速帮我们梳理其之间的关系,如下图所示:

实践

纸上得来终觉浅,觉知此时要躬行。

Dubbo 中负载均衡的源码可自行通过源码来学习,为了加深印象,这里我以 Dubbo 源码中的思路来实现这四个负载均衡策略。

准备工作

定义结点接口

这里我们不实现 Dubbo 中的 Invoker,以一个结点类 Endpoint 代替。

Endpoint.java

阅读全文 »

死锁是指两个或者多个事务在同一个资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象

示例

先用一个示例来演示一下数据库死锁的情况。本示例基于 MySQLInnoDB 存储引擎。

表结构

我们先建一个 wallet 表,表结构如下

1
2
3
4
5
6
7
8
mysql> DESC wallet;
+---------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------------------+------+-----+---------+-------+
| id | bigint(20) unsigned | NO | PRI | NULL | |
| user_id | bigint(20) unsigned | NO | UNI | 0 | |
| balance | bigint(20) | NO | | 0 | |
+---------+---------------------+------+-----+---------+-------+

开启事务 A,并修改 id 为 1 的记录

1
2
3
4
5
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE wallet SET balance = balance + 100 WHERE id = 1;
Query OK, 1 row affected (0.00 sec)

开启事务 B,并修改 id 为 2 的记录

1
2
3
4
5
6
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE wallet SET balance = balance + 100 WHERE id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
阅读全文 »

镜像管理

查询所需的镜像

  • Usage:

    docker search [OPTIONS] TERM

  • Options:

    -f --filter :根据提供的条件过滤输出

    --format :使用 Go 模板进行漂亮的打印搜索

    --limit :最大搜索结果数(默认为 25)

    --no-trunc:不要截断输出

  • Example:

1
docker search mysql

安装镜像

  • Usage:

    docker pull [OPTIONS] NAME[:TAG|@DIGEST]

  • Options:

    -a --all-tags:下载存储库中的所有标记镜像

    --disable-content-trust:跳过图像验证(默认为 true)

  • Example:

    1
    2
    3
    4
    5
    6
    7
    8
    # 安装MySQL的5.7.24版本
    docker pull mysql:5.7.24
    # 缺省TAG时默认安装最新版本
    docker pull mysql
    # 安装所有版本
    docker pull -a mysql
    # 安装zookeeper
    docker pull zookeeper

查看镜像信息

Usage:

docker images [OPTIONS] [REPOSITORY[:TAG]]

Options:

阅读全文 »