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

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


  • 首页

  • 归档

  • 搜索

服务端通过nosql加锁解决并发问题实战 并发问题的解决思路

发表于 2021-11-15

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

并发问题的解决思路

  1. 首先想到的就是加锁,比如mysql加锁,解决并发问题。这类文章很多,就不赘述了。
  2. 再者是nosql加锁,解决mysql加锁解决不了的情况,毕竟nosql的性能是比mysql等关系型数据库快很多的,nosql加锁的并发级别比mysql等关系数据库的要高。

nosql加锁的案例

  1. 我的业务场景是每天第一次登陆APP发优惠券
  2. 我在业务代码里已经添加了是否存在的判断,如果今天已经给同一用户在同一时间下发了相同来源的优惠券则不重复下发
  3. 因为业务需要是相同类型相同来源的优惠券可以给同一个人在同一时间下发多张,我们就不能用MySQL的唯一索引了
  4. 我们在【2】中提到的判断,只能对非并发情况的请求起到限制作用,并发请求是不起作用的。
  5. 这时候就可以使用nosql加锁的思路,比如使用redis锁:
    1. 在业务开始时加锁
    2. 执行业务代码
    3. 业务执行完毕释放锁
  6. 原理:nosql加锁的并发级别比mysql等关系数据库的要高。

示例代码

未加锁的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
php复制代码//每天第一次登陆发放预约券
public static function everydayTriggerCoupon($userid)
{
$userVip = UserVip::getByUserid($userid, 'type');
if ($userVip['type'] == VipInfo::TYPE_USER_NORMAL) {
//非会员发放3张预约券
if (!self::checkCouponExist($userid, CouponInfo::PROP_COUPON_SUBSCRIBE_APPOINTMENT_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_BENEFITS, Utility::getTomorrowTimestamp())) {
self::saveCoupon($userid, CouponInfo::PROP_COUPON_SUBSCRIBE_APPOINTMENT_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_BENEFITS, Utility::getTomorrowTimestamp(), CouponInfo::COUNT_COUPON_EVERYDAY_TRIGGER_NOT_VIP);
}
} else {
//会员发放5张预约券
if (!self::checkCouponExist($userid, CouponInfo::PROP_COUPON_SUBSCRIBE_APPOINTMENT_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_VIP_BENEFITS, Utility::getTomorrowTimestamp())) {
self::saveCoupon($userid, CouponInfo::PROP_COUPON_SUBSCRIBE_APPOINTMENT_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_VIP_BENEFITS, Utility::getTomorrowTimestamp(), CouponInfo::COUNT_COUPON_EVERYDAY_TRIGGER_VIP);
}
//会员额外发放1张超级喜欢券
if (!self::checkCouponExist($userid, CouponInfo::PROP_COUPON_SUPER_LIKE_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_VIP_BENEFITS, Utility::getTomorrowTimestamp())) {
self::saveCoupon($userid, CouponInfo::PROP_COUPON_SUPER_LIKE_ID, CouponInfo::TYPE_COUPON_SOURCE_DAILY_VIP_BENEFITS, Utility::getTomorrowTimestamp());
}
}
}

添加锁的代码

  1. 结合业务需求,每天第一次登陆发放优惠券,即当天有效,所以在设置key时把当前的日期作为其中一个参数
  2. 因为我使用的是Laravel框架,Cache集成了Redis,Cache的add方法和Redis的setnx方法类似,都有存在key值返回0,不存在key值返回1的特性。
  3. 结合我们的业务场景,我在add()的时候添加了24小时的过期时间;当然如果你们的场景对过期时间没有要求,可以在业务代码执行完毕之后主动释放缓存资源。

注意:一定要记得在合适的场景下释放锁资源,避免资源滥用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
php复制代码//每天第一次登陆发放预约券
public static function everydayTriggerCoupon($userid)
{
$userVip = UserVip::getByUserid($userid, 'type');
//解决并发问题 加redis锁
$cacheKey = CacheKey::getCacheKey(CacheKey::TYPE_USER_REWARD_EVERYDAY_TRIGGER, $userid . '_' . date('Y-m-d'));
$res = Cache::add($cacheKey, true, 60 * 60 * 24); //add()不存在添加返回true; 存在返回false
if ($res) {
和未加锁的核心代码一致
.
.
.
}
}

异步任务加nosql锁的注意问题

如果我们的异步任务出现了并发问题,也可以考虑通过引入nosql锁的方式解决。

这时候要注意:如果异步任务是单线程的,是按顺序执行的,要在调用异步任务的方法外加锁,不要加到异步任务方法里,避免造成阻塞。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
php复制代码$lockKey = CacheKey::getCacheKey(CacheKey::TYPE_JOB_USER_EDIT_AVOID_CONCURRENT, $userid);
$lockRes = Cache::add($lockKey, true, 60);
if ($lockRes) {
UserInfoEdit::dispatch([
'userid' => $userid,
.
.
.
])->onQueue(QueueNameBuilder::getName(QueueNameBuilder::USER_INFO_EDIT));
//释放redis锁
Cache::forget($lockKey);
}

Last but not least

技术交流群请到 这里来。 或者添加我的微信 wangzhongyang1993 ,一起学习。

感谢大家的点赞、评论、关注,谢谢大佬们的支持,感谢。

本文转载自: 掘金

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

Serverless 工程实践 快速搭建 Kubeles

发表于 2021-11-15

简介: Kubeless 是基于 Kubernetes 的原生无服务器框架。其允许用户部署少量的代码(函数),而无须担心底层架构。

快速搭建 Kubeless 平台

Kubeless 简介

Kubeless 是基于 Kubernetes 的原生无服务器框架。其允许用户部署少量的代码(函数),而无须担心底层架构。它被部署在 Kubernetes 集群之上,并充分利用 Kubernetes 的特性及资源类型,可以克隆 AWS Lambda、Azure Functions、Google Cloud Functions 上的内容。

Kubeless 主要特点可以总结为以下几个方面。

  • 支持 Python、Node.js、Ruby、PHP、Go、.NET、Ballerina 语言编写和自定义运行时。
  • Kubeless CLI 符合 AWS Lambda CLI。
  • 事件触发器使用 Kafka 消息系统和 HTTP 触发器。
  • Prometheus 默认监视函数的调用和延时。
  • 支持 Serverless 框架插件。

由于 Kubeless 的功能特性是建立在 Kubernetes 之上的,因此对于熟悉 Kubernetes 的人来说非常容易部署 Kubeless。其主要实现是将用户编写的函数在 Kubernetes 中转变为 CRD(Custom Resource Definition,自定义资源),并以容器的方式运行在集群中。

Kubeless 部署

在已有的 Kubernetes 集群上进行 Kubeless 服务的创建:

1
ruby复制代码export RELEASE=$(curl -s https://api.github.com/repos/kubeless/kubeless/releases/   latest | grep tag_name | cut -d '"' -f 4)kubectl create ns kubelesskubectl create -f https://github.com/kubeless/kubeless/releases/download/$RELEASE/   kubeless-$RELEASE.yaml

创建成功后如图所示

安装和配置 Kubeless

查看基本信息:

1
arduino复制代码kubectl get pods -n kubeless

相关 Pod 信息如图所示

查看 Kubeless 相关 Pod

查看 Deployment 信息:

1
arduino复制代码kubectl get deployment -n kubeless

其相关信息如图所示

查看 Kubeless Deployment 相关信息

查看 customresourcedefinition 信息:

1
arduino复制代码kubectl get customresourcedefinition

其相关信息如图所示

查看 customresourcedefinition 信息

下载命令行工具

下载 Kubeless 工具,并解压:

1
bash复制代码export OS=$(uname -s| tr '[:upper:]' '[:lower:]')curl -OL https://github.com/kubeless/kubeless/releases/download/$RELEASE/kubeless_   $OS-amd64.zipunzip kubeless_$OS-amd64.zip

解压之后查看:

1
bash复制代码./bundles/kubeless_linux-amd64/kubeless

具体如图所示

使用 Kubeless 命令行工具

体验测试

创建测试代码 helloworld.py :

1
python复制代码def hello(event, context):   print(event)   return event['data']

部署项目:

1
bash复制代码./bundles/kubeless_linux-amd64/kubeless function deploy hello-world --runtime   python3.6 --from-file helloworld.py --handler helloworld.hello

部署成功之后,查看项目信息:

1
arduino复制代码kubectl get functions

函数列表如图所示

查看函数列表

查看实例函数:

1
bash复制代码./bundles/kubeless_linux-amd64/kubeless function ls

函数状态如图所示

触发函数:

1
bash复制代码./bundles/kubeless_linux-amd64/kubeless function call hello-world --data 'Hello   world!'

触发完成之后,看到输出结果:

查看实例中输出的日志,如图所示

在实例中查看日志

至此,我们在 Kubernetes 集群上成功地创建了 Kubeless 服务,并顺利地体验了 Kubeless 版的 Hello World 实现。

原文链接

本文为阿里云原创内容,未经允许不得转载。

本文转载自: 掘金

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

性能优化-CPU篇

发表于 2021-11-15

性能优化 是个系统工程,总是牵一发而动全身,它涉及了从程序设计、编程语言,再到系统、存储、网络等各种底层基础设施的方方面面。每一个组件都有可能出问题,而且很有可能多个组件同时出问题。

Linux 性能的基本指标、工具,以及相应的观测、分析和调优方法。包括 CPU 性能、磁盘 I/O 性能、内存性能以及网络性能。 系统性能问题,只要你理解了应用程序和系统的少数几个基本原理,再进行大量的实战练习,建立起整 体性能的全局观。

性能指标:“高并发”和“响应快”是从应用负载的视角来考察性能,直接影响了产品终端的用户体验。也正对应着性 能优化的两个核心指标——“吞吐”和“延时”。

随着应用负载的增加,系统资源的使用也会升高,甚至达到极限。而性能问题的本质 ,就是系统资源已经达到瓶颈,但请求的处理却还不够快,无法支撑更多的请求。性能分析,其实就是找出应用或系统的瓶颈,并设法去避免或者缓解它们,从而更高效地利用系统资源处理更多的请求。

性能优化方法论

  • 如何评估性能优化的效果?

确定性能的量化指标、测试优化前的性能指标、测试优化后的性能指标。量化指标的选择。至少要从应用程序和系统资源 这两个维度,分别选择不同的指标:

1)应用程序的维度,我们可以用吞吐量和请求延迟来评估应用程序的性能。
2)系统资源的维度,我们可以用 CPU 使用率来评估系统的 CPU 使用情况。
性能测试注意点:

