Python|发送电子邮件(含附件)

电子邮件自诞生到现在,依旧是重要的通讯工具.在日常工作大量的告警,自动化报表依旧是通过邮件来完成.以前一直是只发送html正文,前两天遇到了发附件的情况,顺道解决了邮件名乱码的问题,记录一下

正常发送邮件

电子邮件到今天这个时间点,处理垃圾邮件的管控,很多云服务商和电子邮件服务商已经不再支持smtp通过25端口来发送,而要使用ssl加密的465端口
本文演示基本腾讯企业邮箱,估计QQ个人邮箱也一样.
Python:3.8.2

#!/usr/local/bin/python3.8.2
# -*- coding: utf-8 -*-
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

mail_host = 'smtp.exmail.qq.com'
mail_user = 'xxx@xxx.xx'
mail_pass = 'xxxxxx'
mail_from = 'rainbird'
mail_to   = 'xxx@xxx.xx'
mail_title= "rainbird's mac book"
me = mail_from +"<"+mail_user+">"

mail_body = '/result_report.html'
msg = MIMEText(mail_body,_subtype='html',_charset='utf8')
msg['Subject']  = mail_title
msg['From']     = me
msg['To']       = mail_to
try:
    s = smtplib.SMTP_SSL(host=mail_host)
    s.connect(mail_host)
    s.login(mail_user,mail_pass)
    s.sendmail(me, mail_to, msg.as_string())
    s.close()
    print('send mail success!')
except Exception as e:
    print(e)

发送附件

这部分比较困难的部分就是邮件名乱码.经过尝试指定邮件名UTF8编码,就可以了.

#!/usr/local/bin/python3.8.2
# -*- coding: utf-8 -*-
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

mail_host = 'smtp.exmail.qq.com'
mail_user = 'xxx@xxx.xx'
mail_pass = 'xxxxxx'
mail_from = 'rainbird'
mail_to   = 'xxx@xxx.xx'
mail_title= "rainbird's mac book"
me = mail_from +"<"+mail_user+">"

def file_get_content(file_name):
    with open (file_name,'r') as f:
        return f.read()

mail_body = '/result_report.html'
mail_att  = '/result.html'
msg = MIMEMultipart()
msg['Subject']  = mail_title
msg['From']     = me
msg['To']       = mail_to
msg.attach(MIMEText(file_get_content(mail_body),_subtype='html',_charset='utf8'))
# 邮件附件
att = MIMEText(file_get_content(mail_att), 'base64', 'utf-8')
att.add_header('Content-Disposition', 'attachment', filename=('utf-8', '', 'report.html'))
msg.attach(att)
try:
    s = smtplib.SMTP_SSL(host=mail_host,port=465)
    s.connect(mail_host)
    s.login(mail_user,mail_pass)
    s.sendmail(me, mail_to, msg.as_string())
    s.close()
    print('send mail success!')
except Exception as e:
    print(e)

结语

发邮件并不是什么困难的事儿,只是邮件涉及一堆参数,主机地址,用户名,密码啥的,把这些东西放在配置项里是好习惯.

Posted in Python | Tagged , , , , , | Leave a comment

Python|fastapi从入门到不放弃(二)

之前写了篇fastapi的简介[Python|介绍一下我的新伙伴fastapi(一)],今天直接进入干货阶段,将最近一段时间对fastapi的探索整理下来,也算是总结回顾.

基础配置

修改标题,描述,版本号
代码

app = FastAPI(
    ttitle="雨鸟飞行fastapi示例",
    description="演示fastapi常用的一些配置",
    version="0.1.0",
)

效果

修改swagger路径

默认/dcos访问swagger可以修改别的,比如/documents
代码

app = FastAPI(docs_url="/documents")

效果
访问/docs返回not found

{
detail: "Not Found"
}

隐藏redoc

fastapi默认自带两种接口显示方式:swagger+redoc,对于我来说swagger已经足够用了,没必要显示redoc直接干掉,也能避免一些安全隐患
代码

app = FastAPI(redoc_url=None)

效果
访问/redoc返回not found

{
detail: "Not Found"
}

### 接口分组
默认所有的接口都是放在default下显示,我们可以通过增加tag的方式,将不同类型的接口分组管理,核心在接口后添加tags=['分组名']
默认效果
代码

@app.get("/items/{item_id}",tags=["items"])
async def read_item(item_id: int, q: Optional[str] = None):
    return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}",tags=["items"])
def update_item(item_id: int, item: Item):
    return {"item_name": item.name, "item_id": item_id}

效果

增加自定义提示和链接

其实就是自定义一些tag,如果没有接口关联,就直接显示在那里了
代码

tags_metadata = [
    {
        "name": "介绍",
        "description": "雨鸟飞行的博客,显示fastapi的基本使用",
        "externalDocs": {
            "description": "博客地址",
            "url": "https://blog.cnrainbird.com",
        },
    }
]

app = FastAPI(openapi_tags=tags_metadata)

效果

中间件获取执行时间

log打印每个请求花费的时间,方便排查问题
代码

@app.middleware("http")
async def log_middle(request: Request, call_next):
    start_time = time.time()
    logger.debug(f"{request.method} {request.url}")
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    logger.info(f"{request.method} {request.url} {process_time}")
    return response

