作者归档:徐承恩

亿级数据库分库分表实践

首先说明一下,这是贝聊2016年针对班级动态所实施的一个数据库分库分表方案,经过一年多的验证,证明我们的方案是可行的,因此分享给大家。

一、业务场景

班级动态是贝聊为家长和老师提供的一个核心功能,类似于微信朋友圈、微博、QQ空间等的好友动态功能,是幼儿园家长老师使用频率最高的功能之一。在贝聊,老师和家长都可以在班级里发布动态、评论动态、点赞动态,内容可以包含文字、图片或者视频等内容。目前只要涉及到好友、粉丝之类的APP或者是网站基本都有这个功能,所以这个业务场景大家应该都很熟悉,就不多介绍了,附一张贝聊APP里班级动态的截图:

二、现状(2016年)

贝聊从2013年成立,至2016年,使用贝聊的幼儿园已经达到几万所,班级动态业务涉及的数据也已经达到亿级,且每天以几十万的速度在增长,预计三年左右就能达到数十亿。与此同时,动态的回复和点赞的数量更大,通常是动态量的几倍到十几倍的样子。

而班级动态、评论与点赞三个表,当时跟幼儿园、班级等主业务数据表放在一起,直接存储在阿里云RDS(MySQL数据库)上,虽然有做主从,但未做分库分表处理(这应该是创业公司初期的通病)。

三、存在问题

容量瓶颈: 单机数据库随着数据量和访问量的增长一定会遇到服务瓶颈。
扩展困难: 单机数据库扩展最终需要依赖硬件升级,扩展过程复杂、对业务影响大。
使用成本高: 单机数据库为获得更高的服务能力,依赖特定的高端设备,成本高昂。
可靠性低: 单机数据库的数据集中存储,宕机时直接影响所有数据库数据的访问。

四、目标

优化后,未来三到五年不需要进行大规模的优化。同时,在避免性能问题的基础上,能支撑几亿甚至几十亿的动态。

五、实施方案

1、独立班级动态数据库
从主业务数据库里剥离班级动态相关数据表,独立成库。原因很简单,一是独立出来方便做处理,二是任何一方有问题,都不会影响另一业务的正常运行。
2、切分班级动态数据库的数据
在要支撑几亿甚至几十亿的数据,同时有频繁的插入和查询的业务场景,不进行数据切分肯定是行不通的。

这里说的数据切分是水平切分,水平切分主要目的是为了突破单节点数据库服务器的 I/O 能力限制,解决数据库扩展性问题。水平切分时首先是通过一系列的切分规则将数据分布到不同的DB或table中,再通过相应的DB路由或者table路由规则找到需要查询的具体DB或者table,最后进行相关操作。

以下看看我们的班级动态、评论与点赞三个表:

由此可见,班级动态表、回复表与点赞表都与幼儿园id相关。实际业务场景就是如此,用户必须在其所在的幼儿园下发布动态,也只能查看、回复与点赞所在幼儿园的动态。那么选择幼儿园id字段作为分库与分表的键,不论是拆分、插入、查询都是没问题的。

那么,分多少个库合适,分多少个表合适,根据什么规则分,一旦单表数量再次达到性能瓶颈怎么办?针对这些问题,我们经过了充分的调研讨论,最后决定采用阿里云 DRDS方案来实施。

 

DRDS(Distributed Relational Database Service,分布式关系型数据库服务 )是阿里巴巴自主研发,致力于解决单机数据库瓶颈而推出的分布式数据库中间件产品。

 

先看看阿里云官网关于DRDS的产品介绍(这块好像DRDS的软文,阿里云是不是该付我们广告费呢): DRDS 高度兼容 MySQL 协议和语法,支持水平拆分、平滑扩容、弹性扩展、透明读写分离和分布式事务等特性,具备分布式数据库全生命周期的运维管控能力。

DRDS 支持库级拆分、表级拆分和分库分表拆分。拆分键暂时只支持单个字段。
分库键:DRDS 根据分库键的值将数据水平拆分到后端的每一个 RDS 分库里。键值相同的数据,一定会位于同一个 RDS 数据库里。
分表键:每一张逻辑表都可以定义自己的分表键,键值相同的数据,一定会位于同一个 RDS 数据表里。

由于我们在使用阿里云的RDS,同时我们只需要根据单个字段(幼儿园id)拆分,所以我们用阿里云 DRDS作为分库分表方案完全没毛病,对吧?切分方案确定后,丢给阿里搞,少死好多脑细胞,马云也一边偷着乐去了。

根据阿里云DRDS的官方建议:未来2年预估班级动态数据总量 = DRDS建议单表容量 X 分表数量 X 分库数量
3、独立动态业务
重构现有的班级动态模块,并将其独立成一个Dubbo服务组件。代码重构的目标主要有以下几点:
1)保证大部分的SQL语句能够使用分库分表键,避免全表扫描
2)避免使用JOIN语句,使用单表查询替代
3)灵活使用缓存策略减少DB压力

六、效果比较

1、 性能大幅度提升
响应速度提升5至10倍,以当时获取班级动态列表接口为例:
分库分表实施前:响应时间大致为200ms至1500ms级之间。
分库分表实施后:响应时间提升至大致为30ms至300ms之间。
2、 可方便快速进行横向扩容
如果需要扩容,直接按照阿里云DRDS指引,增加新的分库即可。
3、 架构的优化
独立动态业务,通过Dubbo提供服务,对系统进行解耦,让业务具备快速横向扩容能力。
4、后遗症
独立后,暂时没有做分布式事务,在业务中需要避免跨库事务。

转载声明

本文转载自知乎:贝聊亿级数据库分库分表实践

Mac重置VMware Fusion虚拟机网络脚本

前面我们说到如何修改VMware Fusion虚拟机的网段,那这篇文章就教你如何重置VMware Fusion虚拟机网段。

问题描述

修改VMware Fusion虚拟机网段的时候把专用网段设置成了公网的网段然后造成虚拟机启动后网络显示被拔出状态。

专用网段

C类专用网段:192.168.0.1~192.168.255.254    255.255.255.0
B类专用网段:172.16.0.1~172.31.255.254      255.255.0.0
A类专用网段:10.0.0.1~10.255.255.254        255.0.0.0
几个特殊的IP地址:127.0.0.1为本地回路测试地址
255.255.255.255代表广播地址
0.0.0.0代表任何网络
网络号全为0的代表本地网络和本地网段
网络号全为1的代表所有的网络
主机位全为0的代表某个网段的任何主机地址
主机位全为1的代表该网段的所有主机

重置脚本

#!/bin/bash
# Reset VMware Fusion Networking