1)避免性能测试工具干扰应用程序的性能;
2)避免外部环境的变化影响性能指标的评估。

  • 多个性能问题同时存在,选择优化哪些?
    二八原则,并不是所有的性能问题都值得优化,找出最重要的、可以最大程度提升性能的问题。优先优化系统资源使用问题、性能指标变化幅度最大的问题。
  • 多种优化方法,选择哪种?
    选能最大提升性能的方法,但性能优化通常会带来复杂度的提升,降低程序的可维护性,还可能引发其他指标的异常。

下面是我们进行系统优化的着手点。
image.png

本篇文章就从CPU着手介绍一下cpu相关的优化方向。

CPU优化

性能统计
  • 平均负载率
    平均负载是指单位时间内,系统处于 可运行状态 和 不可中断状态 的平均进程数,也就是平均活跃进程数,它和 CPU 使用率并没有直接关系。
  • ①可运行状态
    是指正在使用cpu或者等待cpu的进程,通过ps -aux 处于R(Running 或者 Runable)状态的进程
  • ②不可中断状态
    是正处于内核态关键流程中的进程,并且这些流程是不可打断的, 比如最常见的是等待硬件设备的 I/O 响应,也就 是我们在 ps 命令中看到的 D 状(Uninterruptible Sleep,也称为 Disk Sleep)的进程
  • 查看平均负载率
    命令 uptime 查看系统平均负载
1
2
3
4
yml复制代码
[root@iZbp1agiqhn4s2q8a0980xZ ~]# uptime

15:00:00 up 535 days, 37 min, 1 user, load average: 0.04, 0.05, 0.05
1
2
3
4
yml复制代码1.15:00:25 // 当前时间 
2.up 535day  // 系统运行时间
3.user // 正在登录用户数 
4.load average: 0.00, 0.02, 0.05  // 依次则是过去 1 分钟、5 分钟、15 分钟的平均负载

例.比如当平均负载为2时,意味着什么呢?

在只有2个CPU的系统上,意味着所有的 CPU 都刚好被完全占用。
在 4 个 CPU 的系统上,意味着 CPU 有 50% 的空闲。
而在只有 1 个 CPU 的系统中,则意味着有一半的进程竞争不到 CPU
另外还有一个最直接的显示系统平均负载的命令

1
2
3
4
shell复制代码
cat /proc/loadavg

0.10 0.06 0.01 1/72 29632

除了前3个数字表示平均进程数量外,后面的1个分数,分母表示系统进程总数,分子表示正在运行的进程数;最后一个数字表示最近运行的进程ID.

  • 平均负载率设置多少合理
    命令查看CPU 个数 grep 'model name' /proc/cpuinfo | wc -l
    当平均负载高于CPU数量70%的时候,可以分析排查负载高的原因
  • 平均负载与CPU的关系
    平均负载代表是活跃进程数,回到平均负载的含义,平均负载是单位时间内,处于可运行状态和不可中断状态的进程数。所以它不仅仅包括正在运行的CPU进程,还包括等待CPU和等待IO的进程。
    cpu使用率,定义是单位时间内cpu的繁忙情况的统计,跟平均负载不全对应:
  • cpu密集型进程,使用大量的cpu会导致平均负载过高,此时两者是一致的;
  • IO密集型进程,等待IO也会导致平均负载升高,但cpu不一定升高
  • 大量等待cpu的进程调度也会导致平均负载升高,此时cpu使用率会比较高
常用分析命令
  • mpstat

1.常用的多核 CPU 性能分析工具;
2.实时查看每个 CPU 的性能指标以及所有 CPU 的平均指标
运行 mpstat 查看 CPU 使用率的变化情况

1
2
yml复制代码# -P ALL 表示监控所有 CPU,后面数字 5 表示间隔 5 秒后输出一组数据
[root@centos7-2 ~]# mpstat -P ALL 5

image.png
排查是哪个进程导致 CPU 的使用率这么高的,可以使用 pidstat来查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
yml复制代码
# pidstat[选项][<时间间隔>][<次数>]

# -u:默认的参数,显示各个进程的cpu使用统计

# -r:显示各个进程的内存使用统计

# -d:显示各个进程的IO使用情况

# -p:指定进程号

# -w:显示每个进程的上下文切换情况

# -t:显示选择任务的线程的统计信息外的额外信息

[root@centos7-2 ~]# pidstat -u 5 1
上线文切换
  • 概念理解

CPU 上下文切换,就是先把前一个任务的 CPU 上下文(CPU 寄存器和程序计数器)保存起来,然后加载 新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务,而保存下来的上下文, 会存储在系统内核中,并在任务重新调度执行时再次加载进来。

每次上下文切换都需要几十纳秒到到微秒的CPU时间,因此如果进程上下文切换次数过多,就会导致 CPU 将大量时间耗费在寄存器、内核栈以及虚拟内存等资源的保存和恢复上,进而大大缩短了真正运行进程的时间,实际上有效的CPU运行时间大大减少(可以认为上下文切换对用户来说是在做无用功)。

  • 上下文切换时机
  • 根据调度策略,将cpu时间划片对应的时间片。当时间片耗尽,就需要进行上下文切换;
  • 进程在系统资源不足,会在获取到足够的资源之前进程挂起
  • 进程通过sleep函数将自己挂起
  • 当优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会挂起,有高到低进程运行,也就是被抢占
  • 当发生硬中断,cpu上的进程会被中断挂起,转而执行内核中的中断服务程序
  • 上下文切分类
  • 进程上下文切换
  • 线程上下文切换
  • 中断上下文切换(硬件通过触发信号,会导致中断处理程序调用,也是一种常见的任务)
  • 查看上线文切换
  1. vmstat

是一个常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来 分析 CPU 上下文切换和中断的次数

image.png

重点强调下,需要特别关注的四列内容:

  • cs(context switch)是每秒上下文切换的次数;
  • in(interrupt)则是每秒中断的次数。
  • r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。
  • b(Blocked)则是处于不可中断睡眠状态的进程数。

vmstat 只给出了系统总体的上下文切换情况,要想查看每个进程的详细情况,就需要使用我们前面提到过的 pidstat 了。给它加上 -w 选项,就可以查看每个进程上下文切换的情况了。

  • us 用户进程执行消耗cpu时间(user time) us的值比较高时,说明用户进程消耗的cpu时间多,但是如果长期超过50%的使用,那么我们就该考虑优化程序算法或其他措施了
  • sy 系统进程消耗cpu时间(system time) sys的值过高时,说明系统内核消耗的cpu资源多,这个不是良性的表现,我们应该检查原因。这里us + sy的参考值为80%,如果us+sy 大于 80%说明可能存在CPU不足
  • id 空闲时间(包括IO等待时间) 一般来说 us+sy+id=100
  • wa 等待IO时间 wa过高时,说明io等待比较严重,这可能是由于磁盘大量随机访问造成的,也有可能是磁盘的带宽出现瓶颈。
  1. pidstat
1
2
yml复制代码
[root@centos7-2 ~]# pidstat -w -u 3

image.png

结论: 结果中有两列内容是我们的重点关注对象。

一个是cswch,表示每秒自愿上下文切换(voluntary context switches)的次数,

另一个则是nvcswch,表示每秒非自愿上下文切换(non voluntary context switches)的次数。

  • 所谓自愿上下文切换,是指进程无法获取所需资源,导致的上下文切换。比如说,I/O、内存等系统资源不足时,就会发生自愿上下文切换。
  • 而非自愿上下文切换,则是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如 说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换。
CPU使用率
  • 概念

用什么指标来描述系统的 CPU 性能呢 ? 不是平均负载,也不是CPU上下文切换,而是另一个更直观的指标CPU使用率 ,CPU 使用率是单位时间内 CPU 使用情况的统计,以百分比的方式展示

  • 节拍率

为了维护 CPU 时间,Linux 通过事先定义的节拍率(内核中表示为 HZ),触发时间中断,并使用全局变量 Jiffies 记 录了开机以来的节拍数。每发生一次时间中断,Jiffies 的值就加1

  • 查看节拍率
1
2
3
4
yml复制代码
[root@iZbp1agiqhn4s2q8a0980xZ ~]# grep 'CONFIG_HZ=' /boot/config-$(uname -r)

CONFIG_HZ=1000
  • 用户节拍率
    正因为节拍率 HZ 是内核选项,所以用户空间程序并不能直接访问。为了方便用户空间程序,内核还提供了一个用 户空间节拍率 USER_HZ,它总是固定为 100,也就是 1/100 秒。这样,用户空间程序并不需要关心内核中 HZ 被设 置成了多少,因为它看到的总是固定值 USER_HZ