效果

swagger隐藏关键接口

有些敏感接口涉及加密信息等的,不在swagger显示,此处以无用的默认/及favicon.ico为例,关键字是include_in_schema=False
代码

@app.get('/favicon.ico',include_in_schema=False)
@app.get('/',include_in_schema=False)

效果
不知道咋截图,就是在swagger看不到这两个了

暴露健康检查接口

每个应用都应该有个接口,直接查询本服务运行状态,依赖的三方是否正常运行
代码

@app.get('/healthz',tags=["health"])
def healthz_check(request:Request):
    res_dict = {}
    res_dict['headers'] = request.headers
    config = []
    cfg_path = os.path.dirname(os.path.abspath(__file__)) + '/config.json'
    if os.path.isfile(cfg_path):
        config.append(f'''ok {cfg_path} exists''')
    else:
        config.append(f'''failed {cfg_path} exists''')
    res_dict['config'] = config
    res_dict['run']    = {}
    res_dict['system'] = {'hostname':'','ip':''}
    res_dict['env'] = os.environ
    return response_struct(data=res_dict)

效果

接口用途和描述

swagger已经很方便展示接口的入参及类型了,如果再加上一些描述岂不更香!
代码

@app.get('/healthz',tags=["health"])
def healthz_check(request:Request):
    '''
    服务健康检查

    留给运维检查服务状态,此处建议代码检查服务正常运行依赖项
    包括:
    - **headers** 请求信息
    - **config** 配置信息
    - **run** 运行依赖配置
    - **redis**  redis是否正常连接
    - **mysql** mysql连接状态
    - **api** 三方api调用是否正常
    '''

效果

后台执行

对于比较耗时的操作,或者前端只是触发一下并不需要立即返回结果,或者是定时任务只需要触发一下后台慢慢执行等
代码

from fastapi import BackgroundTasks
def do_send_mail(mail_to,mail_body):
    logger.info(mail_to,mail_body)
    time.sleep(3)
    logger.info(mail_to,mail_body)
@app.get('/send_mail',tags=['backgound'])
def send_mail_to_someone(mail_to:str,mail_body:str,background_tasks: BackgroundTasks):
    '''
    异步发送邮件

    比较耗时,后台rv执行
    '''
    background_tasks.add_task(do_send_mail,mail_to,mail_body)
    return response_struct()

效果
可以看到两个mail_to的打印时间中间差了3秒,但前端立即返回了

结尾

在fastapi的使用上,我还是小学生,也会一直继续使用下去,有新的心得会继续发出来共享

fastapi

Posted in Python | Tagged , , , , , , , , , , , , | Leave a comment

Python|介绍一下我的新伙伴fastapi(一)

在日常运维工作中,经常性的会创造出一些快速脚本,用来提高工作效率.一直在想有没有可能写一个Web工具,把这些常用的东西收集起来,用的时候直接点击拿到结果.这样即方便又能避免输错还不用登陆服务器显得更有效率.
直到我遇到了fastapi突然眼前一亮,不就是这货吗?让我等的好苦!

fastapi是什么

FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.6+ 并基于标准的 Python 类型提示。

关键特性:

  • 快速:可与 NodeJS 和 Go 比肩的极高性能(归功于 Starlette 和 Pydantic)。最快的 Python web 框架之一。

  • 高效编码:提高功能开发速度约 200% 至 300%。*

  • 更少 bug:减少约 40% 的人为(开发者)导致错误。*

  • 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。

  • 简单:设计的易于使用和学习,阅读文档的时间更短。

  • 简短:使代码重复最小化。通过不同的参数声明实现丰富功能。bug 更少。

  • 健壮:生产可用级别的代码。还有自动生成的交互式文档。

  • 标准化:基于(并完全兼容)API 的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema。

以上摘自官方文档
https://fastapi.tiangolo.com/zh/

我自己的一些感受

天然集成swagger,有了这个靠山,自然有了以下几大能力

  • 界面有了
    命令行很酷,键指如飞也很让人羡慕,但也仅而已了,真正牛掰的能力是复制,一旦你自身拥有了一项能力,你的团队同时俱备的这项能力,而且同你个人操作没有差别.这应该就是Web一直长盛不衰的本质所在.

  • 入参出参一目了然
    什么类型,是否必须,很直观的显示出来.以前做项目,写完实现,写注释,还要写接口文档给调用方,实在是费时费力,时间久了自己也不记得了.现在好了文档自动生成不说,忘记了直接打开看

  • 界面调试
    你很容易在浏览器里,直接调试你正在编写的接口.以前不觉得的什么,GET接口直接浏览器打开就好了,POST接口自己用curl或者postman造一下就行.现在好了直接内置这些技能,还给你自带一个curl的命令,方便你在命令行测试.当然输出也一目了然.

  • 开发效率
    这个要从几方面来说

    • 自动重启
      这个勉强算是吧,确切的说是uvicorn的能力,加了--reload参数,监控文件变化,自动重启
    • 参数校验
      明确定义参数和类型,少参数和类型不对的情况,fastapi帮你处理,这无形中是巨大的时间成本.
    • 返回结果
      默认json格式返回,以前处理完的结果,别管是数组还是字典都要json.dump一下.现在直接return好了.
  • 兼容flask
    确切的说不能算兼容,只能说语法类型,超级容易转换,只是获取参数的形式变一变,校验参数的部分拿掉,中间逻辑处理不变,返回结果时拿掉json转换
    举例
    转换前

    转换后