# Clear out the Configuration
sudo rm -f /Library/Preferences/VMware\ Fusion/networking*
sudo rm -f /Library/Preferences/VMware\ Fusion/*location*
sudo rm -rf /Library/Preferences/VMware\ Fusion/vmnet*
sudo rm -rf /var/db/vmware/vmnet-dhcpd-vmnet*

# Reconfigure Networking
sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli -c
sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --stop
sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --start
sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --status

 

Mac系统修改VMware Fusion虚拟机IP段

Mac系统上虚拟机软件有很多款,不过萝卜青菜各有所爱,我个人目前在用的是VMware家的Fusion虚拟机毕竟VMware更加专业。目前在使用上有一个不友好的点就是不能可视化的配置虚拟机的IP地址。

桥接模式网络连接

如果您的 Mac 位于以太网、无线网或 FireWire 网络中,则使用桥接网络连接通常是使您的虚拟机可以访问该网络的最简单方法。使用桥接网络连接,虚拟机将显示为与 Mac 相同的物理以太网网络中的其他计算机。

使用桥接网络连接的虚拟机可能会使用在该虚拟机桥接到的网络上提供的任何服务,其中包括文件服务器、打印机和网关。同样,配置有桥接网络连接的任意物理主机或其他虚拟机可以使用虚拟机上的资源,就好像该虚拟机是同一个网络中的物理计算机。

桥接网络适配器称为 vmnet0。在 Fusion 3.x 及更高版本中,该适配器使用 vmnet-bridge 和 vmnet-netifup 服务。

仅主机型网络 – vmnet1

当使用此类型的网络连接时,虚拟机将连接到虚拟专用网络中的 Mac,这在 Mac 以外通常不可见。在同一个 Mac 中配置有仅主机网络的多个虚拟机将位于同一个网络中,并且互相可见。

仅主机网络适配器称为 vmnet1。在 Fusion 3.x 及更高版本中,该适配器使用 vmnet-dhcpd 服务。

网络地址转换 (NAT) 网络 – vmnet8

如果要使用 Mac 拨号网络连接的方法将虚拟机连接到 Internet 或其他 TCP/IP 网络,或者无法向虚拟机提供 Mac 的网络中的 IP 地址,则此类型通常是使您的虚拟机可以访问网络的最简单方法。此类型还允许虚拟机访问 Mac 已连接到的 VPN。

虚拟机在外部网络中没有自己的 IP 地址。相反,会在 Mac 中设置单独的专用网络。虚拟机从 VMware 虚拟 DHCP 服务器中获取该网络上的地址。除非虚拟机启动连接,否则无法直接通过除 Mac 以外的任意计算机或网站连接该虚拟机。

NAT 网络适配器称为 vmnet8。在 Fusion 3.x 及更高版本中,该适配器使用 vmnet-natd、vmnet-dhcpd 和 vmnet-netifup 服务。

自定义网络 IP 地址段

VMware Fusion 有三个网络配置文件:networking、dhcpd.conf 和 nat.conf。

全局:

/Library/Preferences/VMware\ Fusion/networking

vmnet1:

/Library/Preferences/VMware\ Fusion/vmnet1/dhcpd.conf
/Library/Preferences/VMware\ Fusion/vmnet1/nat.conf

vmnet8:

/Library/Preferences/VMware\ Fusion/vmnet8/dhcpd.conf
/Library/Preferences/VMware\ Fusion/vmnet8/nat.conf

修改 IP 地址段步骤如下,在 VMware Fusion 11.5 版本中测试通过。

停止 vmnet 网络服务

执行命令:

sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --stop

备注:这步是可选的,直接下一步也可以

只需要修改 networking 配置文件

执行命令:

sudo vi /Library/Preferences/VMware\ Fusion/networking

示例:将 vmnet1 中的 IP 段修改为 192.168.1.0,将 vmnet8 中的 IP 段修改为 10.10.1.0

VERSION=1,0
answer VNET_1_DHCP yes
answer VNET_1_DHCP_CFG_HASH 305D3393C78096F94C8C979DF2321B14BEE94AB1
answer VNET_1_HOSTONLY_NETMASK 255.255.255.0
answer VNET_1_HOSTONLY_SUBNET 172.16.178.0  # 修改为 192.168.1.0
answer VNET_1_VIRTUAL_ADAPTER yes
answer VNET_8_DHCP yes
answer VNET_8_DHCP_CFG_HASH DE662EAB01380DE3338128A859C717A8F863F3CF
answer VNET_8_HOSTONLY_NETMASK 255.255.255.0
answer VNET_8_HOSTONLY_SUBNET 172.16.24.0  # 修改为 10.10.1.0
answer VNET_8_NAT yes
answer VNET_8_VIRTUAL_ADAPTER yes

配置网络

执行命令:

sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --configure

vmnet-cli 将根据上述修改的地址段自动修改 dhcpd.conf 和 nat.conf 中的 IP 地址。

查看 dhcpd.conf 和 nat.conf 配置文件:

cat /Library/Preferences/VMware\ Fusion/vmnet1/dhcpd.conf
cat /Library/Preferences/VMware\ Fusion/vmnet1/nat.conf

cat /Library/Preferences/VMware\ Fusion/vmnet8/dhcpd.conf
cat /Library/Preferences/VMware\ Fusion/vmnet8/nat.conf

可以看到配置已经修改成功。

启动网络服务

执行命令:

sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --start

验证

执行命令:

ifconfig

可以看到 vmnet1 和 vmnet8 的 IP 地址已经更改成功。

虚拟机重新获取配置

虚机如果是手动配置的 IP,直接修改即可。

虚机如果是 DHCP,可以直接重启 vmware fusion 和 虚机系统,也可以直接在虚机重新获取地址,比如 Linux 命令行中执行 sudo dhclient -v -r eth0 ,eth0 为对应网卡。

使用ZeroTier组网访问局域网设备

使用ZeroTier组网后通过目标设备访问整个局域网设备这时我们需要在目标设备设置iptables进行转发。

管理路由

开启以太网桥

点击小扳手图标展开勾选Allow Ethernet Bridging.如图所示

开启以太网桥

iptables配置NAT

iptables -A FORWARD -d ?.?.?.0/24 -j ACCEPT
iptables -t nat -A POSTROUTING -s ?.?.?.0/24 -j MASQUERADE
iptables -t nat -A POSTROUTING -d ?.?.?.0/24 -j MASQUERADE
iptables -A FORWARD -s S.S.S.0/24 -j ACCEPT
iptables -t nat -I POSTROUTING -o INTERFACE -j MASQUERADE
iptables -I FORWARD -i INTERFACE -j ACCEPT
iptables -I FORWARD -o INTERFACE -j ACCEPT
iptables-save

?.?.?.0替换为局域网网段

S.S.S.0替换为ZeroTier分配的IP网段

INTERFACE替换为ZeroTier分配的网卡接口

惠普iLO序列号亲测可用

iLO是Integrated Ligths-out的简称,是HP服务器上集成的远程管理端口,它是一组芯片内部集成vxworks嵌入式操作系统,通过一个标准RJ45接口连接到工作环境的交换机。只要将服务器接入网络并且没有断开服务器的电源,不管HP服务器的处于何种状态(开机、关机、重启),都可以允许用户通过网络进行远程管理。简单来说,iLO是高级别的远程KVM系统,可以将服务器的显示信息显示在本地,并且使用本地的键盘鼠标控制、操作服务器,并可以将本地的光盘镜像、文件夹作为虚拟光驱映射并加载到服务器中。使用iLO,可以完成低层的BIOS设置、磁盘RAID配置、操作系统的安装等底层的工作,并且可以在完成系统安装后实现系统的远程控制与管理。

iLO4序列号

32CRX-V7BXC-D2MZN-L2DYZ-LZ88W

35395-JZ6HT-LP7QS-RG3V3-H59R2

iLO2序列号

什么是iLO2高级使用许可?

Integrated Lights-Out 2(iLO2)分为两种级别:标准版和高级版。iLO2标准版只具备iLO2的基本功能:

1虚拟电源按键,虚拟UID指示器,虚拟KVM字符模式

2服务器事件日志,可集成于HP 统一智能管理平台SIM

3集成的远程控制台,内置系统健康状况检查

4远程串行终端,支持共享高速工作网络接口

 

而Advanced Pack(iLO 2高级包)则具备对ProLiant服务器全面的Lights-Out远程管理能力。 iLO高级包可以使HP Proliant服务器用户非常简单地升级iLO标准版的功能,包括虚拟图形界面远程控制台、 Microsoft Terminal Services 集成、 虚拟软驱、虚拟光驱、 目录服务。全面的远程管理功能使IT 经理轻松管理跨地域的数据中心和分支机构。  使用iLO高级包,IT经理可以进行远程部署、诊断、ROM升级、配置和系统维护,无需赶到现场。

所有的功能可以通过标准的浏览器访问,如Internet Explorer,在服务器和管理端不需安装任何管理程序。通过TCP/IP网络的管理方式非常适用于大量服务器的管理,并可以节省出差成本,提高工作效率。

iLO高级包可以和企业的目录集成,因此具备更强的安全性。强大的远程管理功能使数据中心的无人部署成为可能,并可以节省键盘、鼠标、显示器、切换控制器和线缆的配置。

无论是在日常还是紧急情况下,您都可利用iLO 2 Advanced对ProLiant服务器实施远程控制。如果使用的是物理服务器,您可以执行任何任务,无论服务器或操作系统的情况如何,您都可随时对其进行远程管理。由于性能获得了大幅度的提升,iLO 2不仅仅是应付紧急情况的终极手段。这种工具还适用于日常管理,可以应付任何情况。购买了 HP ProLiant Essentials Integrated Lights-Out Advanced Pack之后,您可以在新一代HP ProLiant ML/DL 300和500、刀片系列服务器上应用iLO 2。

 

产品优势特点

iLO2在灵活性、安全性、远程管理性方面都有丰富的功能, 具有以下特点:

崭新功能:

a) Console replay :控制台录像。iLO2可以自动记录服务器启动过程中的错误,形成录像,用户可以回放,便于监控和判断故障原因。用户也可以手动录像远程控制台的操作过程。

b)  Shared Remote Console :多用户共享远程控制台;

c) HP SIM SSO:能够与SIM管理平台集成,采用SIM用户通过SIM管理平台即可直接链接并登录iLO2,无需再输入用户名、密码,便于集中管理;

d) 终端服务集成:能够集成Microsoft Terminal Services,用户通过iLO2即可进行远程桌面控制,减少对生产网卡的影响;

 

完全的远程控制:

a) 虚拟KVM: 集成的图形远程控制台;

b) 虚拟介质: 包括虚拟光驱, 虚拟软驱, 虚拟U盘, 虚拟镜像文件,虚拟文件夹等;

c) 虚拟电源: 可远程开机, 关机, 冷启动, 复位等;

d) 虚拟UID: 可控制远程服务器的UID灯;

e) 系统监控: 集成的系统监测, 信息及日志功能;

f) 电源管理: 集成功率调节器, 实时监测电源状态;

g) 脚本接口: 提供简单的脚本接口, 方便自定义功能;

h) 虚拟串口: 提供虚拟串行控制台功能, 方便CLI 管理.

 

可靠的安全性:

a) 数据加密: 使用128 位SSL 加密传输技术, 确保信息不泄漏;

b) 网络隔离: 完全的带外管理, 独立的网络接口, 更利于网络安全;

c) 目录认证: 支持微软活动目录认证功能, 能够与微软域AD管理集成;

d) 两种加密认证手段: 支持智能卡认证和USB Key认证方式;

 

 高度的灵活性:

a) 两种网络连接方式: 即可既可使用独立网卡, 也可共享系统网卡;

b) 多种接口: 支持CLI 和GUI 等多种连接方式, 还支持Telnet等网络协议;

c) 配置伸缩性: 可与RDP等远程部署软件集成, 提供自动化部署方案;

d) 非凡的集成性: 可集成在HP SIM, RDP, OA等多种管理工具中。

 

iLO 2的三种许可(License)有什么区别?
1.)HP ILO2 Standard具备基本的Lights-Out特性,如远程电源控制、串行控制台和设备定位指示灯;网络访问和安全特性,如SSL、Secure shell、专用及共享网络接口;此外。它还可提供时间日志、系统状态、功耗和配置等系统信息的访问。
2.)HP iLO Select Pack适用于分别由iLO 2 Standard Blade Edition和iLO 2 Standard的远程控制台管理ProLiant BL刀片服务器和ProLiant ML/DL服务器,升级到全面的Lights-Out功能。ProLiant BL刀片服务器标配有用于图形控制台访问的虚拟KVM控制台,而所有ProLiant ML、DL和BL服务器都标配有常用于命令行访问的远程串行控制台。HP iLO Select许可密钥可激活增强的控制台协作和重放、企业级安全性、虚拟介质和虚拟文件夹以及电源管理功能。
3.)HP iLO Advanced Pack 可以为ProLiant服务器环境轻松提供全面的Lights-Out功能。HP iLO Advanced许可密钥能够为支持iLO2 Standard的ProLiant服务器激活虚拟KVM远程控制台、增强的控制台协作和重现、虚拟介质与虚拟文件夹、增强的安全性以及电源管理功能。借助iLO Advanced Pack,能够远程实施所有硬件和系统管理任务。HP iLO Advanced Pack可支持iLO 和 ILO 2 两种管理处理器

 

 

Insight Control and ProLiant Essentials Evaluation Keys

The HP Insight Control and ProLiant Essentials 60-day evaluation keys are listed below. All keys are for 10 nodes and 60 days unless otherwise noted. You may print this document for your records.

If you have not done so already, download and install the software you wish to evaluate from http://www.hp.com/go/insightcontrol.

Insight Control suites
Insight Control Environment
Number of licenses: 10
ICE (for ML/DL):
35T73-9ZCT6-V6752-8S6PW-4YMW3
iLO-iLO2 Advanced Pack:
35T6X-PVCHN-T8M96-7YH6J-KYQ62
RDP (30 day): Download your license file*

Insight Control Environment for BladeSystem
Number of licenses: 10
ICE (for BL):
37HHQ-22XM6-GVX7J-NKCL2-DNXXR
iLO Select:
37HHT-N6XX6-NS2B6-WL3W8-HC34W
RDP (30 day): Download your license file*

Insight Control Linux Edition
Number of licenses: 10
35T6X-HXBSC-T55GV-JDRM6-6TB37
iLO Power Management Pack
Number of licenses: 10
Insight Power Mgmt:
35T6X-HXG75-5QHDG-2DMS3-NRQBM
iLO-iLO2 Advanced Pack:
3Q9CJ-RCMGP-KY2BH-3WLBN-NRPZH
iLO Power Management Pack for BladeSystem
Number of licenses: 10
Insight Power Mgmt:
37HHT-V4SRY-77G8P-L627X-QJSCR
iLO Select:
3Q9CJ-Y9N5J-D3ZJ2-5VZJ8-TT6HM

ProLiant Essentials
Product License key
Accelerated iSCSI Pack
Number of licenses: 10
35DQ2-TM6Y2-MY2HK-QXZBB-XZV63
Insight Power Manager
Number of licenses: 10
325XH-WQGRR-SNYT6-99ZRS-XXNS6
Integrated Lights-Out Advanced Pack for ProLiant servers with iLO and iLO 2 management processors
Number of licenses: 10
3Q9CJ-RCPVY-J39N5-5NKCT-SH4ZH
Integrated Lights-Out Select Pack for ProLiant servers and iLO 2 management processors
Number of licenses: 10
3Q9CJ-Y9N4Y-GTQ5Z-7GPLW-VK376
Lights-Out 100i Advanced Pack for ProLiant DL100 Series servers
Number of licenses: 10
356LN-8Q3YS-D3PGY-RR7RZ-T9PGJ
Lights-Out 100i Select Pack for ProLiant DL100 Series servers
Number of licenses: 10
356LN-8Q6DK-J6GG8-PMMSR-8B4JS
Performance Management Pack
Number of licenses: 10
32Q8W-GKL92-Q2GV8-ZP45K-7ZSPX
Rapid Deployment Pack
Number of licenses: 10
Duration: 30 days
Download your license file*

Server Migration Pack – Universal Edition
X to ProLiant Migrations:
Number of licenses: 2
Duration: 60 days
35JZK-7D2MC-KBSGS-ST9VW-225BM

X to Virtual Migrations:
Number of licenses: 2
Duration: 60 days
35JZK-7D2MC-KBSGS-ST9VW-225NS
Virtual Connect Enterprise Manager
For c7000:
Number of licenses: 4
Duration: 90 days
35T8Q-8RM72-LYSRL-RSVML-KY84B

For c3000:
Number of licenses: 4
Duration: 90 days
35T8Q-JHRDD-H28QC-Q5QT2-D27MM
Virtual Machine Management Pack
Number of licenses: 5
35S9T-PVZGQ-73DQS-P2NBW-82GYX
Vulnerability & Patch Management
Number of licenses: 10
325XH-PDGQP-795KW-KCC2S-CM4ZJ
* Instead of using a license key, RDP uses a license file that must be downloaded and applied on your server.

35GNT DG5RB 6LHDM HPTKM 2WRQH

下载慕课网视频技术分析

老姐在微信某课堂付费观看视频学习,观后遂萌生将此视频打包下载的念头,随后将此任务交给了我这个程序员弟弟。原本想找个下载软件应付了事奈何几乎没有这样的软件我也只好硬着头皮上,然后便有了下文。

这篇文章没什么技术含量但要具备一定的前端调试技术和耐心,所以呢我们要熟练的运用Chrome浏览器的开发者工具。

目前主流的在线视频播放都是基于HLS(m3u8文件)协议,简单的说我们观看视频的过程中会不间断的去服务器获取ts文件后缀的视频文件从而保持视频的正常播放。

m3u8文件

#EXTM3U
#EXT-X-TARGETDURATION:10

#EXTINF:9.009,
http://media.example.com/first.ts
#EXTINF:9.009,
http://media.example.com/second.ts
#EXTINF:3.003,
http://media.example.com/third.ts

以上代码是m3u8文件的一个例子。在这个代码中包含了许多ts文件后缀的链接,我们只需将这些链接逐个下载然后合并成一个视频文件就能实现我们的目的。

以上思考看起来视乎非常容易但实现起来往往比较复杂,因为每一家视频平台都有自己的加密和解密逻辑来保护视频版权,所以目前并没有一个万能可下载所有视频平台的软件。

这里我以慕课网为例来演示整个过程。

第一步:获取m3u8文件

打开谷歌浏览器的开发者工具找到network选项卡,选中XHR子选项卡然后随意找到一个视频进行播放并暗中观察如下图所示就是我们要找的m3u8文件。

请求https://www.imooc.com/course/playlist/7403?t=m3u8&_id=58538e6bb3fee3a05d8b5640&cdn=aliyun1得的响应代码

{
    "data": {
        "info": "amhjmVUqVD9UVCZHVFR8B1RUYVRUQCFUVCxkO1RUKEp0JCYBVFQiLCpOXFNUVENUVCIqZVRUCllUVEYwMCYIKj1UVEp/VFQ7VFQyaDFUVDtUVCtUVDNUVD1UVAg9VFRKdVRUO1RUSVRUY1RUR1RUJkNUVDVUVCFUVA1UVCdUVC42ZVRUb1RUPF5SVkRfVFRceVRUR1RUICdUVHQdVFQnVFQ2MD1UVCAHVFRNVFRAVglUVERcKlFUVFR+B1RUPVRUBBdUVAtUVAFUVFtUVH1UVBQcE1RUWgYkH1RUCBFUVB9UVBdUVD1UVAZMEh1UVAtUVB9UVCxIR1RUQVRUCVRUYVRUU1RUUgYWDVRUehYCAhdUVBVUVGIHVFReXBZaf1RURF1UVFdUVFwRVFQCNhYJVFRaAgVUVHpfVFRNVFQQUjxWCAIdVFQWPB5WQlQHVFQ+UlZCHBdUVCIvVFQAElhJVFQzVFQjVFQxVFRUF1RUJ1RUBEYrVFQLVFRHVFQINlgAEVRUFVRUOAZ4BVRUQHwBVFQLVFRaWVRUEDgTVFRcAD1UVDVUVGdUVAIRVFQeNlA7VFQ6BEQmPVRUa1RUHVRUAVRUC1RULVRUCVRULjgcCj4mcEVUVAgvVFQNVFQLVFQ6O1RUQBAqMVRUCAAIMVRUPBQQOHlUVAVUVDoGWVRUBgIxVFRYFBY4H1RUCgNUVDVUVHdUVDFUVAAvVFRTVFQ6HAVUVAgoQ1RUJnwNVFQfVFQBVFQ6VVRUPhtUVAFUVAYGQGdUVAFUVAIPVFQ8EBZdVFRwGiYtVFR/VFQ2BVRUMVRUB1RUJjlUVCp4AVRUMCZ7VFQkAC5TVFQTVFQjVFR4AjtUVAYnVFQAQgVUVCVUVD9UVAVUVCIpVFQMCjAqQVRUN1RUPi5aPCYhVFQ3VFQqMVRUNEFUVAVUVAYGUjNUVBVUVClUVEYlVFQGCC57VFQELiZUI1RUeDg6KVRUADdUVEdUVBg/VFQtVFQhVFQ+B1RUO1RUNVRUQVRUWAYZVFQ3VFQYF1RUBVRUH1RUGVRUdEFUVEJRVFQGTVRUWVRUYVRUUVRUUkFUVElUVD1UVB9UVAwXVFRJVFRfVFQ7VFQGDAtUVEoXVFR6MVRUPh9UVHQIQVRUAVRUKlACLhBCKiZBVFRoH1RUEVRUCCVUVDorVFQIB1RUKCZnVFQdVFQ/VFQcJlA4O1RUDEIpVFRaNkAdVFQqP1RUHDlUVAIIHD4mA1RUYAgqRldUVDBKNHptVFRRVFR0BixGO1RUJ1RUSVRUO1RUG1RUNVRUP1RUJhw9VFQcLVRUWVRUJjQgDiYiP1RUHD1UVBZWSFJULVRUCCNUVD4lVFR4MAYjVFRXVFReTFtUVHlUVEBXVFReEVRUJhdUVCw4KCFUVDtUVD9UVAApVFRPVFQARmpbVFQbVFRTVFRfVFRGQ1RUGBNUVAZBVFQHVFRoREwTVFQDVFQYZ1RUGVRUCh1UVBdUVF58CAlUVAxbVFQZVFQgFAIeHVRUQVRUal5QXFlUVBFUVCtUVBJUFBFUVFRhVFQKU1RUUVRUEFdUVCtUVEVUVFFUVEYBVFRbVFQ/VFQODVRUB1RUAVRUAh1UVGdUVFQHVFQJVFRLVFQxVFQPVFQNVFRZVFQDVFQVVFQgCVRUEhwAUiFUVBgPVFRZVFRJVFRFVFQhVFQILVRUDEQGO1RUAlYsJipfVFQ4KkQNVFR9VFRrVFRYB1RUV1RUF1RUKBonVFRWU1RUBEYfVFQeOlYbVFRNVFQFVFRDVFQ9VFQCa1RUBDNUVB1UVC9UVCFUVB5bVFQHVFQkKkZrVFQgCDoCDjoCE1RUJ1RUMmgIIBNUVFo5VFQgBhwHVFQkPnxCN1RUa1RUOVRUH1RUM1RUJi1UVB9UVCdUVFdUVAdUVFA8OhlUVDNUVFdUVCYrVFQfVFQnVFQxVFQIVVRUM1RUFAAfVFQgRAdUVBdUVBomfkFUVCAfVFQSLg5eLVRUA1RUHjVUVFAERgQeM1RUM1RUGBtUVCtUVEJeGHtUVCApVFQcNAgtVFQmBDVUVANUVFZmIDtUVBgxVFQhVFREHhgdVFRWUDlUVCFUVGNUVF1UVDQwMg42H1RUIggsZiIhVFQGIVRUTVRUG1RUN1RUKA9UVChnVFQyIho3VFQJVFRFVFQ4NigLVFREDlAYMVRUNVRUIVRUMVRUX1RUHidUVFZ/VFQ4MVRUY1RUWClUVDA6KgNUVCQpVFQXVFQ3VFQAO1RUBgYXVFQADgAkWkNUVAdUVERlVFRQUlZGWVRUfFYdVFQTVFRaEVRUVjwKCVRUG1RUACpVVFRUM1RUaBdUVBlUVF45VFQOW1RUPVRUE1RUHVRUQD58IF4LVFQuHjIHVFQDVFQ0Jn4FVFQgBAItVFQwMVRUW1RUBidUVCJAf1RUMDlUVF9UVDAsPVRUF1RUHVRUBDIKaCADVFQcOVRUJVRUUVRUXFVUVA1UVFQ2O1RUF1RUV1RUM1RUN1RUSVRULEIaNVRUJDdUVHA5VFR/VFQiLVRUIk4/VFQbVFQ/VFQgcCA5VFR/VFQiJ1RUWVRUQ1RURVRUaVRUMjwmdVRUI1RUL1RUG1RUN1RULF1UVElUVHxGV1RUQgFUVFhyOVRUJjtUVDdUVCNUVBwkLj1UVH9UVElUVGVUVFNUVFlUVBxAV1RUeVRUegYPVFRFVFQEIVRUUVRUTEtUVANUVBg+Dl4fVFReG1RUMVRURVRUAAtUVEAZVFQ/VFQZVFQDVFQXVFReW1RUZ1RUU1RUV1RUVkwKf1RUEgVUVFdUVEFUVBFUVDdUVFgCVEFUVHFUVAtUVBJSRAVUVEQHVFR9VFQME1RUWgJ6X1RUBVRUTVRUUhA8VggCHVRUFjweVkJUB1RUPlJWQhwXVFQiL1RUABJYSVRUM1RUI1RUMVRUVBdUVCdUVARGK1RUC1RUR1RUCDZYABFUVBVUVDgGeAVUVEB8AVRUC1RUWllUVBA4E1RUXAA9VFQ1VFRnVFQCEVRUHjZQO1RUOgREJj1UVGtUVB1UVAFUVAtUVC1UVAlUVC44HAo+JnBFVFQIL1RUDVRUC1RUOjtUVEAQKjFUVAgHVFQwWAARVFQVVFQnVFQhVFQCQVRUN1RUY1RUBDA8NVRUEB4oD1RUBAQ2AVRUDBwBVFQiV1RUO1RUBDtUVAlUVCoYaBhFVFQFVFQxVFQSFVRUXVRUH1RUBVRUN1RUJVRUXVRUADBZVFQOWj47VFQyB1RUMB5jVFQ2H1RULlIVVFQjVFQDVFQ7VFQgNVRUAVRUN1RUA1RUBVRUUgYvVFQVVFQ6eEYwJhVUVHdUVAQtVFQmI1RUHjdUVDlUVDoCN1RUCAAsM1RUC1RUIj4pVFQ5VFQBVFQzVFQfVFQAI1RULVRUM1RUD1RUUhVUVBVUVANUVCoxVFQ3VFR0RDwFVFRSLjoqeENUVC1UVCZ/VFQxVFQYP1RULVRUIVRUUhtUVCAdVFQBVFQCRVRUFVRUP1RUG1RUXlVUVEJfVFRJVFR+VEADVFRCZ1RUTVRUF1RUC1RUCh9UVCdUVE1UVB9UVA5eG1RUMVRUTlAlVFQtVFQ4DAg2KVRUY1RUHh1UVCVUVDwMLVRUOEQMKggMdAg/VFQxVFQOO1RUO1RUG1RULVRUKDZWHEdUVC9UVAAcLVRUO1RUGAdUVClUVAh7VFQcCAFUVCY6HVRUO1RUG1RUHVRUFVRUKVRUFEdUVE4WUCA=Ipgr1tRkcdto",
        "cdn": [
            "aliyun",
            "aliyun1",
            "letv"
        ]
    }
}

json文件中的info对应的value就是我们所需要的m3u8文件。

第二步:寻找解密函数

已知m3u8文件请求地址我们只需拦截断点调试就可以追踪出请求从哪里发起进而找到解密函数

开发者工具找到Sources选项卡,添加一个XHR请求断点下如图

刷新页面后直接进入断点调试模式跟踪代码执行情况如图

这里我已经找到了解密函数destm_1.default()

解密函数代码

function n(t, e) {
    function r(t, e) {
        var r = "";
        if ("object" == typeof t)
            for (var n = 0; n < t.length; n++) r += String.fromCharCode(t[n]);
        t = r || t;
        for (var i, o, a = new Uint8Array(t.length), s = e.length, n = 0; n < t.length; n++)
            o = n % s, i = t[n], i = i.toString().charCodeAt(0), a[n] = i ^ e.charCodeAt(
                o);
        return a
    }

    function n(t) {
        var e = "";
        if ("object" == typeof t)
            for (var r = 0; r < t.length; r++) e += String.fromCharCode(t[r]);
        t = e || t;
        var n = new Uint8Array(t.length);
        for (r = 0; r < t.length; r++) n[r] = t[r].toString().charCodeAt(0);
        var i, o, r = 0;
        for (r = 0; r < n.length; r++) 0 != (i = n[r] % 3) && r + i < n.length && (o =
            n[r + 1], n[r + 1] = n[r + i], n[r + i] = o, r = r + i + 1);
        return n
    }

    function i(t) {
        var e = "";
        if ("object" == typeof t)
            for (var r = 0; r < t.length; r++) e += String.fromCharCode(t[r]);
        t = e || t;
        var n = new Uint8Array(t.length);
        for (r = 0; r < t.length; r++) n[r] = t[r].toString().charCodeAt(0);
        var r = 0,
            i = 0,
            o = 0,
            a = 0;
        for (r = 0; r < n.length; r++) o = n[r] % 2, o && r++, a++;
        var s = new Uint8Array(a);
        for (r = 0; r < n.length; r++) o = n[r] % 2, s[i++] = o ? n[r++] : n[r];
        return s
    }

    function o(t, e) {
        var r = 0,
            n = 0,
            i = 0,
            o = 0,
            a = "";
        if ("object" == typeof t)
            for (var r = 0; r < t.length; r++) a += String.fromCharCode(t[r]);
        t = a || t;
        var s = new Uint8Array(t.length);
        for (r = 0; r < t.length; r++) s[r] = t[r].toString().charCodeAt(0);
        for (r = 0; r < t.length; r++)
            if (0 != (o = s[r] % 5) && 1 != o && r + o < s.length && (i = s[r + 1], n =
                r + 2, s[r + 1] = s[r + o], s[o + r] = i, (r = r + o + 1) - 2 > n))
                for (; n < r - 2; n++) s[n] = s[n] ^ e.charCodeAt(n % e.length);
        for (r = 0; r < t.length; r++) s[r] = s[r] ^ e.charCodeAt(r % e.length);
        return s
    }

    for (var a = {
        data: {
            info: t
        }
    }, s = {
        q: r,
        h: n,
        m: i,
        k: o
    }, l = a.data.info, u = l.substring(l.length - 4).split(""), c = 0; c < u.length; c++)
        u[c] = u[c].toString().charCodeAt(0) % 4;
    u.reverse();
    for (var d = [], c = 0; c < u.length; c++) d.push(l.substring(u[c] + 1, u[c] + 2)),
        l = l.substring(0, u[c] + 1) + l.substring(u[c] + 2);
    a.data.encrypt_table = d, a.data.key_table = [];
    for (var c in a.data.encrypt_table) "q" != a.data.encrypt_table[c] && "k" != a.data
        .encrypt_table[c] || (a.data.key_table.push(l.substring(l.length - 12)), l = l.substring(
        0, l.length - 12));
    a.data.key_table.reverse(), a.data.info = l;
    var f = new Array(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -
            1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57,
        58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 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, -1, -1, -1,
        -1, -1, -1, 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, -1, -1, -1, -1, -1);
    a.data.info = function (t) {
        var e, r, n, i, o, a, s;
        for (a = t.length, o = 0, s = ""; o < a;) {
            do {
                e = f[255 & t.charCodeAt(o++)]
            } while (o < a && -1 == e);
            if (-1 == e) break;
            do {
                r = f[255 & t.charCodeAt(o++)]
            } while (o < a && -1 == r);
            if (-1 == r) break;
            s += String.fromCharCode(e << 2 | (48 & r) >> 4);
            do {
                if (61 == (n = 255 & t.charCodeAt(o++))) return s;
                n = f[n]
            } while (o < a && -1 == n);
            if (-1 == n) break;
            s += String.fromCharCode((15 & r) << 4 | (60 & n) >> 2);
            do {
                if (61 == (i = 255 & t.charCodeAt(o++))) return s;
                i = f[i]
            } while (o < a && -1 == i);
            if (-1 == i) break;
            s += String.fromCharCode((3 & n) << 6 | i)
        }
        return s
    }(a.data.info);
    for (var c in a.data.encrypt_table) {
        var h = a.data.encrypt_table[c];
        if ("q" == h || "k" == h) {
            var p = a.data.key_table.pop();
            a.data.info = s[a.data.encrypt_table[c]](a.data.info, p)
        } else a.data.info = s[a.data.encrypt_table[c]](a.data.info)
    }
    if (e) return a.data.info;
    var g = "";
    for (c = 0; c < a.data.info.length; c++) g += String.fromCharCode(a.data.info[c]);
    return g
}

试验

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<script src="destm.js"></script>
<script>
    var jsonStr = {
        "info": "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"
    };

    //console.log(jsonStr.result);

    console.log(n(jsonStr.info));
</script>
</html>

控制台输出

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:5
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-KEY:METHOD=AES-128,URI="https://www.imooc.com/video/58538e6bb3fee3a05d8b5640/medium.hxk?timestamp=1607679008&token=imooc%3AN2E0MjQ3MDFiNmNhZWVkMWE4YjA2NDQ0OWRiY2UxN2NkOTI5ODdhYQ%3D%3D"
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/0da0f040c689f359.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/0da3c5c84b0948bb.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/8f14be2a480937ad.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/f8fb64181caf098b.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/d65f29415bc155aa.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/21b5b9e40dd21f86.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/8a247a0a3e8d6f46.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/f51d104dba06e143.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/016e8c2efcdc5082.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/20a4b5e78bafdceb.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/00ba02a4b622e395.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/6d7983c5cd62b0b2.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/fccf885dfa0dd57d.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/29020391c7f38f59.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/f3d5cbdbf69a3315.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/c9aeb8eb96912f08.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/e6418a04fd5235b7.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/a32b2a902218781b.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/24bacb3bcf276d1a.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/f14bbbb43e28563c.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/c0564fc373475e09.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/b78f8878383f6b62.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/47c993e2525ed75e.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/87a61da6af46fb44.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/da3de90571d23476.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/5a568293b2af64d2.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/5353d68510fdd67c.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/0a888ea1522f99ad.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/0404b70e57fea1d1.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/4bb42f970f4a3d25.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/9c33821f34c44fb3.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/caf44f9893d0547a.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/b35fdcbd8e06571f.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/6cdb843f131ed101.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/42c4464f8f414b87.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/0a41d58ed94d5627.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/1268dc0bddd01c2d.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/3f0568d69be9cd3e.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/d69010ec3c7730b8.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/ba05ba8feb915c78.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/a82217e3bb93fccf.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/012f3c7b2d91cef4.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/dedce3cba143e444.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/b09882217d9997dc.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/f1946df7e6a6109d.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/65474544d7d036ea.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/4cb8f3ae680a89f4.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/942ceaa8a46fe634.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/062f8d4b0ebab9d0.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/74214fcd39539a83.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/749f743036c599fe.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/b92b040e7c4076f1.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/5e500a7d294d367a.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/7352a3b6f8bf58cb.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/d5059e122ce693b1.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/ad94b2b2b1d1a95e.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/7f6a1e631670d51f.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/fce47df455dcf00d.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/0f5c1cb19d9d6243.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/71fad14be94d92a0.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/59665f46049acf89.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/ca7a6e2fea6589d0.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/ed8c4ba23cf7403e.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/09f1db14f91e3378.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/5a3abfcbc8775ed1.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/a6db882f61687756.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/543ebc764c58a5f1.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/155682a0c046875e.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/0f470156efc18ddd.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/968b77ca6467890f.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/6a8a554a9e6582ea.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/0d921cabb1a6a9ed.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/3ae3722a1e069a20.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/20a298059ccd26cd.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/1fdb3d23b15de5e0.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/117bddf07baf3ec7.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/53f917271cc9d699.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/78a1873531ef1740.ts
#EXTINF:5.000000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/8e9d8ef519ffd438.ts
#EXTINF:1.050000,
https://video1.sycdn.imooc.com/58538e6bb3fee3a05d8b563d/M/957e67b57b90248e.ts
#EXT-X-ENDLIST

 

Linux根据端口号查进程信息

只知道端口号不知道进程相关信息,那能不能通过端口号查询进程号呢?

使用lsof找出pid

lsof -i:22

例:

[root@xuchengen ~]# lsof -i:80
COMMAND   PID  USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
nginx   15673  root    6u  IPv4 14231513      0t0  TCP *:http (LISTEN)
nginx   15674 nginx    6u  IPv4 14231513      0t0  TCP *:http (LISTEN)

使用netstat找出pid

# 找出非监听端口
netstat -ntp | grep ":22"

例:

[root@xuchengen ~]# netstat -ntp | grep ":3389"
tcp        0      0 172.21.0.16:33464       47.96.175.183:3389      ESTABLISHED 1260/appnode-ccente 
tcp        0      0 172.21.0.16:33544       47.96.175.183:3389      ESTABLISHED 1260/appnode-ccente
# 找出监听端口
netstat -ntpl | grep ":22"

例:

[root@xuchengen ~]# netstat -ntpl | grep ":3389"
tcp        0      0 0.0.0.0:3389            0.0.0.0:*               LISTEN      1264/sshd

使用ps找出进程名

ps -ef|grep $pid

例:

[root@xuchengen ~]# ps -ef|grep 1264
root      1264     1  0 Nov05 ?        00:00:04 /usr/sbin/sshd -D
root     11148  5090  0 16:24 pts/0    00:00:00 grep --color=auto 1264

 

开发者必备Linux基础命令行

作为一名开发者熟练操作Linux命令行算是一项基本功。通常小公司没有运维所以呢开发及运维!

TOP命令

Linux top命令用于实时显示当前操作系统进程的动态。

第1行:系统时间、运行时间、登陆终端数、系统负载(分别为1分钟、5分钟、15分钟内的平均值)

第2行:进程总数、运行中的进程数、睡眠中的进程数、停止的进程数、僵死的进程数。

第3行:用户占用资源百分比、系统内核占用资源百分比、改变过优先级的进程资源百分比、空闲的资源百分比。

第4行:物理内存总量、空闲内存总量、内存使用量、作为内核缓存的内存量。

第5行:虚拟内存总量、空闲虚拟内存总量、虚拟内存使用量、①被提前加载的内存量。

《菜鸟教程Linux top命令》

HTOP命令

htop作为top的升级版可以更加直观的方式显示当前系统的状态。默认情况下Centos不会安装htop命令行程序需要手动安装。

# 安装htop命令
yum -y install htop

htop命令

IFCONFIG命令

Linux ifconfig命令用于显示或设置网络设备。

ifconfig可设置网络设备的状态,或是显示目前的设置。

默认情况下Centos7不会安装ifconfig命令需要手动安装。

# 安装ifconfig命令
yum -y install net-tools

《菜鸟教程Linux ifconfig命令》

TELNET命令

Linux telnet命令用于远端登入。

执行telnet指令开启终端机阶段作业,并登入远端主机。

一般而言我们会使用telnet命令探测目标服务器是否开放指定端口。

手动安装telnet命令程序:

# 安装telnet命令
yum -y install telnet

探测目标端口是否开放:

# 探测目标端口
telnet 127.0.0.1 3306
# 回显表明目标端口是开放状态
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

《菜鸟教程Linux telnet命令》

SSH命令

Linux ssh命令执行远程登录。

windows端使用Xshell客户端登录Linux服务器。

Mac端直接使用终端就够了。

默认22端口:

# ssh登录Linux服务器
ssh root@127.0.0.1

指定其他端口:

# ssh指定其他端口
ssh root@127.0.0.1 -p 3389

SCP命令

Linux scp 命令用于 Linux 之间复制文件和目录。

scp 是 secure copy 的缩写, scp 是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令。

scp 是加密的,rcp 是不加密的,scp 是 rcp 的加强版。

这个命令用的不多,一般也有替代客户端走sftp协议进行上传文件。

《菜鸟教程Linux SCP命令》

SZ命令

sz命令可以将服务器上的文件下载到本地。

通常我们排查应用程序日志会使用cat、tail亦或是vi、vim等命令,如果日志文件过于庞大排查起来也是很困难还不如下载下来用文本编辑器排查,这时候sz命令就派上用场。

# sz命令
sz xxx.log

 

SpringBoot通用日志解决方案

金融项目中对于业务较为敏感我们通常需要将用户的操作形成一个结构化的数据并进行持久化。

结构化日志需要的字段:

操作员信息、客户端IP地址、请求地址、控制器名称、控制器方法名称、HTTP请求类型、HTTP请求参数。

问题描述:

在获取请求参数时必然会读取request.getInputStream。由于流只允许读一次,后续读取必然会导致异常。

解决方案:

在SpringBoot框架中给我们提供了一个基于Filter的简单通用日志——CommonsRequestLoggingFilter,这个日志仅仅只实现了日志文件的输出远远达不到我们的设计目标。

通过阅读源码我发现了ContentCachingRequestWrapper这个类能够解决HttpServletRequest inputStream只能读取一次的问题,但是这个类有缺陷(前提必须是doFilter之前不能使用request.getInputStream()方法)。

配置Filter让后续的请求可以正常request.getInputStream

package com.bbc.ibank.sys.app.filter;

import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 请求上下文缓存过滤器<br>
 * 作者:徐承恩<br>
 * 邮箱:xuchengen@gmail.com<br>
 * 日期:2020/10/12 2:55 下午<br>
 */