1
yml复制代码USER_HZ=100

为了方便用户控件程序,内核还提供了一个用户控件的节拍率,它总是固定为100,也就是1/100秒,这样,用户控 件程序并需要关系内核中HZ被设置成了多少

cpu使用率公式

CPU使用率,就是除了空闲时间外的其他时间占总CPU时间的百分比,用公式来表示就是
image.png
根据这个公式,我们就可以从 /proc/stat 中的数据,很容易地计算出 CPU 使用率。当然,也可以用每一个场景的 CPU 时间,除以总的 CPU 时间,计算出每个场景的 CPU 使用率。

  • 性能工具计算方式

事实上,为了计算机CPU使用率,性能能工具一般都会间隔一段时间(比如 3 秒)的两次值,做差后,再计算出这段

时间的平均CPU使用率

image.png

命令查看CPU使用率

查看 CPU 使用率 top 、ps 和pidstat是最常用的性能分析工具 :

  • top 显示了系统总体的 CPU 和内存使用情况,以及各个进程的资源使用情况。
  • ps 则只显示了每个进程的资源使用情况。
  • pidstat分析每个进程CPU使用情况

1.top图解

image.png
2.pidstat分析每个进程cpu使用情况

top并没有细分进程的用户态CPU和内核态CPU,那要怎么查看每个进程的详细情况?

例子:pidstat 1 5 没秒打一次 打五次

image.png
3.图解pidstat

image.png

cpu使用率过高分析

1.查询使用率过高的进程

通过 top、ps、pidstas等命令

2.定位占用cpu高的是代码中的哪个函数

perf和GDB

GDB(The GNU Project Debugger), 这个功能强大的程序调试利器 ,GDB 调试程序的过程会中断程序运行,这在 线上环境往往是不允许的;

perf 是 Linux 2.6.31 以后内置的性能分析工具。它以性能事件采样为基础,不仅可以分析系统的各种事件和内核性 能,还可以用来分析指定应用程序的性能问题 ,使用 perf 分析 CPU 性能问题

  • 案例

下面介绍两种用法

1.第一种常见用法是 perf top,类似于 top,它能够实时显示占用 CPU 时钟最多的函数或者指令,因此可以用来查找 热点函数,使用界面如下所示:

1
2
3
4
5
6
7
yml复制代码
[root@centos7-2 ~]# perf top
Samples: 724 of event 'cpu-clock', Event count (approx.): 125711088
Overhead Shared Object Symbol

45.11% [kernel] [k] generic_exec_single
...

输出结果中,第一行包含三个数据,分别是采样数(Samples)、事件类型(event)和事件总数量(Event count)。比如这个例子中,perf 总共采集了1000个 CPU 时钟事件,而总事件数则为 271937500。 采样数需要我们 特别注意,如果采样数过少(比如只有十几个),那下面的排序和百分比就没什么实际参考价值了

第一列 Overhead ,是该符号的性能事件在所有采样中的比例,用百分比来表示。

第二列 Shared ,是该函数或指令所在的动态共享对象(Dynamic Shared Object),如内核、进程名、动态链接库

名、内核模块名等。

第三列 Object ,是动态共享对象的类型。比如 [.] 表示用户空间的可执行程序、或者动态链接库,而 [k] 则表示内核 空间。

最后一列 Symbol 是符号名,也就是函数名。当函数名未知时,用十六进制的地址来表示。

  • perf 命令详解
    image.png
  1. 第二种常见用法,也就是 perf record 和 perf report。 perf top 虽然实时展示了系统的性能信息,但它的缺点是并不
    保存数据,也就无法用于离线或者后续的分析。而 perf record 则提供了保存数据的功能,保存后的数据,需要你用
    perf report 解析展示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
yml复制代码
perf record # 按 Ctrl+C 终止采样

[root@centos7-2 ~]# perf report

Samples: 5K of event 'cpu-clock', Event count (approx.): 1332500000

Overhead Command Shared Object Symbol

97.15% swapper [kernel.kallsyms] [k] native_safe_halt

0.49% swapper [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore

...
cpu性能调优策略

CPU 优化 我们接下来从应用程序和系统的角度,分别来看看 如何才能降低 CPU 使用率,提高 CPU 的并行处理能

力

  • 应用程序优化

1.首先,从应用程序的角度来说,降低 CPU 使用率的最好方法当然是,排除所有不必要的工作,只保留最核心的逻辑。比如减少循环的层次、减少递归、减少动态内存分配等等。

这里列举了最常见的一些方法,方便记忆和使用。

1) 编译器优化:很多编译器都会提供优化选项,适当开启它们

2) 在编译阶段你就可以获得 编译器的帮助,来提升性能

3) 算法优化 :使用复杂度更低的算法,可以显著加快处理速度

4)异步处理:使用异步处理,可以避免程序因为等待某个资源而一直阻塞,从而提升程序 的并发处理能力。比如, 把轮询替换为事件通知,就可以避免轮询耗费 CPU 的问题。

5)多线程代替多进程:前面讲过,相对于进程的上下文切换,线程的上下文切换并不切换 进程地址空间,因此可以 降低上下文切换的成本。

6)善用缓存:经常访问的数据或者计算过程中的步骤,可以放到内存中缓存起来,这样在 下次用时就能直接从内存 中获取,加快程序的处理速度

  • 系统优化

从系统的角度来说,优化CPU的运行,一方面要充分利用CPU 缓存的本地性,加速缓存访问;另一方面,就是要控制进程的CPU 使用情况减少进程间的相互影响。

具体来说,系统层面的CPU 优化方法也有不少,这里同样列举了最常见的一些方法,方便你记忆和使用 。

1.CPU绑定:把进程绑定到一个或者多个 CPU 上,可以提高

2.CPU缓存的命中率,减少跨 CPU 调度带来的上下文切换 问题

3.CPU独占:跟CPU绑定类似,进一步将 CPU 分组,并通过 CPU 亲和性机制为其分配进程。这样,这些 CPU 就由 指定的进程独占,换句话说,不允许其他进程再来使用这些 CPU

  1. 优先级调整:使用 nice 调整进程的优先级,正值调低优先级,负值调高优先级。

5.为进程设置资源限制:使用 Linux cgroups 来设置进程的 CPU 使用上限,可以防止由 于某个应用自身的问题,而耗尽系统资源。

6.中断负载均衡:无论是软中断还是硬中断,它们的中断处理程序都可能会耗费大量的 CPU。开启 irqbalance 服务或 者配置 smp_affinity,就可以把中断处理过程自动负载均衡到多个CPU上。

本文转载自: 掘金

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

JDK的第三个LTS版本JDK17来了 简介 JDK17中的

发表于 2021-11-15

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

简介

2021年9月JDK17发布了,JDK17是最新的一个LTS版本。所谓LTS版本就是可以得到至少八年产品支持的版本。从2014年的JDK8,到2018年的JDK11,再到2021年的JDK17。

同时Oracle也调整了LTS版本的发布年限,从之前的三年调整到现在的二年,也就是说下一个LTS版本将会是JDK21,哇喔!

那么如果不是LTS版本呢? 非LTS版本只会得到六个月的产品支持。所以大家还是使用LTS版本吧。

好了,让我们一起来看看JDK17中都有那些新特性吧。

JDK17中的新特性

总中的来说,JDK17提供了14个优化点或者是变动点。我们会一一进行讲解。

语言上的新特性

JDK17在语言上的新特性只有一个 JEP 409: Sealed Classes。

Sealed Classes是在JDK15中引入的概念,它表示某个类允许哪些类来继承它:

1
2
3
4
5
6
7
8
scala复制代码public sealed class SealExample permits Seal1, Seal2{
}

public non-sealed class Seal1 extends SealExample {
}

public final class Seal2 extends SealExample {
}

final表示Seal2不能再被继承了。non-sealed 表示可以允许任何类继承。

核心库的优化

JDK17对JAVA核心库的优化有4个。

  • 第一个是:JEP 306: Restore Always-Strict Floating-Point Semantics

这个是什么呢?简单点说,就是之前的硬件架构,在严格进行浮点语义进行计算的时候,会消耗大量资源。这在很久以前硬件水平都不高的时候,是难以容忍的。

所以在JDK1.2之后,对浮点语义进行了微调,对默认的严格浮点语义进行了修改。

但是现在已经是2021年了,硬件水平得到了飞速的发展,所以之前引入的修改已经是不必要了,在JDK17中被废弃了。

  • 第二个是:JEP 356: Enhanced Pseudo-Random Number Generator

JDK中有一个专门生成随机数的类java.util.Random,但是这个类生成的都是伪随机数。

JDK17对这个类进行了加强,提供了一个RandomGenerator接口,为所有的伪随机数提供统一的API。

RandomGenerators 提供了ints、longs、doubles、nextBoolean、nextInt、nextLong、nextDouble 和 nextFloat等方法,来生成对应的随机数。

RandomGenerator接口又包括4个子接口,分别是:

SplittableRandomGenerator:提供了 split 和 splits 的方法,允许用户从现有的 RandomGenerator 生成一个新的 RandomGenerator.

JumpableRandomGenerator:扩展了RandomGenerator的jump 和 jumps 的方法,允许用户跳过一定数目的随机数。

LeapableRandomGenerator :扩展了RandomGenerator的leap 和leaps 的方法,允许用户跳过大量数目的随机数。

