Vagrant 如果在共享目录的时候无法双向同步,在启动的时日志提示信息,提示系统缺失Virtualbox Guest Additions
插件
1 | No guest additions were detected on the base box for this VM! |
安装vagrant-vbguest
插件,重新启动虚拟机后会自动在虚拟机里面编译安装Virtualbox Guest Additions
插件
1 | $ vagrant plugin install vagrant-vbguest |
关闭默认挂载目录
1 | $ config.vm.synced_folder ".","/vagrant",disabled:true |
配置共享目录,虚拟机的挂载位置不能使用默认目录/vagrant
,这里用/share
目录
1 | $ config.vm.synced_folder "./code", "/share" |
重启虚拟机,完成挂载配置更新
vagrant创建虚拟机,默认磁盘只有8GB,可能不够用
安装插件vagrant-disksize
,默认由8G变为40G,另外还可以手动配置调整到更大空间
1 | $ vagrant plugin install vagrant-disksize |
Vagrant
+ VirtualBox
搭建的开发环境,如果VirtualBox
进程CPU负载莫名其妙飙高到100%+,电脑风扇响个不停
这个貌似是VirtualBox
的BUG
,手动在VirtualBox
里面关掉虚拟机的声卡(虚拟机->设置->声音->勾掉启用声音选项),即可
用 Django + Django-REST-framework
开发后端API接口有一段时间了,工作中每开发一个新的微服务,都需要配置同样的项目结构或设置相同的配置项,不仅浪费时间并且有还容易出错。
用cookiecutter
可以基于模板生成项目,这样就可以解决这些问题了。
我从日常的项目中抽离了一些最基本的功能做成了一个模板,希望能帮助遇到同样困惑的朋友,如果能提供一个解决问题的思路或方向就是最大的收获了。
API接口有多种认证方式,例如基础认证(用户名/密码)
、apikey(秘钥)
、token(令牌)
、JWT(令牌)
、signature(签名)
等。非对称签名认证应用广泛,接下来从客户端到服务端简单实现非对称签名,签名算法采用HMAC SHA256
。
数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用HASH函数对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,否则说明信息被修改,因此数字签名能保证信息传输过程中完整性、提供信息发送者的身份认证和不可抵赖性。可以用数字签名来做API认证。
使用随机函数生成公私钥对,例如
1 | 公钥: "8thcm24furn0likp" |
选择合适的摘要签名信息,可以采用当前时间
1 | 摘要信息: "date: 2019-10-21 10:58:58" |
将摘要信息用HMAC SHA256
进程签名,并对原始的二进制数据进行Base64编码
,得到签名值
1 | 签名值: "33xn46YabD78N372yw+WV4NvNSTRrRbmAV7mcz0vDUQ=" |
客户端header
组装,x-api-key
用来传输公钥,date
用来传输摘要信息,authorization
用来传输完整的签名信息
1 | 'x-api-key': '8thcm24furn0likp', |
python
可以基于HTTPSignatureAuth
来实现
1 | # -*- coding: utf-8 -*- |
如果是Kong的上游请求需要进行签名认证,可以基于openssl.hmac
和ngx
模块实现
1 | local BasePlugin = require "kong.plugins.base_plugin" |
首先,服务端通过客户端请求传输的header
信息,获取摘要信息、签名值、公钥并找到本地对应的私钥,然后使用同样的算法用私钥对摘要信息进行签名,最后通过对比传输的签名值和本地签名值是否一致,来判断认证的有效性。
基于Iris框架的实现
1 | // SignatureAuth 判断签名是否有效 |
给需要认证的API添加拦截器
1 | func before(ctx iris.Context) { |
测试,未通过签名认证认返回信息
1 | { |
Nginx配置跨域请求 Access-Control-Allow-Origin *
Nginx配置跨域访问
CORS跨域资源共享
CROS
全称是跨域资源共享 (Cross-origin resource sharing),跨域资源共享(CORS)标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的HTTP请求方法(特别是GET以外的HTTP请求,或者搭配某些MIME类型的POST请求),浏览器必须首先使用OPTIONS方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求
。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
服务端默认是不被允许跨域的,当浏览器跨域访问出现403
跨域错误的时候No 'Access-Control-Allow-Origin' header is present on the requested resource
。两种解决方法,使用Nginx作为反向代理服务器,对响应添加对应的头信息
,服务端自身实现,允许跨域
。不管哪种方式都是添加响应头信息Access-Control-Allow-Origin *
,表示服务端可以接受所有的请求源Origin
,即接受所有跨域的请求。
通过nginx
作为反向代理服务器,解决跨域问题网上有很多教程,这里就不不累述了。主要描述下怎样在服务端自身实现跨域,这样是比较理想的解决方法,可以通过Iris cors 中间件
实现,也可以自己代码实现。
实例代码可以参考文章 CORS跨域资源共享
自定义middleware
包新增函数Cors
。给所有请求添加头信息Access-Control-Allow-Origin *
,如果是OPTIONS
方法的预请求,成功响应并添加对应的头信息,状态码响应204
或者200
。不设置状态码默认响应301
,预请求响应失败会造成最后跨域访问失败。
1 | package middleware |
如果API接口没有实现OPTIONS
方法,需要给所有接口设置缺省的OPTIONS
方法,不然捕捉不到对应API接口的OPTIONS
请求。
1 | package routers |
然后在所有请求前面添加拦截器,使用自定义的中间件r.Use(cors)
。
1 | package routers |
用OPTIONS
方法请求任一API接口,可以测试是否生效,如下返回的结果显示已生效。
1 | $ curl -X OPTIONS -vv http://localhost:8080/hs |
Kong
支持 同时启用
多个身份认证 (Auth)
插件,允许客户端使用不同的身份验证方法来访问指定的服务 (Services)
或路由(Routes)
。
创建消费者 devops
1 | $ curl -X POST --url http://localhost:8001/consumers/ --data "username=devops" |
为消费者 devops
生成秘钥凭证,可以指定key
值,也可以让插件自动生成key
值,建议插件自动生成
1 | $ curl -X POST --url http://localhost:8001/consumers/devops/key-auth/ |
可以通过接口/key-auths
查看所有消费者的秘钥,通过/consumers/{consumer}/key-auth
接口查看指定消费者的秘钥。
1 | $ curl -X POST http://localhost:8001/consumers/devops/jwt \ |
可以通过/jwts
接口查看所有消费者的jwt
凭证,通过/consumers/{consumer}/jwt
接口查看指定消费者的凭证。
插件可以应用于Route
,Service
,Global
,这里以Global
为例。并同时启动key-auth
和jwt
认证插件。
1 | $ curl -X POST --url http://localhost:8001/plugins/ --data "name=key-auth" |
1 | $ curl -X POST --url http://localhost:8001/plugins/ --data "name=jwt" |
Kong
启用多个认证插件后,默认的执行逻辑是 AND
。例如启用了两个认证插件key-auth
和jwt
,那么每次的请求必须提供这两个插件的认证信息,必须通过所有认证插件的认证,才会将下游的请求转发到上游。
使用key-auth
认证方式,请求状态401
,返回jwt
插件的未认证信息提示{"message":"Unauthorized"}
1 | $ curl -i -X GET --url http://192.168.1.100:8000/api/test/ \ |
使用 jwt
认证方式,请求状态401
,返回key-auth
插件的未认证信息提示{"message":"No API key found in request"}
1 | $ curl -i -X GET --url http://192.168.1.100:8000/api/test/ \ |
同时使用key-auth
和jwt
认证方式,成功返回请求数据,请求状态200
1 | $ curl -i -X GET --url http://192.168.1.100:8000/api/test/ \ |
Kong
启用多个认证插件后,默认的执行逻辑是 AND
,当认证插件都启用匿名访问后,执行逻辑将会变为 OR
。
在不启用匿名访问的时候,
auth插件
将始终执行身份验证,如果未经过验证,则返回40x
响应。当启用匿名访问后auth插件
只会在未经过身份验证的情况下执行身份验证,如果身份验证失败,它不会返回40x
响应,而是将匿名使用者设置为有效的消费者,那么当调用多个auth插件
时,就会导致OR + 匿名访问
的逻辑。如果配合request-termination plugin
插件拦截匿名访问的请求,禁止匿名访问,就可以达到多种认证OR
的效果。
1 | $ curl -X POST --url http://localhost:8001/consumers/ \ |
key-auth
插件启用匿名访问,插件id
为947db416-1f3a-4a54-a5ff-75b6a55206d7
,匿名用户id
为958e85d7-e39d-4d2c-b8a9-888e25dbeed5
1 | $ curl -X PATCH --url http://localhost:8001/plugins/947db416-1f3a-4a54-a5ff-75b6a55206d7/ \ |
jwt
插件启用匿名访问,插件id
为fc54429b-73cd-4215-8f4d-35a21c6a389e
,匿名用户id
为958e85d7-e39d-4d2c-b8a9-888e25dbeed5
1 | $ curl -X PATCH --url http://localhost:8001/plugins/fc54429b-73cd-4215-8f4d-35a21c6a389e/ \ |
启用匿名访问后,请求将不需要认证
1 | $ curl -i -X GET --url http://192.168.1.100:8000/api/test/ |
启用request-termination
插件
1 | $ curl -X POST http://localhost:8001/plugins/ \ |
匿名消费者anonymous
启用该拦截插件,插件id
为e5ff19cf-006d-4fcd-ae00-5837bc5d6938
,匿名消费者id
为958e85d7-e39d-4d2c-b8a9-888e25dbeed5
1 | $ curl -X PATCH http://localhost:8001/plugins/e5ff19cf-006d-4fcd-ae00-5837bc5d6938/ \ |
此时匿名请求已被拦截
1 | $ curl -i -X GET --url http://192.168.1.100:8000/api/test/ |
但是只要通过任一种验证,即可成功请求数据
1 | # key-auth |
此时的多认证插件(Auth)
执行逻辑是 OR
只要 kong
的后端数据库做到了高可用,kong
的高可用水到渠成。
到 Kong
官网下载相应的rpm
安装包或者获取下载链接。
下载安装包
1 | $ wget https://bintray.com/kong/kong-community-edition-rpm/download_file?file_path=centos/6/kong-community-edition-1.0.0.el6.noarch.rpm |
安装
1 | $ yum install kong-community-edition-1.0.0.el6.noarch.rpm |
配置
1 | $ grep -Ev '^#|^$|^ ' kong.conf |
用其中某个
节点,来初始化数据库
1 | Kong版本高于0.14.1 |
各个节点启动服务
1 | $ kong start [-c /etc/kong/kong.conf] |
各个节点配置nginx反向代理
1 | cat kong.conf |
测试服务
1 | $ curl http://kong.domain.com/ |
role | ip | PostgreSQL | Pgpool-II | port |
---|---|---|---|---|
master | 192.168.1.3 | 10.9 | 4.0.5 | 5432 |
slave | 192.168.1.2 | 10.9 | 4.0.5 | 5432 |
vip | 192.168.1.100 | – | – | – |
其中watchdog
端口9000
,pcp
端口9898
,psql
服务连接端口9999
查看集群节点状态 show pool_nodes;
1 | $ psql -p 9999 -h 192.168.1.100 -U postgres |
vip
目前在master
端,先测试非vip端pgpool
服务不可用,再测试vip端pgpool
服务不可用。
停止pgpool
服务
1 | $ systemctl stop pgpool.service |
查看节点状态,服务未中断
1 | $ psql -p 9999 -h 192.168.1.100 -U postgres |
停止pgpool
服务
1 | $ systemctl stop pgpool.service |
此时 非vip端的pgpool
会主动接管vip
继续对外提供服务
1 | ifconfig eth0:0 |
查看节点状态,服务被接管后,继续对外提供服务
1 | $ psql -p 9999 -h 192.168.1.100 -U postgres |
停止postgresql
服务
1 | $ pg_ctl stop |
此时集群节点状态会发生变化,standby
节点变为down
,此时集群仍然可以继续对外提供服务。
1 | $ psql -p 9999 -h 192.168.1.100 -U postgres |
重新启动 standby
节点,模拟节点修复,重新将该节点加入集群
1 | $ pg_ctl start |
standby
重新加入集群后,状态变为up
,继续对外服务。
1 | $ psql -p 9999 -h 192.168.1.100 -U postgres |
当前 192.168.1.2
是 primary
1 | $ psql -p 9999 -h 192.168.1.100 -U postgres |
停止192.168.1.2
上面的 postgresql
服务
1 | $ pg_ctl stop |
此时查看节点状态,集群检查到 192.168.1.2
服务不可用后,自动切换 192.168.1.3
为 primary
,继续对外提供服务,此时集群可读写。
1 | $ psql -p 9999 -h 192.168.1.100 -U postgres |
修复节点 192.168.1.2
的服务,然后重新加入集群作为 standby
继续对外提供服务。如果主备时间线不同步,需要用 pg_rewind
命令修复时间线,然后再启动服务。
1 | $ mv recovery.done recovery.conf |
节点成功加入集群,其状态为 up
1 | $ psql -p 9999 -h 192.168.1.100 -U postgres |
Pgpool-II
是一个位于PostgreSQL服务器
和PostgreSQL数据库客户端
之间的中间件,Pgpool-II
提供了连接池(Connection Pooling)、复制(Replication)、负载均衡(Load Balancing)、缓存(In Memory Query Cache)、看门狗(Watchdog)、超出限制链接(Limiting Exceeding Connections)等功能,可以基于这些特性来搭建PostgreSQL
高可用集群。
官方参考地址
Yum Repository
Installation from RPM
1 | $ yum install http://www.pgpool.net/yum/rpms/4.0/redhat/rhel-7-x86_64/pgpool-II-release-4.0-1.noarch.rpm |
1 | $ yum install pgpool-II-pg10 |
1 | $ systemctl enable pgpool.service |
1 | $ systemctl start pgpool.service |
参考文章
role | ip | PostgreSQL | Pgpool-II | port |
---|---|---|---|---|
master | 192.168.1.3 | 10.9 | 4.0.5 | 5432 |
slave | 192.168.1.2 | 10.9 | 4.0.5 | 5432 |
vip | 192.168.1.100 | – | – | – |
watchdog
端口9000
,pcp
端口9898
,psql
服务连接端口9999
架构图
配置程序运行的用户和组为 postgres
1 | # 创建或修改目录权限 |
配置pool_hba.conf
,要么都是trust
,要么都是md5
验证方式,这里采用了md5
验证方式如下设置
1 | # "local" is for Unix domain socket connections only |
配置pg_hba.conf
,认证方式保持一致
1 | # "local" is for Unix domain socket connections only |
非必要可选步骤,为了集群可扩展性,可以将复制的认证条件放宽
1 | # Allow replication connections from localhost, by a user with the |
pcp.conf
配置用于pgpool
自己登陆管理使用的,一些操作pgpool
的工具会要求提供密码等,配置如下
1 | $ pg_md5 postgres |
在pgpool中添加pg数据库的用户名和密码,数据库登录用户是postgres,这里输入登录密码
1 | $ pg_md5 -p -m -u postgres pool_passwd |
1 | $ chmod +s /sbin/ifconfig |
pgpool.conf
primary
1 | $ cp pgpool.conf.sample-stream pgpool.conf |
编辑内容如下
1 | # - pgpool Connection Settings - |
standby
编辑内容如下
1 | # - pgpool Connection Settings - |
failover_stream.sh
配置failover_stream.sh
脚本,内容如下:
1 | $ pwd |
配置ssh
秘钥,分别在master、slave
上生成ssh密钥对
,并设置主机互信。
1 | $ ssh-keygen -t rsa -b 1024 |
如果这个脚本的执行目标是本地,并且ssh本地登陆没有设置免秘钥,那么这个脚本会一直卡在输入密码的阶段,这时候主备自动切换过程就阻塞了。如果 pgpool
和 postgresql
在同一台机器部署,需要添加本地登陆免秘钥。
1 | $ pwd |
分别启动master
、slave
的PostgreSQL
服务
1 | $ systemctl start postgresql-10.service |
分别启动各节点Pgpool-II
服务
1 | $ systemctl start pgpool.service |
用vip
登录集群,查看状态
1 | $ psql -p 9999 -h 192.168.1.100 -U postgres |
至此,基于 Pgpool-II
中间件的 PostgreSQL
集群搭建完成。
PCP
命令获取节点数
1 | # 获取192.168.1.3可见节点数 |
获取节点信息
1 | # 查看节点0信息 |
从pgpool-II
中脱离一个节点
该命令将节点slave
从pgpool-II
中脱离。一般如果需要维护某个数据库节点、或不希望pgpool-II
将连接分发到该节点时,需要将该节点从pgpool-II
中用该命令脱离。
1 | $ pcp_detach_node -h 192.168.1.100 -p 9898 -U postgres -n 1 |
为pgpool-II
关联一个节点
该命令将节点slave
关联到pgpool-II
中。当维护结束,或新添加一个节点后,可以将节点添加到pgpool-II
。
另外,如果该节点由于主机或数据库故障导致检测到数据库为启动时,即使后期服务器重新修复、数据库手工启动,也需要执行attach
操作。同时需要注意从两个节点上观察是否节点都已经attach
。
1 | $ pcp_attach_node -h 192.168.1.100 -p 9898 -U postgres -n 1 |
PostgreSQL双节点高可用构架中,如果主库宕机或挂了,高可用系统会提升备库为新主库对外继续服务。对于原主库的处理,可以删掉后重搭新备库,也可以降级为备库继续服务。
PostgreSQL热备(HOT-Standby)
如果主库出现异常,备库如何激活,来替换主库工作,有2种方式可以选择
recovery.conf
中有配置项 trigger_file
,它是激活从库的触发文件,当它存在就会激活从库。pg_ctl promote
命令激活。如果备库配置文件 recovery.conf
配置项 trigger_file
不为空,例如:
1 | trigger_file = '/var/lib/pgsql/10/data/trigger_standby' |
创建文件/var/lib/pgsql/10/data/trigger_standby
,就可以激活备库。
1 | $ touch /var/lib/pgsql/10/data/trigger_standby |
在备库上执行命令 pg_ctl promote
就可以激活备库。
1 | $ pg_ctl promote |
数据字典表pg_stat_replication
、命令 pg_controldata
、进程、自带的函数 pg_is_in_recovery()
都可以区别或判断实例的主备状态。
主库标识 in production
1 | $ pg_controldata | grep 'Database cluster state' |
备库标识 in archive recovery
1 | $ pg_controldata | grep 'Database cluster state' |
在主库执行 pg_ctl stop
模拟主库宕机。
1 | $ pg_ctl stop |
这时备库日志会报错,提示 primary
主库连接不上
1 | $ tailf /var/lib/pgsql/10/data/log/postgresql-Tue.log |
在备库执行 pg_ctl promote
激活备库
1 | $ pg_ctl promote |
备库激活后可以插入数据,变为可读写。这时配置文件 recovery.conf
变为 recovery.done
。
备库被激活,标识变为 in production
1 | $ pg_controldata | grep 'Database cluster state' |
此时备库已被激活,可以读写
1 | postgres=# create database test; |
将宕机原主库修复好后,重新作为新主库的备库
pg_hba.conf
,新增对原主库的认证方式,如果存在可以跳过此步骤。1 | host all all 192.168.1.2/32 trust |
pg_ctl reload
使配置生效
1 | $ pg_ctl reload |
pg_archive
,如果存在可以跳过此步骤。1 | $ mkdir /var/lib/pgsql/10/pg_archive |
.pgpass
,如果存在可以跳过此步骤。1 | $ pwd |
1 | hot_standby = on |
1 | $ rm -rf /var/lib/pgsql/10/data/* |
1 | $ pg_basebackup -h 192.168.1.3 -U repuser -D /var/lib/pgsql/10/data/ -X stream -P |
recovery.conf.sample
或 recovery.done
为 recovery.conf
1 | cp /usr/pgsql-10/share/recovery.conf.sample /var/lib/pgsql/10/data/recovery.conf |
更新配置文件 recovery.conf
1 | standby_mode = on |
1 | $ pg_ctl start |
recovery.conf.sample
或 recovery.done
为 recovery.conf
1 | cp /usr/pgsql-10/share/recovery.conf.sample /var/lib/pgsql/10/data/recovery.conf |
更新配置文件 recovery.conf
1 | standby_mode = on |
pg_rewind
同步下时间线1 | $ pg_rewind --target-pgdata=/var/lib/pgsql/10/data --source-server='host=192.168.1.3 port=5432 user=postgres dbname=postgres' -P |
1 | $ pg_ctl start |
PostgreSQL流复制默认是异步的。如果主服务器崩溃,那么某些已被提交的事务可能还没有被复制到从服务器,会导致数据丢失。数据的丢失量与故障转移时的复制延迟成比例。
同步复制能够保证一个事务的所有修改都能被传送到同步的从服务器。提高了事务提交标准的持久化级别,这种保护级别被称为2-safe复制。
同步复制时
,写事务的每次提交将一直等待,直到该提交在主服务器和从服务器上都已被写入到磁盘上的预写日志中。数据会被丢失的唯一可能性是主服务器和后备服务器在同一时间都崩溃
。
一旦流复制已经被配置,配置同步复制就只需要一个额外的配置步骤,将synchronous_standby_names
设置为一个非空值。 synchronous_commit
也必须被设置为on
,但由于这是默认值,通常不需要改变。同步模式比异步模式增加了响应时间,用性能换取了安全
。
更新备库配置文件 recovery.conf
中的 primary_conninfo
参数,默认值为 walreceiver
,需要指定该实例的 application_name
。
更新前
1 | primary_conninfo = 'host=192.168.1.2 port=5432 user=repuser password=repuser' |
主库查询复制状态为
1 | select application_name, client_addr, sync_state from pg_stat_replication; |
更新后
1 | primary_conninfo = 'host=192.168.1.2 port=5432 user=repuser password=repuser application_name=standby003' |
重启从库服务,主库查询复制状态为
1 | select application_name, client_addr, sync_state from pg_stat_replication; |
根据 application_name
参数,可以看出实例名更改已经生效为 standby003
更新主服务器的postgresql.conf
文件
1 | synchronous_standby_names = 'standby003' |
重启主库服务,主库查询复制状态为
1 | select application_name, client_addr, sync_state from pg_stat_replication; |
根据sync_state
参数,可以看出复制模式是同步状态。
同步复制支持一个或多个同步从服务器,提交的事务会一直等待,直到所有同步从服务器都确认收到了数据为止。该参数支持三种配置方式。
1 | [FIRST] num_sync ( standby_name [, ...] ) |
关键字FIRST
加上num_sync
,指定基于优先级的同步复制。 例如,FIRST 3 (s1, s2, s3, s4)
名称出现在列表前面3
个的从服务器被赋予更高的优先级,并将被视为同步复制。在列表后面出现的其它从服务器为潜在的同步从服务器。如果当前的某些同步从服务器断开连接,列表后面的从服务器,优先级将被提高,从而转变为同步从服务器,其中关键字FIRST
是可选的。
1 | ANY num_sync ( standby_name [, ...] ) |
关键字ANY
加上num_sync
,指定基于数量的同步复制,例如,ANY 3 (s1, s2, s3, s4)
每个提交至少被s1
、s2
、 s3
和s4
中的任意三个从服务器同步复制。
1 | standby_name [, ...] |
与第一个使用FIRST
和num_sync
等于1
的语法相同。例如,FIRST 1 (s1, s2)
和s1, s2
具有相同的含义:选择s1
或s2
作为同步服务器。特殊项*
匹配任何备用服务器名称。
PostgreSQL在9.0之后引入了主从的流复制机制,从服务器通过tcp
流从主服务器中同步相应的数据。流复制允许备库更新,同时也能提供只读服务,流复制默认是异步的。
role | ip | port | version |
---|---|---|---|
master | 192.168.1.2 | 5432 | 10.9 |
slave | 192.168.1.3 | 5432 | 10.9 |
架构图
初始化数据库
1 | $ rm -rf /var/lib/pgsql/10/data/* |
启动数据库
1 | $ systemctl start postgresql-10 |
创建用户repuser,并赋予复制和登录的权限。
1 | $ su - postgres |
编辑配置文件pg_hba.conf
,新增
1 | host all all 192.168.1.3/32 trust |
编辑配置文件postgresql.conf
1 | listen_addresses = '*' |
新增归档目录 pg_archive
1 | $ mkdir /var/lib/pgsql/10/pg_archive |
1 | $ systemctl restart postgresql-10 |
1 | $ rm -rf /var/lib/pgsql/10/data/* |
.pgpass
在postgres
用户根目录下创建.pgpass
,并追加认证信息
1 | $ touch .pgpass |
1 | $ su - postgres |
拷贝配置文件recovery.conf.sample
为recovery.conf
1 | $ cp /usr/pgsql-10/share/recovery.conf.sample /var/lib/pgsql/10/data/recovery.conf |
更新配置文件recovery.conf
1 | standby_mode = on |
1 | listen_addresses = '*' |
1 | $ systemctl restart postgresql-10 |
在主节点上命令验证
1 | postgres=# select application_name, client_addr, sync_state from pg_stat_replication; |
说明192.168.1.3
是从服务器,在接收流,而且是异步流复制。
可以分别在主、从节点上运行 ps -ef | grep postgres
来查看进程验证
主服务器有一个 wal sender
进程
1 | $ ps -ef | grep postgres |
从服务器有一个 wal receiver
进程
1 | $ ps -ef | grep postgres |
关于PostgreSQL与MySQL的比较,可以参考 PostgreSQL 与 MySQL 相比,优势何在?
1 | CentOS Linux release 7.4.1708 (Core) |
vi /etc/security/limits.conf
添加
1 | * soft nproc 65535 |
vi /etc/sysctl.conf
添加
1 | fs.file-max = 65535 |
不同系统,不同版本安装都有差异。在官网选择对应的系统环境和安装版本,页面会给出对应的安装指导,官方指导
下面按照官方指导一步一步安装
1 | $ yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm |
1 | $ yum install postgresql10 |
1 | $ yum install postgresql10-server |
1 | $ systemctl enable postgresql-10.service |
1 | $ /usr/pgsql-10/bin/postgresql-10-setup initdb |
1 | $ systemctl start postgresql-10 |
1 | $ systemctl status postgresql-10 |
1 | $ su - postgres |
编辑配置文件postgresql.conf
1 | listen_addresses = '*' |
编辑配置文件pg_hba.conf
1 | # IPv4 local connections: |
配置文件
pg_hba.conf
,配置项说明,可以参考 PostgreSQL 中的客户端认证
TYPE
连接类型,表示允许用哪些方式连接数据库,它允许以下几个值:local
通过 Unix socket 的方式连接。host
通过 TCP/IP 的方式连接,它能匹配 SSL 和 non-SSL 连接。hostssl
只允许 SSL 连接。hostnossl
只允许 non-SSL 连接。DATABASE
可连接的数据库,它有以下几个特殊值:all
匹配所有数据库。sameuser
可连接和用户名相同的数据库。samerole
可连接和角色名相同的数据库。replication
允许复制连接,用于集群环境下的数据库同步。 除了上面这些特殊值之外,我们可以写特定的数据库,可以用逗号 (,) 来分割多个数据库。USER
可连接数据库的用户,值有三种写法:all
匹配所有用户。+
(如:+admin
)。ADDRESS
可连接数据库的地址,有以下几种形式:all
匹配所有 IP 地址。samehost
匹配该服务器的 IP 地址。samenet
匹配该服务器子网下的 IP 地址。ipaddress/netmask
(如:172.20.143.89⁄32),支持 IPv4 与 IPv6。hostname
。 注意: 只有 host, hostssl, hostnossl 会应用个字段。METHOD
连接数据库时的认证方式,常见的有几个特殊值:trust
无条件通过认证。reject
无条件拒绝认证。md5
用 md5 加密密码进行认证。password
用明文密码进行认证,不建议在不信任的网络中使用。ident
从一个 ident 服务器 (RFC1413) 获得客户端的操作系统用户名并且用它作为被允许的数据库用户名来认证,只能用在 TCP/IP 的类型中 (即 host, hostssl, hostnossl)。peer
从内核获得客户端的操作系统用户名并把它用作被允许的数据库用户名来认证,只能用于本地连接 (即 local)。md5
和 password
是需要密码的,其他方式都不需要输入密码认证。1 | $ systemctl restart postgresql-10 |
psql
是 PostgreSQL 的客户端程序,要连接 PostgreSQL 数据库,我们需要指定以下内容:
-d
or --dbname
数据库名-h
or --host
主机名-p
or --port
端口号,默认5432 端口-U
or --username
用户名 1 | $ psql -h 192.168.1.2 -U postgres |
如果Kong前端uris配置正则表达式,虽然Kong能够捕获正则表达式的值,但是它不会用该值去替换上游upstream_url里面预设变量,例如:
前端uris配置
1 | /api/testmap/(?<id>\d+) |
上游upstream_url配置类似这样
1 | http://192.168.1.3:10000/api/testmap/{id} |
前端发起请求
1 | curl http://10.0.0.111/api/testmap/1/ |
上游upstream_url的值都是
1 | http://192.168.1.3:10000/api/testmap/{id} |
既然Kong能够捕获正则表达式的值,只需要用Kong捕获的值,对upstream_url进行正则替换即可。
如果,Kong前端uris配置为
1 | /api/testmap/(?<id>\d+) |
那么,Kong将捕获的值,以table类型存储在变量ngx.ctx.router_matches.uri_captures
中。
如果,前端发起的请求为
1 | curl http://10.0.0.111/api/testmap/2/ |
那么ngx.ctx.router_matches.uri_captures
变量的值为
1 | {"2", id = "2"} |
需要注意的是,如果uris里面没有正则表达式,那么变量ngx.ctx.router_matches.uri_captures
的值为nil
,而不是一个table
。
定义upstream_url里面的变量时候,需要有一定的特征。
例如,uris定义为
1 | /api/testmap/(?<id>\d+) |
那么,upstream_url定义的时候,就将id
用特需符号标识,比如用{}
来标识这是一个待替换的变量
1 | http://192.168.1.3:10000/api/testmap/{id} |
做一个table
的循环,k
是待替换的变量,v
是最后替换的值。
1 | for k, v in pairs(uri_captures) do |
变量替换好后,需要重置upstream_uri
的值。有个坑就是,Kong版本大于等于0.11.0
后,不支持用ngx.req.set_uri()
重置upstream_uri
的值,需要用ngx.var.upstream_uri
重置upstream_uri
。
官方更新日志是这么说的:
The upstream URI is now determined via the Nginx $upstream_uri variable. Custom plugins using the ngx.req.set_uri() API will not be taken into consideration anymore. One must now set the ngx.var.upstream_uri variable from the Lua land.
插件的核心代码handler.lua
1 | local BasePlugin = require "kong.plugins.base_plugin" |
]]>该插件也适用于uris包含多个正则表达式的场景。
插件GitHub开源地址kong-plugin-upstream-url-penetrate
微服务API接口认证的方式有很多,Kong官网关于上游API认证的插件只有一个Upstream HTTP Basic Authentication,是基于基本的用户名和密码认证的。其它的都是前端认证认证插件。如果后端微服务API采用的是HMAC-SHA256
签名认证,就接入不了了,怎么办?首先Google了一番,没有找到此类插件。那么问题来了,如果要用这种认证方式接入Kong,那么就要自己写插件造轮子了。
先看看Lua的基本语法,由于是菜鸟,就去看了Lua 菜鸟教程。
再熟悉学习了Kong的插件大致怎么写,大概看了以下文章:
kong插件官方文档翻译
API网关Kong学习笔记(十一):自己动手写一个插件
Kong Api网关简介(二) 插件
kong 网关插件快速开发指南
好了,看过这几篇文章后,大概了解了Kong的插件是一个怎样的套路了。
还需要了解ngx模块里面的var、ctx、req
等内置变量和方法。
API的签名认证用的是Python的httpsig
模块,签名算法采用HMAC-SHA256
。由于是相对于后端来说,Kong的转发相当于客户端请求,要完成这个插件就需要了解HMAC-SHA256
签名的详细认证过程。
通过阅读和调试httpsig
模块相关代码,签名的过程是以下这样的:
1 | local key = conf.key |
加密的消息一般采用当前时间date
1 | local date = os.date("%Y-%m-%d %H:%M:%S", os.time()) |
将自己的密钥和加密消息,用HMAC-SHA256
算法进行签名。
1 | local signature_salt = string.format("date: %s", date) |
将date
,x-api-key
,authorization
三个值注入到HTTP头字段HTTP header fields
。其中authorization
字段格式有一定的要求,必须包含公钥Signature keyId
、签名算法algorithm
、签名signature
,类似这样的字符串
1 | 'authorization': u'Signature keyId="d1eaffbcb543r357",algorithm="hmac-sha256",signature="pp18H0zla/kvKZP/jAKCTkgqiVO92RRAqTsybZJao/o="' |
组装authorization
字段,signature
的值需要对原始签名的二进制数据进行Base64编码。
1 | local authorization = string.format('Signature keyId="%s",algorithm="hmac-sha256",signature="%s"', key, ngx.encode_base64(signature)) |
用ngx.req.set_header
注入相应值。其中的date
字段值是签名时用的加密消息,x-api-key
是自己的公钥。
1 | req_set_header("date", date) |
最后得到的HTTP头字段类似这样
1 | { |
后端微服务拿到公钥、加密消息、签名算法后,先查询公钥对应的私钥,在用一样的签名算法,计算私钥和加密消息,如果得到的结果一样,那么就认证成功,如果不一样那么就认证失败,返回相应错误。
插件的核心代码handler.lua
,目前只支持HMAC-SHA256
签名认证,自己动手改一改就可以支持其它签名认证了。
1 | local BasePlugin = require "kong.plugins.base_plugin" |
]]>插件GitHub开源地址kong-plugin-upstream-auth-signature
不同系统,不同版本安装都有差异。上官网选择对应的系统环境和安装版本,页面会给出对应的安装指导。
PostgreSQL官网
比如选择版本10,系统CentOS,平台64位。
安装PostgreSQL仓库
1 | $ yum install https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-6-x86_64/pgdg-centos10-10-2.noarch.rpm |
安装客户端
1 | $ yum install postgresql10 |
安装服务端
1 | $ yum install postgresql10-server |
加入开机启动项
1 | $ chkconfig postgresql-10 on |
为了登录不报以下错误:
psql: FATAL: Peer authentication failed for user “xxxx”
psql: FATAL: Ident authentication failed for user “xxxx”
需要修改/var/lib/pgsql/10/data/pg_hba.conf
配置文件
1 | 将以下两行配置peer和ident都修改为trust |
初始化数据库
1 | $ service postgresql-10 initdb |
启动数据库服务
1 | $ service postgresql-10 start |
新建数据库kong
,用户和密码分别为kong
和test
1 | $ su - postgres |
测试数据库连接性
1 | $ psql --username=kong -h127.0.0.1 --password |
至此,数据库准备完毕。
到Kong官网下载相应的rpm安装包“Kong下载”,或者获取下载链接。
下载安装包
1 | $ wget https://bintray.com/kong/kong-community-edition-rpm/download_file?file_path=centos/6/kong-community-edition-1.0.0.el6.noarch.rpm |
安装
1 | $ yum install kong-community-edition-1.0.0.el6.noarch.rpm |
配置
1 | $ cd /etc/kong/ |
修改kong.conf
配置文件,配置数据库连接
1 | pg_host = 127.0.0.1 # The PostgreSQL host to connect to. |
初始化数据库并启动服务
1 | Kong版本高于0.14.1 |
测试服务
1 | $ curl http://localhost:8001/ |
测试返回一串json格式数据,则安装启动服务成功。
Kong还有可视化的控制面板kong-dashboard
,项目地址是kong-dashboard。由于Kong提供了完善的REST API接口,习惯接口操作就不需要安装kong-dashboard
了。
用Django REST framework
来构建Result API
,需要记录用户请求的时间、方法、数据、响应状态等。
用Python的logging
模块结合Django框架的Middleware
,来将每次API请求的详细信息记录下来。
定义一个专门记录API日志的logger,命名为api
。
1 | # LOGGING settings |
在合适的位置新建文件middleware.py
,自定义日志记录中间件ApiLoggingMiddleware
。
1 | # -*- coding: utf-8 -*- |
由于中间件对request
的处理是顺序执行的,对response
处理是逆序执行的,故将ApiLoggingMiddlewar
放在MIDDLEWARE_CLASSES的最后面。
1 | MIDDLEWARE_CLASSES = [ |
1 | INFO 2019-01-11 15:28:50,652 middleware process_response username POST /api/product/ {u'csrfmiddlewaretoken': [u'jAlRDXXXXXXXXzK8UnluvSzaz2yacdDzTwRE005aCVvYnINSG7xvraqi0Pu5QHur'], u'code': [u'test'], u'name': [u'\u53d1\u5e03\u7cfb\u7edf']} 201 Created |
pyenv可以用git克隆安装,也可以下载zip后解压安装。下载zip包解压安装
1 | $ wget https://github.com/pyenv/pyenv/archive/v1.2.7.tar.gz |
pyenv安装完成,用pyenv install --list
命令查看提供可安装的python版本
1 | $ pyenv install --list |
安装或更新python所需依赖
1 | $ yum install zlib-devel bzip2-devel openssl-devel ncurses-devel \ |
比如安装2.7.15版本的python
1 | $ pyenv install 2.7.15 -v |
为所有已安装的可执行文件创建shims
,如:~/.pyenv/versions/*/bin/*
,因此,每当增删了Python版本或带有可执行文件的包(例如celery, uwsgi, pip
)以后,都应该执行一次该命令
1 | $ pyenv rehash |
查看当前已安装python的版本
1 | $ pyenv versions |
设置系统全局python版本为2.7.15
1 | $ pyenv global 2.7.15 |
pyenv优先级顺序shell > local > global
global
全局Python版本,通过命令pyenv global
设置,手动将版本号写入~/.pyenv/version
也可以。作用范围是系统。local
本地的Python版本,通过命令pyenv local
设置,手动将版本号写入当前目录下的.python-version
也可以。作用范围是当前目录,优先级较global高。shell
当前终端的Python版本,通过命令pyenv local
设置,手动定义当前终端的PYENV_VERSION
环境变量也可以。作用范围是当前终端,优先级比local和global都要高。
采用git安装
1 | $ git clone https://github.com/pyenv/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv |
创建隔离环境命令格式
1 | $ pyenv virtualenv Pyhton版本 环境名称 |
例如,创建一个隔离环境devtest
,Python版本2.7.15
1 | $ pyenv virtualenv 2.7.15 devtest |
切换到环境devtest,用pip list
可以看到新的环境安装包只有pip、setuptools、wheel
。这是一个干净的环境,可以开始一个新的Python项目开发。
1 | $ pyenv activate devtest |
退出当前环境
1 | $ pyenv deactivate |
列出所有的环境
1 | $ pyenv virtualenvs |
删除环境devtest
1 | $ pyenv virtualenv-delete devtest |
什么是迷信,百度百科是这样解释的
个人认为风水不是迷信而是中华文明传承的一部分,前面专门写过一篇文章简单论证了这个话题 风水是迷信,还是你太年轻,其实过度相信科学,不也是一种迷信。
其实可以反问一句,现在的人都是知识分子都信科学,科学这么厉害,为什么我们不能长生不老,依旧摆脱不了生老病死轮回。风水的核心是气和平衡。为了保持平衡那么它是不会朝极端发展,从而就不会出现永垂不朽的神迹。不管是科学还是大家嗤之以鼻的传统文化,如果它走向极端必将灭亡,平衡才能发展。
风水据个人验证是有作用的,究竟能有多大,能逆天改命还是能巨富石崇比?风水的作用绝对不是这些。一般就是让不好的事情变好;让好的事情变得更好;让失业的人能找到一份工作;让生病的人缓解下痛苦;让家庭变得更和睦;让店铺营业额多点等。退一万步讲,如果相信风水,有很厉害的地师能逆天改命改运,别人也是不会轻易为你做这些,你也不要强求师傅做这些,因为师傅为你们做到了这些,他一定也会失去某些对他很重要的东西。
这个一定是要给的,作为一种信息
的交换,最好有这个交换过程。师傅将布置改造的信息
给了福主,那么福主应该用红包或礼物作为交换。金额或礼品分量随意,1块钱也行,师傅开盘了,一定不要他空手而归。
不一样,有很大的区别,天下没有相同的两片叶子,也没有相同的风水环境。
为什么不一样?因为存在这些可能,造成了它们的不一样。
有用,但是不用这些风水物品同样能达到一样的效果甚至更佳。因为生活可见的物品都可以归于五行之中。
例如:如果调理风水需要放置水,那么用黑色或灰色地毯、鱼缸、饮水机都是可以的。当然买那种流水的风水物品也是有用的,但是没有什么必要。
古人是不大用现代那些眼花缭乱的风水物品的。调理风水如果要风水物品,用平常用到的物品完全可以。最好的风水调理是不见斧凿之痕。最后提醒一下,网上卖的那些风水物品,貔貅、麒麟是不能乱摆放的,不能放卧室、不靠厕所、不对厨房等,切记不要乱摆放
。
开发过程中遇到了时间格式的更改需求。源时间格式从zstack接口获取,类似这样的格式Dec 14, 2018 7:55:13 PM
,要转换成目标格式,类似这样2018-12-14 19:55:13
。其实只要在Python编程中遇到了和时间相关的问题,都可以用标准库datetime
和time
模块解决。
先将源字符串时间用strptime
转换成时间数组,然后将时间数组用strftime
转换为目标格式的字符串。
例如将字符串Dec 14, 2018 7:55:13 PM
转换为2018-12-14 19:55:13
。
1 | from datetime import datetime |
time
模块也可以实现,稍有不同。
1 | import time |
做时间操作需要知道日期和时间的格式化参数,找到正确的时间格式参数,然后对号入座。
常用格式参数:
1 | %a 星期几简写 |
字符串先转为时间数组,再转换为时间戳
1 | import time |
时间戳先转换为时间数组,然后格式化为目标格式字符串
1 | import time |
1 | import time |
或者用datetime
实现
1 | import datetime |
获取一天前的时间,timedelta()
参数可以是days, hours, seconds, microseconds
。
1 | import datetime |
风水古人称堪舆堪天道、舆地道也
。风水对我们来说熟悉而陌生,熟悉是我们都知道它自古有之,非常神秘,而且无时无刻不受它影响。陌生是风水到底是什么,具体作用是什么,怎么用等。由于受到其它影响,很多人都认为它是迷信并对此嗤之以鼻,不值一提。根据我的实践和经验来看,风水不是迷信,是古人追求天人合一的最佳实践和经验总结。如果你简简单单的把它归纳为迷信,那可能是你太年轻了。风水知识包罗万象,我用最简短文字从现代、历史、传说阐述下我了解的风水。
留心下周围,一般福或贵的人都是有信仰的,信教拜佛的很多,我接触到基本都信风水,因为他们见多识广,对玄学不是抵触而是敬畏。
现代大都市基本都是藏风聚气的大地,比如上海九曲来水,其中陆家嘴就是在吴淞江和黄浦江的交汇处三叉水口
。留心观察你会发现,陆家嘴有很多大型的球形建筑,最有名的非东方明珠了。圆为金,取金水相生之像,金色也为金,震旦大楼是金色的。最重要的是,如果黄浦江是水龙,那么这些球形建筑就是给龙嘻戏玩耍的龙珠
,这样龙气在陆家嘴多停留一会儿,部分龙气则调头从世纪大道进入了世纪公园的那个大水池了,然后陆家嘴和浦东就发展起来了。
上海地图,注意黄浦江九曲来水及和吴淞江交汇处的陆家嘴。
陆家嘴上面的球形建筑,注意球形建筑后面就是世纪大道,通往世纪公园。
徐家汇高架的九龙柱九子镇真龙
。
打开武汉地图,乱流如织锦。
打开重庆地图,典型的三叉水。
说千言道万语,不如你打开地图,你就会发现大城市都是山环水抱,藏风聚气的大地。
看故宫,故宫以景山为玄武,硬生生堆出一座山,不愧皇家大手笔。
再如长泰广场,后面是高耸的写字楼玄武靠山
,左边龙边
多了一座,所谓宁可青龙高千丈,不宜白虎乱抬头。前面左右两边是城门,用了城门诀,中间是喷泉广场可以遇囚不囚
。地铁口,乘旺开门就更不用说了。对了,长泰的泰
还是一卦。
总之,生活中有很多城市规划、建筑布局都用到了风水。他们花重金打造的风水布局,如果没有作用,何意而为之。
自古以来玄学都是帝王之学,为皇家所用,民间很少流传有些风水知是被有意或无意被串改后流传的
。除了上古传世的神话人物,一直到春秋战国公认的堪舆宗师少有历史记载。可能那时堪舆还没有独立出来,春秋的鬼谷子和他的学生应该精通各门玄学,焚书坑儒估计烧了不少玄学的典籍。
可考的,到了晋朝郭璞宗师撰写《葬书》
-葬者,藏也,乘生气也。夫阴阳之气,噫而为风,升而为云,降而为雨,行乎地中则为生气。... ... 虽零散而其深者,犹有聚。古人聚之使不散。行之使有止,故谓之风水
。文中就明确提到了风水二字,而作者书中提到的古人,应该是晋以前的人们。
郭璞也被尊称为风水鼻祖,这个时候堪舆这门玄学才算真正独立出来。
到了唐朝杨筠松进一步把堪舆发扬光大,写了很多著作,《天玉经》
、《撼龙经》
等著作,为什么江西风水师比较多,与杨筠松退休后回到了江西不无关系。
例如《天玉经》
中乾山乾向水朝乾,乾峰出状元,卯山卯向迎源水,骤富石崇比,午山午向午来堂,大将值边疆,坤山坤向水坤流,富贵永无休
。识得父母三般卦,便识真神路,北斗七星去打劫,离宫要相合
。这些口诀都很有名,读起来朗朗上口,读完后发现什么也不明白,一脸懵逼。
是的,古人要的就是这种效果,书上面都只有口诀,没有对应的心法很难顿悟。古人这么做自有他们的道理,一是天机不可泄漏,二是不能为匪人所用。
到了明朝初期,刘伯温著有《堪舆漫兴》
一书。明末清初的出了一代巨擘蒋大鸿先生。蒋大鸿是松江人。著有《地理辩正》
、《平砂玉尺辩伪》
、《天元五歌》
、《阳宅指南》
、《水龙经》
等书,人称地仙,也是天机不可泄漏先生,基本上写的经书没有靠谱的心法看不懂。
对阳宅风水影响最大的是早年写的《天元五歌-阳宅篇》
和晚年写的《阳宅指南》
,这是首次明确把阳宅作为风水一个重要分支单独论述的。对后世阳宅风水的发展影响深远。《天元五歌-阳宅篇》
开头直接了当的概述了阳宅风水的重要性,及阳宅风水和阴宅风水的区别与关联。人生最重是阳基,却与坟茔福力齐,宅气不宁招祸咎,骨埋真穴贵难期,建国定都关治乱,筑城置镇系安危,试看田间丰盈者,半是阳基偶合宜
。比如论开门和气口的,别有旁门并侧户,一通外气即分张,设若便门无好位,一门独出始为奇
。
再如《阳宅指南》
里面关于开门引路的第五开门引路诀,正卦装门莫偏泄;入门之卦宅元神,元神衰旺此中别。一门正卦气无偏,前后门通两卦接;若有旁门破卦身,纵然旺气非清洁。既辨门时更辨路,内路外路须兼顾;路在生方致百祥,煞方引路多灾祸
。
上面两本书注释提到了无心道人和尹一勺
锡山无心道人?是谁?是清朝居住在无锡的章仲山先生,他是继明末清初蒋大鸿后的一代宗师,是蒋大鸿的一个传人。著作有《心眼指要》
、《阴阳二宅录验》
等。
尹一勺也是清朝的一位明师,著有《地理四库全书》
等。
这个时候的玄空风水还是犹抱琵琶半遮面,直到清朝的沈竹礽先生(1849年-1906年)
,他研究三合风水近20载有余,基本不验估计验也是巧合,三合风水中的救贫水有时候和玄空的城门诀吻合
,他很是困惑。直到他到章家找章氏后人花重金手操了一本他们的家传秘本。然后苦心研究,后面道破玄空天机,恍然大悟,朝闻道,夕死可矣!然后《沈氏玄空学》腾空出世。
传说都是神话,风水的传说可以追溯到上古神级文明。九天玄女撰写了《青囊海角权衡三字经》
——天德纯。数乃遵。... ... ... 配九星。推三吉。... ... ...
。
黄帝战蚩尤,久战不胜,九天玄女从天而降,授予黄帝兵符…,这个兵符就是奇门遁甲,这些传说九天玄女都是同一个神话人物。
龙马负图,伏羲见河图作先天八卦。神龟载书,大禹见书治水成功。
这些传说伴随着华夏几千年的文明传承流传了几千年,深远影响着华夏名族。其中的先后天八卦,是后面很多文化的基石,无论易经、中医、风水、命理、奇门、道家、儒家等。
最后,个人认为风水是古人追求天人合一的最佳实践和经验总结,对与大自然和谐共存的探索和对大自然宇宙的敬畏。文中提到三合派,没有抨击的意思,只是描述沈竹礽先生当时的境遇,如有得罪多多包涵
。