public class ContentCachingRequestFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(request);
        filterChain.doFilter(wrapper, response);
    }
}

拦截器

package com.bbc.ibank.sys.app.interceptor;

import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.netfinworks.vfsso.client.authapi.VfSsoUser;
import com.bbc.ibank.dal.mapper.LogDOMapper;
import com.bbc.ibank.dal.model.LogDO;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.WebUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Objects;

/**
 * 通用日志拦截器<br>
 * 作者:徐承恩<br>
 * 邮箱:xuchengen@gmail.com<br>
 * 日期:2020/10/12 10:16 上午<br>
 */
public class LogInterceptor implements HandlerInterceptor {

    private static final Log log = LogFactory.get(LogInterceptor.class);

    @Resource(name = "logDOMapper")
    private LogDOMapper logDOMapper;

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception e) {
        try {
            // 当前登录操作员信息
            String userInfo = JSONUtil.toJsonStr(VfSsoUser.get());

            // 控制器名称
            String controllerName = null;
            // 方法名称
            String actionName = null;

            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                controllerName = handlerMethod.getBean().getClass().getSimpleName();
                actionName = handlerMethod.getMethod().getName();
            }

            // 客户端IP
            String clientIP = ServletUtil.getClientIP(request);
            // 请求地址
            String requestUrl = request.getRequestURL().toString();
            // 请求方法类型
            String method = request.getMethod();
            // 请求参数
            String params = null;
            if (ServletUtil.isGetMethod(request)) {
                params = JSONUtil.toJsonStr(ServletUtil.getParams(request));
            } else if (ServletUtil.isPostMethod(request)) {
                if (ContentType.FORM_URLENCODED.getValue().equals(request.getContentType())) {
                    params = JSONUtil.toJsonStr(ServletUtil.getParams(request));
                } else if (ContentType.JSON.getValue().equals(request.getContentType())) {
                    ContentCachingRequestWrapper nativeRequest =
                            WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
                    if (Objects.nonNull(nativeRequest)) {
                        params = new String(nativeRequest.getContentAsByteArray(), StandardCharsets.UTF_8.name());
                    }
                }
            }

            LogDO logDO = new LogDO();
            logDO.setController(controllerName);
            logDO.setAction(actionName);
            logDO.setUrl(requestUrl);
            logDO.setMethod(method);
            logDO.setIp(clientIP);
            logDO.setCreateTime(new Date());
            logDO.setParams(params);
            logDO.setUserInfo(userInfo);
            logDOMapper.insertSelective(logDO);

        } catch (Exception exception) {
            log.error("通用日志异常:", e);
        }
    }
}