ArbitrouslyJumpableRandomGenerator:扩展了LeapableRandomGenerator,允许用户指定跳过的随机数。

同时还对Random、ThreadLocalRandom 和 SplittableRandom等类进行了重构。

  • 第三个是JEP 382: New macOS Rendering Pipeline

这个是专门为Mac做的优化,使用了最新的Apple Metal API 来实现JAVA的2D渲染。

  • 第四个是JEP 415: Context-Specific Deserialization Filters

JDK中一个很危险的用法就是反序列化,因为你不知道反序列化的对象到底是不是一个危险的对象,为了解决这个问题,在Java 9 中引入了反序列化过滤器,从而在反序列化之前对数据流进行验证。

但是这种基于流的过滤器有几个限制,这种方法不能扩展,并且很难在代码发布后更新过滤器。它也不能对应用程序中第三方库执行的反序列化操作进行过滤。

为了解决这些问题,JEP 290 还引入了一个 JVM 范围的反序列化过滤器,可以通过 API、系统属性或安全属性进行设置。但是这种静态的过滤器,在具有多个执行上下文的复杂应用程序中,往往会不太适用,因为不同的上下文可能需要不同的过滤条件。

JDK17对JDK9的过滤方法进行了改进,可以在JVM范围配置特定于上下文的反序列化过滤器。

支持新的平台

  • JEP 391: macOS AArch 64 Port

Mac的M1芯片都发布好久了,没有理由JDK不支持,这个JEP就是让JDK17支持原生的Apple的新Arm 64架构。

预览特性

  • JEP 406: Pattern Matching for switch (Preview)

这个新特性允许在switch中使用模式匹配。

我们知道,在之前的预览功能中,已经有模式匹配了,不过模式匹配是用在instance of语句中,如下所示:

1
2
3
4
5
6
7
8
9
10
javascript复制代码// Old code
if (o instanceof String) {
String s = (String)o;
... use s ...
}

// New code
if (o instanceof String s) {
... use s ...
}

但是如果instanceof太多的话,也会造成困扰:

1
2
3
4
5
6
7
8
9
10
11
12
13
javascript复制代码static String formatter(Object o) {
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}

最好的办法是将上面的代码转换成为switch:

1
2
3
4
5
6
7
8
9
javascript复制代码static String formatterPatternSwitch(Object o) {
return switch (o) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> o.toString();
};
}

这就是switch中的模式匹配。

  • JEP 412: Foreign Function and Memory API (Incubator)

在JDK14和15中,JDK已经可以调用不属于JVM内部的代码和访问不归JVM管辖的内存空间。这个新特性在JDK17中得到了增强。

想象一下,以后JDK可以原生支持调用非java语言的API,是不是很厉害?

  • JEP 414: Vector API (Second Incubator)

Vector是在JDK16中引入的。可以让向量计算更加快速。 循环遍历的计算,可以用Vector来进行简化。

其他改动

其他的一些改动比如封装JDK内部使用的API ,废弃了Security Manager,Applet API和RMI等等,这里就不一一介绍了。

总结

JDK17是一个LTS版本,也提供了很多优秀的新特性,还不赶紧用起来!

本文已收录于 www.flydean.com/27-jdk17-ne…

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

本文转载自: 掘金

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

简单了解RabbitMQ集群搭建 1Rabbit MQ集群

发表于 2021-11-15

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

1.Rabbit MQ集群

rabbitmq有3种模式,但集群模式是2种。详细如下:

  • 单一模式:即单机情况不做集群,就单独运行一个rabbitmq而已。之前我们一直在用
  • 普通模式:默认模式,以两个节点(A、B)为例来进行说明
+ 当消息进入A节点的Queue后,consum er从B节点消费时,RabbitMQ会在A和B之间创建临时通道进行消息传输,把A中的消息实体取出并经过通过交给B发送给consumer
+ 当A故障后,B就无法取到A节点中未消费的消息实体


    - 如果做了消息持久化,那么得等A节点恢复,然后才可被消费
    - 如果没有持久化的话,就会产生消息丢失的现象
  • 镜像模式:非常经典的mirror 镜像模式,保证100% 数据不丢失。
+ 高可靠性解决方案,主要就是实现数据的同步,一般来讲是2 - 3 个节点实现数据同步
+ 对于100% 数据可靠性解决方案,一般是采用3 个节点。
+ 在实际工作中也是用得最多的,并且实现非常的简单,一般互联网大厂都会构建这种镜像集群模式
  • 还有主备模式,远程模式,多活模式等,本次课程不作为重点,可自行查阅资料了解

1.1 集群搭建

前置条件:准备两台linux,并安装好rabbitmq

  • 集群步骤如下:
  1. 修改/etc/hosts 映射文件

1号服务器:

1
2
3
4
5
6
7
8
9
10
bash复制代码127.0.0.1 A localhost	localhost.localdomain	localhost4

localhost4.localdomain4

::1 A localhost localhost.localdomain localhost6

localhost6.localdomain6

192.168.204.141 A
192.168.204.142 B

2号服务器:

1
2
3
4
5
6
7
bash复制代码127.0.0.1 B localhost	localhost.localdomain	localhost4
localhost4.localdomain4
::1 B localhost localhost.localdomain localhost6
localhost6.localdomain6

192.168.204.141 A
192.168.204.142 B
  1. 相互通信,cookie必须保持一致,同步 rabbitm q的cookie 文件:跨服务器拷贝.erlang.cookie(隐藏文件,使用ls -all 显示)
1
bash复制代码scp /var/lib/rabbitmq/.erlang.cookie 192.168.204.142:/var/lib/rabbitmq

修改cook ie文件,要重启服务器,reboot

  1. 停止防火墙,启动rabbitmq服务
1
2
3
bash复制代码systemctl stop firewalld

systemctl start rabbitmq- ser ver
  1. 加入集群节点
1
2
3
bash复制代码rabbitmqctl	stop_app
rabbitmqctl join_cluster rabbit@A
rabbitmqctl start_app
  1. 查看节点状态
1
bash复制代码rabbitmqctl cluster_status
  1. 查看管理端
* 搭建集群结构之后,之前创建的交换机、队列、用户都属于单一结构,在新的集群环境中是不能用的
* 所以在新的集群中重新手动添加用户即可(任意节点添加,所有节点共享)
1
2
3
4
bash复制代码
rabbitmqctl add_user root 123123
rabbitmqctl set_user_tags root administraor
rabbitmqctl set_permissions -p "/" root " . * " " . * " " . * "
  • 注意:当节点脱离集群还原成单一结构后,交换机,队列和用户等数据都会重新回来

image.png

  • 此时,集群搭建完毕,但是默认采用的模式“普通模式”,可靠性不高

1.2 镜像模式

  • 将所有队列设置为镜像队列,即队列会被复制到各个节点,各个节点状态一致
+ 语法:set\_policy {name} {pattern} {definition}
    - name:策略名,可自定义
    - pattern:队列的匹配模式(正则表达式)


        * "^" 可以使用正则表达式,比如"^queue\_" 表示对队列名称以“queue\_”开头的所有队列进行镜像,而"^"表示匹配所有的队列
    - definition:镜像定义,包括三个部分ha-m ode, ha-param s, ha-sync-m ode


        * ha-mode:(High Available,高可用)模式,指明镜像队列的模式,有效值为all/exactly/nodes,当前策略模式为all,即复制到所有节点,包含新增节点



        
1
2
3
markdown复制代码  - all :表示在集群中所有的节点上进行镜像
- exactly:表示在指定个数的节点上进行镜像,节点的个数由ha-par ams指定
- nodes :表示在指定的节点上进行镜像,节点名称通过ha-params 指定
- ha-params:ha-mode模式需要用到的参数 - ha-sync-mode:进行队列中消息的同步方式,有效值为automatic和manual
1
bash复制代码rabbitmqctl	set_policy xall	"^" '{ "ha-mode" :"all"} '
  • 通过管理端设置镜像策略

image.png

1.3 HAProxy实现镜像队列的负载均衡

  • 虽然我们在程序中访问A服务器,可以实现消息的同步,虽然在同步,但都是A服务器在接收消息,A太累
  • 是否可以想Nginx一样,做负载均衡,A和B轮流接收消息,再镜像同步

1.3.1 HAProxy简介

  • HA(HighAvailable,高可用),Proxy(代理)
  • HAProxy是一款提供高可用性,负载均衡,并且基于TCP和HTTP应用的代理软件
  • HAProxy完全免费
  • HAProxy可以支持数以万计的并发连接
  • HAProxy可以简单又安全的整合进架构中,同时还保护web服务器不被暴露到网络上

image.png

1.3.2 HAProxy与Nginx

  • OSI:(OpenSystem Interconnection:开放式系统互联 是把网络通信的工作分为7层,分别是物理层,数据链路层,网络层,传输层,会话层,表示层和应用层)
  • Nginx的优点:
+ 工作在OSI第7层,可以针对http应用做一些分流的策略
+ Nginx对网络的依赖非常小,理论上能ping通就就能进行负载功能,屹立至今的绝对优势
+ Nginx安装和配置比较简单,测试起来比较方便;
+ Nginx不仅仅是一款优秀的负载均衡器/反向代理软件,它同时也是功能强大的Web应用服务器
  • HAProxy的优点:
