python del与垃圾回收

冒出来一个念头. Python del 对象时,会自动调用__del__方法。那么垃圾回收的时候,是否python也会在删除对象前,调用__del__方法呢?结论是不会,模拟了一个例子,便看到答案了。可是为什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
class XX:
def __init__(self):
print('xxx init')

def __del__(self):
print('yyy del')


a = XX()
del a
XX() //创建了一个变量, 但是没有引用指向,我以为python 垃圾回收机制回收时,会答应出 yyy del. 结果并没有。
import gc
gc.collect() //强制gc一次,也没见到调用
  • del 命令删除的实际上对象的引用。但是删除前会调用__del__,完成方法里定义的善后工作。

  • python的垃圾回收,删除的是引用计数。所以并不会调用__del__方法


python import 注意事项

Python 导入都有哪几种写法呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from xxx import yyy, zzz  //此处的yyy可能对应xx/yyy.py文件。 也有可能是xxx.py中的一个变量。 
from xxx import *
import xxx
import xxx as x

------
#!/bin/env pthon
""" 文件a.py"""
from xxx import yyy // 相当于声明了yyy变量,指向了xxx.py 中的yyy变量
yyy = 1 //此时yyy变量的指向就被改变了。指向了值1

"""文件b.py"""
from xxx import yyy
print(yyy)

s3 bigfile

应该是去年,下班路上的一个想法,一直记在备忘录,没来的及去求证。如今答案应该很清晰了。

我们内部把S3通过工具,mount 在ec2上,当成本地磁盘,用于写入备份数据库文件。于是我便产生了一个疑问,如果这个“磁盘”中,一个数据库备份sql 文件,假设100G,现在我用sed替换其中一个字符串,会产生什么影响呢。按照下文的说法,预计会导致重新上传这个文件。通过监控磁盘流量,或者存盘的时间,应该也可以观测出。

image-20200601225024881

ref: https://books.google.com.hk/books?id=xIauw5xWTO8C&pg=PA52&lpg=PA52&dq=modified+big+file+s3&source=bl&ots=8VrRX-UgCc&sig=ACfU3U1SYvGMkzYZQIGCfAMgP4wT7dce1A&hl=zh-CN&sa=X&redir_esc=y#v=onepage&q&f=false


mitmproxy

公司中层管理的课程,实在是太糟心了。逼迫在短期内,学习大量的管理课程。于是动了“歪脑筋”,想着抓下来,慢慢回顾。

mitmproxy 在新的版本中,已经不再支持使用 P 就复制出python request 脚本,或者curl 命令了。现在采用的是command的方式。但获得请求的curl命令,还是非常常用的需求。

通过配置keys.yaml,就能实现。

1
2
3
4
➜ cat ~/.mitmproxy/keys.yaml
-
key: c
cmd: export.clip curl @focus

python skill
  1. python 查看变量大小

    sys.``getsizeof(object[, default])

  2. 解包

    1
    2
    3
    4
    5
    a, *mylist = 1,2,3,4,5
    *arv = [1, 4, 5] //SyntaxError: starred assignment target must be in a list or tuple
    *arv, = [1, 4, 5] //相当于复制
    ref:https://stackoverflow.com/questions/43190992/understanding-x-lst
    不过python3,copy方法更加的通用,毕竟x = p[:],只能用在可以切片的元素上。
  3. python -c “print(‘Hello World’)”. //python 命令行,直接执行python代码

    Python -m http.server // -m mod : run library module as a script (terminates option list)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /tmp/test/run.py 内容如下:
    #!/bin/env python
    import sys
    print(sys.path)

    当前路径在/tmp下
    执行python3 ./test/run.py,会发现/tmp/test/run 被加入到sys.path中。
    执行python -m test.run,会发现 ‘’, 一个空的字符串被加入到sys.path中。
    Note that the empty '' entry means the current directory。
    所以这个是python -m 与python 直接执行的第一个区别。

    因此,python -m http.server, 这条命令,就可以理解为:python解释器,在sys.path 中找到http模块的server.py,进行运行。
  4. python的私有化

    单一个下划线开头的变量,如_var,类对象/子类都可以访问到,但是from somemodule import * 禁止导入。

    双下划线开头的变量,如__var,类对象/子类都访问不到。from somemodule import * 禁止导入。

    1
    2
    3
    4
    //特别注意一下这种,是可以访问到的
    import somemodule
    print(somemodule._var) //这种模式是可以访问到的
    print(somemodule.__var) //这种模式是可以访问到的
  5. 字典

    首先,字典和列表都是可变变量。

    字典是无序的,查找和插入的时间,不会随着数据增加而增加,但是占用空间大。

    而列表时有序的,查找和插入的时间,随着数据增加而增加,但是空间浪费少。字典的key必须是不可变对象,因为dict存储或是查找时, 都是通过对key进行哈希算法计算,得出value所存储的位置。如果key也可以变,那就乱套了。

    1
    2
    3
    4
    5
    >>> key = [1, 2, 3]
    >>> d[key] = 'a list'
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: unhashable type: 'list'
  6. method方法 & function函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // function 指的是普通的函数。比如:
    In [21]: def hello():
    ...: pass

    In [22]: hello
    Out[22]: <function __main__.hello()>



    // method 指的是实例对象里的方法
    In [23]: class A(object):
    ...: def __init__(self):
    ...: pass
    ...:
    ...: def echo():
    ...: print('b')

    In [24]: A.__init__
    Out[24]: <function __main__.A.__init__(self)>

    In [25]: a = A()

    In [26]: a.echo
    Out[26]: <bound method A.echo of <__main__.A object at 0x1121551d0>>