其实早就知道swagger这一神奇存在,甚至在我用上了fastapi还专门搜索了一下swagger在nodejs,golang,java等的集成方式,发现都是以插件的形式存在,需要不少的定制才能work,当然flask也可以集成swagger,只是有天然集成的存在实在找不出继续使用flask的理由
一口气,泛泛的纸上谈兵说了这么多,后面还会有一些相应的主题跟进,比如写一个比较全的例子,入门更容易一些,还有一些相对高级的诸如安全相关的东西.

fastapi

Posted in Python | Tagged , , , , , , | Leave a comment

2020年十一体重复盘

十一了,有些时间停下来思考,复盘一下体重情况.但是有段时间没写文章了,复盘前先复盘下这事儿.
发现上一篇公众号发的文章还是07.15.从03.16开始第一篇,写了四个月整.共计82篇.为什么没再坚持每天一篇呢?
首先,每天一篇本身就挑战很大,至少要花费两三个小时的时间,整理,发布.随着积累的文章源源不断的吐出,竟然有了江郎才尽的感觉,又不想胡乱发一些东西凑数,索性不如不写.
其次,时间变得有限了.早先突然想再折腾博客是因为疫情影响,半工状态,闲着也是闲着,不如把这些年积累的想法,有用的东西整理出来.随着疫情的恢复,进入正常工作状态.发现每天的业余时间很有限.工作时间不想开小差,晚上下班回家又仅有一个小时左右的时间,要么陪家人,要么两三天赶工写一篇文章.明显选择了前者.
进入正题,体重复盘
2020年疫情影响,不能正常工作,也减少外出锻炼,但是伙食并没有做相应的调整,于是一路飙升.有数据为证

以上数据来自小米智能秤.发现最早一条记录是03.13于是从15号开始隔10天一次整理出来.(智能手机是个好东西,跟智能秤一连接,每天自动收集,当然还有一点,要每天固定时间点秤一次(早,晚两次)).小米秤还会给出来一个得分.顺道也记录下来了.
半年时间里,体重最高166.5,最低138.7乖乖27.8斤的浮动.真有点折腾我这小身板了.再有就是发现这小这个得分竟然真能得90分.我一直以为这东西不准,只能六七十分呢.

一些直观的感受
确实人到中年啥啥都不容易,长胖最容易.之前体重一直稳定到143左右,忽悠一下就长到了167.真是不可思议.翻了翻之前发的朋友圈:

所以今年最重刷新了记录,最轻也刷新了记录,只不过又回来了一些,看来还有不少的下降空间.另外从整年的角度来看,短时间的起伏其实是可以接受的,不要固定下来就好.
接下来复盘三把斧,一一亮出来
1.做的好的地方

  • 每天秤体重
    数据采集很重要,无论数据好坏,收集起来,有了数据才能分析趋势,才能更好的指导方向.
  • 坚持每天快走1小时
    基本做到了每天,除了开始一段时间周末,懒不想出门,后面基本每天手环计步都在一万步.后来为啥周末也出门了呢,因为发现周末不但运动量少,在家吃的也多,两向一中和造成了很长一段时间,周末体重最高,然后周一到周五体重一直降,周五最低.周六又飙升.
  • 晚餐不吃主食(米,面)
    夏天晚上一般是吃西瓜为主,有时也吃其它水果.您可能问了,真能坚持晚上一直不吃主食?答:不能.吃了怎么办呢?还怎么办,接下来几天加大运动量,把吃的减下来呗.据说贾玲和瞿颖两个好朋友身材的区别就是:同样都撸了串,一个攒下来了,一个锻炼下去了.
  • 保持一个好的作息习惯
    早睡且固定时间入睡是一种能力.前面半年孩子一直在家,最晚能拖到11点入睡,孩子上学后更是养成了9点半洗漱,10点睡觉的好习惯.先不管能不能早起,起码睡眠是充足了.

2.做的不好的地方
吃还是控制不好.遇到好吃的,多吃一些,肚子难受,身体也难受.吃七分饱是个很重要的能力.未来还是要重点的练习.
3.以后怎么干
控制体重是个长期的事业,真正的逆水行舟不进则退.接下来天气凉了,肯定又想多吃点儿热乎热乎,又因为冷了,又不想动.怎么办,继续跟自己较劲呗.

转载请注明: 转自Rainbird的个人博客
   本文链接: 2020年十一体重复盘

Posted in 原创 | Tagged , , , | Leave a comment

Python|setuptools错误一例


随着python2.7的即将退出历史舞台,在ubuntu18.04上pip install pyenv竟然提示 Package 'setuptools' requires a different Python: 2.7.12 not in '>=3.5'

原因就是setuptools>=45的版本都不再支持python2.7
所以解决办法也很简单,降级setuptools