+ 工作在网络4层和7层,支持TCP与Http协议,
+ 它仅仅就只是一款负载均衡软件;单纯从效率上来讲HAProxy更会比Nginx有更出色的负载均衡速度,在并发处理上也是优于Nginx的
+ 支持8种负载均衡策略 ,支持心跳检测
  • 性能上HA胜,功能性和便利性上Nginx胜
  • 对于Http协议,Haproxy处理效率比Nginx高。所以,没有特殊要求的时候或者一般场景,建议使用Haproxy来做Http协议负载
  • 但如果是Web应用,那么建议使用Nginx!

1.3.3安装和配置

HAProxy下载:www.haproxy.org/download/1.…

  • 解压
1
bash复制代码tar	-zxvf	haproxy-1.8.12.tar.gz
  • make时需要使用 TARGET指定内核及版本
1
bash复制代码uname	-r 3.10.0-514.6.2.el7.x86_64

根据内核版本选择编译参数:

image.png

  • 进入目录,编译和安装
1
2
3
bash复制代码cd	haproxy-1.8.12
make TARGET=linux2628 PREFIX=/usr/local/haproxy
make install PREFIX=/usr/local/haproxy
  • 安装成功后,查看版本
1
bash复制代码/usr/local/haproxy/sbin/haproxy	-v
  • 配置启动文件,复制haproxy文件到/usr/sbin下 ,复制haproxy脚本,到/etc/init.d下
1
2
3
4
5
bash复制代码cp	/usr/local/haproxy/sbin/haproxy /usr/sbin/

cp ./examples/haproxy.init /etc/init.d/haproxy

chmod 755 /etc/init.d/haproxy
  • 创建系统账号
1
bash复制代码useradd	-r	haproxy
  • haproxy.cfg配置文件需要自行创建
1
2
bash复制代码mkdir	/etc/haproxy
vim/etc/haproxy/haproxy.cfg
  • 添加配置信息到haproxy.cfg
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
vim复制代码#全局配置
global
#设置日志
log 127.0.0.1 local0 info
#当前工作目录
chroot /usr/local/haproxy
#用户与用户组
user haproxy
group haproxy
#运行进程ID
uid 99
gid 99
#守护进程启动
daemon
#最大连接数
maxconn 4096

#默认配置
defaults
#应用全局的日志配置
log global
#默认的模式mode {tcp|http|health},TCP是4层,HTTP是7层,health只返回OK
mode tcp
#日志类别tcplog
option tcplog
#不记录健康检查日志信息


option dontlognull
#3次失败则认为服务不可用
retries 3
#每个进程可用的最大连接数
maxconn 2000
#连接超时
timeout connect 5s
#客户端超时30秒,ha就会发起重新连接
timeout client 30s
#服务端超时15秒,ha就会发起重新连接
timeout server 15s


#绑定配置
listen rabbitmq_cluster
bind 192.168.204.143:5672
#配置TCP模式
mode tcp
#简单的轮询
balance roundrobin
#RabbitMQ集群节点配置,每隔5秒对mq集群做检查,2次正确证明服务可用,3次失败证
明服务不可用
server A 192.168.204.141:5672 check inter 5000 rise 2 fall 3
server B 192.168.204.142:5672 check inter 5000 rise 2 fall 3
#haproxy监控页面地址
listen monitor
bind 192.168.204.143:8100
mode http
option httplog
stats enable
# 监控页面地址 http://192.168.204.143:8100/monitor
stats uri /monitor
stats refresh 5s
  • 启动HAProxy
1
bash复制代码service	haproxy	start
  • 访问监控中心:http://192.168.204.143:8100/monitor

记得关闭防火墙: systemctl stop firewalld

image.png

  • 项目发消息,只需要将服务器地址修改为143即可,其余不变
  • 所有的请求都会交给HAProxy,其负载均衡给每个rabbitmq服务器

1.4 KeepAlived搭建高可用的HAProxy集群

如果HAProxy服务器宕机,rabbitmq服务器就不可用了。所以我们需要对HAProxy也要做高可用的集群

1.4.1 概述

  • Keepalived是Linux下一个轻量级别的高可用热备解决方案
  • Keepalived的作用是检测服务器的状态,它根据TCP/IP参考模型的第三、第四层、第五层交换机制检测每个服务节点的状态,如果有一台web服务器宕机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其他服务器代替该服务器的工作,当服务器工作正常后Keepalived自动将服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的服务器。
  • keepalived基于vrrp(VirtualRouterRedundancyProtocol,虚拟路由冗余协议)协议,vrrp它是一种主备(主机和备用机)模式的协议,通过VRRP可以在网络发生故障时透明的进行设备切换而不影响主机之间的数据通信
  • 两台主机之间生成一个虚拟的ip,我们称漂移ip,漂移ip由主服务器承担,一但主服务器宕机,备份服务器就会抢夺漂移ip,继续工作,有效的解决了群集中的单点故障
  • 将多台路由器设备虚拟成一个设备,对外提供统一ip(VIP)

image.png

1.4.2安装 KeepAlived

  • 修改hosts文件的地址映射
ip 用途 主机名
192.168.204.143 KeepAlivedHAProxy C
192.168.204.144 KeepAlivedHAProxy D
  • 安装keepalived
1
bash复制代码yuminstall	-y	keepalived
  • 修改配置文件(内容大改,不如删掉,重新创建)
1
2
bash复制代码rm -rf /etc/keepalived/keepalived.conf
vim /etc/keepalived/keepalived.conf
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
44
vim复制代码


!ConfigurationFileforkeepalived


global_defs{
router_idC ##非常重要,标识本机的hostname
}
vrrp_scriptchk_haproxy{
script"/etc/keepalived/haproxy_check.sh" ##执行的脚本位置
interval2 ##检测时间间隔
weight-20 ##如果条件成立则权重减20
}

vrrp_instanceVI_1{
stateMASTER ##非常重要,标识主机,备用机143改为 BACKUP
interfaceens33 ##非常重要,网卡名(ifconfig查看)
virtual_router_id66 ##非常重要,自定义,虚拟路由ID号(主备节点要相同)
priority100 ##优先级(0-254),一般主机的大于备机
advert_int1 ##主备信息发送间隔,两个节点必须一致,默认1秒
authentication{ ##认证匹配,设置认证类型和密码,MASTER和BACKUP必须使
用相同的密码才能正常通信
auth_typePASS
auth_pass1111
}
track_script{
chk_haproxy ##检查haproxy健康状况的脚本
}
virtual_ipaddress{ ##简称“VIP”
192.168.204.66/24 ##非常重要,虚拟ip,可以指定多个,以后连接mq就用这个虚
拟ip
}
}

virtual_server192.168.204.665672{ ##虚拟ip的详细配置
delay_loop6 #健康检查间隔,单位为秒
lb_algorr #lvs调度算法rr|wrr|lc|wlc|lblc|sh|dh
lb_kindNAT #负载均衡转发规则。一般包括DR,NAT,TUN3种
protocolTCP #转发协议,有TCP和UDP两种,一般用TCP
real_server192.168.204.1435672{ ##本机的真实ip
weight1 #默认为1,0为失效
}
}
  • 创建执行脚本 /etc/keepalived/haproxy_check.sh
1
2
3
4
5
6
7
8
9
vim复制代码#!/bin/bash
COUNT=`ps -C haproxy --no-header |wc -l`
if [ $COUNT -eq 0 ];then
/usr/local/haproxy/sbin/haproxy -f /etc/haproxy/haproxy.cfg
sleep 2
if [ `ps -C haproxy --no-header |wc -l` -eq 0 ];then
killall keepalived
fi
fi

Keepalived组之间的心跳检查并不能察觉到HAproxy负载是否正常,所以需要使用此脚本。

在Keepalived主机上,开启此脚本检测 HAproxy是否正常工作,如正常工作,记录日志。

如进程不存在,则尝试重启HAproxy,2秒后检测,如果还没有,则关掉主Keepalived,此时备
Keepalived检测到主 Keepalived挂掉,接管VIP,继续服务

  • 授权,否则不能执行
1
bash复制代码chmod	+x	/etc/keepalived/haproxy_check.sh
  • 启动keepalived(两台都启动)
1
2
bash复制代码systemctl	stop	firewalld
service keepalived start | stop | status | restart
  • 查看状态
1
2
bash复制代码ps	-ef	|	grep	haproxy
ps -ef | grep keepalived
  • 查看ip情况 ip addr 或 ip a
1
bash复制代码ip	a

image.png

此时,安装完毕,按照上面的步骤就可以安装第二台了(服务器hostname和ip注意要修改)

常见的网络错误:子网掩码、网关等信息要一致

image.png

1.4.3测试ip漂移的规则

  • 查看虚拟ip ip addr 或 ip a
  • 目前,C节点是主机,所以虚拟ip在C节点

image.png

  • 停止C的keepalived,虚拟ip漂移到D节点

image.png

  • 重新启动C节点keepalived,虚拟ip依旧在D节点,并不会由于C的回归而回归

image.png

  • 停止D的keepalived,虚拟ip再漂移回C节点

image.png

  • 测试vip+端口是否提供服务(在141,A服务器上测试)
1
bash复制代码url	192.168.204.66:5672

AMQP ## 正常提供
AMQP服务,表示通过vip访问mq服务正常

本文转载自: 掘金

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

