憨憨呆呆的IT之旅

我见,我思,我行

笔者近期在MacOS Catalina环境下搭建了VS Code+XDebug的调试环境,用起来体验还不错。首先交代一下我电脑的环境:

1
2
3
System: MacOS Catalina 10.15.4
Editor: Visual Studio Code 1.44.2
Language: PHP 7.4.4 (cli) (built: Mar 19 2020 20:12:27) ( NTS )

搭建步骤如下:

安装并配置XDebug

1. 安装XDebug

首先,遵循XDebug官网指示,安装XDebug。
这里官方建议使用pecl包管理工具安装:

1
pecl install xdebug

也可选择从源码编译安装。安装步骤是标准的configure -> make -> make install, 这里不再赘述,具体请参考官方文档指示。

2. 配置XDebug

这里主要是在php.ini文件中指定XDebug的动态链接库的位置、监听ip和端口等。
由于系统中可能存在多个php.ini文件,一不小心就改错文件了。因此首先需要定位到当前使用中的php.ini文件。这就需要使用命令php --ini,输出如下:

1
2
3
4
Configuration File (php.ini) Path: /usr/local/etc/php/7.4
Loaded Configuration File: /usr/local/etc/php/7.4/php.ini
Scan for additional .ini files in: /usr/local/etc/php/7.4/conf.d
Additional .ini files parsed: /usr/local/etc/php/7.4/conf.d/ext-opcache.ini

从上面的输出内容中可以看出来,我的php.ini文件位于/usr/local/etc/php/7.4/php.ini

然后,在这个php.ini文件中添加[xdebug]配置段。我的配置如下:

1
2
3
4
5
6
7
8
9
; put lines below into your working php.ini
[xdebug]
zend_extension = /usr/local/lib/php/pecl/20190902/xdebug.so
xdebug.remote_enable = true
xdebug.remote_port = 9001
xdebug.profiler_enable = true
xdebug.remote_autostart = 1
xdebug.remote_host=localhost
xdebug.remote_log=/var/log/xdebug/xdebug.log

注意,zend_extension需指向你电脑里的xdebug.so所在位置。
xdebug默认端口是9000,由于我电脑里9000端口被php-fpm占了,这里把xdebug的端口改成了9001。
remote_log有时候还是很有用的,建议开启。

配置完成后,

VS Code安装并配置PHP Debug插件

1. 安装插件

点击vscode左侧边栏Extensions,打开搜索框,输入PHP Debug搜索,同名的插件有好几个,功能几乎没啥区别,我选择了带felixfbecker.php-debug标签的,因为它的配置文档比较详细一些。点击install安装即可。

2. 配置插件

点击vscode左侧边栏Run,在打开的小侧栏里点击右上角的齿轮图标,即可按提示新建或打开插件的配置文件launch.json。修改里边的端口号为你的xdebug监听端口号,其它配置可以不用改。这样就可以了。

测试一下

在vscode中随便打开一个php文件,打上断点(在代码行的左侧边栏点击一下,出现小红点即可)。然后执行它,就可以看到程序停在断点位置了。

初建git项目时,考虑不完整,常常会将logs、cache等目录也纳入版本控制中。后续进入开发过程中,才意识到要将这些目录屏蔽掉。于是想到.gitignore文件。
简单设置.gitignore 文件后,执行git push,发现被屏蔽的目录下文件仍然赫然在列,忽略设置并未生效。咋回事呢?

不生效原因

被纳入版本管理的文件,在git中会有缓存,即使声明了要ignore掉也不行

解决办法

知道是缓存搞怪,清掉本地缓存重新push就ok了。具体操作如下:

1
2
3
4
git rm -r --cached .
git add .
git commit -m "update .gitignore and clear local cache"
git push

Django使用request.GETrequest.POST来获取参数,而这两个属性都是不可变的django.http.QueryDict对象,默认是不支持修改的。而我们有时候需要修改请求参数,怎么做呢?

方法1:拷贝副本-> 修改 -> 赋值回去

可以利用QueryDictcopy()方法,得到一个可变的QueryDict副本,在副本上修改参数值后,再赋值给request.GET对象。 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def local_product_list(request):
"""
获取本地产品列表
"""
params = request.GET.copy()
params['local'] = True
request.GET = params
return product_list(request)


def product_list(request):
"""
获取产品列表
"""
...

方法2:修改不可变属性

直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def local_product_list(request):
"""
获取本地产品列表
"""
mutable_value = request.GET._mutable
if not mutable_value:
# 原来不可变,改为可变
request.GET._mutable = True
# 修改参数值
request.GET['local'] = True
if not mutable_value:
# 原来不可变,这里改回去,保持原值
request.GET._mutable = False
return product_list(request)