pip install setuptools==44.0.0
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Collecting setuptools==44.0.0
  Downloading setuptools-44.0.0-py2.py3-none-any.whl (583 kB)
     |████████████████████████████████| 583 kB 7.1 kB/s 
Installing collected packages: setuptools
  Attempting uninstall: setuptools
    Found existing installation: setuptools 45.0.0
    Uninstalling setuptools-45.0.0:
      Successfully uninstalled setuptools-45.0.0
Successfully installed setuptools-44.0.0

然后就顺利了

转载请注明: 转自Rainbird的个人博客
   本文链接: Python|setuptools错误一例

Posted in Python | Tagged , , , , , | Leave a comment

Python|openpyxl公式的写入及读取

最近excel研究的比较多,先是字体大小,都是单元格宽度,接着链接的保存及读取,这不又要操作公式了,一并记录下来.

写入

写入比较简单,以SUM这个公式举例了

#/usr/bin/python
#-*- coding: utf-8 -*-
import openpyxl
import os
#将Excel文件放在python同级目录
dir_path    = os.path.dirname(os.path.realpath(__file__))
test_xlsx_w = os.path.join(dir_path,f'''test_write.xlsx''')

wb = openpyxl.Workbook()
sheet = wb.active
sheet.cell(1, 1).value = 1
sheet.cell(2, 1).value = 2
sheet.cell(3, 1).value = '=sum(A1:A2)'

#保存文件
wb.save(test_xlsx_w)

读取

读取要的文件名,是上面写入的那个文件,开工

#/usr/bin/python
#-*- coding: utf-8 -*-
import openpyxl
import os
#将Excel文件放在python同级目录
dir_path  = os.path.dirname(os.path.realpath(__file__))
test_xlsx = os.path.join(dir_path,f'''test_write.xlsx''')
wb = openpyxl.load_workbook(test_xlsx)
sheet = wb.active
print(sheet.cell(3, 1).value)

运行

what?
为啥把公式给我输出了,我需要计算后结果啊?
要实现自动公式计算,需要要读取时添加data_only=True参数,以便只读取数据.

wb = openpyxl.load_workbook(test_xlsx,data_only=True)

不过即使添加了这个参数,读取时得到的结果却为None.

这种情况,将之前的Excel打开,再保存一下就可以了.不知道是不是因为openpyxl生成的xlsx的格式有问题,还是什么原因

总结

开头也说了,写过好几篇操作Excel的文章了,罗列一下:
[Python|openpyxl设置单元格样式]()
[Python|openpyxl单元格设置超链接及读取]()
其实直接点击专辑也可以

Posted in Python | Tagged , , , , , , , , | Leave a comment

Python|openpyxl单元格设置超链接及读取

背景

在Excel中,有时为了提高效率会放置一些外部的超链接,方便点击查看.近日遇到Python要读取和设置超链接的情况,记录一下

软件 版本
Python 3.8.2
Openpyxl 2.6.4

读取

通过Python读取超链接要相对简单一些,先准备一个Excel文件,A1里放置一个超链接,然后开工

录入

#/usr/bin/python
#-*- coding: utf-8 -*-
import openpyxl
import os
#将Excel文件放在python同级目录
dir_path  = os.path.dirname(os.path.realpath(__file__))
test_xlsx = os.path.join(dir_path,f'''test.xlsx''')
wb = openpyxl.load_workbook(test_xlsx)
sheet = wb.active
print(sheet.cell(1, 1).value)
print(sheet.cell(1, 1).hyperlink.target)

保存为pyxl_link_read.py执行后输出

rainbird's blog
https://blog.cnrainbird.com/


比较简单,接下来代码创建

写入

excel设置超链接也比较简单,顺手赠一个设置单元格宽度和字体

#/usr/bin/python
#-*- coding: utf-8 -*-
import openpyxl

#将Excel文件放在python同级目录
dir_path    = os.path.dirname(os.path.realpath(__file__))
test_xlsx_w = os.path.join(dir_path,f'''test_write.xlsx''')
wb = openpyxl.Workbook()
sheet = wb.active
sheet.cell(1, 1).value = '''=HYPERLINK("https://blog.cnrainbird.com","rainbird's blog")'''
sheet.cell(1, 1).font  = openpyxl.styles.Font('宋体',size = 16,bold=False,italic=False,strike=False,color='000000')
sheet.column_dimensions['A'].width = 25.0

后话

之前有写过Python|openpyxl设置单元格样式那篇主要设置单元格字体,宽度,颜色等.今天又进一步学习了超链接方法

Posted in Python | Tagged , , , , , , , , | Leave a comment

K8S|config自动更新器reloader

背景

在不使用配置中心的情况,我们写应用一般会使用环境变量和配置文件保存程序运行的依赖条件,比如:监听IP,端口等.这类应用迁入k8s时,一般都会通过configmap的方式来进行管理.那么问题来了:configmap修改以后有时需要重启pod以实现让应用重新加载配置的目的,尤其像如果将配置文件放在了configmap里,只能通过滚动更新来实现加载新配置.
reloader的工作便是:当你修改了configmap或secret,它来帮你重启相应的Pod.

部署

默认部署到default名称空间