Java内存分析工具MAT(Memory Analyzer

发表于 2021-11-15

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

详细介绍了Java内存分析工具MAT(Memory Analyzer Tool)的常见使用方法,MAT可以帮助Java程序员快速进行内存分析,定位问题。

MAT(Memory Analyzer Tool),一个基于Eclipse的跨平台的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它通过读取应用程序运行时由 Java 运行时环境生成的转储文件快照,可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。

官网地址:www.eclipse.org/mat/。

在这里插入图片描述

Memory Analyzer 可以用来处理 HPROF 二进制 Heap Dump 文件 、 IBM 系统 dump 文件(经过处理后)、以及来自各个平台上的 IBM portable Heap Dump s (PHD) 文件。通过Memory Analyzer 能够直观的看到Heap Dump文件中的:

  1. 对象信息:类、成员变量、直接量以及引用值;
  2. 类信息 类加载器 、 名称 、 超类 、 静态成员;
  3. Garbage Collections Roots JVM 可达的对象;
  4. 线程栈以及本地变量 获取快照时的线程栈信息 以及局部变量的详细信息 。

1 下载MAT

官方下载地址:www.eclipse.org/mat/downloa…。

在这里插入图片描述

下载完毕之后,解压,可以看到MemoryAnalyzer.exe文件,打开即可使用。

在这里插入图片描述

2 获取Dump文件

获取Heap Dump文件的方式有很多,通常是通过参数配置在特定的条件下触发堆转储,也可以通过工具来获取。常见命令的方式有如下两个:

  1. 启动时添加JVM参数-XX:+HeapDumpOnOutOfMemoryError,将会在发生系统OutOfMemoryError后自动转储一份HPROF二进制Heap Dump文件。
  2. 该配置会把快照保存在项目目录或者tomcat目录下,也可以通过 -XX:HeapDumpPath=/xxx/heapdump.hprof 来显示指定路径。
  3. OnOutOfMemoryError参数还允许用户指定当出现OOM时,指定某个脚本来完成一些动作,比如自动重启tomcat:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx/heapdump.hprof -XX:OnOutOfMemoryError="sh ~/restart.sh"
  4. 使用jmap参数,jmap -dump:[live,]format=b,file=<filename>,将会立即将生成堆转储快照到指定的位置指定的文件中。

3 编写错误程序

我们编写一个简单的代码,循环的创建UUID字符串拼接并添加到一个list中:

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码public class TestJvmOOM {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
String str = "";
for (int j = 0; j < 100; j++) {
str += UUID.randomUUID().toString();
}
list.add(str);
}
System.out.println("ok");
}
}

4 转储Heap Dump

设置启动参数,最大内存为4m,并且自动转储Heap Dump:-Xms4m -Xmx4m -XX:+HeapDumpOnOutOfMemoryError,IDEA这么设置:

在这里插入图片描述

运行程序之后很快抛出了OOM,并且自动生成了Heap Dump文件:

在这里插入图片描述

默认位置在项目目录下面:

在这里插入图片描述

下面开始分析!

4 使用MAT

双击MemoryAnalyzer.exe文件,打开:

在这里插入图片描述

点击File,Open Heap即可选择一个Heap Dump文件,我们选择刚才生成的文件。

在这里插入图片描述

选择打开的模式,我们选择第一个Leak Suspects Report模式,即用于分析内存泄漏,点击Finish即可打Heap Dump文件:

在这里插入图片描述

常见选项的含义:

  1. Leak Suspects Report:内存泄漏可疑点报告,自动检查堆转储是否存在泄漏嫌疑,报告哪些对象被保存起来,为什么它们没有被垃圾收集,最常用的模式。
  2. Component Report: 元件报告,分析一组对象是否存在可疑的内存问题:重复的字符串、空集合、终结器、弱引用等。
  3. Re-open previously run reports: 打开以前的运行报告;

4.1 OverView概览视图

Dump文件越大,打开的时间就越长,打开之后,OverView界面如下,Overview视图,显示了此份Dump文件的概要的信息,并展示了MAT常用的一些功能:

在这里插入图片描述

  1. Details:显示了一些统计信息,包括HeapDump的大小、类(Class)的数量、对象(Object)的数量、类加载器(Class Loader)的数量。
  2. Biggest Objects by Retained Size :以饼图的方式直观地显示了在dump中最大的几个对象,当鼠标光标移到某块区域的时候会在左边Inspector和Attributes窗口中显示详细的信息,在区块上点击左键可以通过菜单获取更详细的信息。
  3. Actions :几种常用到的操作,包括 Histogram、Dominator Tree、Top Consumers、Duplicate Classes。
  4. Reports :列出了常用的报告信息,包括 Leak Suspects和Top Components。
  5. Step By Step :以向导的方式逐步的引导使用功能,包括Component Report。

下面我们来看看其他常用的功能。

4.2 Histogram直方图

Histogram直方图,用于展示每个类型的实例的数量,以及 shallow size 和 retained size :

  1. shallow size:浅堆,代表了对象本身的内存占用,包括对象自身的内存占用,以及“为了引用”其他对象所占用的内存。
  2. 非数组类型的对象的 shallow heap=对象头+各成员变量大小之和+对齐填充。其中,各成员变量大小之和就是实例数据,如果存在继承的情况,需要包括父类成员变量
  3. 数组类型的对象的shallow heap=对象头+类型变量大小*数组长度+对齐填充,如果是引用类型,则是四字节或者八字节(64 位系统),如果是 boolean 类型,则是一个字节。这里类型变量大小*数组长度就是数组实例数据,强调是变量不实际是对象本身。
  4. Retained heap:深堆,一个统计结果,会循环计算引用的具体对象所占用的内存。但是深堆和“对象大小”有一点不同,深堆指的是一个对象被垃圾回收后,能够释放的内存大小,这些被释放的对象集合,叫做保留集(Retained Set)。

在这里插入图片描述

4.2.1 outgoing reference与incoming reference

当右键单击任何对象时,将看到下拉菜单,如果选择“ListObjects”菜单项,可以查看对象的outgoing reference(对象的引出)和incoming reference(对象的引入)。

在这里插入图片描述

Incomming Reference 指的是引用当前对象的对象,Outgoing Reference 指的是当前对象引用的对象。对象的incomming reference 保证对象处于 alive 从而免于被垃圾回收掉 ;Outgoing reference 则展示了对象内部的具体内容,有助于我们分析对象的属性 。

我们看看第一个最大的char[]的Incomming Reference:

在这里插入图片描述

可以看到,它们几乎都是被一个String对象的value属性引用的,实际上这个String就是我们拼接的UUID字符串。

4.3 Dominator Tree支配树

列出Heap Dump中处于活跃状态中的最大的几个对象,默认按 retained size进行排序,因此很容易找到占用内存最多的对象。

排在第一的最大的对象就是占用内存最多的对象,它在树中的子节点都是被该对象直接或间接引用的对象(这意味着当这个对象被回收的时候它的子节点对象也会被回收)。

一般定位OOM的时候,都是直接查看支配树的最大的对象,我们的Heap Dump中的支配树中,很明显ArrayList占用了最大的内存,里面的元素就是一个个拼接的UUID字符串,就是因为这个原因导致了OOM。

在这里插入图片描述

4.4 Thread Overview线程视图

点击上面的黄色齿轮,可以生成Heap Dump文件的时候线程视图Thread Overview,查看线程的运行情况,抛出的异常的分析。

在这里插入图片描述

我们的Heap Dump中的Thread Overview,结果如下,很明显OutOfMemoryError是由main线程抛出的:

在这里插入图片描述

4.5 Leak Suspects泄露分析报告

MAT会分析 Heap Dump 文件并检测内存泄漏的可能,比如一个或一组异常大的对象 。Leak Suspects用于生成内存泄漏嫌疑分析报告,非常有用,能够帮组我们快速的定位OOM的原因。

查看我们的Heap Dump中的Thread Overview,结果如下,如果此前的几项你还不确定OOM的原因的话,那么这里可以说直接告诉你原因了:

在这里插入图片描述

可以看到,有66.79%的内存由Object[]数组占有,所以比较可疑,这是非常有可能出现内存溢出的。点击Details查看详情:

在这里插入图片描述

可以看到集合中存储了大量的UUID字符串。实际上这里已经告诉我们是不是因此导致的内存溢出了,我们在details详情的最下面可以看到:

在这里插入图片描述

就是这个大对象数组(list集合使用数组存放的元素),因为需要分配连续的内存,而内存不足导致的OOM。

至此,分析完毕!

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

本文转载自: 掘金

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

319 灯泡开关 经典数论推论题

发表于 2021-11-15

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

题目描述

这是 LeetCode 上的 319. 灯泡开关 ,难度为 中等。

Tag : 「数学」

初始时有 n 个灯泡处于关闭状态。第一轮,你将会打开所有灯泡。接下来的第二轮,你将会每两个灯泡关闭一个。

第三轮,你每三个灯泡就切换一个灯泡的开关(即,打开变关闭,关闭变打开)。

第 i 轮,你每 i 个灯泡就切换一个灯泡的开关。直到第 n 轮,你只需要切换最后一个灯泡的开关。

找出并返回 n 轮后有多少个亮着的灯泡。

示例 1:

1
2
3
4
5
6
7
8
9
10
11
ini复制代码输入:n = 3

输出:1

解释:
初始时, 灯泡状态 [关闭, 关闭, 关闭].
第一轮后, 灯泡状态 [开启, 开启, 开启].
第二轮后, 灯泡状态 [开启, 关闭, 开启].
第三轮后, 灯泡状态 [开启, 关闭, 关闭].

你应该返回 1,因为只有一个灯泡还亮着。

示例 2:

1
2
3
ini复制代码输入:n = 0

输出:0

示例 3:

1
2
3
ini复制代码输入:n = 1

输出:1

提示:

  • 0<=n<=1090 <= n <= 10^90<=n<=109

数学

这是一道经典的数论题。