![image-20200606144203893](https://tva1.sinaimg.cn/large/007S8ZIlgy1gfikcyn7ugj31e20u01kx.jpg)
  1. 精度问题,是的,javascript也有这个问题,原因是小数通过二进制保存,巴拉巴拉的。。。 😁

    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
    In [10]: x = 0.1
    In [11]: y = 0.2
    In [12]: z = x + y
    In [13]: z
    Out[13]: 0.30000000000000004

    // --即使使用float类型,仍然有同样的问题--
    // 矫正一下,摆脱 0.1 本来就是浮点型啊。我当时是在梦游吗? 写的什么鬼
    In [23]: x = float('0.1')
    In [24]: y = float('0.2')
    In [25]: z = x + y
    In [26]: z
    Out[26]: 0.30000000000000004


    // 通过decimal解决
    from decimal import Decimal
    x = Decimal('0.1') + Decimal('0.4') //此处参数是字符串哦。
    print(x)

    //用==来对比浮点数时,可能出现意料外的事,这时候,用可以用<0.000001来比对。
    a = 0.1
    b = 0.2
    c = 0.3
    print(a + b == c)
    print(abs(a + b - c) < 0.000001)

    结论:如果需要精度非常高的,decimal 是一个选项, 但是日常总是免不了遇到浮点型的数据操作。面对这种日常场景如何处理呢。

    • python的// 返回的总是小数,而 / 返回的就只有整数。所以拿到//的返回值,做一些判断的时候,要多加注意。
  1. Python 枚举

    刷TypeScript的时候,遇到了枚举类型。我突然意识到Python中,基础的数据类型中,没有枚举。

    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
    // enum 只有在python v3.4 才开始有
    In [3]: from enum import Enum

    In [4]: class Vip(Enum):
    ...: MONDAY = 1

    In [5]: class Vip(Enum):
    ...: MONDAY = 0
    ...: TUESDAY = 1
    ...: WEDNESDAY = 2

    In [6]: print(Vip.MONDAY)
    Vip.MONDAY

    In [7]: print(Vip(1))
    Vip.TUESDAY

    In [8]: print(type(Vip.MONDAY))
    <enum 'Vip'>

    In [9]: print(Vip.MONDAY.name)
    MONDAY

    In [10]: print(Vip.MONDAY.value)
    0

    // 从Enum派生出类后,直接引用。如果具体的值不重要,你可以使用 auto(),将为你选择适当的值。

    In [14]: class cat_type(Enum):
    ...: long_cat = enum.auto()
    ...: short_cat = enum.auto()

    In [15]: print(cat_type.long_cat)
    cat_type.long_cat

    In [16]: print(cat_type.long_cat.name)
    long_cat

    In [17]: print(cat_type.long_cat.value)
    1

    回顾logging模块里的CRITICAL,WARNNING,DEBUG,其实也并不是我猜测的enum类型。
    enum最大的特点在于不可更改,又带有枚举的特性。
  1. python 默认数据类型有数组吗?在Typescript中是有数组的。我起初以为list,没有二维数组。

    Note: Python does not have built-in support for Arrays, but Python Lists can be used instead.

    答案是没有,python的list并不是数组,

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
# 如下list也是支持多维
In [11]: o = [1, 2, 3]
...: i = ['a', 'b', 'c']
...: z = [o, i]
...: print(z)
[[1, 2, 3], ['a', 'b', 'c']]

# 如下array模块的数组,必须是同一个类型
In [12]: import array as arr
...:
...: a = arr.array('d',[1,2,3,'a',5])
...: print(a)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-db567c5861b7> in <module>
1 import array as arr
2
----> 3 a = arr.array('d',[1,2,3,'a',5])
4 print(a)

TypeError: must be real number, not str

# Python 也带有array模块,但是并不支持二位数据组,
In [15]: import array as arr
...:
...: a = arr.array('d',[[1,2,3],[4,5,6]])
...: print(a)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-14dc313d3c9e> in <module>
1 import array as arr
2
----> 3 a = arr.array('d',[[1,2,3],[4,5,6]])
4 print(a)

TypeError: must be real number, not list

重点:list中的数据类型保存的是数据所存放的地址,简单的说就是指针,并非数据,这样保存一个list就太麻烦了,例如list1=[1,2,3,’a’]需要4个指针和四个数据,增加了存储和消耗cpu。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# numpy 会更灵活,也支持多维
In [1]: import numpy as np
...:
...: a = np.array([1, 2, 3, 4, 5])
...: b = np.array([[1, 2, 3], [4, 5, 6]])
...: c = list(a) # array到list的转换
...: print(a)
...: print(b)
...: print(c)
...:
[1 2 3 4 5]
[[1 2 3]
[4 5 6]]
[1, 2, 3, 4, 5]
  1. python 使用@property, 切记使用@property所使用的必须是收保护的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Student():
    def __init__(self, age, gender):
    self.age = age
    self.gender = gender

    @property
    def gender(self):
    return self.gender

    s = Student(age=14, gender='male')
    print(s.gender)

python SystemExit
1
2
3
import sys
sys.exit(0) //
sys.exit(1) // 两者有什么区别,都会raise SystemExit Exception吗?

于是,做了一个小测试,结果让我有点傻眼。

1
2
3
4
5
6
7
try:
import sys
sys.exit(1)
except Exception as e:
print('hello exception')
// 居然没有答应出hello exception,考究了一下,sys.exit(),会raise SystemExit异常,可是这个SystemExit并不是派生自Exception,而是BaseException。而BaseException是Exception的父类。
// 所以会看到有一种说法,称之以上这种写法可以捕获,除与程序退出sys.exit()相关之外的所有异常。
1
2
3
4
5
6
try:
import sys
sys.exit(1) //
except:
print('hello exception')
// 结果打印出‘hello exception’,这种写法会捕获所有的exception,包含sys.exit,键盘中断和程序退出请求。如果在此处被捕获了,也就退不出去了。因此不建议这种写法。

回到开头的问题 sys.exit(0) 与 sys.exit(1) 的区别,都会raise SystemExit异常,但是区别在于程序的返回码。

sys.exit(0) 为正常退出,echo $?,结果为0

sys.exit(1) 为非正常退出,echo $, 结果为1

题外话:如何查看类的父类:

1
2
3
4
5
6
7
8
In [2]: Exception.__bases__
Out[2]: (BaseException,)

In [3]: SystemExit.__bases__
Out[3]: (BaseException,)

In [4]: IOError.__bases__
Out[4]: (Exception,)

vpc private subnet

经历了漫长的迭代,终于需要重新创建AWS VPC的时候了。现有的VPC在我入职前就已经存在,虽然取名标示私有子网,但是却附加了互联网网关, 完全丧失了意义,主要原因便是开发需要连接到“私有子网”的数据库。现在的我当然知道ELB,ssh隧道都可以转发解决🥴。现在由于想要为生产Kubernetes,规划的一个VPC,因此就开始了回顾VPC。

  1. 创建一个VPC,面临一个决策,VPC内的子网划分,是否有必要划分public或private 子网。组里的同事,认为无需再划分private区域了。通过我们现有的安全组规划,就可以实现隔离。并且开发同事总有需要连接到private subnet里资源的时候,比如之前的private 区域的RDS,最终还是添加了互联网网关。我权衡再三,决定还是将子网划分出两个网段,理由如下:

    a. Public 与Private 子网。public子网充当DMZ区,作为面向互联网外部访问的网段,常常用于架设网页服务器,ELB均衡器。而private 意味是内部安全资源,仅仅允许内部访问。

    b. 如果不划分Private子网,意味所有主机访问互联网,都需要添加一个公有IP,或者弹性IP。ipv4毕竟是有限资源,而且弹性IP本来就有limit,每次超过限制,都需要申请。

    c. private子网内的资源,未来需要被访问,需要额外的负载均衡器。

  2. AWS 的VPC Best Practice的架构图,非常有借鉴意义。设计时候,要考虑现有的办公网段,毕竟未来随时有可能在新的region创建VPC

    image-20200603164247190

  3. 创建NAT GATEWAY

    两种方式: 1. 自己用EC2搭建 2. 采用托管的NAT GATEWAY。健壮性考虑,我们选用了第二种。

  4. 设置private 子网的路由策略。


dns resolve

image-20200519113212054

如上图Route53,dns的解析 dev1-dop.nearbydirect.com 返回来的是dop-gateway-web-dev1.dop-dev1.svc.cs-software.local。然后客户端再发起dop-gateway-web-dev1.dop-dev1.svc.cs-software.local的解析请求。误区:并不是由ns服务器完成全部解析将最终ip返回给客户端

今天这个错误,犯的让我自己自己生自己气了。😠😠😠


MAC 上的快速IP计算工具:ipcalc

🤩🤩🤩 运维,怎么可能脱离网络呢。

mac上,发现一个工具,IP地址快速计算神器。

工具虽好,但是原理必须清清楚楚呢!

image-20200517140113630


gitlab kubernetes runner

提交代码后,由kubernetes中的gitlab-runner,编译构建发布。光是想想就好鸡冻了,对不对。如果gitlab-runner 也直接在kubernetes 中,随着任务量的变动而自动伸缩,多美妙啊。

  1. 部署gitlab runner

    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
    # gitlab-runner chart的value文件,最重要是修改gitlabUrl, 与注册token。

    [root@ip-10-20-1-93 gitlab-runner]# grep -v '#' values.yaml | grep -v '^$'
    imagePullPolicy: IfNotPresent
    gitlabUrl: http://gitlab.srv.sunvalley/
    runnerRegistrationToken: "3Pvqyxm3AJUURXkHW44K"
    unregisterRunners: true
    terminationGracePeriodSeconds: 3600
    concurrent: 10
    checkInterval: 30
    rbac:
    create: true
    clusterWideAccess: false
    metrics:
    enabled: true
    runners:
    image: ubuntu:16.04
    privileged: true
    pollTimeout: 180
    outputLimit: 4096
    cache: {}
    builds: {}
    services: {}
    helpers: {}
    securityContext:
    fsGroup: 65533
    runAsUser: 100
    resources: {}
    affinity: {}
    nodeSelector: {}
    tolerations: []
    hostAliases: []
    podAnnotations: {}
    podLabels: {}
  2. 下一步在代码仓库中,配置k8s集群的信息。企业版可以对不同的环境配置做权限管控以及owner才有的发布控制,强烈推gitlab企业版。

    image-20201102053124592

  3. 接下来就是让gitlab runner 跑起来印证咯。

    代码仓库中,创建.gitlab-ci.yml。

    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
    #variables:
    # MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode"
    # MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
    # DOCKER_HOST: "http://10.20.0.108:2375"

    #build:
    # stage: build
    # image: harbor.sunvalley.com.cn/library/maven:3.0.5
    # script:
    # - test CI_COMMIT_REF_NAME == "master" && IMAGE_VERSION="latest" || IMAGE_VERSION=$CI_COMMIT_REF_NAME
    # - mvn $MAVEN_CLI_OPTS clean package docker:build -DpushImage -Dmaven.test.skip=true -Dimage.version=${IMAGE_VERSION}
    # cache:
    # paths:
    # - .m2/repository/
    # artifacts:
    # paths:
    # - target/*.jar
    # expire_in: 2 week
    # environment:
    # name: test
    # only:
    # - tags

    test:
    stage: test
    script: echo "skipping test"
    only:
    - tags

    deploy:
    stage: deploy
    image: harbor.sunvalley.com.cn/library/kubectl-tools:1.16.3
    environment:
    name: test
    url: https://kube-dashboard.sunvalley.com.cn #内网开发注册中心
    when: manual
    only:
    - tags # tag分支 commit 之后触发
    script:
    - echo "deploy to test"
    - kubectl version
    - cd manifests/
    - sed -i "s/__CI_PROJECT_NAME__/${CI_PROJECT_NAME}/" *.yaml
    - sed -i "s/__CI_ENVIRONMENT_SLUG__/${CI_ENVIRONMENT_SLUG}/" *.yaml
    - sed -i "s/__VERSION__/${CI_COMMIT_REF_NAME}/" *.yaml
    - |
    if kubectl apply -f deployment.yaml | grep -q unchanged; then
    echo "=> Patching deployment to force image update."
    kubectl patch -f deployment.yaml -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"ci-last-updated\":\"$(date +'%s')\"}}}}}"
    else
    echo "=> Deployment apply has changed the object, no need to force image update."
    fi
    # - kubectl rollout status -f deployment.yaml
    - kubectl get all,ing -l env=${CI_ENVIRONMENT_SLUG}