2020年06月

2020-6-1 周一

搭建 ELK

ELK 是指 ElasticsearchLogstashKibana

Elasticsearch 是一个搜索和分析引擎。Logstash 是服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到诸如 Elasticsearch 等“存储库”中。Kibana 则可以让用户在 Elasticsearch 中使用图形和图表对数据进行可视化

官方网站:https://www.elastic.co/cn/

这里在 Docker 环境上搭建它们

因为 Logstash 暂时用不到,且可以有其它方式替代所以没建这个

先拉 elasticsearch:7.7.1 镜像,完了之后启动的时候要指定 Docker Env discovery.type=single-node,模式是必填项,我这里用的单例模式

然后是 kibana:7.7.1,这里有个坑是明明官方文档中说可以用 Docker Env 进行的设置却没有生效,得到文件中设置:/usr/share/kibana/config

然后访问端口 5601
也支持十分蹩脚的中文

官方提供了三个数据实例集
可视化也给了 Demo

美中不足的是不知道为何它似乎不支持用户鉴权,任何人都能访问

同样是日志收集,如果要找BUG 其实用 Sentry,更方便,ELK更偏向于更重量级的系统了

这篇博客在:Docker部署ELK

2020-6-2 周二

ElasticSearch 与 MySQL 相比

index(索引)

相当于mysql中的数据库

type(类型)

相当于mysql中的一张表

document(文档)

相当于mysql中的一行(一条记录)

field(域)

相当于mysql中的一列(一个字段)

分片

将一份数据划分为多小份的能力,允许水平分割和扩展容量。多个分片可以响应请求,提高性能和吞吐量。

副本

复制数据,一个节点出问题时,其余节点可以顶上。

倒排索引

可参考 https://www.elastic.co/guide/cn/elasticsearch/guide/current/inverted-index.html

参考文章

python MySQL 插入Elasticsearch

Elasticsearch: 权威指南

2020-6-3 周三

搭建 JupyterHub

先拉取 JupyterHubDocker 镜像
然后正常启动 Docker

安装完后需要在容器中执行:
adduser:设置用户名和密码
手动安装 JupyterLab

 pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install jupyterlab -i https://pypi.tuna.tsinghua.edu.cn/simple

 

不安装则会报错 Spawn failed: Server at http://127.0.0.1:39684/user/username/ didn't respond in 30 seconds

2020-6-4 周四

Windows Gitlab Runner 配置

Windows 平台的 Gitlab Runner 官方下载地址:https://docs.gitlab.com/runner/install/windows.html

config.toml 配置文件中支持 Sentry 的配置
sentry_dsn = https://85555505d5b25555555f5532652c4555@tabll-bug.cpolar.cn/666
文档上有写这个,但是设置了这个会导致 runner 无法启动

安装的时候不用输入 powershell 就填 shell 就行,它也会默认用 powershell 的:

 [[runners]]
  name = "windows production"
  url = "https://gitlab.tabll.cn/"
  token = "XXXXXXXXX"
  executor = "shell"
  shell = "powershell"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

 

安装、启动、注册、重启的命令:

2020-6-5 周五

Windows 脚本自动应答

自动执行某些脚本的时候会需要输入 y/n 以继续/取消
加上 echo 就可以自动回答了

 echo y | php artisan migrate

 
 **************************************
*     Application In Production!     *
**************************************

 Do you really wish to run this command? (yes/no) [no]:
 >
Nothing to migrate.

 

其实还有一种方式:

 php artisan migrate --no-interaction --force

 

直接强制迁移且不询问

2020-6-8 周一

HTTP 各个请求

请求方法 操作
GET 请求指定的页面信息,并返回实体主体
HEAD 只请求页面的首部
POST 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体
PUT 从客户端向服务器传送的数据取代指定的文档的内容
DELETE 请求服务器删除指定的页面
OPTIONS 允许客户端查看服务器的性能
TRACE 请求服务器在响应中的实体主体部分返回所得到的内容
PATCH 实体中包含一个表,表中说明与该URI所表示的原内容的区别
MOVE 请求服务器将指定的页面移至另一个网络地址
COPY 请求服务器将指定的页面拷贝至另一个网络地址
LINK 请求服务器建立链接关系
UNLINK 断开链接关系
WRAPPED 允许客户端发送经过封装的请求
Extension-mothed 在不改动协议的前提下,可增加另外的方法

2020-6-9 周二

Docker 退出时自动删除

有的时候 docker 容器只需要运行一次就销毁掉

添加上 --rm 参数即可:

 docker run --rm

 

2020-6-10 周三

Redis Hash

字典 Hash

无序字典,是 数组 + 链表 的二维结构,第一维 hash 的数组位置碰撞时,会将碰撞的元素用链表串联起来