def product_list(request):
"""
获取产品列表
"""
...

注意:QueryDictupdate()方法跟我们常用的dict.update()有所不同。
以下两段代码的效果是不一样的:

1
2
3
4
5
6
>>> q2 = QueryDict('local=False', mutable=True)
>>> q2
<QueryDict: {'local': ['False']}>
>>> q2['local'] = True
>>> q2
<QueryDict: {'local': [True]}>
1
2
3
4
5
6
>>> q3 = QueryDict('local=False', mutable=True)
>>> q3
<QueryDict: {'local': ['False']}>
>>> q3.update({'local': True})
>>> q3
<QueryDict: {'local': ['False', True]}>

这是因为, request.GET['local'] = True是直接修改原local参数的值,而request.GET.update({'local': True})追加新的值到local参数值的数组中。请务必小心!

近期在做一个数据迁移的项目时,需要把产品分类数据导入新表中。因为数据是爬虫抓取下来的,有些字段数据内容不完整或格式错误,因此执行了多次导入才最终导入成功。期间需要清空错误数据然后重新导入,如果不重置自增ID,则每次导入同样的数据,ID会越来越大。 那么如何设置自增ID呢?
首先,可以用如下语句查看当前的自增ID是多少:

1
SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your-database-name' AND TABLE_NAME = 'your-table-name';

然后,我们来修改自增ID:

1
ALTER TABLE your-table-name AUTO_INCREMENT=1000;

以上语句就可以把自增ID设置为1000。
至于最终设置的对不对,用上面第一条SQL查一下就知道了。

Python默认的for ... in ...循环是不带索引序号的,例如:

1
2
3
4
presidents = ["Washington", "Adams", "Jefferson", "Madison", "Monroe", "Adams", "Jackson"]
for name in presidents:
print("President : {}".format(name))

执行结果如下:

1
2
3
4
5
6
7
President : Washington
President : Adams
President : Jefferson
President : Madison
President : Monroe
President : Adams
President : Jackson

在这个例子中,我们可以打印出总统的名字,但不能打印出该总统是第几任总统。那么,如何打印出是第几任总统呢?用enumerate()函数。改成如下形式:

1
2
3
4
presidents = ["Washington", "Adams", "Jefferson", "Madison", "Monroe", "Adams", "Jackson"]
for num, name in enumerate(presidents, start=1):
print("President {}: {}".format(num, name))

执行结果如下:

1
2
3
4
5
6
7
President 1: Washington
President 2: Adams
President 3: Jefferson
President 4: Madison
President 5: Monroe
President 6: Adams
President 7: Jackson

搞定!

现在大家打字聊天都喜欢发表情符号,而一个表情符号需要4个字节存储,对应的字符编码是utf8mb4。MySQL如果不做设置,默认编码会是乱七八糟的,什么都可能出现。比如下面这个:

1586571131272

如上图所示,我们可以使用SHOW VARIABLES WHERE Variable_name LIKE 'character_set_%' OR Variable_name LIKE 'collation%';命令查询数据库服务器的编码设置。

使用utf8编码大家应该都没有异议,虽然关于utf8的争议从来就没有消停过,但它仍然一骑绝尘,逐渐成为存储多字节文字的事实标准。所以在这里不讨论为何不适用gbk甚至更古老的gb2312的话题。

为什么使用utf8mb4

那么,为什么一定要使用utf8mb4编码呢?继续使用utf8编码不可以吗? 以前可以,现在不可以了。简单来说,utf8mb4是utf8的超集。utf8mb4完全兼容utf8,换句话说,用utf8mb4代替utf8是没有问题的;反之,utf8是不能替代utf8mb4的。采用utf8mb4,主要是为了解决文章开头我们提到的存储表情符号的问题。关于mysql使用utf8编码,网上有很多惨痛案例, 比如记住:永远不要在 MySQL 中使用 UTF-8

如何设置

首先,设置 my.cnf文件,这个文件一般位于/etc/my.cnf。设置如下:

1
2
3
4
5
6
7
8
9
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
character-set-client-handshake=FALSE
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'

注意,不同版本mysql或mariadb, 配置文件位置可能不一样,请灵活处理。

设置完成后,需要重启mysql服务。重启后,登陆mysql后台,继续用SHOW VARIABLES WHERE Variable_name LIKE 'character_set_%' OR Variable_name LIKE 'collation%';命令检查配置是否OK。如果出现下面的配置,说明配置OK了。