整理一下题意:第 iii 轮改变所有编号为 iii 的倍数的灯泡的状态(其中灯泡编号从 111 开始)。

一个编号为 xxx 的灯泡经过 nnn 轮后处于打开状态的充要条件为「该灯泡被切换状态次数为奇数次」。

同时,一个灯泡切换状态的次数为其约数的个数(去重)。

于是问题转换为:在 [1,n][1,n][1,n] 内有多少个数,其约数的个数为奇数。这些约数个数为奇数的灯泡就是最后亮着的灯泡。

又根据「约数」的定义,我们知道如果某个数 kkk 为 xxx 的约数,那么 xk\frac{x}{k}kx​ 亦为 xxx 的约数,即「约数」总是成对出现,那么某个数的约数个数为奇数,意味着某个约数在分解过程中出现了 222 次,且必然重复出现在同一次拆解中,即 k=xkk = \frac{x}{k}k=kx​,即有 xxx 为完全平方数(反之亦然)。

问题最终转换为:在 [1,n][1,n][1,n] 中完全平方数的个数为多少。

根据数论推论,[1,n][1,n][1,n] 中完全平方数的个数为 ⌊n⌋\left \lfloor \sqrt{n} \right \rfloor⌊n​⌋,即最后亮着的灯泡数量为 ⌊n⌋\left \lfloor \sqrt{n} \right \rfloor⌊n​⌋。

代码:

1
2
3
4
5
Java复制代码class Solution {
public int bulbSwitch(int n) {
return (int)Math.sqrt(n);
}
}
  • 时间复杂度:O(1)O(1)O(1)
  • 空间复杂度:O(1)O(1)O(1)

最后

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

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

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

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

本文转载自: 掘金

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

使用友盟+的APM服务实现对移动端APP的性能监控 背景 体

发表于 2021-11-15

简介: 对于信息系统服务,一般我们的重点监控对象都是核心的后端服务,通常会采用一些主流的APM(Application Performance Management)框架进行监控、告警、分析。那么对于移动端的APP、小程序的运行时状态如何进行实时监控与分析呢?经过这次CSDN官方的推荐,友盟+提供的APM服务可以实现我们的这一目标,下面我们就尝试集成体验下友盟+提供的这款APM服务。

背景

对于信息系统服务,一般我们的重点监控对象都是核心的后端服务,通常会采用一些主流的APM(Application Performance Management)框架进行监控、告警、分析。那么对于移动端的APP、小程序的运行时状态如何进行实时监控与分析呢?经过这次CSDN官方的推荐,友盟+提供的APM服务可以实现我们的这一目标,下面我们就尝试集成体验下友盟+提供的这款APM服务。

由于我们的 APP是基于华为的鸿蒙操作系统进行开发,而注册友盟后,发现当前对于移动端APP,友盟官方提供了 Android与 iOS移动端操作系统的集成与接入,尚未提供对华为鸿蒙操作系统的支持(不过后来看到在上报的异常处可按照 只看鸿蒙设备 进行筛选:指发生错误/异常的设备为HarmonyOS,但尚未支持HarmonyOS系统级别错误)。

体验流程

本着体验下友盟+提供的APM服务的目的,这里使用一个基于 Android 的 Hello World 示例:

  • 首先按照官方文档完成移动端 APP 与友盟 SDK 的集成;
  • 然后在示例中抛出一些异常( OOM 等);
  • 最后在友盟的移动端应用监控首页可以看到我们应用上报的异常信息。

使用IDEA创建Android项目

当然,集成之前需要先搭建一个极简的 Android 的 Hello World 应用。

由于我之前并没有Android的移动端开发经验,当然也没有对应的开发环境,那么这里直接使用IDEA创建一个Android应用。

安装AndroidSDK

在IDEA中创建一个Android项目,先安装Android的SDK,我这里使用默认位置直接安装。

创建一个空的Activity

创建一个虚拟设备

虚拟设备安装完成

集成友盟+APM的SDK

友盟+的APM的SDK集成过程也是相当丝滑,参考官方文档推荐的 maven 线上接入 SDK,只需三步:1.创建应用;2.添加SDK依赖;3.初始化信息上报。

1. 创建应用

2. 添加SDK依赖

3. 初始化信息上报

涉及到的配置与代码如下:

dependencies {

implementation fileTree(dir: ‘libs’, include: [‘*.jar’])

implementation ‘androidx.appcompat:appcompat:1.0.2’

implementation ‘androidx.constraintlayout:constraintlayout:1.1.3’

testImplementation ‘junit:junit:4.12’

androidTestImplementation ‘androidx.test:runner:1.1.1’

androidTestImplementation ‘androidx.test.espresso:espresso-core:3.1.1’

// 友盟基础组件库(所有友盟业务SDK都依赖基础组件库)

implementation “com.umeng.umsdk:common:9.4.2” // 版本号(必选))

implementation “com.umeng.umsdk:asms:1.4.1” // asms包依赖(必选)

implementation “com.umeng.umsdk:apm:1.4.2” // U-APM产品包依赖(必选)

}

// umeng

buildscript {

repositories {

google()

jcenter()

maven { url ‘repo1.maven.org/maven2/‘ }

}

dependencies {

classpath ‘com.android.tools.build:gradle:3.4.0’

// NOTE: Do not place your application dependencies here; they belong

// in the individual module build.gradle files

}

}

allprojects {

repositories {

google()

jcenter()

maven { url ‘repo1.maven.org/maven2/‘ }

}

}

// 通用初始化

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

// umeng

UMConfigure.setLogEnabled(true);

UMConfigure.init(this, “613b2a85314602341a0df802”, “csdn”, UMConfigure.DEVICE_TYPE_PHONE, “”);

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

}

最终配置

启动后报错:无法联网

由于项目启动后,友盟+的SDK会进行信息上报,这时需要联网,而默认创建项目后没有联网权限,因此会出现一个报错信息。

为APP配置联网权限

集成成功效果

模拟各类异常

为了体验友盟+APM服务基本的监控功能,我们这里模拟一些异常,然后看这些异常信息是否可以上报至APM平台。

  • 普通的运行时异常:ArithmeticException/NullPointerException
  • 栈溢出异常:StackOverflowError
  • 内存溢出异常:. OutOfMemoryError

除0异常:ArithmeticException

// 发送异常按钮的点击事件:模拟除零异常

public void exception(View view) {

try {

int value = 8 / 0;

} catch (Exception e) {

UMCrash.generateCustomLog(e, “5001”);

}

}

栈溢出异常:StackOverflowError

// 发送SoF异常按钮的点击事件:模拟栈溢出异常

public void exceptionStackOverFlow(View view) {

try {

exceptionStackOverFlow(view);

} catch (Exception e) {

UMCrash.generateCustomLog(e, “5002”);

}

}

内存溢出异常:. OutOfMemoryError

// 发送OOM异常按钮的点击事件:模拟内存溢出异常,当然OOM还有其他类型,这里以堆溢出为例进行演示

public void exceptionOutOfMemory(View view) {

try {

List<byte[]> list = new ArrayList<>();

while (true) {

list.add(new byte[1024 * 1024]);

}

} catch (Exception e) {

UMCrash.generateCustomLog(e, “5003”);

}

}

异常自动上报

除0异常上报:ArithmeticException

栈溢出异常上报:StackOverflowError

内存溢出异常上报:OutOfMemoryError

异常告警

支持邮箱、钉钉、企业微信、飞书以及Webhook方式推送。

统计分析

可对不同类型的异常进行多维度的统计与分析,大家可以继续探索挖掘。

总结

通过以上流程,我们测试了移动端APP异常数据上报与统计功能,这个其实有点类似我们物联网设备数据采集、传输与分析的流程,而且整个集成的流程也比较顺畅,简单几步就给我们的APP做了做了一个服务远程监控端。

后面我们APP上线后,友盟+这款APM服务将是我们线上移动端应用监控的一把利器。另外,期待友盟+官方可以提供鸿蒙操作系统的接入,以便直接支持Huawei DevEco Studio中的基于HarmonyOS 虚拟设备,同时方便开发与调试。

作者:牛晓青

  1. 曾参与脑电信号模式识别研究,教学软件、分布式互联网项目以及大型物联网平台的设计开发等工作。
  1. 有4年团队管理经验,曾从0到1搭建技术团队,从最初的1人到现在的25人。负责的项目已落地多个城市。
  1. 负责20+人团队管理、日常工作任务分配、工作进度跟踪、工作成果汇报、团队内部分享。精进管理,取得PMP项目管理专业人士资格认证。
  1. 负责公司业务平台架构设计,方案编写,资金申报,研发部团队建设,项目验收培训、对外宣讲等工作。持续输出,获得CSDN博客专家认证。

原文链接

本文为阿里云原创内容,未经允许不得转载。

本文转载自: 掘金

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

kafka(一):kafka集群部署(kafka+zk模式)

发表于 2021-11-15

kafka集群部署(kafka+zk模式)

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

kafka+zk模式

1. 集群规划

如果没有物理机,也可以弄3台虚拟机。ubuntu系统不会安装的话可以自己百度哦,这里就不细说了。

作者用的是虚拟机,下面是3台电脑的配置

主机名 ip地址 broker.id myid
kafka1 192.168.56.107 0 1
kafka2 192.168.56.108 1 2
kafka3 192.168.56.109 2 3

注:环境篇 一一 Virtualbox+Vagrant安装Centos7

2. 下载安装包

官网下载

image.png

wget https://dlcdn.apache.org/kafka/3.0.0/kafka_2.13-3.0.0.tgz

