2020年06月
2020-6-1 周一
搭建 ELK
ELK
是指 Elasticsearch
、Logstash
和 Kibana
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
参考文章
2020-6-3 周三
搭建 JupyterHub
先拉取 JupyterHub
的 Docker
镜像
然后正常启动 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
/lpush
,lpop
/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 相关
下载并安装 Ruby
:https://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