tonglin0325的个人主页

使用postman创建mock server

可以使用postman创建一个mock server用于临时测试API,参考官方文档:Configure and use a Postman mock server

选择Mock servers,点击+号创建一个mock server

创建

最后会得到一个URL,这就是mock server请求的URL

测试一下

查看postman的logs

使用mock server也可以使用变量和模板定义response

参考:https://learning.postman.com/docs/designing-and-developing-your-api/mocking-data/creating-dynamic-responses/

在 Response Body 中可以使用模板来自定义response,比如

1
2
3
4
5
6
7
8
9
{
"name": "{{$randomFullName}}",
"userName": "{{$randomUserName}}",
"location": "{{$randomCity}}",
"company": "{{$randomCompanyName}}",
"jobTitle": "{{$randomJobTitle}}",
"updatedAt": "{{$timestamp}}"
}

全文 >>

Python爬虫——selenium语法

1.获取元素#

通过a标签的文本筛选

1
2
driver.find_element(By.LINK_TEXT, 'xx').click()

通过css筛选

1
2
3
4
driver.find_element(By.CSS_SELECTOR, "input[type='email']").send_keys("xxx")
driver.find_element(By.CSS_SELECTOR, "button[type='button']").click()
driver.find_element(By.CSS_SELECTOR, "a.btn_download").click()

通过element name或者css name筛选

1
2
3
driver.find_element(By.NAME, "xx").send_keys("xxx")
driver.find_element(By.CLASS_NAME, "xx").click()

通过xpath筛选,只会contains,starts-with等语法

1
2
3
driver.find_element(By.XPATH, '//a[starts-with(@title, "xx")]').click()
driver.find_element(By.XPATH, '//iframe[contains(@title, "xx")]').click()

2.时间筛选器选择时间#

可以使用driver.execute_script(js)将元素的readonly属性去掉,再click后clear掉日期,最后send_keys输入新的日期时间

1
2
3
4
5
6
7
element = driver.find_elements(By.CSS_SELECTOR, "input[class='xxx']")
js = 'document.getElementsByClassName("xxx")[0].removeAttribute("readonly");'
driver.execute_script(js)
element[0].click()
element[0].clear()
element[0].send_keys("2022-01-01")

参考:selenium控制日历控件,readonly为true

3.指定chrome下载路径#

在使用selenium下载文件的时候,如果不指定chrome的下载路径,会弹出下载框让你确定文件名和下载路径

需要在selenium的driver中使用prefs属性添加如下属性

1
2
3
prefs = {'profile.default_content_settings.popups': 0,  # 设置为 0 禁止弹出窗口
'download.default_directory': '/Users/seluser/Downloads'} # 指定下载路径

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
from selenium import webdriver

profile = webdriver.ChromeOptions()

prefs = {'profile.default_content_settings.popups': 0, # 设置为 0 禁止弹出窗口
'download.default_directory': 'd:\\'} # 指定下载路径

profile.add_experimental_option('prefs', prefs)

# executable_path这个是chromedriver的路径 如果设置过环境变量,此参数可以省略
chromedriver_path = "D:\\path\\chromedriver.exe" # 自己本地电脑路径
driver = webdriver.Chrome(executable_path=chromedriver_path, chrome_options=profile)

参考:selenium+python自动化80-文件下载(不弹询问框)

如果使用的是undetected_chromedriver,则不支持chrome_options参数,需要使用如下方式进行添加prefs

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
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
52
53
54
55
56
57
58
59
60
61
62
import json
import os
import tempfile
from functools import reduce

import undetected_chromedriver as webdriver

class ChromeWithPrefs(webdriver.Chrome):
def __init__(self, *args, options=None, **kwargs):
if options:
self._handle_prefs(options)

super().__init__(*args, options=options, **kwargs)

# remove the user_data_dir when quitting
self.keep_user_data_dir = False