3. kafka集群搭建

  1. 解压安装包指定目录

tar -zxvf kafka_2.13-3.0.0.tgz -C /app

  1. 创建数据文件夹

cd kafka_2.13-3.0.0/ & mkdir /app/kafka_2.13-3.0.0/logs

  1. 修改配置文件server.properties

vi config/server.properties

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
sh复制代码#advertised_listeners` 是对外暴露的服务端口,真正建立连接用的是 `listeners`,集群要改为自己的iP
advertised.listeners=PLAINTEXT://192.168.56.107:9092
#broker 集群中的全局唯一编号,不能重复
broker.id=0
#删除 topic 功能
delete.topic.enable=true
#自动创建topic,false:生产者发送信息到已存在topic才没有报错
auto.create.topics.enable = false
#处理网络请求的线程数量
num.network.threads=3
#用来处理磁盘 IO 的现成数量
num.io.threads=8
#发送套接字的缓冲区大小
socket.send.buffer.bytes=102400
#接收套接字的缓冲区大小
socket.receive.buffer.bytes=102400
#请求套接字的缓冲区大小
socket.request.max.bytes=104857600
#修改kafka的日志目录和zookeeper数据目录,因为这两项默认放在tmp目录,而tmp目录中内容会随重启而丢失
log.dirs=/app/kafka_2.13-3.0.0/logs
#topic 在当前 broker 上的分区个数.分区数量一般与broker保持一致
num.partitions=3
#用来恢复和清理 data 下数据的线程数量
num.recovery.threads.per.data.dir=1
#segment 文件保留的最长时间,超时将被删除
log.retention.hours=168
#配置连接 Zookeeper 集群地址,新版自带Zookeeper
zookeeper.connect=192.168.56.107:2181,192.168.56.108:2181,192.168.56.109:2181

注:

  • 如果kafaka启动时加载的配置文件中server.properties没有配置delete.topic.enable=true,那么此时的删除并不是真正的删除,而是把topic标记为:marked for deletion
  • kafka如何彻底删除topic及数据
  • advertised.listeners 集群中不一样
  • broker.id 集群中不一样
  1. 配置环境变量

注:其他两台服务器相同操作,broker.id在集群中要唯一

4. 配置自带的zk

  1. 创建文件夹
    快照日志的存储路径
    mkdir /app/zookeeper/data -p

事物日志的存储路径
mkdir /app/zookeeper/datalog -p

  1. 修改配置文件zookeeper.properties

vi /app/kafka_2.13-3.0.0/config/zookeeper.properties

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
44
45
46
sh复制代码# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# the directory where the snapshot is stored.、
#快照日志的存储路径
dataDir=/app/zookeeper/data
# the port at which the clients will connect
#这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。修改他的端口改大点
clientPort=2181
# disable the per-ip limit on the number of connections since this is a non-production config
maxClientCnxns=0
# Disable the adminserver by default to avoid port conflicts.
# Set the port to something non-conflicting if choosing to enable this
admin.enableServer=false
# admin.serverPort=8080

####################自定义####################
#这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
tickTime=2000
#这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,
#而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超 过 5个心跳的时间(也就是 tickTime)
#长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10 秒
initLimit=10
#这个配置项标识 Leader 与Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是5*2000=10秒
syncLimit=5

#事物日志的存储路径,如果不配置这个那么事物日志会默认存储到dataDir制定的目录,这样会严重影响zk的性能,当zk吞吐量较大的时候,产生的事物日志、快照日志太多
dataLogDir=/app/zookeeper/datalog


server.1=192.168.56.107:2888:3888
server.2=192.168.56.108:2888:3888
server.3=192.168.56.109:2888:3888
#server.1 这个1是服务器的标识也可以是其他的数字, 表示这个是第几号服务器,用来标识服务器,这个标识要写到快照目录下面myid文件里
#192.168.7.107为集群里的IP地址,第一个端口是master和slave之间的通信端口,默认是2888,第二个端口是leader选举的端口,集群刚启动的时候选举或者leader挂掉之后进行新的选举的端口默认是3888
  1. 在配置dataDir路径下创建myid文件
服务器 创建命令
192.168.56.107 echo “1” > /app/zookeeper/data/myid
192.168.56.108 echo “2” > /app/zookeeper/data/myid
192.168.56.109 echo “3” > /app/zookeeper/data/myid

注:

  • myid文件和server.myid 在快照目录下存放的标识本台服务器的文件,他是整个zk集群用来发现彼此的一个重要标识。
  • 其他两台服务器相同操作,myid文件内容在集群中要唯一

5. jdk安装并配置环境变量

注:

  • 环境篇 一一 linux安装jdk(收藏篇)
  • zookeeper是用java写的所以他的需要java环境

6. kafka环境变量配置

新建一个配置
sudo vi /etc/profile.d/kafka.sh

1
2
3
sh复制代码#KAFKA_HOME
export KAFKA_HOME=/app/kafka_2.13-3.0.0
export PATH=$PATH:$KAFKA_HOME/bin

刷新配置
source /etc/profile

7. 启动集群

  1. 先启动zookeeper
1
sh复制代码bin/zookeeper-server-start.sh -daemon config/zookeeper.properties
1
sh复制代码nohup bin/zookeeper-server-start.sh config/zookeeper.properties > log/zookeeper/zookeeper.log 2>1 &
  1. 再启动kafka
1
sh复制代码bin/kafka-server-start.sh -daemon config/server.properties
1
sh复制代码nohup bin/kafka-server-start.sh config/server.properties > zklog/kafka.log 2>1 &

依次在 kafka1、kafka2、kafka3 节点上启动

8. 关闭集群

依次在 kafka1、kafka2、kafka3 节点上关闭 kafka

1
sh复制代码bin/kafka-server-stop.sh stop

本文转载自: 掘金

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

Flask 入门系列之 Cookie&session!

发表于 2021-11-15

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

Cookie的使用

什么是Cookie

我们都知道,HTTP 协议是无状态的,也就是说,在一次请求响应结束后,服务端不会保留任何对方状态的信息,对于某些需要保留的信息,可以通过 Cookie 技术来完成,通过在请求和响应报文中添加 Cookie 数据来保存客户端的状态信息。

Web 服务器为了存储某些数据(比如用户信息) 而保存在浏览器上的小型文本数据,浏览器会在一定时间内保存它,在下次向该服务器发送请求时附带这些数据,Cookie 通常被用来进行用户会话管理(比如用户的登录状态),保存用户的一些信息。

在Flask中使用Cookie

在 Flask 中,如果想要在响应中添加一个 cookie,可以使用 response 对象的set_cookie()方法。

set_ cookie()方法的参数如下:

  • key:cookie 键名
  • value:cookie 值
  • max_age:cookie 被保存的时间,单位:秒
  • expires:具体的过期时间
  • path:限制 cookie 可用的路径,默认为整个域名
  • domain:cookie 可用的域名
  • secure:设置为 True,只有通过 HTTPS 才可使用
  • httponly:设置为True,禁止客户端 js 获取 cookie

具体使用如下:

1
2
3
4
5
python复制代码@app.route('user/<name>')
def user(name):
response = make_response('hello {}'.format(name), 200)
response.set_cookie("name", name)
return response

在 Flask 中,Cookie 可以通过 request 请求对象 cookies 属性获取。

1
2
3
4
5
6
python复制代码@app.route('/hello')
def hello():
user = request.args.get('name')
if not user:
user = request.cookies.get("name", 'default')
return 'Hello {}!'.format(user)

session的使用

什么是session

session 指用户会话,会话可以用来保存当前请求的一些状态,以便于在请求之前共享信息。session是存储在服务端的,通过唯一标识区分用户 session,即 session id,一般情况下,session id 是存在 Cookie 中的,服务端可以获取 Cookie 中的 session id 来获取用户 session。

Flask中的session对象

我们了解了 Cookie 最重要的功能是用来保存客户端用户的状态信息。但是存在这样一个问题,Cookie 是保存在客户端的,在浏览器中可以轻易的添加和修改 Cookie,而且如果把用户的状态信息以明文的方式存储在 Cookie 里的话,那么就可以通过伪造 Cookie 信息来伪造别人的用户信息,从而获取一些权限。为了避免这个问题,我们要对敏感的 Cookie 内容进行加密。Flask 提供了 session 对象就是用来将 Cookie 数据加密存储的。

在Flask中使用session

session 需要通过密钥对数据进行签名以加密数据,因此,需要先设置一个密钥app.secret_key,这里设置的密钥只是一个简单示例,在生产环境中,必须要随机生成的密钥,保证秘钥的复杂度度和随机性,才更安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
python复制代码# 设置密钥
app.secret_key = 'qwertyuiop'


@app.route('/user', methods=['POST', 'GET'])
def user():
if request.method == 'POST':
user = request.form['user_name']
session['user_name'] = request.form['user_name']
return 'Hello {}!'.format(user)
else:
if 'user_name' in session:
return 'Hello {}!'.format(session["user_name"])

session 对象的使用就跟字典一样,如上,用户使用POST请求登录后,在 session 中保存用户名,下次再使用GET请求时,不传递任何信息,也可以在 session 中获取该用户的信息。

想要清除 session 指定信息的话,例如用户名,可以使用 session.pop("user_name", None),清除全部的话可以使用session.clear()。

原创不易,如果小伙伴们觉得有帮助,麻烦点个赞再走呗~

最后,感谢女朋友在工作和生活中的包容、理解与支持 !

本文转载自: 掘金

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

1…338339340…956

开发者博客

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