ReHash 操作

使用渐进式 rehash 策略

保留新旧两个 hash,途中查询时同时查询两个 hash 结构,结束后只保留新的

优点,和字符串比可以部分读取,缺点,和字符串比存储消耗大

 # 设置
r.hset('books', 'java', 'this is java')
r.hset('books', 'php', 'this is php')
r.hset('books', 'python', 'this is python')

# 打印
r.hgetall('books')

# 长度
r.hlen('books')

# 获取
r.hget('books', 'php')

r.hset('books', 'php', 'this is new php')  #  更新操作返回 0

# 在 Redis 4.0 版本被弃用:
r.hmset('books', {'java' : 'java', 'php' : 'php'})  # 不推荐使用 复杂度 O(n) 慎用

# 自增
r.hincrby('user-tabll', 'age', 2)  # 自增使用 hincrby

 

2020-6-11 周四

Redis

跳跃列表

相当于多层级的金字塔

可以类似于:亚洲->中国->浙江省->杭州市->上城区->中华路->XX号

延迟队列

如果对消息可靠性没有极高要求时可以使用Redis

List 数据结构通常作为异步消息队列使用,rpush/lpushlpop/rpop

阻塞读

blpop/brpop

阻塞读在没有数据时会进入休眠状态,数据到来时会立刻醒过来,几乎没有延迟

空闲连接问题

长时间阻塞会变为闲置连接,太久服务器会主动断开连接,blpop/brpop 会抛出异常

解决方法是捕获异常时重试

锁冲突

加锁失败
- 直接抛出和异常给用户让用户自己重试,也可以前端重试
- 等待一会儿重试(不建议),如果是死锁会导致线程堵死
- 将请求转移至延迟队列,以避开冲突

2020-6-12 周五

Redis 延迟队列

延迟队列可以使用有序列表(ZSet)来实现,到期处理时间是 score,使用多线程轮询的方式对到期的任务进行处理

 # 代码示例(执行无效)
def loop():
    while True:
        values = r.zrangebyscore('delay-queue', 0, time.time(), start=0, num=1)
        if not values:
            time.sleep(1)
            continue
        value = value[0]
        # 移除
        result = r.zrem('delay-queue', value)  # 成功则为 True 代表当前线程抢到了执行权
        if result:
            msg = json.loads(value)
            handle_msg(msg)

 

2020-6-15 周一

Redis 位图

用来存储 bool 类型的数据,例如用户的签到记录,签了是 1 没签是 0,需要记录 365 天

位图不是特殊的数据结构,其实就是一个 byte 数组,使用 get/set 或者 getbit/setbit 操作都可以

 bin(ord('h'))  # 获取字符的 ASCII 的二进制值
# 返回 '0b1100101'

 
 # 以二进制值的方式存入两个字符
r.setbit('bit', 1, 1)
r.setbit('bit', 2, 1)
r.setbit('bit', 4, 1)
r.setbit('bit', 9, 1)
r.setbit('bit', 10, 1)
r.setbit('bit', 13, 1)
r.setbit('bit', 15, 1)

r.get('bit')  # 取出(如果是不可打印字符,则会显示该字符的16进制形式)
# 返回 he

r.bitcount('bit')  # 统计个数
r.bitcount('bit', 0, 0)  # 第一个字符中 1 的个数
r.bitcount('bit', 0, 2)  # 前三个字符中 1 的个数(其实这里只有两个字符)

r.bitpos('bit', 0)  # 第一个 0 的位置
r.bitpos('bit', 1)  # 第一个 1 的位置
r.bitpos('bit', 1, 2, 2)  # 第三个字符里第一个 1 的位置
r.bitpos('bit', 1, 1, 1)  # 第二个字符里第一个 1 的位置

 

2020-6-16 周二

Redis HyperLogLog

这个很强,不精确计数

标准误差是 0.81%

 r.pfadd('HyperLogLogCounter', 'user_id_1')  # 添加

r.pfcount('HyperLogLogCounter')  # 计数

r.pfadd('HyperLogLogCounter2', 'user_id_2')  # 添加

r.pfmerge('HyperLogLogCounter', 'HyperLogLogCounter2')  # 合并

 
 # 尝试打印出第一个出现误差的时间点
for i in range(10000):
    r.pfadd('HyperLogLogCounter', "user_id_%d" % i)
    total = r.pfcount('HyperLogLogCounter')
    if total != i + 1:
        print(str(total) + " " + str(i + 1))
        break

 

这里我的返回是 129 128

这个网站演示了 HyperLogLog 是如何执行的:
http://content.research.neustar.biz/blog/hll.html

2020-6-17 周三

Redis HyperLogLog