@staticmethod
def _handle_prefs(options):
if prefs := options.experimental_options.get("prefs"):
# turn a (dotted key, value) into a proper nested dict
def undot_key(key, value):
if "." in key:
key, rest = key.split(".", 1)
value = undot_key(rest, value)
return {key: value}

# undot prefs dict keys
undot_prefs = reduce(
lambda d1, d2: {**d1, **d2}, # merge dicts
(undot_key(key, value) for key, value in prefs.items()),
)

# create an user_data_dir and add its path to the options
user_data_dir = os.path.normpath(tempfile.mkdtemp())
options.add_argument(f"--user-data-dir={user_data_dir}")

# create the preferences json file in its default directory
default_dir = os.path.join(user_data_dir, "Default")
os.mkdir(default_dir)

prefs_file = os.path.join(default_dir, "Preferences")
with open(prefs_file, encoding="latin1", mode="w") as f:
json.dump(undot_prefs, f)

# pylint: disable=protected-access
# remove the experimental_options to avoid an error
del options._experimental_options["prefs"]


if __name__ == "__main__":
prefs = {
"profile.default_content_setting_values.images": 2,
# "download.default_directory": "d:/temp",
# "plugins.always_open_pdf_externally": True,
}
options = webdriver.ChromeOptions()
options.add_experimental_option("prefs", prefs)

# use the derived Chrome class that handles prefs
driver = ChromeWithPrefs(options=options)

参考:https://github.com/ultrafunkamsterdam/undetected-chromedriver/issues/524

parquet-tools使用

使用parquet-tools的方法有2种

1.在安装了CDH的机器上,会自动有parquet-tools命令

1
2
3
4
lintong@master:/opt/cloudera/parcels/CDH/bin$ ls| grep parquet-tools
parquet-tools
lintong@master:/opt/cloudera/parcels/CDH/bin$ parquet-tools

全文 >>

使用avro-protobuf将protobuf转换成avro

avro-protobuf项目提供ProtobufDatumReader类,可以用于从protobuf定义生成的java class中获得avro schema

使用方法如下:

1.引入依赖

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro-protobuf</artifactId>
<version>1.11.1</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.21.7</version>
</dependency>

2.定义protobuf schema,名为other.proto,schema如下

1
2
3
4
5
6
7
8
9
10
11
12
syntax = "proto3";
package com.acme;

message MyRecord {
string f1 = 1;
OtherRecord f2 = 2;
}

message OtherRecord {
int32 other_id = 1;
}

从使用protobuf定义生成java class

1
2
protoc -I=./ --java_out=./src/main/java ./src/main/proto/other.proto

3.编写java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.demo;

import com.acme.Other;

import java.util.*;
import org.apache.avro.protobuf.ProtobufDatumReader;

public class Demo {

public static void main(String[] args) throws Exception {

ProtobufDatumReader<Other.MyRecord> datumReader = new ProtobufDatumReader<Other.MyRecord>(Other.MyRecord.class);
System.out.println(datumReader.getSchema().toString(true));

}

}

输出如下

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
26
{
"type" : "record",
"name" : "MyRecord",
"namespace" : "com.acme.Other",
"fields" : [ {
"name" : "f1",
"type" : {
"type" : "string",
"avro.java.string" : "String"
},
"default" : ""
}, {
"name" : "f2",
"type" : [ "null", {
"type" : "record",
"name" : "OtherRecord",
"fields" : [ {
"name" : "other_id",
"type" : "int",
"default" : 0
} ]
} ],
"default" : null
} ]
}

注意:该工具在把protobuf schema转换成avro schema的时候,可能会出现不严谨的时候,比如在转换protobuf的uint32(0 到 2^32 -1)的时候,会统一转换成int(-2^31 ~ 2^31-1),这可能会产生问题,解决方法是使用confluent schema registry提供的工具,参考:使用confluent schema registry将protobuf schema转换成avro schema

Amazon EMR使用指南