wget https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml
sed -i 's#RELEASE-NAME#config#g' reloader.yaml
kubectl apply -f reloader.yaml

部署到 kube-system 命名空间下

reloader.yaml

---
# Source: reloader/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: kube-system
  labels:
    app: config-reloader
    chart: "reloader-v0.0.39"
    release: "config"
    heritage: "Tiller"
    group: com.stakater.platform
    provider: stakater
    version: v0.0.39

  name: config-reloader
spec:
  replicas: 1
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: config-reloader
      release: "config"
  template:
    metadata:
      namespace: kube-system
      labels:
        app: config-reloader
        chart: "reloader-v0.0.39"
        release: "config"
        heritage: "Tiller"
        group: com.stakater.platform
        provider: stakater
        version: v0.0.39

    spec:
      containers:
      - env:
        image: "stakater/reloader:v0.0.39"
        imagePullPolicy: IfNotPresent
        name: config-reloader
        args:
      serviceAccountName: reloader

---
# Source: reloader/templates/clusterrole.yaml

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  labels:
    app: config-reloader
    chart: "reloader-v0.0.39"
    release: "config"
    heritage: "Tiller"
  name: config-reloader-role
  namespace: kube-system
rules:
  - apiGroups:
      - ""
    resources:
      - secrets
      - configmaps
    verbs:
      - list
      - get
      - watch
  - apiGroups:
      - "apps"
    resources:
      - deployments
      - daemonsets
      - statefulsets
    verbs:
      - list
      - get
      - update
      - patch
  - apiGroups:
      - "extensions"
    resources:
      - deployments
      - daemonsets
    verbs:
      - list
      - get
      - update
      - patch

---
# Source: reloader/templates/rolebinding.yaml

---
# Source: reloader/templates/clusterrolebinding.yaml

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  labels:
    app: config-reloader
    chart: "reloader-v0.0.39"
    release: "config"
    heritage: "Tiller"
  name: config-reloader-role-binding
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: config-reloader-role
subjects:
  - kind: ServiceAccount
    name: reloader
    namespace: kube-system

---
# Source: reloader/templates/serviceaccount.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: kube-system
  labels:
    app: config-reloader
    chart: "reloader-v0.0.39"
    release: "config"
    heritage: "Tiller"
  name: reloader
kubectl apply -f reloader.yaml

deployment 使用说明

如果某deployment需要随着configmap的更新而自动重启pods
只需要添加注释reloader.stakater.com/auto: "true"即可:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {APP_NAME}-deployment
  annotations:
    reloader.stakater.com/auto: "true"
... ...

拓展

  • 指定 configmap 更新,滚动更新deployment

configmap中以逗号间隔

kind: Deployment
metadata:
  annotations:
    configmap.reloader.stakater.com/reload: "foo-configmap,bar-configmap,baz-configmap"
spec:
  template:
    metadata:
  • 指定 secret, 与 configmap 配置一致

参考链接

https://github.com/stakater/Reloader

Posted in K8S | Tagged , , , , , , , , | Leave a comment

ubuntu|编译安装mysql2pgsql

dbsync 项目目标是围绕 PostgreSQL Greenplum ,实现易用的数据的互迁功能。
官方地址:
https://github.com/aliyun/rds_dbsync
提供mysql2pgsql和pgsql2pgsql两个工具.
不过竟然只提供了Centos版或者说rpm版.而我司服务器清一色ubuntu,不能因为这个工具,存在一个异类吧?于是开启了这趟不大顺利的编译之旅

系统

Distributor ID: Ubuntu
Description:    Ubuntu 16.04.6 LTS
Release:        16.04
Codename:       xenial

下载代码

放在src目录是了习惯

cd /usr/local/src
git clone https://github.com/aliyun/rds_dbsync.git

安装mysql的开发包

既然编译安装自然依赖mysql的开发包

apt-get install  mysql-community-embedded-devel

安装pg的开发包

pg的包安装相对麻烦一些,毕竟不常用

echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" >> /etc/apt/sources.list.d/pgdg.list
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add
apt-get update

apt-get install postgresql-server-dev-9.6
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages were automatically installed and are no longer required:
  comerr-dev krb5-multidev libgssrpc4 libkadm5clnt-mit9 libkadm5srv-mit9 libkdb5-8
Use 'sudo apt autoremove' to remove them.
The following additional packages will be installed:
  libpq-dev libpq5 postgresql-client-9.6
Suggested packages:
  postgresql-doc-12 postgresql-9.6 postgresql-doc-9.6
The following NEW packages will be installed:
  postgresql-client-9.6 postgresql-server-dev-9.6
The following packages will be upgraded:
  libpq-dev libpq5