Redis 使用了 16384(2¹⁴) 个桶,每个桶的 maxbits 需要6个bit来存储,(2¹⁴) x 6 / 8 = 12 KB (除以 8 是为了转换成 KB)

2020-6-18 周四

Redis 布隆过滤器

估算是否存在,这个同样非常强大

这个需要安装并启用插件

bf.add 之前可以用 bf.reserve 指令显式创建,支持三个参数 key(可以在其中找到过滤器的键)、error_rate(错误率,默认0.01,越接近0消耗资源越大)、initial_size(打算添加到过滤器中的条目数,默认100)
相关文档:https://oss.redislabs.com/redisbloom/Bloom_Commands/

 r.execute_command('bf.add', 'bloom', 'mark')
# r.execute_command('BF.MADD', 'bloom', *[i for i in range(int(1e5))])
# r.execute_command('BF.MEXISTS', 'bloom', *[1, 27, 1000001])

 

在爬虫系统中可以使用它,标记已爬过的网页

布隆过滤器可以显著降低数据库的IO请求,可以通过内存中的过滤器滤掉大量不存在的请求

垃圾邮件过滤功能也普遍用到了它,所以有时会有误判进垃圾邮件中

2020-6-19 周五

转换高德/百度地图的经纬度

 //BD-09(百度)坐标转换成GCJ-02(火星,高德)坐标
function bd_decrypt($bd_lon,$bd_lat){
$x_pi = 3.14159265358979324 * 3000.0 / 180.0;
$x = $bd_lon - 0.0065;
$y = $bd_lat - 0.006;
$z = sqrt($x * $x + $y * $y) - 0.00002 * sin($y * $x_pi);
$theta = atan2($y, $x) - 0.000003 * cos($x * $x_pi);
$gg_lon = $z * cos($theta);
    $gg_lat = $z * sin($theta);
    // 保留小数点后六位
    $data['gg_lon'] = round($gg_lon, 6);
    $data['gg_lat'] = round($gg_lat, 6);
return $data;
}

//GCJ-02(火星,高德)坐标转换成BD-09(百度)坐标
function bd_encrypt($gg_lon,$gg_lat){
    $x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    $x = $gg_lon;
    $y = $gg_lat;
    $z = sqrt($x * $x + $y * $y) - 0.00002 * sin($y * $x_pi);
    $theta = atan2($y, $x) - 0.000003 * cos($x * $x_pi);
    $bd_lon = $z * cos($theta) + 0.0065;
    $bd_lat = $z * sin($theta) + 0.006;
    $data['bd_lon'] = round($bd_lon, 6);
    $data['bd_lat'] = round($bd_lat, 6);
    return $data;
}

 

参考:百度地图(BD-09)坐标系和高德(GCJ-02)坐标系的相互转换(PHP版)

2020-6-22 周一

Composer 安装包时提示内存不足

 > composer require dompdf/dompdf --ignore-platform-reqs
Using version ^0.8.5 for dompdf/dompdf
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
PHP Fatal error:  Allowed memory size of 1610612736 bytes exhausted (tried to allocate 4096 bytes) in phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/
DependencyResolver/Solver.php on line 223

Fatal error: Allowed memory size of 1610612736 bytes exhausted (tried to allocate 4096 bytes) in phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/Depen
dencyResolver/Solver.php on line 223

Check https://getcomposer.org/doc/articles/troubleshooting.md#memory-limit-errors for more info on how to handle out of memory errors.

 