Amazon EMR是Amazon提供的托管大数据套件,可选的组件包括Hadoop,Hive,Hue,Hbase,Presto,Spark等

使用Amazon EMR的好处是快速伸缩,版本升级也较为方便,如果配合S3存储,可以做到计算和存储分离,这样对于运维的压力会小一些,存储的稳定性交给S3,计算集群即使故障也可以很方便的进行重建,很适合小团队。缺点是界面友好程度远不如CDH和HDP。

如果使用Amazon EMR,最好阅读一下官方的2个文档:

1.Amazon EMR最佳实践

1
2
https://d0.awsstatic.com/whitepapers/aws-amazon-emr-best-practices.pdf

2.Amazon EMR迁移指南

1
https://d1.awsstatic.com/whitepapers/amazon_emr_migration_guide.pdf

1.创建EMR集群

在创建Amazon EMR集群的时候可以选择快速模式,界面如下

也可以选择高级模式

集群启动了之后,EMR大数据组件的安装目录在/usr/lib

1
2
3
4
5
6
7
8
[hadoop@ip-xxxxxxxx lib]$ ls
bigtop-groovy dracut hadoop hadoop-yarn java jvm ld-linux-aarch64.so.1 modules-load.d rpm systemd
bigtop-utils fontconfig hadoop-hdfs hbase java-1.5.0 jvm-commmon livy oozie sendmail tez
binfmt.d games hadoop-httpfs hive java-1.6.0 jvm-exports locale presto sendmail.postfix tmpfiles.d
cpp gcc hadoop-kms hive-hcatalog java-1.7.0 jvm-private lsb python2.7 spark udev
cups gems hadoop-lzo hudi java-1.8.0 kbd modprobe.d python3.7 sse2 yum-plugins
debug grub hadoop-mapreduce hue java-ext kernel modules ranger-kms sysctl.d zookeeper

EMR管理组件的安装目录在/usr/share/aws/emr

1
2
3
4
[hadoop@ip-xxxxxxxxxx emr]$ ls
cloudwatch-sink ddb emr-log-analytics-metrics goodies instance-controller node-provisioner s3select
command-runner emrfs emr-metrics-collector hadoop-state-pusher kinesis s3-dist-cp scripts

2.EMR CLI使用

查看集群的cluster id,参考:https://docs.aws.amazon.com/zh_cn/emr/latest/ManagementGuide/emr-manage-view-clusters.html

1
2
aws emr list-clusters

根据cluster id查看集群配置

1
2
aws emr describe-cluster --cluster-id j-xxxxxx

3.EMR角色分布

全文 >>

Grafana学习笔记——filter语法

在使用grafana的filter的时候,其支持一些语法用于对指标进行过滤,如下

literal_or : tagv的过滤规则: 精确匹配多项迭代值,多项迭代值以’|’分隔,大小写敏感

iliteral_or: tagv的过滤规则: 精确匹配多项迭代值,多项迭代值以’|’分隔,忽略大小写

wildcard: tagv的过滤规则: 通配符匹配,大小写敏感

iwildcard: tagv的过滤规则: 通配符匹配,忽略大小写

regexp: tagv的过滤规则: 正则表达式匹配

not_literal_or: tagv的过滤规则: 通配符取非匹配,大小写敏感

not_iliteral_or: tagv的过滤规则: 通配符取非匹配,忽略大小写

全文 >>

Mongo的oplog和change stream

1.oplog

MongoDB副本集由一组服务器组成,这些服务器都具有相同数据的副本,复制可确保客户端对副本集主副本集上的文档所做的所有更改都正确应用于其他副本集的服务器,称为secondaries副本。

MongoDB 复制的工作原理是让主节点在其操作日志(或操作日志)中记录更改,然后每个从节点读取主节点的操作日志并将所有操作按顺序应用到它们自己的文档中。

将新服务器添加到副本集时,该服务器首先对主服务器上的所有数据库和集合执行snapshot,然后读取主服务器的

全文 >>