2 upgraded, 2 newly installed, 0 to remove and 52 not upgraded.
Need to get 2,438 kB of archives.
After this operation, 10.8 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Get:1 http://apt.postgresql.org/pub/repos/apt xenial-pgdg/main amd64 libpq-dev amd64 12.3-1.pgdg16.04+1 [139 kB]
Get:2 http://apt.postgresql.org/pub/repos/apt xenial-pgdg/main amd64 libpq5 amd64 12.3-1.pgdg16.04+1 [172 kB]                                   
Get:3 http://apt.postgresql.org/pub/repos/apt xenial-pgdg/main amd64 postgresql-client-9.6 amd64 9.6.18-1.pgdg16.04+1 [1,343 kB]                
Get:4 http://apt.postgresql.org/pub/repos/apt xenial-pgdg/main amd64 postgresql-server-dev-9.6 amd64 9.6.18-1.pgdg16.04+1 [784 kB]              
Fetched 2,438 kB in 4min 24s (9,223 B/s)                                                                                                        
(Reading database ... 105399 files and directories currently installed.)
Preparing to unpack .../libpq-dev_12.3-1.pgdg16.04+1_amd64.deb ...
Unpacking libpq-dev (12.3-1.pgdg16.04+1) over (9.5.21-0ubuntu0.16.04.1) ...
Preparing to unpack .../libpq5_12.3-1.pgdg16.04+1_amd64.deb ...
Unpacking libpq5:amd64 (12.3-1.pgdg16.04+1) over (9.5.21-0ubuntu0.16.04.1) ...
Selecting previously unselected package postgresql-client-9.6.
Preparing to unpack .../postgresql-client-9.6_9.6.18-1.pgdg16.04+1_amd64.deb ...
Unpacking postgresql-client-9.6 (9.6.18-1.pgdg16.04+1) ...
Selecting previously unselected package postgresql-server-dev-9.6.
Preparing to unpack .../postgresql-server-dev-9.6_9.6.18-1.pgdg16.04+1_amd64.deb ...
Unpacking postgresql-server-dev-9.6 (9.6.18-1.pgdg16.04+1) ...
Processing triggers for man-db (2.7.5-1) ...
Processing triggers for libc-bin (2.23-0ubuntu11) ...
Processing triggers for postgresql-common (173ubuntu0.3) ...
Building PostgreSQL dictionaries from installed myspell/hunspell packages...
Removing obsolete dictionary files:
Setting up libpq5:amd64 (12.3-1.pgdg16.04+1) ...
Setting up libpq-dev (12.3-1.pgdg16.04+1) ...
Setting up postgresql-client-9.6 (9.6.18-1.pgdg16.04+1) ...
update-alternatives: using /usr/share/postgresql/9.6/man/man1/psql.1.gz to provide /usr/share/man/man1/psql.1.gz (psql.1.gz) in auto mode
Setting up postgresql-server-dev-9.6 (9.6.18-1.pgdg16.04+1) ...
Processing triggers for libc-bin (2.23-0ubuntu11) ...

编译

尴尬开始