1586573272451

另外,不要忘了把现有的数据库和表都转成utf8mb4编码。
更改数据库编码:

1
ALTER DATABASE caitu99 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

更改表编码:

1
ALTER TABLE TABLE_NAME CONVERT TO CHARACTER SET utf8mb4 COLLATEutf8mb4_general_ci; 

整理了一下我的Django开发环境配置,步骤如下:

  1. 安装并配置conda
    我直接下载Anaconda

最近在用Django做项目,整理了一下常用的命令,以备自查。

工程构建&运行

  1. 查看Django版本
    1
    python -m django --version
  2. 创建工程
    1
    django-admin startproject your_project_name
  3. 创建App
    1
    python manage.py startapp your_app_name
  4. 本地测试运行工程
    1
    python manage.py runserver 8000
  5. 创建后台管理员
    1
    python manage.py createsuperuser

数据迁移migration

  1. 执行迁移
    1
    python manage.py migrate
  2. 创建迁移
    1
    python manage.py makemigrations your_app_name
  3. 检查迁移sql
    1
    python manage.py sqlmigration your_app_name your_migration_sequence_no

日常调试 & 测试

  1. 打开命令行AP
    1
    python manage.py shell
  2. 执行自动测试
    1
    python manage.py test your_app_name

sqlite是世界上最广泛部署的数据库引擎。它无需服务器,仅仅需要一个数据文件,就能够实现自给自足、零配置且带事务支持的数据库引擎。因为轻便简单,是各种嵌入式程序的首选数据库。通过命令行操作sqlite也十分简单。最常用的命令列举如下:

  1. 打开数据库

    1
    sqlite3 your_db_filename.sqlite3

    sqlite数据库文件一般以*.sqlite3.db*结尾。

  2. 查看帮助

    1
    .help
  3. 查看数据库表

    1
    .table
  4. 查看建表语句

    1
    .schema your_table_name
  5. 查询某个表的数据

    1
    select * from your_table_name;

    注意这里的sql语句后面必须带分号。

select一样,一般的标准sql语句都是支持的。

经常在网上看到因Elasticsearch服务裸奔造成的安全事件,比如某婚庆网站因Elasticsearch数据暴露在公网上被曝光,我本来不以为意,没想到自己的一台小服务器今天也中招了。

1
2
3
# curl -XGET 192.168.1.11:9200/_cat/indices
yellow open locationkeywordv1 XH3c3vSWRAWOeAD9ZRDUKA 5 1 93 0 147.4kb 147.4kb
yellow open nightlionsecurity.com X_uOYfJ9ThitglEA17LzbA 5 1 0 0 955b 955b

攻击者删除了我几乎所有的索引数据,还嚣张地以用他的网站域名nightlionsecurity.com建了一个新索引。

看来侥幸心理要不得,安全问题随时都不能马虎。那么,如何保障Elasticsearch服务的安全呢?
今天只说一条:禁止外网访问Elasticsearch。

禁止外网访问Elasticsearch

首先要做的就是修改Elasticsearch的配置文件。

1. 将Elasticsearch绑定到内网地址

修改配置文件elasticsearch.yml,设置固定的内网IP:

1
network.host: 192.168.1.11

然后需要禁止从外网访问Elasticsearch。可以从以下三方面入手:

2. 防火墙禁止Elasticsearch端口访问

Elasticsearch节点暴露了三个默认端口:

  • 9200: 集群对外访问端口
  • 9300: 集群内部通信端口
  • 5601:Kibana的对外访问端口

我们需要做的,就是限制直接从外网访问这几个端口。如果已经修改了默认端口,限制方法相同。我们拿9200端口举例,看一下如何限制访问。

在这里我们把目标明确一下:外网无法访问9200端口,内网同一网段可以访问。
假定我的内网IP为192.168.1.30。按以下步骤操作

1
2
3
4
5
# 允许内网同一网段ip过来的请求
iptables -A INPUT -p tcp --dport 9200 -s 192.168.1.0/24 -j ACCEPT
# 拒绝其他请求
iptables -A INPUT -p tcp --dport 9200 -j DROP
#

其他两个端口9300和5601按类似步骤操作即可。
配置完成后,可使用以下命令检查规则顺序:

1
iptables -L -n

最后,这里设置的规则只是临时的,重启后就失效了。如果想重启后继续生效,需要执行以下命令将规则保存起来:

1
service iptables save

注意,若执行 service iptables save时提示不存在save指令等情况,说明没有安装iptables-service 服务。需要先安装iptables-service后再执行保存。

0%