解决方法:
PHP.ini 文件中的内存限制调整为 2048M (原来我是 1024M

 ; Maximum amount of memory a script may consume (128MB)
; http://php.net/memory-limit
memory_limit=2048M

 

2020-6-23 周二

docker error “The input device is not a TTY”

移除命令中的 -it 操作

什么是TTY?这是一个终端接口,它支持颜色输出,转义序列,移动光标等,这是从老式笨拙的终端连接到大型机上来的。今天,它是由Linux命令终端和ssh接口提供的

2020-6-24 周三

2020-6-25 周四

2020-6-26 周五

2020-6-22 周一

2020-6-23 周二

2020-6-24 周三

2020-6-29 周一

Gitlab KEY 相关

下载并安装 Rubyhttps://rubyinstaller.org/downloads/
安装选项都是默认即可

安装 gitlab-license

 gem install gitlab-license

 

新建 license.rb 文件:

 require "openssl"
require "gitlab/license"

key_pair = OpenSSL::PKey::RSA.generate(2048)
File.open("license_key", "w") { |f| f.write(key_pair.to_pem) }

public_key = key_pair.public_key
File.open("license_key.pub", "w") { |f| f.write(public_key.to_pem) }

private_key = OpenSSL::PKey::RSA.new File.read("license_key")
Gitlab::License.encryption_key = private_key

license = Gitlab::License.new
license.licensee = {
  "Name" => "none",
  "Company" => "none",
  "Email" => "example@test.com",
}
license.starts_at = Date.new(2020, 1, 1) # 开始时间
license.expires_at = Date.new(2050, 1, 1) # 结束时间
license.notify_admins_at = Date.new(2049, 12, 1)
license.notify_users_at = Date.new(2049, 12, 1)
license.block_changes_at = Date.new(2050, 1, 1)
license.restrictions = {
  active_user_count: 10000,
}

puts "License:"
puts license

data = license.export
puts "Exported license:"
puts data
File.open("GitLabBV.gitlab-license", "w") { |f| f.write(data) }

public_key = OpenSSL::PKey::RSA.new File.read("license_key.pub")
Gitlab::License.encryption_key = public_key

data = File.read("GitLabBV.gitlab-license")
$license = Gitlab::License.import(data)

puts "Imported license:"
puts $license

unless $license
  raise "The license is invalid."
end

if $license.restricted?(:active_user_count)
  active_user_count = 10000
  if active_user_count > $license.restrictions[:active_user_count]
    raise "The active user count exceeds the allowed amount!"
  end
end

if $license.notify_admins?
  puts "The license is due to expire on #{$license.expires_at}."
end

if $license.notify_users?
  puts "The license is due to expire on #{$license.expires_at}."
end

module Gitlab
  class GitAccess
    def check(cmd, changes = nil)
      if $license.block_changes?
        return build_status_object(false, "License expired")
      end
    end
  end
end

puts "This instance of GitLab Enterprise Edition is licensed to:"
$license.licensee.each do |key, value|
  puts "#{key}: #{value}"
end

if $license.expired?
  puts "The license expired on #{$license.expires_at}"
elsif $license.will_expire?
  puts "The license will expire on #{$license.expires_at}"
else
  puts "The license will never expire."
end

 

双击执行该文件,会生成三个文件

/opt/gitlab/embedded/service/gitlab-rails# vi .license_encryption_key.pub

原有的 KEY

 -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Hxv3MkkZbMrKtIs6np9
ccP4OwGBkNhIvhPjcQP48hbbascv5RqsOquQGrYSD2ZrE/kbkRdkIcoHEeTZLif+
bDKFZFI7o5x0H92o9/GSvxHJhQ8mkmvwxD7lssGShwZEm8WG+U7BZqUV/gGmCDqe
9W8H8Fq2B0ck8IXjbQ4Zz+JlyV/NHZTZcs69plFiLKh4N6GYVftOVwSomh0bbypP
OB9WnLC7RC9a2LRrhtf8sqa2rRFmtyMMfgFFzLMzS+w+1K4+QLnWP1gKQVzaFnzk
pnwKPrqbGFYbRztIVEWbs8jPYlLkGb8ME4C84YVtQgbQcbyisU/VW3wUGkhT+J0k
xwIDAQAB
-----END PUBLIC KEY-----

 

现有的 KEY

 -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0JUeDeEuZ0SIGwGJ459f
4rPDdW+xT0XNW/wMdDN8qvEcvHgD74FvtIODl9sFwPDECB3RdV1o47FNHZalxDVy
xAYisWs+TD5pqp3CUhFLyFtYxN7+Uof7ekIAJD2Ng7htQ11/2rGSeuMKEr1Shedl
PWAH4Q7lf4FU4JU0tDNubXCZNPDMRInc6Aq8fKjPLVwA47lhT827Q8or01a/r7vw
/XlrwMfjQU9osXeTCNjXz2lpUuUfqVksHLPeDQHGrb9Q4NrEeP9qAL0pFSad1XN0
P63YyWG9lVpeBpzZwh4OTWexEls/OVpmU7xkZZWBUnNW2L7bSA0o8XMhowtTiLjx
4wIDAQAB
-----END PUBLIC KEY-----

 

修改完上传后缀为 .gitlab-license 的文件

/opt/gitlab/embedded/service/gitlab-rails/ee/app/models/license.rb

 --- /opt/gitlab/embedded/service/gitlab-rails/ee/app/models/license.rb
+++ /opt/gitlab/embedded/service/gitlab-rails/ee/app/models/license.rb

  end

  def plan
-    restricted_attr(:plan).presence || STARTER_PLAN
+    restricted_attr(:plan).presence || ULTIMATE_PLAN
  end

  def edition

 

参考:生成 GitLab EE 许可证
参考:Gitlab::License

2020-6-30 周二

miniConda 安装

使用清华的软件源
所有的版本在这里:https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/?C=M&O=D

 wget -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py38_4.8.2-Linux-x86_64.sh

 

唤醒精灵