注册Filter和拦截器

package com.bbc.ibank.sys.app.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import cn.hutool.setting.Setting;
import com.netfinworks.vfsso.client.filter.VfSsoCasFilter;
import com.bbc.ibank.sys.app.annotation.IgnoreLoginCheck;
import com.bbc.ibank.sys.app.constant.AppConst;
import com.bbc.ibank.sys.app.constant.SymbolConst;
import com.bbc.ibank.sys.app.filter.ContentCachingRequestFilter;
import com.bbc.ibank.sys.app.interceptor.*;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 过滤器配置<br>
 * 作者:徐承恩<br>
 * 邮箱:xuchengen@gmail.com<br>
 * 日期:2020/5/11 10:56 上午<br>
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    private static final Log log = LogFactory.get(WebConfig.class);

    @Value(value = "${profile}")
    private String profile;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 执行顺序就是添加的顺序

        // 通用日志拦截器
        registry.addInterceptor(logInterceptor())
                .addPathPatterns(AppConst.INTERCEPTOR_API_BASE_PATH);
    }

    @Bean
    public LogInterceptor logInterceptor() {
        return new LogInterceptor();
    }

    @Bean
    public FilterRegistrationBean<ContentCachingRequestFilter> contentCacheingRequestFilter() {
        FilterRegistrationBean<ContentCachingRequestFilter> registration =
                new FilterRegistrationBean<>(new ContentCachingRequestFilter());
        registration.addUrlPatterns(AppConst.FILTER_API_BASE_PATH);
        registration.setName(AppConst.CONTENT_CACHING_REQUEST_FILTER_NAME);
        registration.setOrder(1);
        return registration;
    }
}