cd rds_dbsync/dbsync
make
/usr/lib/postgresql/9.6/lib/pgxs/src/makefiles/pgxs.mk:219: warning: ignoring old recipe for target 'clean'
g++ -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fPIC -pie -fno-omit-frame-pointer -fPIC -fPIC demo.o pg_logicaldecode.o pqformat.o stringinfo.o utils.o misc.o pgsync.o ini.o -L/usr/lib/postgresql/9.6/lib -lpgcommon -lpgport -L/usr/lib/x86_64-linux-gnu -lpq '-Wl,-rpath,$ORIGIN,-rpath,$ORIGIN/lib,-rpath,$ORIGIN/../lib,-rpath,/usr/lib/x86_64-linux-gnu,-rpath,/usr/lib/x86_64-linux-gnu' -L/usr/lib/x86_64-linux-gnu  -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now  -L/usr/lib/x86_64-linux-gnu/mit-krb5 -Wl,--as-needed  -lpthread -o demo 
/usr/bin/ld: demo.o: relocation R_X86_64_32S against `.rodata' can not be used when making a shared object; recompile with -fPIC
demo.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
Makefile:27: recipe for target 'all' failed
make: *** [all] Error 1

网上查到两个同类问题

问题:fileappender.o relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPI
解决:
使用 ar -x liblog4cplus.a 解包
readelf --relocs fileappender.o | egrep '(GOT|PLT|JU?MP_SLOT)' 如果输出为空,则该静态库没有position-independent,需要在该静态库的编译脚本添加 -fPIC

ffmpeg  --enable-shared编译时
libavcodec/mqc.o: relocation r_x86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fpic
ffmpeg编译错误,修改Makefile
121:$$(OBJS-$(1)): CFLAGS  += $(CFLAGS-$(1))
为
121:$$(OBJS-$(1)): CFLAGS  += -fPIC $(CFLAGS-$(1))
重新make,成功。

解决办法都是添加-fPIC
经过一番尝试,发现其实编译过程已经添加了-fPIC,但是始终编译不过
查看demo.cpp发现,里面竟然写死了数据库连接的用户名和密码

看来是测试的demo了,那不是可以直接不编译这哥们儿?
将Makefile中demo相关的代码注释掉

-all: demo.o dbsync-pgsql2pgsql.o mysql2pgsql.o dbsync-mysql2pgsql.o readcfg.o
-       $(CXX) $(CFLAGS) demo.o $(OBJS) $(libpq_pgport) $(RPATH_LDFLAGS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o demo 
+#all: demo.o dbsync-pgsql2pgsql.o mysql2pgsql.o dbsync-mysql2pgsql.o readcfg.o
+all: dbsync-pgsql2pgsql.o mysql2pgsql.o dbsync-mysql2pgsql.o readcfg.o
+       #$(CXX) $(CFLAGS) demo.o $(OBJS) $(libpq_pgport) $(RPATH_LDFLAGS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o demo 
        $(CXX) $(CFLAGS) readcfg.o dbsync-pgsql2pgsql.o $(OBJS) $(libpq_pgport) $(RPATH_LDFLAGS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o pgsql2pgsql
        $(CXX) $(CFLAGS) readcfg.o ini.o mysql2pgsql.o dbsync-mysql2pgsql.o misc.o stringinfo.o $(libpq_pgport) $(RPATH_LDFLAGS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -L$(mysql_lib_dir) -lmysqlclient -o mysql2pgsql

@@ -36,7 +37,7 @@ package:
        mkdir -p install/bin
        mkdir -p install/lib
        cp -fr pgsql2pgsql install/bin
-       cp -fr demo install/bin
+#      cp -fr demo install/bin
        cp -fr ali_recvlogical.so install/lib
        cp -fr mysql2pgsql install/bin
        cp -fr $(mysql_lib_dir)/libmysqlclient.so* install/lib

再次make,顺利通过

打包二进制

# make package 

mkdir -p install
mkdir -p install/bin
mkdir -p install/lib
cp -fr pgsql2pgsql install/bin
cp -fr ali_recvlogical.so install/lib
cp -fr mysql2pgsql install/bin
cp -fr /usr/lib/x86_64-linux-gnu/libmysqlclient.so* install/lib
cp -fr /usr/lib/x86_64-linux-gnu/libpq.so* install/lib

将生成一个install目录,里面有二进制和lib

验证

#cd install/bin/
#ls
mysql2pgsql  pgsql2pgsql
#./mysql2pgsql 
Failed to load config file
read config file error, insufficient permissions or my.cfg does not exist

看起来工作ok

总结

编译过程看似复杂,其实就卡在一个测试的demo文件上.花了有两个小时研究解决方案.好尴尬.

转载请注明: 转自Rainbird的个人博客
   本文链接: ubuntu|编译安装mysql2pgsql

Posted in linux | Tagged , , , , , , , , , , , , | Leave a comment

联想员工亲历联想大裁员:公司不是家


转自:
https://it.sohu.com/20040720/n221093232.shtml

  今天,恐怕是联想历史上规模最大的一次大裁员。我们部门9个人,今天送走了三个,还有三个要转岗,剩下三个。整个研究院走了30多人,转岗20多人。这是我经历的第二次所谓战略性调整,有很多感触,却又好像什么都堵在心里,说不出来。干脆简单记录下这段往事,提醒自己。

[联想精细化裁员]

  昨天晚上,研究院秘密召开紧急会议。有20多位“责任经理”参加,我才清楚了整个裁员过程。3月6日启动计划,7日讨论名单,8日提交名单,9-10日hr审核,并办理手续,11日面谈。整个过程一气呵成。

  今天就是面谈日。在b座一层的两个小会议室。进去的人,领导首先肯定他过去的成绩,然后解释战略裁员的意思,然后告知支付的补偿金数额,然后递上所有已经办好的材料,然后让他在解除劳动关系合同上签字。平均每个人20分钟。

  被裁的员工事先都完全不知情。在面谈之前,他们的一切手续公司都已经办完,等他们被叫到会议室的同时,邮箱、人力地图、ic卡全部被注销,当他们知道消息以后,两个小时之内必须离开公司。

  所有这一切,都是在高度保密的过程中进行。即使我是责任经理,我也只知道明天由我陪同的员工----坐在我隔壁办公位的,朝夕相处两年多的一个女孩,邵隽。

[邵隽]

  我不知道昨晚我是怎么过的,心情特别不好。根据公司规定,我不能提前告诉她。只觉得心里堵得慌。和我朝夕相处两年的同事,明天就要被裁员了,而她一点也不知道。开完会打车回家时,我感到特别疲惫。司机开口了:你怎么会累呢?你们这一行挣钱多容易呀。我苦笑了一下,没有回答。

  早上,邵隽比我到得要早。向她问声早上好后,我就心虚的不敢再说一句话了。我照例喂我桌上的小金鱼。研究院乔迁研发大厦的时候,每个人发了两条小金鱼,但这帮粗心的研发人员照顾不周,能活到现在的,实在是不容易。邵隽还拿我的鱼开玩笑,说这整儿一鱼精,居然还能活着。

  我不再说话,坐在电脑边发呆,等待着那一刻的到来。电话终于响了,我走到邵隽面前,先和她握手,再叫她去楼下的会议室。她知道去会议室意味着什么。那两个会议室从早忙到晚,所有进去的人,出来后就直接收拾东西走人。但邵隽一直很平静,因为在她之前,我们部门已经进去两个了。是清涛和她谈的,大家都这么熟了,也不用多说什么,不到五分钟,就结束了所有谈话,在解除劳动关系合同上签了字,走了出来。邵隽是fm365转过来的,经历过365那次疯狂的裁员,她那次也送过好多人,所以她很清楚这一切。

  然后回到办公位的时候,陪她收拾东西。到午饭时间了,她说,先去食堂吃饭吧。但我不忍心告诉她,她的ic卡现在已经被注销了。所以我劝她去外边吃。负责另外一个人的责任经理却直接说出来了,还有人告诉她,人力地图也已经注销了,当时邵隽明显非常失落,感觉突然和公司一点牵连都没有了。她在联想工作三年了,可就在两个小时之内,联想就不再有她的任何痕迹。被公司抛弃了。就这么抛弃了?转眼功夫,就不再是曾经引以为豪的:“联想人”啦?

  中午,部门全体去辣婆婆吃散伙饭。不记得说了些什么。

  下午,我送邵隽到家。路过一个小学门口,堵车,她说,我还从来没有下午从这里走过,从来没见过这群孩子们放学。是呀,我也是每天工作到很晚,白天回家还真不习惯。在她家坐了一会儿,因为我知道她这时候心里肯定非常不好受。她说了很多当年365的事情。是呀,不管你如何为公司卖命,当公司不需要你的时候,你曾经做的一切都不再有意义。我特意多呆了一会儿,听她说话,因为我知道,邵隽虽然表现的很坚强,但我一转身走掉,她很可能会哭的,就像今天裁掉的许多人一样。

[重灾区]

  服务器、职能,是这次裁员的重灾区。其中服务器研究室今后可能就不存在了,今天裁得只剩下5个人。早上我就听说那边已经走空了,有几个人哭了,但我没有过去看。有的人情绪非常激动,因为绝对想不到会落在自己头上,但是,战略裁员的意思就是说,不是以你的业绩作为标准,换句话说,就是没有标准。有好几个原来的大牛人,甚至是当时重金从外面聘请的博士后,也就那么走了,没有一点商量余地。就连服务器研究室的主任都走了。这整个方向不要了,这是谁的错?不知道,但只知道受伤的是最底层的员工,难怪有个清华刚毕业的女孩,哭得一塌糊涂。

  职能的助理几乎走光了。和我熟悉的安欣、秦莉,都还没来得及说再见。现在研究院不设置助理岗位了。前几天在食堂碰见她们,我还称她们是研究院的形象代言人。

  武庄也走了,这是我的老战友了。我初进联想的那个项目组,到现在,还在联想的,只剩下我和郭明亮、金峰了。我还记得,那年,我们项目组被号称是研究院的一面旗帜。因为我们开发的内容管理系统,成功地挽救了fm365。后来365倒了,我们就支持赢时通。后来赢时通也倒了,于是研究院信息工程研究室也就没有了。整个研究室当年的30多号人,到现在,还在原岗位的,只剩下我和王江、于兴业了。武庄非常惨,他的老婆在怀孕,而他自己刚刚买房子。我不敢替他想象未来,因为我不能为他做些什么。

  这次裁员的重点,是新来的员工,和呆了好多年的老联想。工作10年的,奔50的人,也照样该走就走了。我真想和他们谈谈心,50岁的时候被公司抛弃,不知道会有什么样的感触。我不敢想。

  回到家里,和小丁聊天,我才知道,服务器的周密走了,这不是新闻,因为服务器的人走得差不多了,但是她的老公也走了!今天他俩还一起上班的,不知道会不会一起回家。他俩和我住在同一个小区,刚结婚不久,刚买的房。

  我突然想起来二战时某位著名将军说的话:我让士兵上战场的时候,我会把他们想象成一堆蚂蚁,而不是人。因为我一想到他们有妻子、孩子、父母,我就不忍心让他们去送死。不知道领导在讨论名单的时候,是把我们想象成蚂蚁吗?

[到底是谁的错]

  我在联想的这三年,亲眼见到联想从全面扩张,到全面收缩的全过程。当年提出的口号是:高科技的联想,服务的联想,国际化的联想。现在,高科技仅剩下关联应用或者,而且还不知道能不能成功。代表服务的it服务群组被划归为c类业务,自身难保了。软件设计中心也即将和联想没有任何关系了。联想四面出击,却伤横累累。

  是谁的错?是领导的错!包括fm365在内,这些方向都是看好的,都是挣大钱的,但为什么联想会失败?我不想在这里深究,但只是觉得,领导犯下的错,只有我们普通员工来承担。

[联想不是家]

  这是我亲历这两次重大战略调整,所得出的结论。我想,我比许多人都体会深刻。员工和公司的关系,就是利益关系,千万不要把公司当作家。

  当然,这不是说我工作会偷懒。我仍然会好好工作,我要对得起联想。同时,我也觉得联想没有欠我的。联想给了我这么好的工作环境,这么好的学习机会,还有不错的待遇。但,公司就是公司,公司为我做的这一切,都是因为我能为公司做贡献,绝对不是像爸爸妈妈的那种无私奉献的感情。认识到这一点,当我将来离开时,领导会肯定我的业绩,我也会对领导说谢谢,不再会感伤。

  杨元庆说,希望这一次调整给联想带来10年的好运气,但回想上一次战略调整,也就是在2001年11月1日,不禁让人对这句话产生怀疑。怀疑归怀疑,事情还是要做的。生活还要继续。

  ----怀念和我一起共事的众多同事们!

Posted in 转载 | Tagged , , , , , , | Leave a comment