SegmentFault Happy Hacking最新的文章
2024-01-17T16:50:20+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
k8s service-node-port-range ip_local_reserved_ports 记录
https://segmentfault.com/a/1190000044563207
2024-01-17T16:50:20+08:00
2024-01-17T16:50:20+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>k8s ServiceNodePortRange 是什么</h2><p>在 Kubernetes 中,ServiceNodePortRange 是一个用于指定 NodePort 服务端口范围的参数。该参数定义了可以分配给 NodePort 服务的端口范围。默认情况下,NodePort 服务使用的端口范围是 30000 到 32767。</p><h2>如何查看 k8s ServiceNodePortRange</h2><p>k8s master 节点上的 kube-apiserver 进程启动时,会指定参数 --service-node-port-range=xxx-xxx,该参数的值就是 ServiceNodePortRange 的值</p><p><img src="/img/remote/1460000044563209" alt="Alt text" title="Alt text"></p><h2>使用 net.ipv4.ip_local_reserved_ports 配置保留端口范围</h2><p>文档</p><pre><code>ip_local_reserved_ports - list of comma separated ranges
Specify the ports which are reserved for known third-party
applications. These ports will not be used by automatic port
assignments (e.g. when calling connect() or bind() with port
number 0). Explicit port allocation behavior is unchanged.
The format used for both input and output is a comma separated
list of ranges (e.g. "1,2-4,10-10" for ports 1, 2, 3, 4 and
10). Writing to the file will clear all previously reserved
ports and update the current list with the one given in the
input.
Note that ip_local_port_range and ip_local_reserved_ports
settings are independent and both are considered by the kernel
when determining which ports are available for automatic port
assignments.
You can reserve ports which are not in the current
ip_local_port_range, e.g.:
$ cat /proc/sys/net/ipv4/ip_local_port_range
32000 60999
$ cat /proc/sys/net/ipv4/ip_local_reserved_ports
8080,9148
although this is redundant. However such a setting is useful
if later the port range is changed to a value that will
include the reserved ports.
Default: Empty</code></pre><p>ref: <a href="https://link.segmentfault.com/?enc=rIUuGdLlb%2FyIsJWY17%2FTeQ%3D%3D.ySUZCD8Ys68KjZTfpPR7vhQFQ9bFyllwJZf1%2FSN2jhiy%2FuoRLjurSOSJXcmcvyPhZsLtu1Qr0fibBkYEjEWV9ak6gnGUjcFVCABjmQH8cX8%3D" rel="nofollow">https://www.kernel.org/doc/Documentation/networking/ip-sysctl...</a></p><p>net.ipv4.ip_local_reserved_ports 是 Linux 内核参数,用于指定保留的本地端口范围,这些端口不会被随机分配给普通用户程序</p><p>防止冲突<br>防止普通用户程序占用 NodePort 服务端口范围,导致 NodePort 服务无法正常使用</p><pre><code>net.ipv4.ip_local_reserved_ports="30000–32768"</code></pre><p>这么写在我的系统上会报错 <code>sysctl: setting key "net.ipv4.ip_local_reserved_ports": Invalid argument</code></p><p>这么写是可以的</p><pre><code>sysctl -w net.ipv4.ip_local_reserved_ports=31000,32222,30080</code></pre><blockquote>修改NodePort端口范围时必须十分谨慎。务必保证NodePort端口范围与集群节点上Linux内核提供的net.ipv4.ip_local_port_range参数中的端口范围不冲突。该内核参数ip_local_port_range控制了Linux系统上任意应用程序可以使用的本地端口号范围。ip_local_port_range的默认值为32768~60999。</blockquote><h2>查看已经使用的 NodePort</h2><pre><code>kubectl get svc -A -o jsonpath='{range .items[*]}{.spec.ports[*].nodePort}{","}'</code></pre><h2>ref</h2><ul><li><a href="https://link.segmentfault.com/?enc=hfocLGCJlDUlwZaFp9h5ow%3D%3D.0dTPDW8MpBUze%2BQU2yMe3FYCP7QUdzqej%2F%2BhspyKOCdCil0j1ufFcyyn%2BD0PshmuJhF1w7A0YI4en0aXHuoUIw%3D%3D" rel="nofollow">https://www.cnblogs.com/charlieroro/p/17387611.html</a></li><li><a href="https://link.segmentfault.com/?enc=HeSzBHYVx56wK%2B%2FzpMQLAA%3D%3D.eWEICLuo912M8Qvtx%2BZvRsLL1hIAagzykWPt3IfqMkmQUqQ9wyh6f46ZX9kU6KiNNc4svyqhRB3IbZqM7cwihg%3D%3D" rel="nofollow">https://www.cnblogs.com/zhangmingcheng/p/15031143.html</a></li><li><a href="https://link.segmentfault.com/?enc=tnFoKmUh0vaumuL7Ltg%2FIg%3D%3D.cQyBXuHHPNlLpZHeswFNNxBbEvzPdarxZEwiujkWMGIKq169Cw%2Fl%2FUq5VVCvePGNlwmKymuZXtB%2BTLNSKQYNSaAoXZwQL3EobuIoDW35RJjWehviaoBagcILPCcAVu3f" rel="nofollow">https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/...</a></li><li><a href="https://link.segmentfault.com/?enc=V8rLY5nR%2Be8D0cW68zWTJw%3D%3D.Ks9L9qidD4psUerAtPXdTz83UyiuptOO1v6JMqTrDeCCAFC5QzeJ9fERCg2zFvxHa3Cat%2BzvCNzZZMc%2BPFahswY3dRZiNXeZHYinmPv2G2g%3D" rel="nofollow">https://www.kernel.org/doc/Documentation/networking/ip-sysctl...</a></li><li><a href="https://link.segmentfault.com/?enc=VL8O9ef2PSb6Gy%2BhcTKF%2FA%3D%3D.TGaHiONPgTuPJXHuwgLgpHpzYWmFNRjy9bpwr2tf0%2FLrqG2geIqhIOjgT%2BKlgsN%2BgSCWgM6XeEpASB3sqPxgFA%3D%3D" rel="nofollow">https://github.com/kubernetes/kubernetes/pull/115374</a></li><li><a href="https://link.segmentfault.com/?enc=l38TfcRSXCLz4yh2kDSqSg%3D%3D.VbcXOMVsg7lSliNfAZQptqxoc5DG3e0OiCn%2Bmx%2BiljIpIbkh9BsUaEd5OJMdCsaNxbE642c7HQoCHj7ToSIJiQ%3D%3D" rel="nofollow">https://github.com/kubernetes/kubernetes/issues/111144</a></li></ul>
在PG中使用JavaScript
https://segmentfault.com/a/1190000040658838
2021-09-09T17:24:55+08:00
2021-09-09T17:24:55+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>在PG中使用JavaScript 需要使用 plv8 扩展</p><p>需要开启扩展</p><pre><code>create extension plv8;</code></pre><p>创建函数模版</p><pre><code>create or replace function function_name()
returns void as $$
// V8 Javascript
// 在这里写 Javascript Code
$$ language plv8;</code></pre><p>执行的话运行</p><pre><code>select function_name();</code></pre><p>plv8 helloworld 示例</p><pre><code>create or replace function hello_world(name text)
returns text as $$
let output = `Hello, ${name}!`;
return output;
$$ language plv8;</code></pre><p>执行sql 使用 <code>plv8.execute</code> 函数</p><pre><code>create or replace function update_user(id bigint, first_name text)
returns smallint as $$
var num_affected = plv8.execute(
'update profiles set first_name = $1 where id = $2',
[first_name, id]
);
return num_affected;
$$ language plv8;</code></pre><h2>links</h2><ul><li><a href="https://link.segmentfault.com/?enc=w18RGvxvc35pdp54kijJiQ%3D%3D.kj2mFoWU0haQY5v9viE32RLAOSilWbBY3BDkSxswbN8d3LxvwOfs%2BHXOweolqEg9HK1eLzIfg%2BXMz%2FBSFRns6g%3D%3D" rel="nofollow">https://supabase.io/docs/guid...</a></li><li><a href="https://link.segmentfault.com/?enc=v1UeW1kdzJyIwrfkNJgi3g%3D%3D.AuTzdyFncMSpPtkdmD%2Bp%2FumtBnuo3YbVQ8wTdpFY2yjk7ysDsvfHBy5%2FXCfeb6Fywv0cgSEw3CJOQJlido%2Fzrg%3D%3D" rel="nofollow">https://github.com/nelhage/pl...</a></li></ul>
Python ThreadPoolExecutor 限制_work_queue 大小
https://segmentfault.com/a/1190000040550148
2021-08-20T17:12:14+08:00
2021-08-20T17:12:14+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>使用python的<code>futures.ThreadPoolExecutor</code>是,如果调用submit提交任务<br>ThreadPoolExecutor的会向执行</p><pre><code>self._work_queue.put(w)</code></pre><p>其中</p><pre><code>self._work_queue = queue.SimpleQueue()</code></pre><p>SimpleQueue 是不限制队列大小的,如果提交的任务太多,处理不及时,则导致占用太多内存</p><p>可以替换到_work_queue的实现,使用<code>queue.Queue(maxsize=maxsize)</code></p><pre><code>class ThreadPoolExecutorWithQueueSizeLimit(futures.ThreadPoolExecutor):
def __init__(self, maxsize=50, *args, **kwargs):
super(ThreadPoolExecutorWithQueueSizeLimit, self).__init__(*args, **kwargs)
self._work_queue = queue.Queue(maxsize=maxsize)
</code></pre><p>links:</p><ul><li><a href="https://link.segmentfault.com/?enc=NHI%2BJ%2F9WyaiAxM9juz6FlQ%3D%3D.P8sRmmXbapcZcE40pqzhx1QEEfUcQouY4Z2%2BZaXe8TjKlXl2Lc%2BatVKVjZ8OKSOFQoKS9wdO6ndKzlQAravlN9yxs3mWHoGXqFe%2FlKF4nnbOfnA4IhAj85X64y0P%2FKEK" rel="nofollow">https://stackoverflow.com/que...</a></li></ul>
mongodb geoNear 使用记录
https://segmentfault.com/a/1190000040284843
2021-07-03T23:29:07+08:00
2021-07-03T23:29:07+08:00
lidashuang
https://segmentfault.com/u/lidashuang
1
<p>需要,查询一个坐标点的附近位置</p><h2>数据格式</h2><p>可以用数组或者geojson的形式</p><pre><code><field>: [ <x>, <y> ]
<field>: [<longitude>, <latitude> ]</code></pre><p>GeoJSON</p><pre><code>location: {
type: "Point",
coordinates: [-73.856077, 40.848447]
}</code></pre><p>先列出经度,然后再列出纬度</p><p>比如 loc 字段</p><pre><code> {
"city": "北京市",
"geo_id": 565932,
"geo_name": "万柳园(长春堂药店)",
"lat": 39.850201868495773283,
"lng": 116.33426020654366084,
"loc": "[116.33426020654366084,39.850201868495773283]",
"status": 0
}</code></pre><h2>索引</h2><p>创建 2dsphere 索引 </p><p>2dsphere索引支持查询球面几何实体对象</p><pre><code>db.collection.createIndex( { <location field> : "2dsphere" } )</code></pre><p>pymongo查询例子</p><pre><code class="python"> lng = geo['lng']
lat = geo['lat']
result = geos_collection.aggregate([
{"$geoNear": {
"near": {
"type": "Point",
"coordinates": [lng, lat] },
"distanceField": "distance",
"maxDistance": 2000,
"query": {"status": -1},
"spherical": True }
},
{"$limit": 10}
])</code></pre><p>mongodb shell</p><pre><code class="js">db.o2o_geos.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [120.13606048541625171, 30.29447292933346958 ] },
distanceField: "distance",
maxDistance: 2000,
query: { status: -1 },
spherical: true
}
}
])</code></pre><ul><li>coordinates 查询的坐标点</li><li>maxDistance 最大距离</li><li>query 过滤条件</li></ul><h2>links</h2><ul><li><a href="https://link.segmentfault.com/?enc=SJicWc%2BS%2F9JGzXjKRiQEZA%3D%3D.TV2VpY4tEbPSklDfVcFEfChMo%2F%2FhvtsOexDgOoFLtn5Yv6uo5dsiC91LyeHig%2BNPsrmm21uMYGvwEGayDwTk7duwh%2FrBuNSDPdUxgUY8gYw%3D" rel="nofollow">https://docs.mongodb.com/manu...</a></li><li><a href="https://link.segmentfault.com/?enc=AjYnENryfGc4vAPbTNbe0w%3D%3D.zxmbfCHDz0jbYRwRqXmdxg7ovE%2Bl8oUceYno4aPu2MjciWQPydsrqLvbve0CQNGP" rel="nofollow">https://docs.mongodb.com/manu...</a></li><li><a href="https://link.segmentfault.com/?enc=92KJtzeCbsLCvzNnW3vu%2Bg%3D%3D.nWW7za5D69sSoUl84G5InYP1WL4RxTgI8%2Fzrt7P44QpoeeXTShaULfWgWf5J3smkFL7ZFxPoEKzqbQZp%2FSSAH8n3mqDRN3WzaTW78JHFkbo%3D" rel="nofollow">https://docs.mongodb.com/manu...</a></li></ul>
ejabberd配置admin api
https://segmentfault.com/a/1190000038508016
2020-12-17T13:12:29+08:00
2020-12-17T13:12:29+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>配置 <code>ejabberd_http</code>导出 <code>mod_http_api</code> handler</p><pre><code>listen:
-
port: 5281
module: ejabberd_http
request_handlers:
"/api": mod_http_api</code></pre><p>api 权限</p><p>无权限配置 <code>who</code> 配置成 <code>all</code></p><pre><code>api_permissions:
"API used from localhost allows all calls":
who:
- all
what:
- "*"
- "!stop"
- "!start</code></pre><p>只允许localhost访问</p><pre><code>api_permissions:
"API used from localhost allows all calls":
- who:
- ip: "127.0.0.1/8"
- what:
- "*"
- "!stop"
- "!start"</code></pre><p>一些api需要配置 <code>mod_admin_extra</code> 才能使用,比如 <code>send_message</code></p><pre><code>modules:
mod_admin_extra: {}</code></pre><p>测试api</p><pre><code>deploy@atest:~$ curl -s POST http://localhost:5280/api/registered_vhosts | jq
[
"atest.example.com.cn"
]</code></pre><h2>links</h2><ul><li><a href="https://link.segmentfault.com/?enc=0jwZPKbnsOXaKaAYGQecGw%3D%3D.ovGrY4vcOl2K%2BRBLczMxkquLb7hScuHSSOWcW7pHMRuOGROD5hRJ2nB0r76oGigbJAl3NxZ3pNe1mCgnabGXRcyMd2U5mEYvFrq104kH278%3D" rel="nofollow">https://docs.ejabberd.im/deve...</a></li></ul>
PG导入数据 duplicate key value violates unique constraint解决
https://segmentfault.com/a/1190000038494338
2020-12-16T11:43:47+08:00
2020-12-16T11:43:47+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>PG的staging环境导入测试数据,出现 duplicate key value violates unique constraint</p><p><img src="/img/bVcLGh5" alt="image.png" title="image.png"></p><p>表: <code>userextrainfo</code></p><p>最大id</p><pre><code>SELECT max(id) from userextrainfo;
1300</code></pre><p>下一个自增id</p><pre><code>SELECT nextval('userextrainfo_id_seq');</code></pre><p>需要重新reset下自增id</p><pre><code>select setval('userextrainfo_id_seq',1301);</code></pre><p>one sql</p><pre><code>SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename)+1)</code></pre><p>多个表批量更新sql</p><p>来源 <a href="https://link.segmentfault.com/?enc=dzjTAB3oHvLvm%2FKTua3bTw%3D%3D.c1fT496nY%2BzovCpYI6t%2Fnu0eyEO%2BWPJKKFRwfV8LS6NnQ%2B5Z5nVrLh8bPcZmnGTyt8G%2FfRZMPJCgsybcDXnXVUJyTizf%2FmLCkkFKTZgs0kAKOyW%2FCENzNVKTR%2BIYDeZnhEEQsOcqx584I26QXQfZ%2B%2BlbsFONnzLDVi2bIv1DQFw%3D" rel="nofollow">https://stackoverflow.com/que...</a></p><pre><code>do
$block$
declare
r record;
stmt text;
max_id integer;
begin
for r in (
select *
from (
select table_schema,
table_name,
column_name,
pg_get_serial_sequence(table_schema||'.'||table_name, column_name) as col_sequence
from information_schema.columns
where table_schema not in ('pg_catalog', 'information_schema')
) t
where col_sequence is not null
)
loop
stmt := 'select coalesce(max('||r.column_name||'), 0) + 1 from '||r.table_schema||'.'||r.table_name;
execute stmt into max_id;
raise notice 'Next ID for %.%.% is %', r.table_schema, r.table_name, r.column_name, max_id;
perform setval(r.col_sequence, max_id);
end loop;
end;
$block$</code></pre>
Phoenix 集成 ejabberd
https://segmentfault.com/a/1190000038261824
2020-11-23T20:17:58+08:00
2020-11-23T20:17:58+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>mix.exs 添加依赖</h2><pre><code>{:ejabberd, "~> 20.4"}</code></pre><p>配置 <code>ejabberd</code> application </p><p><img src="/img/bVcKHOc" alt="image.png" title="image.png"></p><h2>config/config.exs</h2><pre><code>
config :ejabberd,
file: "config/ejabberd.yml",
log_path: 'logs/ejabberd.log'
config :mnesia,
dir: 'mnesiadb/'
</code></pre><p>下载官方示例配置文件到 <code>config/ejabberd.yml</code></p><p><a href="https://link.segmentfault.com/?enc=wyLVAxdkpsNx1PaEKPjb9w%3D%3D.dD47p2BWPI%2Fwp9FuTwh6%2FGq%2BGTpHMiXgBl2f4iF3AjMQyYBXd9UYrf7RKaTZWn5LtpTcqQAWHLchlnoNHhTjGsO3o%2FaJ2irD58NO0EnuN2k%3D" rel="nofollow">https://github.com/processone...</a></p><h2>编译</h2><pre><code>mix.deps get
mix compile</code></pre><h2>openssl 问题</h2><pre><code>===> /Users/lidashuang/Github/ejabberd/deps/fast_tls/c_src/fast_tls.c:21:10: fatal error: 'openssl/err.h' file not found</code></pre><p>openssl <a href="https://link.segmentfault.com/?enc=59faZDci2OH1dy7F8tMLJQ%3D%3D.fQQJrMYHBQli6ksBsP%2FafQnPKSp1xah8pyuNxN%2Bw0NjAvfUDY0fzBgab43zG3dR3TiVq4WoUyrCSRE%2Bz5doNIA%3D%3D" rel="nofollow">https://github.com/processone...</a></p><p>设置环境变量</p><pre><code>export LDFLAGS="-L/usr/local/opt/openssl/lib"
export CFLAGS="-I/usr/local/opt/openssl/include/"
export CPPFLAGS="-I/usr/local/opt/openssl/include/"</code></pre>
golang项目 配置Gitlab CI
https://segmentfault.com/a/1190000023116169
2020-07-07T14:49:29+08:00
2020-07-07T14:49:29+08:00
lidashuang
https://segmentfault.com/u/lidashuang
5
<h2>pipeline流程</h2>
<ul>
<li>使用golangci-lint 检查代码</li>
<li>编译代码</li>
<li>部署二进制</li>
</ul>
<h3>before_script 设置环境变量</h3>
<p>主要 GOPROXY设置</p>
<pre><code class="yaml">before_script:
- echo "before_script"
- git version
- go env -w GOPRIVATE=code.haiziwang.com
- mkdir -p .go
- go version
- go env -w GO111MODULE=on
- go env -w GOPROXY="https://goproxy.io,direct"</code></pre>
<h3>golangci-lint</h3>
<p>默认集成了很多开箱即用的linter</p>
<p><img src="/img/bVbI9Ia" alt="image.png" title="image.png"></p>
<p><a href="https://link.segmentfault.com/?enc=dp2wP8w17SO8xoQNfUYPLQ%3D%3D.Y4Rte17ykNmha%2B2uEOw7u3Q7snUJipzA8ipLcExvMnM%3D" rel="nofollow">https://golangci-lint.run/</a></p>
<pre><code class="yaml">golangci-lint:
image: golangci/golangci-lint:v1.27.0
stage: lint
extends: .go-cache
allow_failure: true
script:
- golangci-lint run -v</code></pre>
<p>allow_failure 表示失败了可以继续跑后续的job</p>
<h3>编译</h3>
<pre><code class="yaml">compile:
stage: build
extends: .go-cache
script:
- go mod download
- go build -race -o $OUTPUT_NAME
artifacts:
paths:
- $OUTPUT_NAME</code></pre>
<h3>缓存 go mod</h3>
<pre><code class="yaml">.go-cache:
variables:
GOPATH: $CI_PROJECT_DIR/.go
cache:
paths:
- .go/pkg/mod/</code></pre>
<h2>full example</h2>
<pre><code class="yaml"># This file is a template, and might need editing before it works on your project.
image: hub-mirror.c.163.com/library/golang:latest
.go-cache:
variables:
GOPATH: $CI_PROJECT_DIR/.go
cache:
paths:
- .go/pkg/mod/
variables:
OUTPUT_NAME: helloworld-app
stages:
- lint
- build
- deploy
before_script:
- echo "before_script"
- git version
- go env -w GOPRIVATE=code.haiziwang.com
- mkdir -p .go
- go version
- go env -w GO111MODULE=on
- go env -w GOPROXY="https://goproxy.io,direct"
golangci-lint:
image: golangci/golangci-lint:v1.27.0
stage: lint
extends: .go-cache
allow_failure: true
script:
- golangci-lint run -v
compile:
stage: build
extends: .go-cache
script:
- go mod download
- go build -race -o $OUTPUT_NAME
artifacts:
paths:
- $OUTPUT_NAME
deploy-dev:
stage: deploy
script:
- echo "deploy dev environment"
</code></pre>
Redis optimistic lock with golang demo
https://segmentfault.com/a/1190000022866082
2020-06-08T02:02:40+08:00
2020-06-08T02:02:40+08:00
lidashuang
https://segmentfault.com/u/lidashuang
1
<p>redis 事务处理命令</p>
<ul>
<li>MULTI:开启一个事务</li>
<li>EXEC:事务执行,将一次性执行事务内的所有命令</li>
<li>DISCARD:取消事务</li>
</ul>
<p>使用 WATCH+MULTI 的方式来实现乐观锁 </p>
<p>WATCH:监控一个或多个键,如果事务执行前某个键发生了改动,那么事务也会被打断<br>UNWATCH:取消 WATCH 命令对所有键的监视</p>
<p>使用go-redis package模拟用户抢票的流程</p>
<ul>
<li>开启多个goroutine模拟并发抢票</li>
<li>go-redis <code>TxPipelined</code> 执行事务</li>
<li>go-redis <code>client.Watch</code> 监控某个键</li>
</ul>
<pre><code class="go">package main
import (
"errors"
"fmt"
"sync"
"github.com/go-redis/redis/v7"
)
func main() {
client := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
key := "ticket_count"
client.Set(key, "5", 0).Err()
val, _ := client.Get(key).Result()
fmt.Println("current ticket_count key val: ", val)
getTicket(client, key)
}
func runTx(key string, id int) func(tx *redis.Tx) error {
txf := func(tx *redis.Tx) error {
n, err := tx.Get(key).Int()
if err != nil && err != redis.Nil {
return err
}
if n == 0 {
return errors.New("票没了")
}
// actual opperation (local in optimistic lock)
n = n - 1
// runs only if the watched keys remain unchanged
_, err = tx.TxPipelined(func(pipe redis.Pipeliner) error {
// pipe handles the error case
pipe.Set(key, n, 0)
return nil
})
return err
}
return txf
}
func getTicket(client *redis.Client, key string) {
routineCount := 8
var wg sync.WaitGroup
wg.Add(routineCount)
for i := 0; i < routineCount; i++ {
go func(id int) {
defer wg.Done()
for {
err := client.Watch(runTx(key, id), key)
if err == nil {
fmt.Println(id, "成功")
return
} else if err.Error() == "票没了" {
fmt.Println(id, "票没了")
return
} else {
fmt.Println(err, "retry")
}
}
}(i)
}
wg.Wait()
}</code></pre>
<p>Github code: <a href="https://link.segmentfault.com/?enc=ZxuE7kRNRyh102aWl%2FbZMQ%3D%3D.SCRfj6VFqqdvDCh5SGnqqgMZ6A1CsZYA6f1%2FyFmGRtkC%2B6K%2FyT%2BO6hKw0QN7aZkXHwM9dqTgKpxA%2BslVotpOkw%3D%3D" rel="nofollow">https://github.com/defp/redis...</a></p>
<pre><code>current ticket_count key val: 5
7 成功
6 成功
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
3 成功
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
2 成功
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
5 成功
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
4 票没了
1 票没了
0 票没了</code></pre>
<h2>links</h2>
<ul>
<li><a href="https://link.segmentfault.com/?enc=YkuFxTdVHi%2FmP0ePMyYLfQ%3D%3D.bEFOVPrxLJSBhTDVLf0w33QFZrBu162k%2B2hHEjf%2Fr875wqdNgKoZIoO%2F7FAZfoYh2YKB%2BaQpmeoDqFbnnrJUjYbbk8N9qTttc2bxFGy1saG2b0WWHiMv%2BRz4bq%2FxLWt4FWOda56Rg%2FIvaCyU2fw%2Bgw%3D%3D" rel="nofollow">How to create Redis Transaction in Go using go-redis/redis package? - Stack Overflow</a></li>
<li><a href="https://link.segmentfault.com/?enc=aRnhsS2U%2FiavWsJHVImn2g%3D%3D.42Eq3echGNR5BQ7Vp0016tK6eYqt%2FAvzs3ziH8N6ksXNEargJ7AIRp2loOXjiDhRukN0EMI48z29fL%2FdSnV3P9SrNbcSeadfGYoW%2Bo5FcC8%3D" rel="nofollow">redis package · pkg.go.dev</a></li>
<li><a href="https://link.segmentfault.com/?enc=J0d8rcZ3VNcOHCT%2Bj%2FOpSA%3D%3D.tBkf3trBJuDC91zYIQP%2Bapoinhl5i0B3oYq6E%2F5GV9kokfTs5YykFRS4dVYAZusfty79TlwIT2z0ngqZlylT2Q%3D%3D" rel="nofollow">Redis 写缓存</a></li>
<li><a href="https://link.segmentfault.com/?enc=dqZl6EGXaAzLDzb1gMyWuw%3D%3D.CXCKknsG0bl%2BTacKCPaRJKJk4ECLR%2FWLAiMPNwvm2yfuWAQVpQw1n1mIXR25gsAiI1aAg3Nsl7SLMPg8U5pXcw%3D%3D" rel="nofollow">defp/redis-watch-multi-exec-demo</a></li>
<li><a href="https://link.segmentfault.com/?enc=AkxFwYhV6mGP8JSDpUB5QA%3D%3D.Ps09sUJYfOfPpWTAsoGVUiJhmvDaOkYfmDj8nAqKJl3vIqH2LEkZIZ%2BhPQQN%2FBCO" rel="nofollow">42丨如何使用Redis来实现多用户抢票问题</a></li>
</ul>
[工具分享] git-extras
https://segmentfault.com/a/1190000022864577
2020-06-07T20:43:21+08:00
2020-06-07T20:43:21+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p><a href="https://link.segmentfault.com/?enc=avoW9zllXj3Xn0za364awQ%3D%3D.k9dvzqbav1hsKHnnt8uwEATLeMupa8Jb%2BmfFo2feBQeglv6m8Vb0OoBl88IAuF4N" rel="nofollow">https://github.com/tj/git-extras</a></p>
<p>有时候想测试些东西,本地改起来比较快,但是要部署到服务器上才能验证,通过 git 的话得反复改反复提交到 gitlab,导致提交历史乱七八糟,这个时候就可以用 git scp 快速复制文件:</p>
<p>git extras 里的 git scp 很好用</p>
<p>比如我代码部署在 deploy@server1.com:/data/deploy/project1</p>
<pre><code>git remote add cc0 deploy@server1.com:/data/deploy/project1
</code></pre>
<p>把本地没有提交的更改复制到指定服务器上,方便我们快速验证</p>
<pre><code>git scp cc0
</code></pre>
<p>把当前目录下相比 origin/master 有更改的文件复制到指定服务器上</p>
<pre><code> git scp cc0 origin/master
</code></pre>
<p>底层其实就是用 rsync 把修改过的文件复制过去。</p>
ActiveRecord’s queries tricks 小记
https://segmentfault.com/a/1190000018386416
2019-03-04T22:57:28+08:00
2019-03-04T22:57:28+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>ActiveRecord’s queries tricks 小记</h2>
<p>原文 <a href="https://link.segmentfault.com/?enc=15E6bBCepvUa800fNr76Hg%3D%3D.wTLDPDS2NL1biDg2kGAZvyEs8Z9Pg5ZuTv2MHcVvYCrvDLiGjNc7kwm2VyRBws82PZ7t0mJzJ2sVaDAeoaLbIExyn3Vg8EcWZUtiQiVEvI0%3D" rel="nofollow">https://medium.com/rubyinside...</a></p>
<h3>关联表join时使用条件</h3>
<pre><code class="ruby"># User model
scope :activated, ->{
joins(:profile).where(profiles: { activated: true })
}</code></pre>
<p>更好的做法</p>
<pre><code># Profile model
scope :activated, ->{ where(activated: true) }
# User model
scope :activated, ->{ joins(:profile).merge(Profile.activated) }</code></pre>
<p>关于 merge<br><a href="https://link.segmentfault.com/?enc=iVGWVYdWRsnNdpnIj2XDrg%3D%3D.W%2BRDNJ9zjW7tOdRsVDPqPJRG%2FMm2nEp9Nl2KGhiC6G%2FKzRIy6VI3ZNFRjh7HFWJuLu4B0iPfcPYmzxiyNTdL3g%3D%3D" rel="nofollow">https://apidock.com/rails/Act...</a><br><a href="https://link.segmentfault.com/?enc=IcKaMNBadxlw3C%2Foi%2BIczA%3D%3D.AF740gzv4%2FhBsbW4rv96l5EsMeEXWoQa3Qpt4L7s0TPb%2Fm27e2H1CqLn0S2MNtw3yO3gozp%2FrH80VD3fIR7Axv3xrTuJoa0XDfabFcbgLvU%3D" rel="nofollow">https://api.rubyonrails.org/c...</a></p>
<h3>嵌套join的差异</h3>
<ul>
<li><strong>User has_one Profile</strong></li>
<li><strong>Profile has_many Skills</strong></li>
</ul>
<pre><code>User.joins(:profiles).merge(Profile.joins(:skills))
=> SELECT users.* FROM users
INNER JOIN profiles ON profiles.user_id = users.id
LEFT OUTER JOIN skills ON skills.profile_id = profiles.id
# So you'd rather use:
User.joins(profiles: :skills)
=> SELECT users.* FROM users
INNER JOIN profiles ON profiles.user_id = users.id
INNER JOIN skills ON skills.profile_id = profiles.id
</code></pre>
<p>内链接和外连接</p>
<h3>Exist query</h3>
<p>存在和不存在</p>
<pre><code># Post
scope :famous, ->{ where("view_count > ?", 1_000) }
# User
scope :without_famous_post, ->{
where(_not_exists(Post.where("posts.user_id = users.id").famous))
}
def self._not_exists(scope)
"NOT #{_exists(scope)}"
end
def self._exists(scope)
"EXISTS(#{scope.to_sql})"
end</code></pre>
<h3>Subqueries 子查询</h3>
<p>比如查询部分用户(user)的帖子(post)</p>
<p>不好的做法</p>
<pre><code>Post.where(user_id: User.created_last_month.pluck(:id))</code></pre>
<p>这里的缺陷是将运行两个SQL查询:一个用于获取用户的ID,另一个用于从这些user_id获取帖子</p>
<p>这样写一个查询就可以了</p>
<pre><code>Post.where(user_id: User.created_last_month)</code></pre>
<h3>基础</h3>
<p>.to_sql 生成 SQL 语句字符串<br>.explain 获取查询分析</p>
<h3>Booleans</h3>
<p>对于<code>User.where.not(tall: true)</code>在pg下会生成<br><code>SELECT users.* FROM users WHERE users.tall <> 't'</code> <br>这返回 tall 是 false 的 记录,不包括是null 的</p>
<p>包括null应该这么写</p>
<pre><code>User.where("users.tall IS NOT TRUE")</code></pre>
<p>or</p>
<pre><code>User.where(tall: [false, nil])</code></pre>
PostgreSQL 5.7. Schemas 笔记
https://segmentfault.com/a/1190000018386391
2019-03-04T22:51:51+08:00
2019-03-04T22:51:51+08:00
lidashuang
https://segmentfault.com/u/lidashuang
2
<h2>PostgreSQL 5.7. Schemas 笔记</h2>
<p><a href="https://link.segmentfault.com/?enc=lyceZDjW7S8bXTuiPq0A7Q%3D%3D.IZ5hAqRrlO5iMGA0sbY1lwP2JBpJqW7mKjh0xyNwckM5xXT3LoL5BpRxhSJA7fuFWodpP61CjRRsv5t7hKU%2FzQ%3D%3D" rel="nofollow">https://www.postgresql.org/docs/9.4/ddl-schemas.html</a></p>
<blockquote>A database contains one or more named schemas, which in turn contain tables. Schemas also contain other kinds of named objects, including data types, functions, and operators. The same object name can be used in different schemas without conflict; for example, both schema1 and myschema can contain tables named mytable. Unlike databases, schemas are not rigidly separated: a user can access objects in any of the schemas in the database he is connected to, if he has privileges to do so.</blockquote>
<ul>
<li>一个数据库包含多个schema, schema里包含tables, database 的下一层逻辑结构就是 schema</li>
<li>schema 也包括各种 objects, data types, functions, operators</li>
<li>不同schem里的table名可以相同</li>
</ul>
<blockquote>在创建一个新的 database 时, PostgreSQL 会自动为其创建一个 名为 public 的 schema。 如果未设置 search_path 变量,那 么 PostgreSQL 会将你创建的所有对象默认放入 public schema 中。_</blockquote>
<p>使用schema带来的好处</p>
<ul>
<li>允许多用户使用一个数据库而不会相互干扰, 数据隔离</li>
<li>将数据库对象组织到逻辑组中以使其更易于管理</li>
<li>第三方应用程序可以放在单独的模式中, 这样它们就不会与其他对象的名称冲突</li>
</ul>
<h3>5.7.1. Creating a Schema</h3>
<p>code example:</p>
<pre><code class="sql">CREATE SCHEMA myschema;</code></pre>
<p>访问schema的表</p>
<pre><code class="sql">schema.table</code></pre>
<p>实际上,更通用的语法</p>
<pre><code class="sql">database.schema.table</code></pre>
<p>在schema里创建表</p>
<pre><code class="sql">CREATE TABLE myschema.mytable (
...
);</code></pre>
<p>删除空schema</p>
<pre><code class="sql">DROP SCHEMA myschema;</code></pre>
<p>删除schema 并且也删除其中的对象</p>
<pre><code class="sql">DROP SCHEMA myschema CASCADE;</code></pre>
<p>为某个用户创建schema</p>
<pre><code class="sql">CREATE SCHEMA schemaname AUTHORIZATION username;</code></pre>
<h3>5.7.2. The Public Schema</h3>
<p>默认创建的表都在<code>public schema</code>里</p>
<p>下面两条语句是等价的</p>
<pre><code class="sql">CREATE TABLE products ( ... );
CREATE TABLE public.products ( ... );</code></pre>
<h3>5.7.3. The Schema Search Path</h3>
<p>当执行类 似 <code>SELECT * FROM dogs</code> 这种语句时, PostgreSQL 是怎么知道要查的是哪个 schema 中的表 呢?</p>
<p>可以加schema前缀解决, 也可以设置 <code>search_path</code> 变量解决</p>
<p>查看</p>
<pre><code class="sql">SHOW search_path;</code></pre>
<pre><code class="sql">search_path
--------------
"$user",public</code></pre>
<blockquote>PostgreSQL 有一个少为人知的系统变量叫作 user , 它代表了当前登录用户的名称。 执行 SELECT user 就能看到其名称。<br>对于search_path 里的$user, 如果当前登录的角色是 <code>doggy</code>, 那么所有的查询都会优先去 <code>doggy</code> schema 中寻找目标表, 如果找不到才会去 public schema 下找</blockquote>
<p>设置新的schema倒search path里</p>
<pre><code class="sql">SET search_path TO myschema,public;</code></pre>
<p>这样 默认 创建访问 table 都在 <code>myschema</code> schema里</p>
Rails Docker开发环境配置
https://segmentfault.com/a/1190000016808014
2018-10-25T23:43:16+08:00
2018-10-25T23:43:16+08:00
lidashuang
https://segmentfault.com/u/lidashuang
4
<p>rails mysql redis 的开发环境</p>
<p>首先构建自己的镜像</p>
<h2>Dockerfile.development</h2>
<pre><code>FROM ruby:2.3.4-slim
RUN apt-get update && apt-get install -y \
build-essential \
nodejs \
libmysqlclient-dev
RUN mkdir -p /app
WORKDIR /app
COPY Gemfile Gemfile.lock /app/
RUN gem install bundler && bundle install --jobs 20 --retry 5
COPY . /app
EXPOSE 4000
ENTRYPOINT ["bundle", "exec"]
CMD ["rails", "server", "-b", "0.0.0.0", "-p", "4000"]
</code></pre>
<h2>docker-compose.yml 配置</h2>
<p>使用mysql redis,数据库配置里host要写成 mysql, redis</p>
<pre><code>version: '3'
services:
mysql:
image: mysql:5.7.17
command: --sql-mode=""
restart: always
volumes:
- ./mysql_data/:/var/lib/mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: shiji_development
redis:
image: redis
command: redis-server
volumes:
- ./redis_data:/data
ports:
- 6379:6379
web:
build:
context: .
dockerfile: Dockerfile.development
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 4000 -b '0.0.0.0'"
stdin_open: true
tty: true
volumes:
- .:/app
ports:
- "4000:4000"
depends_on:
- mysql
- redis
</code></pre>
Golang http.RoundTripper 笔记
https://segmentfault.com/a/1190000016153599
2018-08-26T17:29:35+08:00
2018-08-26T17:29:35+08:00
lidashuang
https://segmentfault.com/u/lidashuang
8
<blockquote>RoundTripper is an interface representing the ability to execute a single HTTP transaction, obtaining the Response for a given Request.</blockquote>
<p>对于http客户端,可以使用不同的实现了 <code>RoundTripper</code> 接口的<code>Transport</code>实现来配置它的行为</p>
<p><code>RoundTripper</code> 有点像 <code>http.Client</code> 的中间件</p>
<p>接口定义</p>
<pre><code>type RoundTripper interface {
RoundTrip(*Request) (*Response, error)
}</code></pre>
<p>需要实现RoundTrip函数</p>
<pre><code>type SomeClient struct {}
func (s *SomeClient) RoundTrip(r *http.Request)(*Response, error) {
//Something comes here...Maybe
}</code></pre>
<h2>场景</h2>
<p>原文: <a href="https://link.segmentfault.com/?enc=cepnflH7jDIAdovOHkDv8g%3D%3D.TMy6RTiN5iNpf%2FQ%2F1%2Ff79wLqEpmdyU4JdCZOdE5ND6yC1Gc0pUMBh3HXFknzZ2byPyLWXZoR5Uyr59tamYzv5g%3D%3D" rel="nofollow">https://lanre.wtf/blog/2017/0...</a></p>
<ul>
<li>
<p>缓存 responses,比如 app需要访问 Github api,获取 trending repos,这个数据变动不频繁,假设30分钟变动一次,你显然不希望每次都要点击api都要来请求Github api,解决这个问题的方法是实现这样的<code>http.RoundTripper</code></p>
<ul>
<li>有缓存时从缓存取出response数据</li>
<li>过期,数据通过重新请求api获取</li>
</ul>
</li>
<li>根据需要设置http header, 一个容易想到的例子<a href="https://link.segmentfault.com/?enc=Zrtpxuu6Wg2qKWWOJNJpeA%3D%3D.6bPeAn5NqPEnIQjcnfI3G%2FWDTfr%2FpJZib2gorsy1Lp9xFSkkgag%2Fl1Qavs1sk4Pt" rel="nofollow">go-github</a>一个Github的 api的go客户端。某些github api不需要认证,有些需要认证则需要提供自己的http client,比如 <a href="https://link.segmentfault.com/?enc=OlWjMP947Wz0t0NSbUi%2BAA%3D%3D.DECfg3KzV%2BkW8IcqDawDVAQhLFM3r5uGjWYR5Py3sHiFqB%2BSSKN8De%2F0exBPj6Id" rel="nofollow">ghinstallation</a>,下面是ghinstallation 的 <a href="https://link.segmentfault.com/?enc=IlhVAro%2F7oNQPfDw%2FdA9hQ%3D%3D.h668aILjneLRCzMxw2tlHOyakgjZRTTam8JBtr2FSK%2FXF1iEUMuwc2v6X5nNq2PwM%2Fdurr0LI1GkA0gqfdrfICpWyt74WElMaknIfGnVrOM%3D" rel="nofollow">RoundTrip 函数实现</a>,设置 Authorization 头</li>
</ul>
<p><img src="/img/bVbfWr2?w=2082&h=562" alt="clipboard.png" title="clipboard.png"></p>
<ul><li>限速(Rate limiting) 控制请求速率</li></ul>
<h2>实际的例子</h2>
<p>实现<code>http.RoundTripper</code> 缓存 http response的逻辑。</p>
<p>一个http server的实现</p>
<pre><code class="go">import (
"fmt"
"net/http"
)
func main() {
// server/main.go
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// This is here so we can actually see that the responses that have been cached don't get here
fmt.Println("The request actually got here")
w.Write([]byte("You got here"))
})
http.ListenAndServe(":8000", mux)
}</code></pre>
<p>http client中创建新的 <code>http.Transport </code> 实现 <code>http.RoundTripper</code>接口</p>
<p>主程序main实现 <br><a href="https://link.segmentfault.com/?enc=vNhrmKsGkAWM0kwMBnq9IQ%3D%3D.b2eNjwUmL8tawcIePAA9%2ByfFSLzj1kVsb7TTC2uv1bPMqb6K5R%2B5CpItLgbCEaFvNkawG7EM7DzaPv19gqjdCw%3D%3D" rel="nofollow">https://github.com/adelowo/ro...</a></p>
<pre><code class="go">func main() {
cachedTransport := newTransport()
// cachedTransport 是自定义实现http.RoundTripper接口的 Transport
client := &http.Client{
Transport: cachedTransport,
Timeout: time.Second * 5,
}
// 每5秒清除缓存
cacheClearTicker := time.NewTicker(time.Second * 5)
//每秒请求一次,可以看出response是从缓存获取还是从服务器请求
reqTicker := time.NewTicker(time.Second * 1)
terminateChannel := make(chan os.Signal, 1)
signal.Notify(terminateChannel, syscall.SIGTERM, syscall.SIGHUP)
req, err := http.NewRequest(http.MethodGet, "http://localhost:8000", strings.NewReader(""))
if err != nil {
panic("Whoops")
}
for {
select {
case <-cacheClearTicker.C:
// Clear the cache so we can hit the original server
cachedTransport.Clear()
case <-terminateChannel:
cacheClearTicker.Stop()
reqTicker.Stop()
return
case <-reqTicker.C:
resp, err := client.Do(req)
if err != nil {
log.Printf("An error occurred.... %v", err)
continue
}
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("An error occurred.... %v", err)
continue
}
fmt.Printf("The body of the response is \"%s\" \n\n", string(buf))
}
}
}
</code></pre>
<p>cacheTransport 中 RoundTrip 函数实现读取缓存中的reponse</p>
<pre><code class="go">func (c *cacheTransport) RoundTrip(r *http.Request) (*http.Response, error) {
// Check if we have the response cached..
// If yes, we don't have to hit the server
// We just return it as is from the cache store.
if val, err := c.Get(r); err == nil {
fmt.Println("Fetching the response from the cache")
return cachedResponse([]byte(val), r)
}
// Ok, we don't have the response cached, the store was probably cleared.
// Make the request to the server.
resp, err := c.originalTransport.RoundTrip(r)
if err != nil {
return nil, err
}
// Get the body of the response so we can save it in the cache for the next request.
buf, err := httputil.DumpResponse(resp, true)
if err != nil {
return nil, err
}
// Saving it to the cache store
c.Set(r, string(buf))
fmt.Println("Fetching the data from the real source")
return resp, nil
}</code></pre>
<p>运行结果</p>
<p><img src="/img/bVbfWr4?w=1366&h=738" alt="clipboard.png" title="clipboard.png"></p>
<p>links:</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=XKN3f7CfIfbooZsFHgSWiQ%3D%3D.dLAgJytJnNOpRN6fEl%2F7nSP%2FzyEdmJNb9ELj7J5CxkvgLjf%2FCUvwUnaIv2PTSTl1lZ8OC6J89oZRoF5vbVPj1A%3D%3D" rel="nofollow">https://lanre.wtf/blog/2017/0...</a></li>
<li><a href="https://link.segmentfault.com/?enc=UFrlL9OtWe86a%2BqJq%2FoR%2FQ%3D%3D.CVMEwq2gGpcLNUmy6zop8PFn8G1pVhQvbJ4YVt%2B3NcWxqVJ2ouXPmA4HuojkHdrV" rel="nofollow">https://golang.org/pkg/net/ht...</a></li>
<li><a href="https://link.segmentfault.com/?enc=gQcLIi%2B4oeQC6oMjNoxi9w%3D%3D.Ye5OQHCB2a%2B8XLwgFs7e4Y3XMyjFuQYO7gMrVn8WtMBfKELbLTJ2kWRSrKfq0hiw" rel="nofollow">https://www.jianshu.com/p/dd0...</a></li>
<li><a href="https://link.segmentfault.com/?enc=r%2BYDIQv8FNvCxzexGl9UQA%3D%3D.wO19RpK55pCzJ6syVSxNUxYs6TCYrNUhHYHXH9O5nuaVoxf62v1jB%2Bl5LPHrAsU8" rel="nofollow">https://github.com/linki/inst...</a></li>
<li><a href="https://link.segmentfault.com/?enc=yHApLZDA0N5Byy1zYIjJmg%3D%3D.VxgXQXE7rfrX16vwiwDeHmpeNPyx7BIMn7IjwT63d%2FP9bXHSB0yeWelR7N99Wvgs" rel="nofollow">https://github.com/bradleyfal...</a></li>
<li><a href="https://link.segmentfault.com/?enc=YVYax21w732M6xJ12vwVWQ%3D%3D.6wL5XLSzU1z7jPR45aRXC2NLZJT4P5JaxVTqBaait6sbyezgcyp3CBqOVqwVwXMK" rel="nofollow">https://github.com/adelowo/ro...</a></li>
<li><a href="https://gist.github.com/lidashuang/a75de1eceef52f5b50559d9287169962">https://gist.github.com/lidas...</a></li>
</ul>
使用 GB 构建 Go project
https://segmentfault.com/a/1190000014371544
2018-04-13T17:36:31+08:00
2018-04-13T17:36:31+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>GB 介绍</h2>
<ul>
<li>Site: <a href="https://link.segmentfault.com/?enc=0VcDyZOkErSxOG7WDSUiMQ%3D%3D.jPhXaSn5bZMpbdRp8LgjwK29%2B2xi9NaTx4i8zJ5IWOM%3D" rel="nofollow">https://getgb.io/</a>
</li>
<li>Github: <a href="https://link.segmentfault.com/?enc=bPzuPn%2FkC3LL5y1h8dVxiQ%3D%3D.wOaEYFD9nv0s%2F1Fh7tVI8Q8vuK7RE0Hpg40B%2BLxts1JiTMNEmS5NZPTVZLUnM2tW" rel="nofollow">https://github.com/constabula...</a>
</li>
</ul>
<p>官网首页介绍<br><code>A project based build tool for the Go programming language.</code></p>
<p>所以gb 是一个构建工具,依赖管理插件完成的 gb-vendor<br><a href="https://link.segmentfault.com/?enc=ehqCExgEORC8jptD0h%2F37A%3D%3D.aJ96zcn%2F%2BzZckb6vQ%2BxqoqWIoY4Gw49JyDO4VMPMViVlEeWezzB6ZoO2V3YidJdFbbG9QwMqDBs89Zn3V3qcWQ%3D%3D" rel="nofollow">doc</a></p>
<p>为什么用gb,最喜欢的一点是 <code>Project based workflow</code> 基于 project<br>bashd,而不是 <code>GOPATH bashd</code></p>
<p>项目不需要必须在 <code>GOPATH</code> 下</p>
<h3>项目结构</h3>
<p>两个重要的目录</p>
<ul>
<li>&dollar;PROJECT/src/ 我们自己的业务代码</li>
<li>&dollar;PROJECT/vendor/src/ 第三方的依赖在这</li>
</ul>
<p>比如 一个 名字叫 <code>redeem</code> 的项目, 项目结构</p>
<pre><code>.
├── README.md
├── bin
│ └── redeem
├── src
│ └── redeem
│ └── main.go
└── vendor
├── manifest
└── src
├── github.com
└── gopkg.in</code></pre>
<p>使用 gb vendor 处理依赖, 比如</p>
<pre><code>$ gb vendor fetch github.com/urfave/cli</code></pre>
<h2>Gitlab CI</h2>
<p>要想使用Go build 方式在 GOPATH 里构建也非常方便, 我在Gitlab ci<br>就是这么处理的</p>
<p>Gitlab ci上构建项目使用了 <code>image: golang:latest</code><br>并没有安装gb工具,可以直接使用 <code>go build</code></p>
<p>只需要把依赖 <code>/vendor/src</code> 放到 <code>src/redeem/vendor</code> , GOPATH 设置了<br><code>GOPATH: $CI_PROJECT_DIR</code></p>
<p>如 <code>before_script:</code> 部分</p>
<pre><code>image: golang:latest
variables:
GOPATH: $CI_PROJECT_DIR
before_script:
- mv vendor/src/* src/redeem/vendor/
- cd src/redeem
stages:
- test
- build
format:
stage: test
script:
- go fmt $(go list ./... | grep -v /vendor/)
- go vet $(go list ./... | grep -v /vendor/)
- go test -race $(go list ./... | grep -v /vendor/)
compile:
stage: build
script:
- GOOS=darwin GOARCH=amd64 go build -o $CI_PROJECT_DIR/redeem_mac
- GOOS=windows GOARCH=amd64 go build -o $CI_PROJECT_DIR/redeem_win
artifacts:
paths:
- redeem_mac
- redeem_win
</code></pre>
Redis replication 笔记
https://segmentfault.com/a/1190000010750409
2017-08-20T00:25:55+08:00
2017-08-20T00:25:55+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<blockquote><p>“关系数据库通常会使用一个主服务器(master)向多个从服务器(slave)发送更新,并使用从服务器来处理所有读请求。Redis也采用了同样的方法来实现自己的复制特性,并将其用作扩展性能的一种手段”</p></blockquote>
<p>Excerpt From: [美] Josiah L. Carlson. “Redis实战.” iBooks.</p>
<h2>Ubuntu 16.04 安装 Redis Server</h2>
<p>安装新一点版本的redis 使用<a href="https://link.segmentfault.com/?enc=%2FtqQzp60rAyzQ1OUtKYF1A%3D%3D.g2mXixaiPtDuzSjjLWJtXpRm%2BWkneknvjuABpUdkuimcIXFRJmbc49s069OqdMgYZfYFEj1YaQ01xw2qFIwLmQ%3D%3D" rel="nofollow">redis-server : chris lea</a>这个源</p>
<pre><code>$ sudo apt-add-repository ppa:chris-lea/redis-server</code></pre>
<p>安装</p>
<pre><code>$ sudo apt-get update
$ sudo apt-get install redis-server</code></pre>
<p>可以ping下 <code>redis-cli ping</code> 返回 <code>Pong</code> 安装成功</p>
<p><img src="/img/remote/1460000010750412" alt="" title=""></p>
<p>测试环境两台机器</p>
<ul>
<li><p>10.211.55.6 (master)</p></li>
<li><p>10.211.55.7 (slave)</p></li>
</ul>
<h2>slaveof 命令和配置项</h2>
<blockquote><p>“但开启从服务器所必须的选项只有slaveof一个。如果用户在启动Redis服务器的时候,指定了一个包含slaveof host port选项的配置文件,那么Redis服务器将根据该选项给定的IP地址和端口号来连接主服务器。对于一个正在运行的Redis服务器,用户可以通过发送SLAVEOF no one命令来让服务器终止复制操作,不再接受主服务器的数据更新;也可以通过发送SLAVEOF host port命令来让服务器开始复制一个新的主服务器。”</p></blockquote>
<p>Excerpt From: [美] Josiah L. Carlson. “Redis实战.” iBooks. </p>
<p>配置很简单,就两种方式配置方式</p>
<ul>
<li><p>在slave服务器用redis-cli执行slaveof命令</p></li>
<li><p>写在配置文件slaveof host port</p></li>
</ul>
<p>配置文件好处<strong>重启配置不失效</strong></p>
<p>在 <code>10.211.55.7</code> 上执行 命令设置</p>
<pre><code>$ redis-cli
127.0.0.1:6379> SLAVEOF 10.211.55.6 6379
OK </code></pre>
<p>使用info 命令查看Replication部分状态</p>
<h3>slave</h3>
<pre><code># Replication
role:slave
master_host:10.211.55.6
master_port:6379
master_link_status:up</code></pre>
<p>slave已经连接上master服务器了</p>
<h3>master</h3>
<pre><code>role:master
connected_slaves:1
slave0:ip=10.211.55.7,port=6379,state=online,offset=98,lag=1</code></pre>
<p>如果发现没同步,检查bind配置</p>
<pre><code>bind 10.211.55.6 127.0.0.1</code></pre>
<p>另外 说是在redis.conf 里配置</p>
<pre><code>slaveof <masterip> <masterport></code></pre>
<h2>测试</h2>
<p><img src="/img/remote/1460000010750413" alt="" title=""></p>
<h2>注意</h2>
<ul>
<li><p>从服务器在进行同步时,会清空自己的所有数据</p></li>
<li><p>不支持主主复制,但是从服务器可以连接从服务器复制数据</p></li>
</ul>
logrotate使用
https://segmentfault.com/a/1190000010710818
2017-08-17T14:24:29+08:00
2017-08-17T14:24:29+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>logrotate使用</h2>
<p><img src="/img/remote/1460000010710848" alt="" title=""></p>
<p>配置在 <code>/etc/logrotate.d/</code>下</p>
<p>配置示例</p>
<pre><code># /etc/logrotate.d/rails_log.conf
/var/www/app/current/log/*.log {
daily
size=200M
rotate 10
compress
nodelaycompress
missingok
notifempty
su deploy deploy
create 664 deploy deploy
copytruncate
}</code></pre>
<p>参数</p>
<ul>
<li><p>daily 表示每天检查</p></li>
<li><p>size=200M 超过200M日志文件处理</p></li>
<li><p>rotate 10 保存多少份</p></li>
<li><p>compress 表示压缩</p></li>
<li><p>missingok 表示如果找不到日志也没关系</p></li>
<li><p>notifempty 表示如果日志是空的,就不rotate</p></li>
<li><p>su deploy deploy 新建日志文件指定用户和用户组</p></li>
<li><p>create 664 deploy deploy 指定权限</p></li>
<li><p>copytruncate 先把原始文件拷贝一份重命名,然后把原始文件清空</p></li>
</ul>
<p>logrotate 依赖于cron运行</p>
<p><img src="/img/remote/1460000010710849" alt="" title=""></p>
Gitlab CI 本地Debug
https://segmentfault.com/a/1190000010710755
2017-08-17T14:22:20+08:00
2017-08-17T14:22:20+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>Gitlab CI 本地Debug</h2>
<p>对一个go项目配置gitlab 的ci<br>配置文件</p>
<pre><code>image: golang:alpine
before_script:
- go version
- go env
- mkdir -p workspace/src/publisher-cmd/
- mv *.go workspace/src/publisher-cmd/
- mv vendor workspace/src/publisher-cmd/
build:
script:
- export GOPATH=$PWD/workspace
- cd workspace/src/publisher-cmd
- go build -o publisher-cmd_linux
- GOOS=windows GOARCH=amd64 go build -o publisher-cmd_win
- GOOS=darwin GOARCH=amd64 go build -o publisher-cmd_mac
- mv publisher-cmd_linux publisher-cmd_mac publisher-cmd_linux ../../../
artifacts:
name: publisher-cmd
paths:
- publisher-cmd_linux
- publisher-cmd_mac
- publisher-cmd_linux
</code></pre>
<p>过程输出</p>
<pre><code>Running with gitlab-ci-multi-runner 1.8.1 (a2efdd4)
Using Docker executor with image golang:alpine ...
Using locally found image version with exactly the same ID
Pulling docker image golang:alpine ...
Running on runner-c8b90077-project-110-concurrent-0 via office...
Cloning repository...
Cloning into '/builds/xi/publisher-cmd'...
Checking out b915dc91 as master...
$ mkdir -p workspace/src/publisher-cmd/
$ mv *.go workspace/src/publisher-cmd/
$ mv vendor workspace/src/publisher-cmd/
$ export GOPATH=$PWD/workspace
$ cd workspace/src/publisher-cmd
$ go build -o publisher-cmd_linux
$ GOOS=windows GOARCH=amd64 go build -o publisher-cmd_win
$ GOOS=darwin GOARCH=amd64 go build -o publisher-cmd_mac
$ mv publisher-cmd_linux publisher-cmd_mac publisher-cmd_linux ../../../
mv: can't rename 'publisher-cmd_linux': No such file or directory
ERROR: Build failed: exit code 1
</code></pre>
<p>配置gitlab ci时遇到报错,需要在本地debug,只需要在本地运行 <code>gitlab-runner </code></p>
<p>安装<code>gitlab-runner</code> <a href="https://link.segmentfault.com/?enc=8CGJPxoDZcIPqol6EOKHCQ%3D%3D.ftTB5d0l6RQOqrtbnGWpxPrCux84e%2FRDFyNmd1E8NwMaluTuBTeiKKgtf32%2FidNb" rel="nofollow">Install on macOS - GitLab Documentation</a></p>
<p>上面的配置中需要跑的job是<code>build</code></p>
<p>在项目目录里执行</p>
<pre><code>$ gitlab-runner exec docker build</code></pre>
<p>本地更改</p>
<p>把<code>build</code>替换成你的job 名字就可以</p>
Ruby 继承父类有参数如何实现?
https://segmentfault.com/a/1190000010428226
2017-07-31T14:11:39+08:00
2017-07-31T14:11:39+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>Ruby 继承父类有参数如何实现?</h2>
<p>英文原文 <a href="https://link.segmentfault.com/?enc=ouYV81RD2TEwCBa2WBUr%2BQ%3D%3D.B9eyHGhGxn6%2BTusgJXx%2BSFoS9Io5qtgh7lPZEhKMNSNYFu7kQWL9oNvqtU0SxT8WpIm27AUkbySClgvpvxV0m98MKwPxoDVGUYUp2%2FskeXM%3D" rel="nofollow">http://rubyblog.pro/2017/07/i...</a></p>
<p>Rails 5 中可以看到migration定义中会有版本参数 <code>ActiveRecord::Migration[5.0]</code></p>
<p>原理是只要<code><</code> 后边返回的是类对象就可以</p>
<p>基本继承</p>
<pre><code>class Human; end
class Man < Human
end</code></pre>
<p>也可以这样,foo 为 Human 类</p>
<pre><code>class Human; end
foo = Human
class Man < foo
end</code></pre>
<p>类似的</p>
<pre><code>class Human; end
def parent_class
Human
end
class Man < parent_class
end</code></pre>
<p>实现带参数版本</p>
<pre><code>class Human
def self.[](version)
puts version
self
end
end
class Man < Human[:basic]
end</code></pre>
<p>会打印 basic,<code>Human[:basic]</code> 表示调用类方法<code>Human.[]</code> 参数是 <code>:basic</code></p>
rsyslog debug mode
https://segmentfault.com/a/1190000010076814
2017-07-06T15:54:33+08:00
2017-07-06T15:54:33+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>rsyslog debug mode</h2>
<p><a href="https://link.segmentfault.com/?enc=amQzVxDRk098Z%2BL%2B5fkGYA%3D%3D.o5jGadkuU5NPKX0in1loYOdaCAlzdRlDqhjpjgWImVRdJ3As17tT1w3vBF6BLXbB7o9soHJGB%2BhgQDEcr6OhiqbPd%2Fefijv9d%2BCCIeT6uVKVK2gtv5FUVrWBWfzo8rH1lHnmC6b4rZQCawADk%2FI2EIDgnYoqTxVxDi6hFzF2snyOu4PE0CCFraKgKPQJeOHZ" rel="nofollow">readhat DEBUGGING RSYSLOG doc</a></p>
<p>debug 模式下 启动rsyslog, rsyslogd的日志打印到标准输出</p>
<pre><code>$ rsyslogd -dn</code></pre>
<p><code>-n </code> 表示<code>not fork</code> </p>
<p>rsyslog 支持设置下面的环境变量,改变debug日志文件位置</p>
<pre><code>export RSYSLOG_DEBUGLOG="path"
export RSYSLOG_DEBUG="Debug"</code></pre>
<p>RSYSLOG_DEBUGLOG 表示日志文件位置</p>
<p>RSYSLOG_DEBUG 支持下面的选项 man<code>rsyslogd(8)</code></p>
<ul>
<li><p>Debug</p></li>
<li><p>DebugOnDemand</p></li>
<li><p>LogFuncFlow</p></li>
<li><p>FileTrace</p></li>
<li><p>PrintFuncDB</p></li>
<li><p>PrintAllDebugInfoOnExit</p></li>
<li><p>PrintMutexAction</p></li>
<li><p>NoLogTimeStamp</p></li>
<li><p>NoStdOut</p></li>
</ul>
<p>检查配置文件语法</p>
<pre><code>rsyslogd -N 1</code></pre>
rsyslog 输出模块 omprog
https://segmentfault.com/a/1190000010076431
2017-07-06T15:39:21+08:00
2017-07-06T15:39:21+08:00
lidashuang
https://segmentfault.com/u/lidashuang
1
<h2>rsyslog 输出模块 omprog</h2>
<p>文档: <a href="https://link.segmentfault.com/?enc=fHzXRuo5wjU8iM%2BOyccEkw%3D%3D.vQNG7qCoESG6mW1gtbf5GSEXup8CdYkX5el3qPtGUnh8nQIZqd7sq4JBuzNqgJDCjBH8humPYx13%2BVfUgglG7Q6%2BaqMAhnF77FDfc%2BoNoHs%3D" rel="nofollow">http://www.rsyslog.com/doc/ma...</a></p>
<h3>规则</h3>
<p>把数据通过<code>stdin</code>传给外部程序处理</p>
<ul>
<li><p>一条log一行</p></li>
<li><p>从<code>stdin</code> 读取数据直到<code>EOF</code></p></li>
<li><p>处理数据</p></li>
<li><p>读到<code>EOF</code>时退出</p></li>
</ul>
<pre><code>while not EOF(stdin) do {
Read msg from stdin
Process msg
}</code></pre>
<h3>配置示例</h3>
<pre><code>module(load="omprog")
action(type="omprog"
binary="/pathto/omprog.py --parm1=\"value 1\" --parm2=\"value2\""
template="RSYSLOG_TraditionalFileFormat")</code></pre>
<h3>测试</h3>
<p>读取<code>/var/log/test.in</code>数据 -> <code>omprog</code> 模块处理 -> 脚本写入数据到 <code>/var/log/test.out</code></p>
<p>配置文件</p>
<pre><code> lidashuang@ubuntu:/var/log$ cat /etc/rsyslog.d/10-file-test.conf
# This configuration has been generated by using the
# rsyslog Configuration Builder which can be found at:
# http://www.rsyslog.com/rsyslog-configuration-builder/
#
# Default Settings
# Load Modules
module(load="imfile" PollingInterval="1")
module(load="omprog")
# rsyslog Templates
# rsyslog Input Modules
input(type="imfile"
File="/var/log/test.in"
Tag="test1"
Severity="info"
Facility="local0"
ruleset="MyRuleSet")
# rsyslog RuleSets
ruleset(name="MyRuleSet") {
action(type="omprog" binary="/tmp/test.sh")
stop
}
# This configuration was generated on '2017-07-05 13:05:10'
</code></pre>
<p><img src="/img/remote/1460000010076434" alt="" title=""></p>
<p>配置的脚本为 <code>/tmp/test.sh</code></p>
<pre><code>#!/usr/bin/env bash
read msg
echo $msg >> /var/log/test.out</code></pre>
<p>写入数据</p>
<pre><code>while true; do echo date: `date +%Y-%m-%d:%H:%M:%S` >> test.in; sleep 2; done</code></pre>
<p><img src="/img/remote/1460000010076435" alt="" title=""></p>
rsyslog 输入模块imfile
https://segmentfault.com/a/1190000010066046
2017-07-05T22:17:28+08:00
2017-07-05T22:17:28+08:00
lidashuang
https://segmentfault.com/u/lidashuang
1
<h2>rsyslog 输入模块imfile</h2>
<p>实验: 读取 <code>/var/log/test.in</code> 日志, 输出到 <code>/var/log/test.out</code></p>
<p>文档: <a href="https://link.segmentfault.com/?enc=3ntb6C3TNiDX85h7SBMPvg%3D%3D.dbxZj2aobGuf7Ln6QlAwc8629ex5G2LFKrq%2F1zTwo6zUasy8a2n4J2u2TX8G03tPSGiI3bnMN7kflzrcsjjmsVMwaxvntID4VTPeemFJHN8%3D" rel="nofollow">imfile: Text File Input Module</a></p>
<h3>版本</h3>
<p><img src="/img/remote/1460000010066049?w=545&h=247" alt="" title=""></p>
<p>创建 <code>/etc/rsyslog.d/10-file-test.conf</code> 配置文件</p>
<pre><code>lidashuang@ubuntu:/etc/rsyslog.d$ ls
10-file-test.conf 20-ufw.conf 50-default.conf</code></pre>
<h3>配置</h3>
<p>配置文件生成器 </p>
<p><a href="https://link.segmentfault.com/?enc=fcmhewxvkGoqdzBz0k6yIw%3D%3D.HYfSH2bzirS5MlzI2F6kejNTQK7I8BjzJKlejPXXve39YtsUt5MsO3fWE8GodPqaC1id%2B01Fq594EngcRU66Ow%3D%3D" rel="nofollow">http://www.rsyslog.com/rsyslo...</a></p>
<p><img src="/img/remote/1460000010066050?w=918&h=832" alt="" title=""></p>
<p>生成的配置, 适当的改了下</p>
<pre><code># This configuration has been generated by using the
# rsyslog Configuration Builder which can be found at:
# http://www.rsyslog.com/rsyslog-configuration-builder/
#
# Default Settings
# Load Modules
module(load="imfile" PollingInterval="1")
# rsyslog Templates
# rsyslog Input Modules
input(type="imfile"
File="/var/log/test.in"
Tag="test1"
Severity="info"
Facility="local0"
ruleset="MyRuleSet")
# rsyslog RuleSets
ruleset(name="MyRuleSet") {
action(type="omfile"
File="/var/log/test.out")
stop
}
# This configuration was generated on '2017-07-05 13:05:10'
</code></pre>
<h3>配置说明</h3>
<ul>
<li><p>input module 为 imfile, input module 以im开头</p></li>
<li><p>output module 为 omfile, output module 以om开头</p></li>
<li><p>File 表示文件路径</p></li>
<li><p>Severity 日志等级</p></li>
<li><p>ruleset 规则集</p></li>
<li><p>PollingInterval 表示轮询时间,单位是秒</p></li>
</ul>
<h3>Tips</h3>
<p>使用 <code> rsyslogd -N 1</code> 检查配置文件是否正确,比如</p>
<p><img src="/img/remote/1460000010066051?w=920&h=127" alt="" title=""></p>
<h3>测试</h3>
<p>重启服务, 向<code>test.in</code>文件写入数据</p>
<pre><code>while true; do echo date: `date +%Y-%m-%d:%H:%M:%S` >> test.in; sleep 2; done</code></pre>
<p>查看输出<code>tail -f test.in -f test.out</code></p>
<p><img src="/img/remote/1460000010066052?w=800&h=638" alt="" title=""></p>
Ruby 使用 Fiddle 调用 C 函数
https://segmentfault.com/a/1190000009551020
2017-05-25T11:50:40+08:00
2017-05-25T11:50:40+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>Ruby 使用 Fiddle 调用 C 函数</h2>
<p>写一个c函数</p>
<p>split.c</p>
<pre><code class="c">double split(double num)
{
double ret = 0;
ret = num / 2;
return ret;
}</code></pre>
<p>编译成动态库</p>
<pre><code> gcc -o libsplit.so -shared split.c</code></pre>
<p>在 split.rb 里调用 libsplit.so 里的 split 函数</p>
<pre><code class="ruby">require 'fiddle'
# Open the file
libsplit = Fiddle.dlopen('./libsplit.so')
# Load the `split` function
split = Fiddle::Function.new(
libsplit['split'],
[Fiddle::TYPE_DOUBLE],
Fiddle::TYPE_DOUBLE
)
# Call the `split` function
puts split.call(10) # => 5 </code></pre>
<ul>
<li><p>Fiddle.dlopen,与c中调用动态链接库方法名相同dlopen</p></li>
<li><p>Fiddle::Function.new 参数为 函数名,参数,返回值</p></li>
</ul>
<p>还可以通过 <code>Fiddle::Importer</code> mixin提供的DSL</p>
<pre><code class="ruby">module Test
extend Fiddle::Importer
dlload './libsplit.so'
extern 'double split(double)'
end
puts Test.split(10) # => 5 </code></pre>
<h3>links</h3>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=O5SAlC7%2FiaIeShbWylpVCQ%3D%3D.CI5A2WyEcHfPEWssGYkKLvlHbNIK52lDxwM1UDGlQUvJksKFV9Bcxkq0JDDXuGTzULoWsMsMf0nUK1Fk6yLqVlrx%2BVVmvb9aDYThc9oSfWQ%3D" rel="nofollow">RubyLetter - Shared Libraries: How Ruby and C Work Together</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=N5l59QZ%2B%2F1GRom%2FHfgOmFQ%3D%3D.bTJk9B4GxPcL9fw%2B2IQIrIwFJi1cOTOT%2ByigYthQM1o%3D" rel="nofollow">ffi/ffi: Ruby FFI</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=LgCf4Bwf58MWb67AIM559w%3D%3D.GZuibWzSUG06snONTGcTJsqnFvLukY2GSANu0ncTnYqkUFV0%2BQKUAGkaOBFKFam2" rel="nofollow">(5) 使用 Fiddle 调用 C 函数 · Ruby China</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=EEBR38ySuUDxJrEKvqK5ZQ%3D%3D.BMPyKkiz3b7x%2FceRsqFhwnxwOumBpfPQpC3qgvjAMpCgaOLLrM0z9atVJE%2BtaVzM" rel="nofollow">(5) 使用 ruby::fiddle 封装动态链接库 · Ruby China</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=nC0nhzufqUMkRDjakQwn9A%3D%3D.fNoWkrQb4nUcY5I0yGabvInXrS7I83bLG6HRlgYAa5NbGqV2t4hG2Oc9ln0xCjZGc0%2B%2Bzclx5%2B9wnhFJRxQmmg%3D%3D" rel="nofollow">Module: Fiddle (Ruby 2.0.0)</a></p></li>
</ul>
Lua os.date 笔记
https://segmentfault.com/a/1190000009103768
2017-04-18T11:37:25+08:00
2017-04-18T11:37:25+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<pre><code>os.date ([format [, time]])</code></pre>
<p>若设置time参数,则按time指定的时间格式化,否则按当前时间格式化</p>
<pre><code>> os.date()
Mon May 16 14:26:32 2016
> os.date("%x", os.time())
05/16/16</code></pre>
<p>如果format 以 <code>!</code> 开始, date 会被格式化成<code>协调世界时(Coordinated Universal Time)</code></p>
<p><code>*t</code> 将返一个带year(4位), month(1-12), day (1--31), hour (0-23), min (0-59), sec (0-61), wday (星期几, 星期天为1), yday (年内天数), isdst (是否为日光节约时间true/false)的表</p>
<pre><code>> print(os.date ("*t"))
table: 0x7fc1aaf01200
> print(os.date ("*t").year)
2017
> print(os.date ("*t").hour)
14
> print(os.date ("*t").wday)
3</code></pre>
<p>若没有<code>*t</code>则返回一个按C的strftime函数格式化的字符串</p>
<p>若不带参数,则按当前系统的设置返回格式化的字符串 os.date() <=> os.date("%c")</p>
<h2>format 参考</h2>
<pre><code>%a - Abbreviated weekday name (eg. Wed)
%A - Full weekday name (eg. Wednesday)
%b - Abbreviated month name (eg. Sep)
%B - Full month name (eg. September)
%c - Date and time representation appropriate for locale (eg. 23/04/07 10:20:41)
(Standard date and time string ) - see below for using os.setlocale to get the correct locale.
%d - Day of month as decimal number (01 - 31)
%H - Hour in 24-hour format (00 - 23)
%I - Hour in 12-hour format (01 - 12)
%j - Day of year as decimal number (001 - 366)
%m - Month as decimal number (01 - 12)
%M - Minute as decimal number (00 - 59)
%p - Current locale’s A.M./P.M. indicator for 12-hour clock (eg. AM/PM)
%S - Second as decimal number (00 - 59)
%U - Week of year as decimal number, with Sunday as first day of week 1 (00 - 53)
%w - Weekday as decimal number (0 - 6; Sunday is 0)
%W - Week of year as decimal number, with Monday as first day of week 1 (00 - 53)
%x - Date representation for current locale (Standard date string)
%X - Time representation for current locale (Standard time string)
%y - Year without century, as decimal number (00 - 99) (eg. 07)
%Y - Year with century, as decimal number (eg. 2007)
%Z - Time-zone name or abbreviation; no characters if time zone is unknown
%% - Percent sign</code></pre>
<h2>示例</h2>
<p>获取一年第几周</p>
<pre><code>> os.date ("%V")
12</code></pre>
<p>年</p>
<pre><code>> os.date ("%Y")
2017</code></pre>
<h2>参考</h2>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=oUGXLhCbQkjr4kv3%2BQkxvA%3D%3D.Di0WKgmdFhv3pF9YnP6XPl9yCru0fqb2GKDdmCBgqwRTu5ZlS1wHDtCXHNkOnB8K" rel="nofollow">What's the Current Week Number?</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=c8xEn%2FSjT6WEFItIX%2BYTAQ%3D%3D.BECBFZKOTkMN9eI%2FVjwm18j2wGtazTiAp%2FAixFFqhxXRPa4CImwOzuif662zPIyq7yR0IKHW1FPzoU1AEn3QXw%3D%3D" rel="nofollow">MUSHclient documentation: contents</a></p></li>
</ul>
如何实现命令行进度条?
https://segmentfault.com/a/1190000009058725
2017-04-15T22:15:00+08:00
2017-04-15T22:15:00+08:00
lidashuang
https://segmentfault.com/u/lidashuang
2
<p>关键点</p>
<ul>
<li><p><code>\r</code></p></li>
<li><p><code>-ne</code></p></li>
</ul>
<p><code>\r</code> 保证从行开始处开始输出<br><code>-ne</code> 不输出换行 <code>\n</code></p>
<p>demo:</p>
<pre><code>echo -ne '##### (33%)\r'
sleep 1
echo -ne '############# (66%)\r'
sleep 1
echo -ne '####################### (100%)\r'
echo -ne '\n'</code></pre>
<p>参考</p>
<p><a href="https://link.segmentfault.com/?enc=FPgoisJahV4VsQfoo2HD1A%3D%3D.c6iskYtZ97GaNzCPqNqRHZOBZrPj7f7okZNVfHAa0gP%2FMs7%2F1tsZAIl0Nt3Eblzte8JFtk0%2B6R5IkjHtd29LSwwLHP7GaqtA%2Bnf%2Be20QM46mZIq6HYWDurqSDvxBiLOo" rel="nofollow">http://stackoverflow.com/ques...</a></p>
<h2>一些 Golang 项目</h2>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=Rx7V2l7NKR55is7Q3fv3gg%3D%3D.zuRPwS9AVtQd5bdnbWemFZCo4B3F05Q8hQrRBTRF2crT0ob4UBcjiJSx46NVn4Uh" rel="nofollow">mitchellh/ioprogress: Go (golang) package for progress bars around io.Reader/Writers.</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=OnCemiw7g8oO1x1jsPwNDA%3D%3D.inyiGxkPCKQuzY6lIzPVKPyqHu8ilq06SYUQr%2FzXbXE%3D" rel="nofollow">cheggaaa/pb: Console progress bar for Golang</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=%2B%2FO7RAuy%2FtmJEfSEC4jDUQ%3D%3D.GKa1JiSlZCIXHvvd1l196GI3HQ8YmaEdAYDHZhS0xEXrVmoy6PClDCMGfvQWFuCC" rel="nofollow">sethgrid/multibar: Display multiple progress bars in Go (golang).</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=APgvUIcdjfTMysQWPWxHRw%3D%3D.3gcgVMqRw6Ex6N0LfSmk5uJoM6LbXEijJv8a47cXLk%2F1DJwWo6ArUTpv0F93O0EF" rel="nofollow">mitchellh/cli: A Go library for implementing command-line interfaces.</a></p></li>
</ul>
Bash 数组示例
https://segmentfault.com/a/1190000009058884
2017-04-14T09:30:00+08:00
2017-04-14T09:30:00+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>原文 <a href="https://link.segmentfault.com/?enc=21%2FS1KWmZAuiZhk4IpDR5w%3D%3D.D%2FtKWzvJWbzZnwE3EjOJaEBqUFm4Xau7lNsWKqAF3LA5PWKfhst29hOxckdYGpO6f1PUOdODjgWCui%2B8%2FywGgA%3D%3D" rel="nofollow">http://www.thegeekstuff.com/2...</a></p>
<h2>1 数组声明</h2>
<p>像下面 会自动创建 name 数组,不用声明</p>
<pre><code>name[index]=value</code></pre>
<p>例如</p>
<pre><code class="bash">$ cat arraymanip.sh
#! /bin/bash
Unix[0]='Debian'
Unix[1]='Red hat'
Unix[2]='Ubuntu'
Unix[3]='Suse'
echo ${Unix[1]}
$./arraymanip.sh
Red hat</code></pre>
<p>通过索引访问元素 <code>${name[index]}</code></p>
<h2>2 声明时初始化数组</h2>
<p>语法</p>
<pre><code>Syntax:
declare -a arrayname=(element1 element2 element3)</code></pre>
<p>元素使用空格分隔</p>
<pre><code>#! /bin/bash
$cat arraymanip.sh
declare -a Unix=('Debian' 'Red hat' 'Red hat' 'Suse' 'Fedora');</code></pre>
<h2>3 打印整个数组</h2>
<p>如果索引是 <code>@</code> 或 <code>*</code> 表示整个数组</p>
<pre><code>echo ${Unix[@]}
# Add the above echo statement into the arraymanip.sh
#./t.sh
Debian Red hat Ubuntu Suse</code></pre>
<h2>4 获取数组长度</h2>
<p>语法</p>
<p><code>${#arrayname[@]}</code></p>
<pre><code>$ cat arraymanip.sh
declare -a Unix=('Debian' 'Red hat' 'Suse' 'Fedora');
echo ${#Unix[@]} # 数组长度
echo ${#Unix} # 这个是获取数组第一个元素字符数量 .i.e Debian
$./arraymanip.sh
4
6</code></pre>
<h2>5 数组中第n个元素长度</h2>
<p><code>${#arrayname[n]}</code></p>
<pre><code>$cat arraymanip.sh
#! /bin/bash
Unix[0]='Debian'
Unix[1]='Red hat'
Unix[2]='Ubuntu'
Unix[3]='Suse'
echo ${#Unix[3]} # length of the element located at index 3 i.e Suse
$./arraymanip.sh
4</code></pre>
<h2>6 通过数组的偏移量和长度来提取子数组</h2>
<p>下面的例子从数组Unix 位置3开始,提取2个元素, 索引从0开始</p>
<pre><code>$cat arraymanip.sh
Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux');
echo ${Unix[@]:3:2}
$./arraymanip.sh
Suse Fedora</code></pre>
<h2>7 根据偏移和长度获取数组元素的一部分</h2>
<p>例如,位于数组的第二个索引的Ubuntu 获取基 第0个到第4个字符</p>
<pre><code>$cat arraymanip.sh
#! /bin/bash
Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux');
echo ${Unix[2]:0:4}
./arraymanip.sh
Ubun</code></pre>
<p>上述示例从数组的第二个索引元素中提取前四个字符</p>
<h2>8 搜索 替换 数组中元素</h2>
<p>下面的代码 搜索 <code>Ubuntu</code> 然后 替换成 <code>SCO Unix</code></p>
<pre><code>$cat arraymanip.sh
#!/bin/bash
Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux');
echo ${Unix[@]/Ubuntu/SCO Unix}
echo ${Unix[@]}
$./arraymanip.sh
Debian Red hat SCO Unix Suse Fedora UTS OpenLinux
Debian Red hat Ubuntu Suse Fedora UTS OpenLinux</code></pre>
<p>在这个例子中, 它用“SCO Unix”替换了第二个索引“Ubuntu”中的元素, 但是这个例子不会改变原来数组</p>
<h2>9 向数组中添加元素</h2>
<p>向数组Unix中添加 <code>AIX</code> <code>HP-UX</code></p>
<pre><code>$cat arraymanip.sh
Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux');
Unix=("${Unix[@]}" "AIX" "HP-UX")
echo ${Unix[7]}
$./arraymanip.sh
AIX</code></pre>
<h2>10 在从数组删除元素</h2>
<p>使用 <code>unset</code> 删除索引3的<code>Suse</code></p>
<pre><code>$cat arraymanip.sh
#!/bin/bash
Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux');
unset Unix[3]
echo ${Unix[3]}</code></pre>
<p>上面的例子打印索引3的值为空,并不是我们想要的结果, 下面的例子完整的移除一个元素</p>
<pre><code>$ cat arraymanip.sh
Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux');
pos=3
Unix=(${Unix[@]:0:$pos} ${Unix[@]:$(($pos + 1))})
echo ${Unix[@]}
$./arraymanip.sh
Debian Red hat Ubuntu Fedora UTS OpenLinux</code></pre>
<p>上面的代码中 <code>${Unix[@]:0:$pos}</code> 表示 从索引0到pos的子数组 <code>${Unix[@]:4}</code> 表示从索引4到最后一个元素的子数组<br>然后 把两个子数组合并成一个数组</p>
<h2>11 通过模式删除数组元素</h2>
<p>在搜索条件下,您可以给出模式,并将剩余元素存储到另一个数组,比如</p>
<pre><code>$ cat arraymanip.sh
#!/bin/bash
declare -a Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora');
declare -a patter=( ${Unix[@]/Red*/} )
echo ${patter[@]}
$ ./arraymanip.sh
Debian Ubuntu Suse Fedora</code></pre>
<p>上述示例删除了具有模式<code>Red*</code> 的元素</p>
<h2>12 复制一个数组</h2>
<p>展开数组元素并将其存储到新的数组中</p>
<pre><code>#!/bin/bash
Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux');
Linux=("${Unix[@]}")
echo ${Linux[@]}
$ ./arraymanip.sh
Debian Red hat Ubuntu Fedora UTS OpenLinux</code></pre>
<h2>13 合并两个数组</h2>
<p>展开两个数组的元素并将其分配给新数组</p>
<pre><code>$cat arraymanip.sh
#!/bin/bash
Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux');
Shell=('bash' 'csh' 'jsh' 'rsh' 'ksh' 'rc' 'tcsh');
UnixShell=("${Unix[@]}" "${Shell[@]}")
echo ${UnixShell[@]}
echo ${#UnixShell[@]}
$ ./arraymanip.sh
Debian Red hat Ubuntu Suse Fedora UTS OpenLinux bash csh jsh rsh ksh rc tcsh
14</code></pre>
<h2>14 删除整个数组</h2>
<p>使用 unset 命令</p>
<pre><code>$cat arraymanip.sh
#!/bin/bash
Unix=('Debian' 'Red hat' 'Ubuntu' 'Suse' 'Fedora' 'UTS' 'OpenLinux');
Shell=('bash' 'csh' 'jsh' 'rsh' 'ksh' 'rc' 'tcsh');
UnixShell=("${Unix[@]}" "${Shell[@]}")
unset UnixShell
echo ${#UnixShell[@]}
$ ./arraymanip.sh
0</code></pre>
<h2>15 从文件加载数组</h2>
<p>读取文件每一行,一行作为数组中的一个元素</p>
<pre><code>#Example file
$ cat logfile
Welcome
to
thegeekstuff
Linux
Unix
$ cat loadcontent.sh
#!/bin/bash
filecontent=( `cat "logfile" `)
for t in "${filecontent[@]}"
do
echo $t
done
echo "Read file content!"
$ ./loadcontent.sh
Welcome
to
thegeekstuff
Linux
Unix
Read file content!</code></pre>
生成指定版本的Rails项目
https://segmentfault.com/a/1190000009058597
2017-04-13T21:59:23+08:00
2017-04-13T21:59:23+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>本地 Rails 默认5.0.2 版本</p>
<pre><code>$ gem list --local
rails (5.0.2, 5.0.0.1, 5.0.0, 4.2.8)</code></pre>
<p>使用 <code>_version_</code> 生成指定version的项目</p>
<pre><code>rails _version_ new application_name</code></pre>
<p>例如生成 4.2.8</p>
<pre><code>rails _4.2.8_ new todolists</code></pre>
「小技巧」 SSH 连接断了但是不想关掉终端重新打开怎么办?
https://segmentfault.com/a/1190000009052304
2017-04-13T14:25:26+08:00
2017-04-13T14:25:26+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>SSH 连接断了但是不想关掉终端重新打开怎么办?</h2>
<p>SSH 连接断了但是不想关掉终端重新打开怎么办?依次输入 <code>回车 ~ .</code></p>
<p><code>~ </code>是 SSH 的命令,使用<code> ~?</code> 可以看到全部命令,只能在行开始的时候有效</p>
<pre><code>ian@ops:~$ ~?
Supported escape sequences:
~. - terminate connection (and any multiplexed sessions)
~B - send a BREAK to the remote system
~C - open a command line
~R - request rekey
~V/v - decrease/increase verbosity (LogLevel)
~^Z - suspend ssh
~# - list forwarded connections
~& - background ssh (when waiting for connections to terminate)
~? - this message
~~ - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)</code></pre>
Lua 错误处理 pcall & xpcall
https://segmentfault.com/a/1190000008920156
2017-04-01T15:38:49+08:00
2017-04-01T15:38:49+08:00
lidashuang
https://segmentfault.com/u/lidashuang
2
<h2>pcall</h2>
<p>pcall 指的是 <code>protected call</code> 类似其它语言里的 <code>try-catch</code>, 使用pcall 调用函数,如果函数 f 中发生了错误, 它并不会抛出一个错误,而是返回错误的状态, 为被执行函数提供一个保护模式,保证程序不会意外终止</p>
<p>语法</p>
<pre><code>pcall( f , arg1,···)</code></pre>
<p>返回值</p>
<ol>
<li>
<p>函数执行状态 (boolean)</p>
<ul>
<li><p>没有错误返回 <code>true</code></p></li>
<li><p>有错误返回 <code>false</code></p></li>
</ul>
</li>
<li><p>发生错误返回错误信息,否则返回函数调用返回值</p></li>
</ol>
<p>pcall 示例</p>
<p>使用pcall 处理错误</p>
<pre><code class="lua">function square(a)
return a * "a"
end
local status, retval = pcall(square,10);
print ("Status: ", status) -- 打印 "false"
print ("Return Value: ", retval) -- 打印 "input:2: attempt to perform arithmetic on a string value"</code></pre>
<p>正常没错误</p>
<pre><code class="lua">function square(a)
return a * a
end
local status, retval = pcall(square,10);
print ("Status: ", status) -- 打印 "true"
print ("Return Value: ", retval) -- 打印 "100"</code></pre>
<h2>xpcall</h2>
<pre><code>xpcall (f, msgh [, arg1, ···])</code></pre>
<p><code>xpcall</code> 类似 <code>pcall</code> xpcall接受两个参数:调用函数、错误处理函数</p>
<p>比如使用 debug.traceback 获取栈信息</p>
<pre><code>> status, err, ret = xpcall(square, debug.traceback, 10)
> status
false
> err
stdin:2: attempt to perform arithmetic on a string value
stack traceback:
stdin:2: in function 'square'
[C]: in function 'xpcall'
stdin:1: in main chunk
[C]: in ?
></code></pre>
使用Ansible部署Phoenix项目
https://segmentfault.com/a/1190000004426172
2016-02-10T23:09:18+08:00
2016-02-10T23:09:18+08:00
lidashuang
https://segmentfault.com/u/lidashuang
1
<p>使用 <a href="https://link.segmentfault.com/?enc=RII2rNr3g4RNPTwPl5W9hQ%3D%3D.nTkIl0eS%2Fm9uqjfonI8UPc4D0nWFqOP2%2FK6XbvZMDkFY%2BiKYoE6Bnub6O4fMczcNR4nIEfU097o6GsuebFwiEg%3D%3D" rel="nofollow">ansible-elixir-stack</a> 这个项目部署Phoenix项目, 做到一键部署</p>
<p><a href="https://link.segmentfault.com/?enc=t3vb11H19unlpJo3en1ePQ%3D%3D.WJ9T3C3gm%2F1npzF2YmOzkgiN10Ief%2FHIf7vV7diAPmIx3bUpzenlee%2BkUfAG0Tn6UqpI4c6klhr4E1sLJFAA2A%3D%3D" rel="nofollow">ansible-elixir-stack</a> 用到的工具有:</p>
<ul>
<li><p>exrm 打包发布工具, 代码热更新</p></li>
<li><p>monit 用于监控和自动重启应用</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=AuIYSEuQZgB3m3YoyTjjiQ%3D%3D.Gdu4BJra6DJpB0Ui8VjsExYPCKxrPZU1lKKrmDgnu1p5HIs66mjSHNbRHQaTKscx" rel="nofollow">asdf</a> 用于安装Elixir, Erlang</p></li>
</ul>
<p>创建一个用于实验的Phoenix项目</p>
<p><code>$ mix phoenix.new hello_phoenix</code></p>
<p>项目跑起来后在Github 上创建<a href="https://link.segmentfault.com/?enc=LpH5Ruj4BgizXOdXVOtN%2FQ%3D%3D.t4RdAduiaNiRF2XwKquO7dbtRWTdy4HHTSQJroX1ItrAnDNF3Wh3u7MG8WE28Trf" rel="nofollow">项目</a></p>
<p>下面就是 使用 <a href="https://link.segmentfault.com/?enc=5e0PuDBa5TllgDH9GXSMdw%3D%3D.4UGIEUrDk5t00xbtm2dn8dorkoO0g0Ggxv1kvk%2BZHriGxx%2BAUF9GUQE%2FQn%2FQc4IgeX3yA6%2BfyD2kOJhR0izWxg%3D%3D" rel="nofollow">ansible-elixir-stack</a> 这个项目部署配置ansible, 在项目目录运行</p>
<p><code>$ curl -L http://git.io/ansible-elixir-stack.sh | bash</code></p>
<p>这个脚本会创建ansible的配置文件</p>
<p><img src="http://lidashuang.u.qiniudn.com/16-2-10/54381648.jpg" alt="" title=""></p>
<p>其中需要配置的是<code>inventory</code> 文件, 这个文件用于配置服务器的ip地址</p>
<p>inventory</p>
<pre><code>[app-servers]
117.121.26.170
# Delete the above IP addresses and add your server's IP addresses</code></pre>
<p><code>.tool-versions</code> 配置erlang,elixir,nodejs的版本</p>
<pre><code>$ cat .tool-versions
erlang 18.0
elixir 1.0.5
nodejs 0.12.5</code></pre>
<p>还需要加上exrm这个项目<br><img src="http://lidashuang.u.qiniudn.com/16-2-10/20953224.jpg" alt="" title=""><br><a href="https://link.segmentfault.com/?enc=6TDksy0HiuKApc%2FALH9hnw%3D%3D.Fx6SIWI5%2BOw%2F9TWNnY9FX%2FNbG41CJ8k1UULDa9vCBFHcF0M9LVSnbbBKV5bZklYC" rel="nofollow">exrm</a>用来打包Elixir项目</p>
<p><code>HashNuke.elixir-stack</code> 这个role需要用ansible-galaxy(ansible的包管理工具)安装</p>
<p><code>$ ansible-galaxy install HashNuke.elixir-stack</code></p>
<p>可以配置<code>ansible.cfg</code>配置role的保存路径, 当然也可以用默认的<br><img src="http://lidashuang.u.qiniudn.com/16-2-10/81304912.jpg" alt="" title=""><br><code>roles_path = roles</code></p>
<p>这样安装的ansible role模块会安装在项目的roles文件夹</p>
<p>配置好之后就可以部署了</p>
<p><code>$ ansible-playbook playbooks/setup.yml</code></p>
<p>默认配置是使用的是root用户<br>请确保root用户的ssh key已经加到服务器上了</p>
<p>以后再部署的话, 使用</p>
<p><code>$ ansible-playbook playbooks/deploy.yml</code></p>
<p>更多自定义配置查看<a href="https://link.segmentfault.com/?enc=ehrCvvtVq7Cj0b1HYykf0A%3D%3D.9EjM4mLEbVn0ARR94%2Fwrj%2FTy%2BCG%2BalMi08t4neGz8N6X6ZmZlGvSlE%2BkgXVhY9goEUV%2BAfu%2Frq76YZt%2BRl2variL4Q2gwt9%2BmBgJUs0lqUJsb8pTUWPnINKYJC6%2Fd7Yb" rel="nofollow">Configuration options 文档</a></p>
<p>测试demo: https://github.com/lidashuang/hello_phoenix</p>
[Ansible学习一] Ansible Galaxy使用小记
https://segmentfault.com/a/1190000004419028
2016-02-05T21:21:21+08:00
2016-02-05T21:21:21+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>[Ansible学习一] Ansible Galaxy使用小记</h2>
<p>ansible也有类似rubygems.org 的网站, 让ansible用户分享ansible功能模块<br>网址 <a href="https://link.segmentfault.com/?enc=GzZNAvkozGKKXpoiHme2rg%3D%3D.YOK9FcoXxd3l%2BT1HTQ3dKb4BosG2ueb0NRO9snyI2%2BI%3D" rel="nofollow">https://galaxy.ansible.com/</a></p>
<h3>安装模块</h3>
<p>直接使用<code>ansible-galaxy</code> 安装一些模块</p>
<pre><code>$ ansible-galaxy install username.rolename
</code></pre>
<p>当然也支持 <code>批量安装</code>, Ansible 提供了两种方式配置依赖模块,可以批量安装模块</p>
<h3>方式1 使用 <code>roles.txt</code>
</h3>
<pre><code># roles.txt
user1.role1,v1.0.0
user2.role2,v0.5
user2.role3
</code></pre>
<p>配置规则格式是 <code> username.rolename[,version]</code> 的形式</p>
<pre><code>$ ansible-galaxy install -r roles.txt
</code></pre>
<p>方式1 不能指定安装路径,也不能指定安装名字</p>
<p>ansible提供了更高级的配置yml的格式</p>
<h3>方式2 使用yml格式的配置文件</h3>
<p>创建 <code>install_roles.yml</code> 配置文件</p>
<pre><code># install_roles.yml
# from galaxy
- src: yatesr.timezone
# from github
- src: https://github.com/bennojoy/nginx
# from github installing to a relative path
- src: https://github.com/bennojoy/nginx
path: vagrant/roles/
# from github, overriding the name and specifying a specific tag
- src: https://github.com/bennojoy/nginx
version: master
name: nginx_role
# from a webserver, where the role is packaged in a tar.gz
- src: https://some.webserver.example.com/files/master.tar.gz
name: http-role
# from bitbucket, if bitbucket happens to be operational right now :)
- src: git+http://bitbucket.org/willthames/git-ansible-galaxy
version: v1.4
# from bitbucket, alternative syntax and caveats
- src: http://bitbucket.org/willthames/hg-ansible-galaxy
scm: hg
</code></pre>
<p>安装配置好的role模块</p>
<pre><code>$ ansible-galaxy install -r install_roles.yml
</code></pre>
<p>可以在项目里创建ansible.cfg配置文件,配置role模块下载位置 </p>
<p>ansible.cfg</p>
<pre><code>[defaults]
hostfile = inventory
roles_path = roles
</code></pre>
<h3>创建模块</h3>
<p>比如创建一个acme模块,</p>
<pre><code>$ ansible-galaxy init acme --force
</code></pre>
<p>文件结构</p>
<pre><code> .
├── README.md
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
</code></pre>
<h4>Meta Data - meta/main.yml</h4>
<p><code>meta/main.yml</code> 用来配置模块一些元信息,比如支持的平台,Ansible最小依赖版本,<br>这个模块的标签,依赖,作者的信息等等。</p>
<p><code>ansible-galaxy init</code> 创建项目生成的模板如下</p>
<pre><code>---
galaxy_info:
author: John Doe
description: Quick and easy acme web installer.
company: Acme
license: MIT
min_ansible_version: 1.9
platforms:
- name: EL
versions:
- all
galaxy_tags:
- acme
- installer
- web
dependencies:
- { role: username.common, some_parameter: 3 }
- { role: username.iptables, open_port: 80 }
</code></pre>
<p>dependencies 用于配置依赖的其它模块</p>
<p>比如</p>
<pre><code>dependencies:
- { role: stouts.elasticsearch }</code></pre>
<p>也可以传参数</p>
<pre><code>dependencies:
- { role: stouts.elasticsearch, elasticsearch_home: "{{ install_path_variable }}", elasticsearch_http_port: 8900 }</code></pre>
<h4>task/main.yml</h4>
<p>是这个role的入口文件,命令从main.yml开始执行</p>
<h4>files</h4>
<p>files 目录是放一些文件用的,比如可以用copy命令,把files目录下的文件复制到服务器上</p>
<pre><code>- name: copy source.list.{{ansible_distribution_release}} file
copy: src=sources.list.{{ansible_distribution_release}} dest=/etc/apt/sources.list backup=yes</code></pre>
<h4>templates</h4>
<p>用于jinjia2模板文件</p>
<h4>defaults/main.yml</h4>
<p>默认的配置变量写在defaults/main.yml里</p>
<h4>handlers/main.yml</h4>
<p>一般用来重启服务,比如</p>
<pre><code>
---
# this might be in a file like handlers/handlers.yml
- name: restart apache
service: name=apache state=restarted
</code></pre>
腾讯MSDK支付接入记录
https://segmentfault.com/a/1190000003042145
2015-08-01T18:18:59+08:00
2015-08-01T18:18:59+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=ma%2Fb7z7ObrpBrMheBHxqtA%3D%3D.6ndAdTkJnBzJlocA9dH6FjuImyu7gUZ02utDARZJbkc%3D" rel="nofollow">腾讯开放平台联调工具集</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=v6xMKVRNf4OtDN8f3zI46Q%3D%3D.ZUUnk%2FbXTk31H8BdQQ0WQ%2BdlivdKSzSLoOdRPBJKcgXHSBleUkSEZDkT8VqwxE1Z0KW3g82PaW59rJ4WDWgdsQbYWHKF0xS85RCiKz9KYs2OES8gXwNcbk5sovKAfMxe" rel="nofollow">签名SDK下载</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=CyOi196srxZOd4GqB3GB1g%3D%3D.TVjiEojUbiEz9%2B1I80tv1PloWWHZ3r7PIJWlf3LDMG%2FF5ZgzVA8KX2Igs62rCj86U157OJ5cOhKojDXrN4rcNwGoinbFNAgV25hLf4eKvQk%3D" rel="nofollow">支付API接口的联调</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=USGF%2Besl2eYYWPqRDLM4%2BA%3D%3D.UoIPl%2BG%2BCwS3KYuYRVF3W2ItjvW4orX7FSU%2BCeHoaJPreIZF8xisjI6pyh0K1ZQpVP0KFAz8RV6dfNho3s1a4cN5hh3FFkSXzDaiahPF68jlwMI%2Bjo9TgEOVCjI8y8pIPU9x%2F%2F5GSKK3DEPZyxLxCUAy2xPOE5xwSMEvNXj%2BmplyCucAPjrjNncdlPuObB%2B7utrsmNSySqkjw%2Bu5u%2BFUds025rfPPw0l3%2BIJqgP%2BfXXvDUK%2FT4UFDZeqrFiH%2FNcifla87n2kdf4T3ICww1R8Yg%3D%3D" rel="nofollow">sig 说明</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=oXykba7n7ijs6YKMfYzEQA%3D%3D.Uh7iIPotdQieR7T0otUEol44V3tQPO0XdfJgAYtKIn8cbTuf05oZb6lBfMA1ZUF1jPr7d1%2BqeAT%2FdaeaNEQoKJWR50%2BBieaA4Asfm38V7%2BYONONbQ7lMW2dzHs3t8gYP" rel="nofollow">Android常见错误码</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=dG4f%2BnhGB828ZzcrGoXkgg%3D%3D.NjdOups2KNuxrYgIaEejr5OWp8VRde231zXWElUxywb8ys58vFUYxr35Rcj%2FKlXQM9llgWHb6%2FURwO9Y4YOG8w%3D%3D" rel="nofollow">包下载地址</a></p></li>
</ul>
<h2>登入问题</h2>
<p><img src="http://lidashuang.u.qiniudn.com/15-8-2/94315585.jpg" alt="" title=""></p>
<blockquote><p>游戏进行手Q登录授权的是提示“该应用非官方正版应用,请去应用宝下载后进行QQ登录。(错误码100044)</p></blockquote>
<p><a href="https://link.segmentfault.com/?enc=uIQybx0tsi9D41WtUHZFaA%3D%3D.LByLxeTI5%2Fl5kloTTlH9AoqCmKsygGyXLQJCmb5SURJ4thVQujPG40sYok3gppEoBLoNJxvUFE%2BOnfUtkZ3E3dX6W2T3uckHSbA66k7Mcro%3D" rel="nofollow">查到是 客户端和管理端的签名不一致</a></p>
<p>还有网上其它人说的</p>
<p><img src="/img/bVmVT8" alt="clipboard.png" title="clipboard.png"><br>需要有后台确认下签名问题和测试问题</p>
<h2>签名问题</h2>
<pre><code>=> {"ret"=>1001, "err_code"=>"1001-1001-0", "msg"=>"请求参数错误(sig error)"}</code></pre>
<p>签名ruby版</p>
<p>参考 <a href="https://link.segmentfault.com/?enc=wX%2F3Og74hRiLBxZjUgaemw%3D%3D.aaztqUwRsfQ36hqUUy1wkU9R2y9EQ33s%2BVrZACrFxH6kLBQfRwHSfQzMTcKJVoncpw%2BGmZa%2BHMyRadIfAt18O9%2Bs30GqLNsuSwQchQ8IBXQ%3D" rel="nofollow">https://github.com/zires/open_qq/blob/master/lib/open_qq/signature.rb</a></p>
<pre><code> def make_source(http_method, url, params)
escape_opt = params.sort_by{|k,v| k.to_s}.map{|kv| "#{kv.first}=#{kv.last}" }.join('&')
"#{http_method}&#{url_escape(url)}&#{url_escape(escape_opt)}"
end
def get_sign(platform, http_method, url, sign_params)
if platform == 'qq'
key = "#{qq_app_key}&"
else
key = "#{wx_app_key}&"
end
signature(key, make_source(http_method, url, sign_params))
end
</code></pre>
<h2>关于回调</h2>
<p><img src="http://lidashuang.u.qiniudn.com/15-8-2/66992273.jpg" alt="" title=""></p>
<p>腾讯是让这样验证订单是否成功的</p>
<blockquote><pre><code> 开发者可根据get_balance_m接口去查询用户的累计充值金额来判断用户的充值变化信息。</code></pre></blockquote>
<h2>切换到release环境</h2>
<p><img src="http://lidashuang.u.qiniudn.com/15-8-3/85562803.jpg" alt="" title=""><br>切换到release环境后需要申请一下正式环境的审核</p>
<pre><code>1.Android assets/msdkconfig.ini 设置 MSDK_URL=http://msdk.qq.com;其他未使用域名用;注释掉。
2.支付客户端调用setEnv方法,设置成release
3.IOS MSDK_URL=http://msdk.qq.com
4.后台所有接口使用http://msdk.qq.com </code></pre>
<p><img src="http://lidashuang.u.qiniudn.com/15-8-3/19265570.jpg" alt="" title=""></p>
<h2>pay_token</h2>
<p>get_balance_m 这个api 微信是要留空的</p>
<pre><code>requires :pay_token, type: String, allow_blank: true, desc: '手Q登录时从手Q登录态中获取的pay_token的值,使用MSDK登录后获取到的eToken_QQ_Pay返回内容就是pay_token; 微信登录时特别注意该参数传空。'</code></pre>
<h2>openkey</h2>
<p>sdk 这边取qqtoken是手q的,wxToken通过getAccessToken获取</p>
<pre><code>if(loginRet.flag == 0){
for (TokenRet tr : loginRet.token) {
switch (tr.type) {
case TokenType.eToken_QQ_Pay:
qqToken = tr.value;
break;
default:
break;
}
}
}
wxToken = loginRet.getAccessToken();</code></pre>
<h2>微信的appid appkey</h2>
<p>支付的时候如果平台是weixin也要用手q的appid和appkey<br><img src="http://lidashuang.u.qiniudn.com/15-8-4/60419451.jpg" alt="" title=""></p>
<h2>审核</h2>
<p>只接1, 2就可以提交审核</p>
<p><img src="http://lidashuang.u.qiniudn.com/15-8-3/95032802.jpg" alt="" title=""></p>
<h2>支付服务端延迟问题</h2>
<p>腾讯的支付是有延迟的,意味着你去请求<code>get_balance_m</code> 这个 api的时候,结果可以是一直不变的,<br>比较玩家是银行卡支付的。一般大约两三分钟</p>
<p>解决方案是,对于<code>get_balance_m</code> 返回不变的情况,加重试逻辑,可以20-30秒重试一次,重试两到三分钟</p>
<p>还有就是腾讯的支付参数,其中有些是有失效时间的,遇到有重试过程中,参数失效的情况。现在没好的解决方案,<br>只能手动补单了</p>
observer note
https://segmentfault.com/a/1190000002465148
2015-01-06T00:10:02+08:00
2015-01-06T00:10:02+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>observer 是Erlang查看系统状态的图形化工具(A GUI tool for observing an erlang system)</p>
<p>在iex 打开是这样的</p>
<pre><code>iex(1)> :observer.start</code></pre>
<p><img src="/img/bVkvsq" alt="图片描述" title="图片描述"></p>
<p>标签页可以查看系统,负载,Application, 进程等</p>
<p>启动的Application状态</p>
<p><img src="/img/bVkvsu" alt="图片描述" title="图片描述"></p>
<p>进程状态</p>
<p><img src="/img/bVkvsB" alt="图片描述" title="图片描述"></p>
Elixir 学习资源
https://segmentfault.com/a/1190000002458978
2015-01-03T13:08:12+08:00
2015-01-03T13:08:12+08:00
lidashuang
https://segmentfault.com/u/lidashuang
3
<h3>Elixir</h3>
<ul>
<li><a rel="nofollow" href="http://elixir-lang.org/">官网</a></li>
<li><a rel="nofollow" href="http://elixir-lang.org/getting_started/1.html">getting started官方入门学习资源</a></li>
<li><a rel="nofollow" href="http://elixir-lang.org/docs/stable/elixir/">官方文档</a></li>
<li><a rel="nofollow" href="https://hex.pm/">hex 包管理系统</a></li>
<li>
<a rel="nofollow" href="http://elixirsips.com/">elixir sips</a> 比较不错视频课程</li>
<li><a rel="nofollow" href="http://elixir-cn.com/">Elixir China 中文论坛</a></li>
<li><a rel="nofollow" href="https://github.com/elixir-lang/elixir/wiki">官方wiki</a></li>
<li><a rel="nofollow" href="http://jeregrine.github.io/elixir-by-example/">Elixir by Example</a></li>
<li><a rel="nofollow" href="https://github.com/h4cc/awesome-elixir">Awesome Elixir</a></li>
<li><a rel="nofollow" href="http://elixirquiz.github.io/">Elixir Quiz 通过做题学习Elixir</a></li>
</ul>
<h3>中文资料翻译</h3>
<ul>
<li>
<a rel="nofollow" href="https://github.com/straightdave/programming-elixir">Elixir编程</a> 图灵社区 <a rel="nofollow" href="http://www.ituring.com.cn/minibook/10831">Elixir中文入门教程</a>
</li>
<li><a rel="nofollow" href="https://github.com/straightdave/elixir_adv">Elixir进阶</a></li>
</ul>
<h3>宏</h3>
<ul>
<li><a rel="nofollow" href="http://blog.8thlight.com/patrick-gombert/2013/11/26/lispy-elixir.html">Lispy Elixir</a></li>
<li><a rel="nofollow" href="http://www.theerlangelist.com/2014/06/understanding-elixir-macros-part-1.html">The Erlangist: Understanding Elixir Macros 有五部分</a></li>
</ul>
<h3>书籍</h3>
<ul>
<li><a rel="nofollow" href="https://pragprog.com/book/elixir/programming-elixir">Programming Elixir: Functional |> Concurrent |> Pragmatic |> Fun</a></li>
</ul>
<h3>框架, 库</h3>
<ul>
<li><a rel="nofollow" href="https://github.com/phoenixframework/phoenix">phoenix web框架</a></li>
</ul>
<h3>Phoenix</h3>
<p><a rel="nofollow" href="https://github.com/lancehalvorsen/phoenix-guides">Phoenix web开发教程</a><br><a rel="nofollow" href="https://github.com/slogsdon/web-development-using-elixir">Web Development with Elixir</a></p>
<h2>TODO</h2>
mysql innobackupex restore note
https://segmentfault.com/a/1190000000718525
2014-10-11T16:51:17+08:00
2014-10-11T16:51:17+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>innobackupex 恢复遇到问题</h2>
<h3>error: 'Access denied for user 'debian-sys-maint'@'localhost' (using password: YES)'</h3>
<p>解决:</p>
<p><a rel="nofollow" href="http://stackoverflow.com/questions/11644300/access-denied-for-user-debian-sys-maint">http://stackoverflow.com/questions/11644300/access-denied-for-user-debian-sys-maint</a></p>
<p>That’s because Debian has a MySQL account debian-sys-maint used for switching on/off and checking status. The password for that user should be the same as stored in /etc/mysql/debian.cnf. The file looks like this:</p>
<pre><code># Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host = localhost
user = debian-sys-maint
password = <password>
socket = /var/run/mysqld/mysqld.sock
[mysql_upgrade]
host = localhost
user = debian-sys-maint
password = <password>
socket = /var/run/mysqld/mysqld.sock
basedir = /usr
</code></pre>
<p>If the password doesn't match (for example because you changed it manually) the init script won't work anymore. You should set the password according to the file. So</p>
<pre><code>mysql -u root -p
# Then type MySQL root password
GRANT ALL PRIVILEGES ON *.* TO 'debian-sys-maint'@'localhost' IDENTIFIED BY '<password>';
</code></pre>
<h3>innobackupex: Error: Original data directory '/var/lib/mysql' is not empty!</h3>
<h4>
<code>1.5.1</code>的一个bug</h4>
<ul>
<li><a rel="nofollow" href="http://willvvv.iteye.com/blog/1561124">http://willvvv.iteye.com/blog/1561124</a></li>
<li><a rel="nofollow" href="http://www.percona.com/blog/2011/12/19/percona-xtrabackup-1-6-4/">http://www.percona.com/blog/2011/12/19/percona-xtrabackup-1-6-4/</a></li>
</ul>
<p>源里的xtrabackup 版本是<code>2.2.5</code>,而innobackupex 版本是<code>1.5.1</code>, 一开始的解决方法是直接删除<code>/var/lib/mysql</code></p>
zabbix web监控添加 note to self
https://segmentfault.com/a/1190000000704260
2014-10-01T00:31:26+08:00
2014-10-01T00:31:26+08:00
lidashuang
https://segmentfault.com/u/lidashuang
2
<h2>web监控</h2>
<p>通过<code>Configuration</code> => <code>host</code> => <code>web</code> 添加</p>
<p><img src="/img/bVc7mU" alt="1412045209838.png?resizeSmall&width=832"></p>
<h3>create scenario</h3>
<p>点击 create scenario</p>
<p><img src="/img/bVc7m1" alt="1412045295473.png?resizeSmall&width=832"></p>
<h3>settings</h3>
<p>开启设置</p>
<p><img src="/img/bVc7m3" alt="1412045585624.png?resizeSmall&width=832"></p>
<h3>steps</h3>
<p>steps是添加具体监控项目,在这里设置要监控的url,http 状态码</p>
<p><img src="/img/bVc7m4" alt="1412045802518.png?resizeSmall&width=832"></p>
<h3>添加完成</h3>
<p>add之后,就可以看到添加完成的</p>
<p><img src="/img/bVc7m5" alt="1412045856788.png?resizeSmall&width=832"></p>
<h2>报警设置</h2>
<p>主要监控http的status code,不是200的时候报警, <code>Configuration</code> => <code>host</code> => <code>Triggers</code></p>
<p><img src="/img/bVc7m6" alt="1412046370164.png?resizeSmall&width=832"></p>
<h3>create trigger</h3>
<p>创建触发器</p>
<p><img src="/img/bVc7m7" alt="1412046691172.png?resizeSmall&width=832"></p>
<h3>监控response code</h3>
<p><img src="/img/bVc7m8" alt="1412046985865.png?resizeSmall&width=832"></p>
<h3>设置条件</h3>
<p><img src="/img/bVc7m9" alt="1412047073826.png?resizeSmall&width=832"></p>
<p>点击<code>Insert</code></p>
<p><img src="/img/bVc7na" alt="1412047399726.png?resizeSmall&width=832"></p>
<p><strong>表达式值那改成200</strong></p>
<p>Severity级别我设置成了<code>High</code></p>
<p>save之后可以看到成功添加的trigger</p>
<p><img src="/img/bVc7nb" alt="1412047517357.png?resizeSmall&width=832"></p>
zabbix slack报警
https://segmentfault.com/a/1190000000704239
2014-10-01T00:16:50+08:00
2014-10-01T00:16:50+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>创建 media类型</h2>
<p><img src="/img/bVc7mv" alt="1412091364641.png?resizeSmall&width=832" title="1412091364641.png?resizeSmall&width=832"></p>
<p>创建Type是script,<br>script name 是脚本名字<br>script要放在<code>AlertScriptsPath</code>里,这个可以设置,也可以用默认值</p>
<p><img src="/img/bVc7my" alt="1412091567630.png?resizeSmall&width=832" title="1412091567630.png?resizeSmall&width=832"></p>
<h2>先创建个user用于发提醒用</h2>
<p><img src="/img/bVc7mz" alt="1412066748868.png?resizeSmall&width=832" title="1412066748868.png?resizeSmall&width=832"></p>
<h3>设置用户</h3>
<p><img src="/img/bVc7mA" alt="1412066858888.png?resizeSmall&width=832" title="1412066858888.png?resizeSmall&width=832"></p>
<h3>在用户Media选项卡,添加Media类型</h3>
<p>send_to 是slack channel的token</p>
<p><img src="/img/bVc7mB" alt="1412067045766.png?resizeSmall&width=832" title="1412067045766.png?resizeSmall&width=832"></p>
<h3>
<code>user type</code> 我直接选了 super admin</h3>
<p><img src="/img/bVc7mF" alt="1412067240210.png?resizeSmall&width=832" title="1412067240210.png?resizeSmall&width=832"></p>
<h2>创建 action</h2>
<p><img src="/img/bVc7mG" alt="1412067286945.png?resizeSmall&width=832" title="1412067286945.png?resizeSmall&width=832"></p>
<h3>配置</h3>
<p><img src="/img/bVc7mH" alt="1412067345265.png?resizeSmall&width=832" title="1412067345265.png?resizeSmall&width=832"></p>
<h3>在operations配置发送者</h3>
<p><img src="/img/bVc7mJ" alt="1412067496430.png?resizeSmall&width=832" title="1412067496430.png?resizeSmall&width=832"></p>
<h3>加完后</h3>
<p><img src="/img/bVc7mK" alt="1412067535850.png?resizeSmall&width=832" title="1412067535850.png?resizeSmall&width=832"></p>
<p>可以在<code>Administration => Audit => Actions</code> 查看脚本执行状态 </p>
<p><img src="/img/bVc7mL" alt="1412077461788.png?resizeSmall&width=832" title="1412077461788.png?resizeSmall&width=832"></p>
<h2>添加脚本</h2>
<p>slack自定义脚本要处理三个参数 send_to, subject, message </p>
<p>写了golang版本的<br><a href="https://link.segmentfault.com/?enc=ofT4LE5k7KM%2B5DwGv0UoRw%3D%3D.4WbfIqbI17LT6fo1PkLrXJX5Nvc2rlxMH%2Fg7Ly9p%2BABKxdqGlJyFvXLBQbBNK4r%2B" rel="nofollow">https://github.com/lidashuang...</a><br>编译完放到AlertScriptsPath就可以了,名字和添加media type里的设置一样</p>
<p>url:</p>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=rOJT6ljJDNfAIsLBb6XNiQ%3D%3D.l6%2ByQ0Tut6QZIL%2BIlZYsf%2BUhXPMLMxRe0UCte1mIn%2B2XF8O2nBslyFekMt0n4vqm%2ByE9sdQHKWvssmyEL6gp%2Fr%2BRruHUXadf9bwDCZhrkqwLpSE%2Bb3zBDvIMVHL96iq0" rel="nofollow">https://www.zabbix.com/docume...</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=es0FyYQ4oRf5U6tGuRM1xA%3D%3D.qwWg3slt87jVyVBvFwxFMWzmi6n6C%2BCgl3ziEuof5LF8S5GJ9ZSAg7343A1%2BvX0pKwnVWnEgkn6Z79II4IPozAE5UzlC27aYzZqqIgsLpBY%3D" rel="nofollow">https://www.zabbix.com/docume...</a></p></li>
</ul>
Elasticsearch ubuntu 14.04 安装 note to self
https://segmentfault.com/a/1190000000703282
2014-09-30T14:08:15+08:00
2014-09-30T14:08:15+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>jdk版本: <code>jdk-7u51-linux-x64.gz</code></p>
<p>elasticsearch版本: <code>elasticsearch-1.3.2.deb</code></p>
<h2>安装java环境</h2>
<p>重命名为<code>default-java</code>,目录位置 <code>/usr/lib/jvm/</code>,因为es启动脚本在这个位置找java,所以就直接改成这样了</p>
<pre><code>deploy@db01:~$ mv jdk1.7.0_51/ default-java
deploy@db01:~$ sudo mv default-java/ /usr/lib/jvm/
</code></pre>
<p><code>.profile</code>配置, 使<code>deploy</code> 用户部署,添加到deploy下的.profile就可以</p>
<pre><code>export JAVA_HOME=/usr/lib/jvm/default-java
export PATH=$PATH:$JAVA_HOME/bin:~/bin
</code></pre>
<h2>elasticsearch安装</h2>
<pre><code>deploy@db01:~$ sudo dpkg -i elasticsearch-1.3.2.deb
deploy@db01:~$ sudo /etc/init.d/elasticsearch start
</code></pre>
<p>添加到默认启动:</p>
<pre><code>deploy@db01:~/es$ sudo update-rc.d elasticsearch defaults 95 10
</code></pre>
<h2>测试</h2>
<p><img src="/img/bVc67O" alt="图片描述"></p>
<h2>安装head插件,可选</h2>
<p>使用<code>root</code>用户安装的,<code>.profile</code>也要配置java</p>
<pre><code>root@db01:/usr/share/elasticsearch# bin/plugin -install mobz/elasticsearch-head
</code></pre>
<p>查看地址</p>
<p><a rel="nofollow" href="http://121.40.85.12:9200/_plugin/head/">http://121.40.85.12:9200/_plugin/head/</a></p>
<h2>分词使用<code>ansj</code>
</h2>
<blockquote>
<p>Notice: 使用最新1.x.1</p>
</blockquote>
<p>安装:</p>
<pre><code>./bin/plugin -u http://maven.ansj.org/org/ansj/elasticsearch-analysis-ansj/1.x.1/elasticsearch-analysis-ansj-1.x.1-release.zip -i ansj
</code></pre>
<p>配置文件示例</p>
<p><a rel="nofollow" href="https://gist.github.com/lidashuang/12029bf6525475d722b6">https://gist.github.com/lidashuang/12029bf6525475d722b6</a>'</p>
<p>重启</p>
<pre><code>deploy@db01:/etc/elasticsearch$ sudo service elasticsearch restart
</code></pre>
<h2>信息</h2>
<pre><code>主目录: /usr/share/elasticsearch
配置文件: /etc/elasticsearch/elasticsearch$.yml
日志文件: /var/log/elasticsearch/elasticsearch.log
</code></pre>
ubuntu server 14.04 添加swap分区
https://segmentfault.com/a/1190000000670332
2014-09-16T11:08:30+08:00
2014-09-16T11:08:30+08:00
lidashuang
https://segmentfault.com/u/lidashuang
3
<p>阿里云主机默认没有swap分区</p>
<p>先看看是否有swap分区</p>
<pre><code>deploy@web03:~$ sudo swapon -s
[sudo] password for deploy:
Filename Type Size Used Priority
</code></pre>
<p>这样就是没有</p>
<p>也可以通过free命令</p>
<pre><code>deploy@web03:~$ free -m
total used free shared buffers cached
Mem: 3951 504 3447 0 151 199
-/+ buffers/cache: 153 3798
Swap: 0 0 0
</code></pre>
<p>swap total是0</p>
<p>创建swapfile</p>
<pre><code>sudo fallocate -l 4G /swapfile
</code></pre>
<p>设置/swapfile权限</p>
<pre><code>sudo chmod 600 /swapfile
</code></pre>
<p>设置swapfile</p>
<pre><code>sudo mkswap /swapfile
</code></pre>
<p>启用</p>
<pre><code>sudo swapon /swapfile
</code></pre>
<p>查看</p>
<pre><code>deploy@web03:~$ sudo swapon -s
[sudo] password for deploy:
Filename Type Size Used Priority
/swapfile file 1048572 0 -1
</code></pre>
<p>修改fstab</p>
<pre><code>sudo vi /etc/fstab
</code></pre>
<p>添加</p>
<pre><code>/swapfile none swap sw 0 0
</code></pre>
<p>参考</p>
<p><a rel="nofollow" href="https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04">https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04</a></p>
dsh - Distributed shell, or dancer's shell
https://segmentfault.com/a/1190000000666902
2014-09-13T11:14:44+08:00
2014-09-13T11:14:44+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>一个在多台机器上批量执行命令的工具, 支持分组</p>
<p>简单例子</p>
<pre><code> $ dsh -a -r ssh -c uptime
10:53:36 up 44 days, 1:01, 0 users, load average: 0.00, 0.01, 0.05
10:53:37 up 7 days, 19:34, 0 users, load average: 0.00, 0.01, 0.05
10:53:37 up 47 days, 1:27, 0 users, load average: 0.00, 0.01, 0.05
10:53:37 up 44 days, 19:29, 0 users, load average: 0.00, 0.01, 0.05
10:53:37 up 44 days, 1:01, 0 users, load average: 0.00, 0.01, 0.05
10:53:37 up 18 days, 20:39, 0 users, load average: 0.00, 0.01, 0.05
10:53:37 up 49 days, 19:33, 0 users, load average: 0.00, 0.01, 0.05
</code></pre>
<p>在多台机器上执行uptime命令。</p>
<p>dsh可以从<code>~/.dsh/machines.list</code>下面读服务器列表</p>
<pre><code>$ cat .dsh/machines.list
deploy@gitlab
deploy@puppet
deploy@php-staging
deploy@web01
deploy@web02
deploy@web03
deploy@db01
</code></pre>
<p>也可以使用-m选项指定主机</p>
<pre><code>$ dsh -r ssh -m deploy@web01,deploy@web02 -c uptime
11:16:44 up 44 days, 19:52, 0 users, load average: 0.00, 0.01, 0.05
11:16:44 up 44 days, 1:24, 0 users, load average: 0.23, 0.08, 0.06
</code></pre>
<p>主配置文件是/etc/dsh/dsh.conf 或者是~/.dsh/dsh.conf</p>
<p>比如</p>
<pre><code>$ cat .dsh/dsh.conf
remoteshell = ssh
</code></pre>
<p>设置remoteshell为ssh</p>
Elixir web frameworks
https://segmentfault.com/a/1190000000660472
2014-09-06T12:09:45+08:00
2014-09-06T12:09:45+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>Elixir社区现有和几个web开发框架:</p>
<ul>
<li><a rel="nofollow" href="https://github.com/dynamo/dynamo">dynamo</a></li>
<li><a rel="nofollow" href="https://github.com/phoenixframework/phoenix">phoenix</a></li>
<li><a rel="nofollow" href="https://github.com/sugar-framework/sugar">sugar</a></li>
<li><a rel="nofollow" href="https://github.com/elixir-web/weber">weber</a></li>
</ul>
<p>另外,还有<a rel="nofollow" href="https://github.com/elixir-lang/plug">plug</a><br>
plug不是一个web框架,是一个类似Ruby中Rack的基础库,为中间件提供统一的api</p>
<hr>
<h2>状态</h2>
<h3>dynamo</h3>
<p>dynamo 是Elixir作者开发的,不过作者说项目已经进入维护状态,不建议选择<br>
另外 dynamo 比 plug 项目要早,不是基于plug项目。</p>
<h3>sugar</h3>
<p>这个框架也是基于plug的,不过作者已经把这个项目作为一个<code>educational project</code>,作者还建议你如果在生产环境中使用 phoenix</p>
<h3>phoenix</h3>
<p>phoenix 是Elixir社区关注度比较高的web框架,基于plug。</p>
<p>特点:</p>
<ul>
<li>DSL Routing, 支持resource</li>
<li>支持websocket</li>
<li>code reload</li>
<li>提供了Topic的概念,简单的publish/subscribe机制</li>
<li>...</li>
</ul>
<p>具体可以看 <a rel="nofollow" href="https://github.com/phoenixframework/phoenix#feature-roadmap">Feature Roadmap</a></p>
<h3>weber</h3>
<p>基于plug,关注度感觉不如 phoenix, 目前为止感觉 phoenix开发上更活跃一些。</p>
Elixir alias, require, import
https://segmentfault.com/a/1190000000659399
2014-09-04T23:12:51+08:00
2014-09-04T23:12:51+08:00
lidashuang
https://segmentfault.com/u/lidashuang
2
<h2>alias</h2>
<pre><code>alias(module, opts)
</code></pre>
<p>alias 顾名思义就是设置别名。<br>
比如</p>
<pre><code>defmodule Math do
alias MyKeyword, as: Keyword
end
</code></pre>
<p>MyKeyword设置别名为Keyword,访问Keyword就相当于MyKeyword,如果要访问原始的<br>
Keyword模块的话需要在<code>Elixir</code>名称空间下。</p>
<pre><code>Keyword.values #=> uses MyKeyword.values
Elixir.Keyword.values #=> uses Keyword.values
</code></pre>
<p>当然 <code>as</code> 选项也不是必须的, 不加as选项的话最后一部分会作为模块名</p>
<pre><code>alias Foo.Bar.Baz
</code></pre>
<p>相当于</p>
<pre><code>alias Foo.Bar.Baz, as: Baz
</code></pre>
<h2>import</h2>
<pre><code>import(module, opts)
</code></pre>
<p>作用是导入其它模块的函数</p>
<p>比如</p>
<pre><code>iex> import List
iex> flatten([1, [2], 3])
[1,2,3]
</code></pre>
<p>默认是导入模块中所有public的函数,当然也有<code>only</code>选项和<code>except</code>选项可以使用。</p>
<p>比如只导入List模块的函数或者宏</p>
<pre><code>import List, only: :functions
import List, only: :macros
</code></pre>
<p>也可以指定函数</p>
<pre><code>import List, only: [flatten: 1]
import String, except: [split: 2]
</code></pre>
<p>函数是要有arities(参数个数)的</p>
<p>另外,这个例子中,except声明包含前面only声明的函数<code>flatten</code>,结果是这个函数不会被导入。</p>
<pre><code>import List, only: [flatten: 1, keyfind: 3]
import List, except: [flatten: 1]
</code></pre>
<h2>require</h2>
<pre><code>require(module, opts)
</code></pre>
<p>require作用是编译加载给定的模块<br>
如果想使用另一个模块的宏,你必须使用require 模块,然后才能调用,否则会报异常</p>
<pre><code>defmodule Math do
require MyMacros
MyMacros.if do_something, it_works
end
</code></pre>
<p>因为宏展开是发生在编译时的,使用require可以确保模块被编译</p>
<p>例子:</p>
<pre><code>iex> Integer.odd?(3)
** (CompileError) iex:1: you must require Integer before invoking the macro Integer.odd?/1
iex> require Integer
nil
iex> Integer.odd?(3)
true
</code></pre>
<p><code>Integer.odd?/1</code> 是一个宏,要想调用必须先require</p>
Ruby class_eval and instance_eval notes
https://segmentfault.com/a/1190000000610006
2014-07-18T13:54:44+08:00
2014-07-18T13:54:44+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>区别:</p>
<p><code>instance_eval</code>的接受者为对象,为对象创建了一个<code>单键方法</code>,class_eval接受者为一个类时,定义一个类的实例方法</p>
<p>当对象是类对象时,该方法成为类方法,因为类也是Class的实例</p>
<h2>class_eval</h2>
<p>我们有个Game类</p>
<pre><code class="lang-ruby">class Game
attr_accessor :name, :owner
def initialize(name)
@name = name
end
end
</code></pre>
<p>使用<code>class_eval</code>添加实例方法</p>
<pre><code class="lang-ruby">Game.class_eval do
def hello
puts "helloworld"
end
end
</code></pre>
<p>我们定义了一个实例方法hello,打印helloworld出来</p>
<pre><code>irb(main):017:0> Game.new("asdf").hello
helloworld
=> nil
</code></pre>
<p>也可以用<code>class_eval</code>添加类方法,比如对Game类添加<code>find_by_owner</code>的类方法</p>
<p>代码:</p>
<pre><code class="lang-ruby">Game.class_eval do
def self.find_by_owner(name)
end
end
</code></pre>
<p>没有self,则class_eval添加的是实例方法</p>
<h2>instance_eval</h2>
<p>使用<code>instance_eval</code>设置对象的实例属性</p>
<pre><code class="lang-ruby">contra_game = Game.new('Contra')
</code></pre>
<p>设置<code>contra_game</code>对象的owner实例属性为<code>Alice</code></p>
<p>代码:</p>
<pre><code class="lang-ruby">contra_game = Game.new('Contra')
contra_game.instance_eval do
@owner = "Alice"
end
</code></pre>
<p>如果对Game类使用<code>instance_eval</code>定义的方法就是Game类的类方法,Game是<code>Class</code>的实例,Game的<code>单键方法</code>就是<code>Game</code>的<code>类方法</code></p>
<p>比如这个例子中,定义<code>find_by_owner</code>的类方法</p>
<pre><code class="lang-ruby">irb(main):018:0> Game.instance_eval do
irb(main):019:1* def find_by_owner(name)
irb(main):020:2> puts name
irb(main):021:2> end
irb(main):022:1> end
=> nil
</code></pre>
<p>就可以调用定义好的类方法了</p>
<pre><code class="lang-ruby">irb(main):023:0> Game.find_by_owner("who")
who
=> nil
</code></pre>
<h3>在对象的上下文中执行一个block</h3>
<p>使用<code>instance_eval</code>和<code>block_given?</code> 在Game类的构造函数中实现接受一个可选的block,并调用block</p>
<pre><code class="lang-ruby">class Game
def initialize(&block)
instance_eval(&block) if block_given?
end
def owner(name=nil)
if name
@owner = name
else
@owner
end
end
end
</code></pre>
<p>links</p>
<ul>
<li><a rel="nofollow" href="http://ruby-china.org/topics/2442">http://ruby-china.org/topics/2442</a></li>
<li><a rel="nofollow" href="http://stackoverflow.com/questions/4409023/rubys-def-and-instance-eval-vs-class-eval">http://stackoverflow.com/questions/4409023/rubys-def-and-instance-eval-vs-class-eval</a></li>
</ul>
Ruby methods notes
https://segmentfault.com/a/1190000000609413
2014-07-17T22:19:27+08:00
2014-07-17T22:19:27+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<h2>使用define_method定义方法</h2>
<p>这段代码中,Game类中的以runs_on开头的方法,可以用<a rel="nofollow" href="http://apidock.com/ruby/Module/define_method">define_method</a>重构, <a rel="nofollow" href="http://apidock.com/ruby/Module/define_method">define_method</a></p>
<pre><code class="lang-ruby">class Game
SYSTEMS = ['SNES', 'PS1', 'Genesis']
attr_accessor :name, :year, :system
def runs_on_snes?
self.system == 'SNES'
end
def runs_on_ps1?
self.system == 'PS1'
end
def runs_on_genesis?
self.system == 'Genesis'
end
end
</code></pre>
<p>重构后的代码,不必再定义一坨坨的方法</p>
<pre><code class="lang-ruby">class Game
SYSTEMS = ['SNES', 'PS1', 'Genesis']
attr_accessor :name, :year, :system
SYSTEMS.each do |system|
define_method "runs_on_#{system.downcase}?" do
self.system = system
end
end
end
</code></pre>
<h2>send 调用方法</h2>
<p>ruby提供了<a rel="nofollow" href="http://apidock.com/ruby/Object/send">send</a>方法,可以实现对某个对象方法的调用,send方法中第一个参数是方法名,其余的是参数列表</p>
<pre><code class="lang-ruby">library = Library.new(GAMES)
library.list
library.emulate("Contra")
game = library.find("Contra")
</code></pre>
<p>使用send方法就是这样,ruby还提供了<a rel="nofollow" href="http://apidock.com/ruby/Object/public_send">public_send</a>方法,public_send方法顾名思义只用于public的方法</p>
<pre><code class="lang-ruby">library = Library.new(GAMES)
library.send(:list)
library.send(:emulate, "Contra")
game = library.send(:find, "Contra")
</code></pre>
<h2>method对象</h2>
<p>通常我们是这样调用方法的</p>
<pre><code class="lang-ruby">library = Library.new(GAMES)
library.list
library.emulate("Contra")
</code></pre>
<p>也可以获得method对象,并使用method的call方法调用,比如上面的代码可以这样写,使用method方法获取一个Method对象</p>
<pre><code class="lang-ruby">library = Library.new(GAMES)
list = library.method(:list)
list.call
emulate = library.method(:emulate)
emulate.call("Contra")
</code></pre>
<h2>使用define_method和send重构</h2>
<p>需重构的代码,需要把each,map,select重构,这几个方法非常的类似,可以用define_method代替</p>
<pre><code class="lang-ruby">class Library
attr_accessor :games
def each(&block)
games.each(&block)
end
def map(&block)
games.map(&block)
end
def select(&block)
games.select(&block)
end
end
</code></pre>
<p>第一步,需要我们定义三个不同的方法,如下</p>
<pre><code class="lang-ruby">class Library
attr_accessor :games
[:each, :map, :select].each do |method|
define_method method do |&block|
end
end
end
</code></pre>
<p>然后需要用send方法调用对象的不同的方法,可以这样写</p>
<pre><code class="lang-ruby">define_method method do |&block|
game.send(method, &block)
end
</code></pre>
<p>重构完就是这样的样子</p>
<pre><code class="lang-ruby">class Library
attr_accessor :games
[:each, :map, :select].each do |method|
define_method(method) do |&block|
games.send(method, &block)
end
end
end
</code></pre>
<h2>method_missing</h2>
<p>当调用一个对象不存在的方法时,会触发该对象的method_missing方法,比如</p>
<p>我们定义一个Library类,创建对象并调用test_method("arg1","arg2")方法,触发了一个<br>
NoMethodError的异常</p>
<pre><code class="lang-ruby">irb(main):001:0> class Library
irb(main):002:1> end
=> nil
irb(main):003:0> Library.new.test_method("arg1","arg2")
NoMethodError: undefined method `test_method' for #<Library:0x2745678>
from (irb):3
from D:/Ruby200/bin/irb:12:in `<main>'
</code></pre>
<p>可以覆盖method_missing方法,来捕获NoMethodError异常,比如</p>
<pre><code class="lang-ruby">class Library
def method_missing(method_name, *args)
puts "call method: #{method_name}, args is: #{args}"
end
end
</code></pre>
<p>继续调用test_method方法,得到的结果</p>
<pre><code class="lang-ruby">irb(main):013:0> Library.new.test_method("arg1","arg2")
call method: test_method, args is: ["arg1", "arg2"]
=> nil
</code></pre>
<h2>method_missing, define_method和send的例子</h2>
<p>实现Library类中的method_missing方法,使用define_method和method_missing动态的定义方法,需要定义的实例方法在SYSTEM数组里,使用self.class.class_eval动态的添加实例方法,define_method定义方法,比如调用librar.pc时,触发method_missing,pc在SYSTEM数组里,然后定义pc方法,并调用find_by_system方法</p>
<pre><code class="lang-ruby">class Library
SYSTEMS = ['arcade', 'atari', 'pc']
attr_accessor :games
def method_missing(name, *args)
system = name.to_s
if SYSTEMS.include?(system)
self.class.class_eval do
define_method(system) do
find_by_system(system)
end
end
send(system)
else
super
end
end
private
def find_by_system(system)
games.select { |game| game.system == system }
end
end
</code></pre>
<h2>respond_to?</h2>
<p>在ruby中,可以使用respond_to?判断某个对象是否可以响应某个方法</p>
<p>比如"hello" 能够响应upcase方法</p>
<pre><code class="lang-ruby">irb(main):017:0> "hello".respond_to?(:upcase)
=> true
</code></pre>
<p>当然,我们也可以自己定义respond_to?方法,比如Library类例子中,我们动态的定义了'arcade', 'atari', 'pc'方法,但是Library类的实例是不会响应动态定义的方法的。我们需要自己定义Library类的respond_to?方法</p>
<p>代码</p>
<pre><code class="lang-ruby">def respond_to?(name)
SYSTEMS.include?(name.to_s) || super
end
</code></pre>
<p>当方法名在SYSTEMS数组是,返回true,代表响应相应的方法</p>
<p>other posts<br>
- <a rel="nofollow" href="http://www.alfajango.com/blog/method_missing-a-rubyists-beautiful-mistress/">http://www.alfajango.com/blog/method_missing-a-rubyists-beautiful-mistress/</a><br>
- <a rel="nofollow" href="http://ruby-china.org/topics/3434">http://ruby-china.org/topics/3434</a><br>
- <a rel="nofollow" href="http://ruby-china.org/topics/4313">http://ruby-china.org/topics/4313</a></p>
Ruby Blocks, Procs, & Lambdas
https://segmentfault.com/a/1190000000608566
2014-07-17T13:25:57+08:00
2014-07-17T13:25:57+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>之前看过codeschool的rubybits的教程,但是看的比较快,掌握的不是太扎实,重新复习下rubybits2的课程,记录一些笔记。</p>
<h2>Proc</h2>
<h3>创建Proc</h3>
<p>可以使用 <strong>Proc.new</strong> 创建一个Proc类的实例,Proc.new返回一个Proc类的对象。比如在codeschool rubybit2 channenge1的main.rb文件里使用Proc.new创建的print_details,这个proc接受一个game实例的参数并打印信息。</p>
<pre><code class="lang-ruby">library = Library.new(GAMES)
print_details = Proc.new do |game|
puts "#{game.name} (#{game.system}) - #{game.year}"
end
library.exec_game("Contra", print_details)
</code></pre>
<p><strong>library.exec_game("Contra", print_details)</strong> 这句代码中proc可以直接作为参数传递。</p>
<h3>调用proc</h3>
<p>在library.rb的exec_game方法中接受传递的参数并作用于game实例,其中exec_game方法中action是显示传递的proc,每个proc都有一个call方法,调用call方法就会执行这个proc。</p>
<p>代码:</p>
<pre><code class="lang-ruby">class Library
attr_accessor :games
def initialize(games)
@games = games
end
def exec_game(name, action)
game = games.detect { |game| game.name = name }
action.call(game) if game.name == "Contra"
end
end
</code></pre>
<h2>使用lambda 重构</h2>
<p>上面例子中的Proc.new可以用lambda代替</p>
<pre><code class="lang-ruby">print_details = lambda {|game| puts "#{game.name} (#{game.system}) - #{game.year}" }
</code></pre>
<h3>Ruby 新的lanbda语法</h3>
<p>ruby1.9后支持的新的语法</p>
<pre><code class="lang-ruby">print_details = ->(game){ puts "#{game.name} (#{game.system}) - #{game.year}" }
</code></pre>
<p>改变<br>
- lambda关键字改为 -><br>
- 参数在->后面并在括号里,在大括号之外</p>
<h3>proc 到 block</h3>
<p>使用<code>&proc</code>的形式将proc作为blcok传递<br>
例如</p>
<pre><code class="lang-ruby">library = Library.new(GAMES)
library.each { |game| puts "#{game.name} (#{game.system}) - #{game.year}" }
</code></pre>
<p>可以这样</p>
<pre><code class="lang-ruby">library = Library.new(GAMES)
print_detail = Proc.new do |game|
puts "#{game.name} (#{game.system}) - #{game.year}"
end
library.each(&print_detail)
</code></pre>
<h3>symbol的to_proc</h3>
<p>经常看到这样的写法</p>
<pre><code class="lang-ruby">def names
games.map(&:name)
end
</code></pre>
<p>ruby允许用&开头的符号作为代码块被传递到一个迭代器中,这段代码中将符号(symbol) :name转换成proc传递给map,等价于</p>
<pre><code class="lang-ruby"> def names
games.map { |game| game.name }
end
</code></pre>
<h3>optional blocks</h3>
<p>ruby中block可以是可选的参数,ruby提供了block_given? 方法让我们判断是否有block传递,比如这段代码中的list方法,<br>
list有block传递的时候,调用block,没有的调用game.name</p>
<pre><code class="lang-ruby">class Library
attr_accessor :games
def initialize(games)
@games = games
end
def list
games.each do |game|
if block_given?
puts yield game
else
puts game.name
end
end
end
</code></pre>
<h3>proc和lambda的区别</h3>
<ul>
<li>lambda会进行参数检查,proc不会</li>
<li>proc执行return语句,它会从代码块所在的方法中返回,而lambda仅仅从lambda自身返回,会继续执行方法后面的语句</li>
</ul>
<p>others:</p>
<ol>
<li><a rel="nofollow" href="http://rubyer.me/blog/917/">理解Ruby的4种闭包:blocks, Procs, lambdas 和 Methods</a></li>
<li><a rel="nofollow" href="http://ruby-windy.iteye.com/blog/1197302">理解Ruby中block的本质</a></li>
</ol>
Ruby extend self
https://segmentfault.com/a/1190000000608378
2014-07-17T10:57:50+08:00
2014-07-17T10:57:50+08:00
lidashuang
https://segmentfault.com/u/lidashuang
0
<p>在<a rel="nofollow" href="https://github.com/laserlemon/figaro/blob/master/lib/figaro.rb#L5-L30">figaro</a>这个项目中看到 <code>extend self</code>的用法.</p>
<pre><code>module Util
extend self
def double(i)
i * 2
end
end
puts Util.double(4) #=> 8
</code></pre>
<p>module是不能实例化的,<code>extend self</code> 会把module的实例方法作为类方法暴露,<br><code>self</code>在例子中是<code>Util</code>,在Ruby中,<code>Object.extend</code>方法使指定模块的实例方法成为接收者的<br>
单键方法. 接收者是Class实例, 这些方法就成为类方法.</p>
<p>资料:</p>
<ul>
<li><a rel="nofollow" href="http://blog.bigbinary.com/2012/06/28/extend-self-in-ruby.html">http://blog.bigbinary.com/2012/06/28/extend-self-in-ruby.html</a></li>
<li><a rel="nofollow" href="http://stackoverflow.com/questions/1733124/ruby-extend-self">http://stackoverflow.com/questions/1733124/ruby-extend-self</a></li>
</ul>
Elixir 模块属性
https://segmentfault.com/a/1190000000592316
2014-07-01T02:19:30+08:00
2014-07-01T02:19:30+08:00
lidashuang
https://segmentfault.com/u/lidashuang
1
<p><a rel="nofollow" href="http://elixir-lang.org/docs/stable/elixir/">Module Package Docs</a></p>
<h2>模块属性</h2>
<h3>@after_compile</h3>
<p>编译后hook, 后边加的参数是 模块名或者元组<code>{<module>, <function atom>}</code>,如果是使用元组<code>{<module>, <function atom>}</code>作为参数,函数参数是模块的环境变量和字节码(bytecode), 如果仅是模块名,after_compile hook 会调用模块的<code>__after_compile__/2</code>函数。</p>
<p>例子</p>
<pre><code>defmodule M do
@after_compile __MODULE__
def __after_compile__(env, _bytecode) do
IO.inspect env
end
end
</code></pre>
<h3>@before_compile</h3>
<p>在模块编译这前的hook, 参数是module或者 元组 {<module>, <function/macro atom>},如果参数只是模块名则调用<code>__before_compile__/1</code> 函数或者宏。函数或宏接受module的环境变量(ENV)作为参数。和<code>@after_compile</code>不一样的是<br>
回调函数必须是另一个模块内,因为在编译时当前模块还不存在。</module></p>
<p>例子:</p>
<pre><code>defmodule A do
defmacro __before_compile__(_env) do
quote do
def hello, do: "world"
end
end
end
defmodule B do
@before_compile A
end
</code></pre>
<h3>@behaviour</h3>
<p>定义otp的行为模式</p>
<pre><code>defmodule M do
@behaviour gen_event
# ...
end
</code></pre>
<h3>@compile</h3>
<p>设置编译参数。</p>
<pre><code>defmodule M do
@compile {:inline, myfun: 1}
def myfun(arg) do
to_string(arg)
end
end
</code></pre>
<h3>@doc</h3>
<p>设置函数或者宏的文档,可以是string或heredoc或false</p>
<pre><code> defmodule M do
@doc "Hello world"
def hello do
"world"
end
@doc """
Sum.
"""
def sum(a, b) do
a + b
end
end
</code></pre>
<h3>@file</h3>
<p>设置stack traces显示文件信息。参数是string</p>
<pre><code> defmodule M do
@doc "Hello world"
@file "hello.ex"
def hello do
"world"
end
end
</code></pre>
<h3>@moduledoc</h3>
<p>模块文档</p>
<pre><code> defmodule M do
@moduledoc """
A very useful module
"""
end
</code></pre>
<h3>@on_definition</h3>
<p>用于当前模块的函数或者宏定义时调用hook, 参数是模块名或者元组{<module>, <function>}. 函数接受6个参数:</function></module></p>
<ul>
<li>模块环境变量</li>
<li>类型: :def, :defp, :defmacro, or :defmacrop </li>
<li>function/macro 名字 </li>
<li>参数列表</li>
<li>函数guard列表</li>
</ul>
<p>@on_definition 只会调用函数,不会调用宏,即@on_definition 的 参数是函数。<br>
未指定函数时, 会 调用<code>__on_definition__/6</code>函数。</p>
<pre><code> defmodule H do
def on_def(_env, kind, name, args, guards, body) do
IO.puts "Defining #{kind} named #{name} with args:"
IO.inspect args
IO.puts "and guards"
IO.inspect guards
IO.puts "and body"
IO.puts Macro.to_string(body)
end
end
defmodule M do
@on_definition {H, :on_def}
def hello(arg) when is_binary(arg) or is_list(arg) do
"Hello" <> to_string(arg)
end
def hello(_) do
:ok
end
end
</code></pre>
<h3>@on_load</h3>
<p>模块加载时调用 hook, 参数是当前模块中函数的函数名(atom)。 设置的函数不能有参数,返回值是:ok,否则模块会被忽略。</p>
<pre><code> defmodule M do
@on_load :load_check
def load_check do
if some_condition() do
:ok
else
nil
end
end
def some_condition do
false
end
end
</code></pre>
<h3>@vsn</h3>
<p>设置模块版本</p>
<pre><code>defmodule M do
@vsn "1.0"
end
</code></pre>
ssdb协议笔记
https://segmentfault.com/a/1190000000590955
2014-06-29T03:46:44+08:00
2014-06-29T03:46:44+08:00
lidashuang
https://segmentfault.com/u/lidashuang
1
<p>ssdb自己的协议非常简单,可以非常容易的封装和解析。</p>
<h2>请求</h2>
<p>官网的协议描述</p>
<pre><code>Packet := Block+ '\n'
Block := Size '\n' Data '\n'
Size := literal_integer
Data := size_bytes_of_data
</code></pre>
<pre><code>Request := Cmd Blocks*
Cmd := Block
</code></pre>
<p>比如 get key 是a的命令是这样的</p>
<pre><code>3\nget\n1\na\n\n
</code></pre>
<h2>回复</h2>
<p>协议描述文件</p>
<pre><code>Response := Status Block*
Status := Block
</code></pre>
<p>响应状态码包括: ok, not_found, error, fail, client_error</p>
<p>比如get a 时返回 "b"<br>
响应就是</p>
<pre><code>2\nok\n1\n1\n\n
</code></pre>
<p>SSDB 的协议是应用无关的, 它只关心 Block, 而不管 Block 是什么数据,什么数据类型,根据命令的类型来决定, 所以在处理响应的时,会根据命令来处理返回数据。而redis会在协议里有描述,比如<br>
redis中多条批量回复的第一个字节为 "*" , 后跟一个字符串表示的整数值, 这个值记录了多条批量回复所包含的回复数量,在SSDB处理的时候,你需要为每一类的命令单独处理,有点不方便</p>
<p>Links:</p>
<p><a rel="nofollow" href="http://ssdb.io/docs/zh_cn/protocol.html">http://ssdb.io/docs/zh_cn/protocol.html</a><br><a rel="nofollow" href="http://redis.readthedocs.org/en/latest/topic/protocol.html">http://redis.readthedocs.org/en/latest/topic/protocol.html</a></p>
<p>ssdb Elixir 客户端</p>
<p><a rel="nofollow" href="https://github.com/lidashuang/ssdb_elixir">https://github.com/lidashuang/ssdb_elixir</a></p>
[Translation] Elixir Getting Started 3 Basic operators
https://segmentfault.com/a/1190000000527386
2014-06-01T17:25:37+08:00
2014-06-01T17:25:37+08:00
lidashuang
https://segmentfault.com/u/lidashuang
1
<p><a rel="nofollow" href="http://blog.segmentfault.com/lds/1190000000527338"> Elixir Getting Started 2 Basic types</a></p>
<p>在之前的几章中,我们看到Elixir提供了<code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>作为基本的算术操作符,外加函数<code>div/2</code>和<code>rem/2</code>用来做整数的除法和余数运算。</p>
<p>Elixir也提供了<code>++</code>和<code>--</code>操作符操纵列表。</p>
<pre><code class="lang-iex">iex> [1,2,3] ++ [4,5,6]
[1,2,3,4,5,6]
iex> [1,2,3] -- [2]
[1,3]
</code></pre>
<p>字符串连接操作符<>:</p>
<pre><code class="lang-iex">iex> "foo" <> "bar"
"foobar"
</code></pre>
<p>Elixir也提供了布尔值操作符<code>or</code>, <code>and</code>和<code>not</code>. 这些操作符它们一个参数必须是一个布尔值(true或者false):</p>
<pre><code class="lang-iex">iex> true and true
true
iex> false or is_atom(:example)
true
</code></pre>
<p>非布尔值会导致异常:</p>
<pre><code class="lang-iex">iex> 1 and true
** (ArgumentError) argument error
</code></pre>
<p><code>or</code>和<code>and</code>是一对短路操作符. 只有当它们左侧的表达式不足以满足条件的时候才会执行右侧的表达式:</p>
<pre><code class="lang-iex">iex> false and error("This error will never be raised")
false
iex> true or error("This error will never be raised")
true
</code></pre>
<blockquote>
<p>注意:如果你是Erlang的开发者, Elixir中的<code>and</code>和<code>or</code>实际上是Erlang中的<code>andalso</code>和<code>orelse</code>操作符。</p>
</blockquote>
<p>除此之外, Elixir也提供了 <code>||</code>, <code>&&</code> 和 <code>!</code>, 这些操作符接受任何类型的参数. 对于这些操作, 除了false和nil之外的所有值都为真:</p>
<pre><code class="lang-iex"># or
iex> 1 || true
1
iex> false || 11
11
# and
iex> nil && 13
nil
iex> true && 17
17
# !
iex> !true
false
iex> !1
false
iex> !nil
true
</code></pre>
<p>简单来说,当你预期参数是布尔值的时候,用<code>and</code>,<code>or</code>和<code>not</code>。如果你不确定,那就使用<code>||</code>,<code>&&</code>和<code>!</code><br>
Elixir也提供了比较操作符<code>==</code>, <code>!=</code>, <code>===</code>, <code>!==</code>, <code><=</code>, <code>>=</code>, <code>></code>和<code><</code>:</p>
<pre><code class="lang-iex">iex> 1 == 1
true
iex> 1 != 2
true
iex> 1 < 2
true
</code></pre>
<p><code>==</code>和<code>===</code>之间的不同在于,后者在比较整数和浮点数的时候更加严格:</p>
<pre><code class="lang-iex">iex> 1 == 1.0
true
iex> 1 === 1.0
false
</code></pre>
<p>Elixir允许在不同类型之间进行比较:</p>
<pre><code class="lang-iex">iex> 1 < :atom
true
</code></pre>
<p>我们可以在Elixir在不同数据类型之间比较是本着实用主义, 有一个内建的不同类型之间的顺序, 所以算法在排序时无需担心数据类型上 的不同。下面就是内建的类型之间的顺序:</p>
<pre><code>number < atom < reference < functions < port < pid < tuple < maps < list < bitstring
</code></pre>
<p>你并不需要去记忆这些顺序,重要的是了解的确有这么一个东西。</p>
<p>好了,简单介绍到这里。在下一章中,我们将讨论函数,数据类型之间的转换和流程控制。</p>
[Translation] Elixir Getting Started 2 Basic types
https://segmentfault.com/a/1190000000527338
2014-06-01T16:17:48+08:00
2014-06-01T16:17:48+08:00
lidashuang
https://segmentfault.com/u/lidashuang
1
<p><a rel="nofollow" href="http://blog.segmentfault.com/lds/1190000000493951">Elixir Getting Started 1 交互式Elixir</a></p>
<h2>2 Basic types</h2>
<p>在这章中我们将学习Elixir中的一些基础的数据类型:整数(integers),浮点数(floats),原子(atoms),列表(lists)和字符串(strings)。它们是:</p>
<pre><code>iex> 1 # 整数
iex> 0x1F # 整数
iex> 1.0 # 浮点数
iex> :atom # 原子 / 符号
iex> "elixir" # 字符串
iex> [1, 2, 3] # 列表
iex> {1, 2, 3} # 元组
</code></pre>
<h3>2.1 基本运算</h3>
<p>打开<code>iex</code>, 输入下面的表达式:</p>
<pre><code class="lang-iex">iex> 1 + 2
3
iex> 5 * 5
25
iex> 10 / 2
5.0
</code></pre>
<p>注意<code>10 / 2</code> 返回 浮点数 <code>5.0</code>而不是整型<code>5</code>,这是合理的,因为在Elixir中,操作符<code>/</code>总是返回浮点数。如果你想得到整数或是求余,请使用<code>div</code>和<code>rem</code>函数:</p>
<pre><code class="lang-iex">iex> div(10, 2)
5
iex> div 10, 2
5
iex> rem 10, 3
1
</code></pre>
<p>你可能注意到了,在Elixir中调用函数时圆括号并不是必须的。<br>
Elixir还支持方便的输入二进制,八进制和十六进制数:</p>
<pre><code class="lang-iex">iex> 0b1010
10
iex> 0777
511
iex> 0x1F
31
</code></pre>
<p>浮点数需要在一个点之后跟随至少一位数字,同时也支持指数<code>e</code>的形式:</p>
<pre><code class="lang-iex">iex> 1.0
1.0
iex> 1.0e-10
1.0e-10
</code></pre>
<p>在Elixir中,浮点类型都是64位双精度。</p>
<h3>2.2 布尔值 Booleans</h3>
<p>Elixir支持两种布尔值,true和false。</p>
<pre><code class="lang-iex">iex> true
true
iex> true == false
false
</code></pre>
<p>Elixr提供了一些断言(predicate function)函数用来检查值的类型。比如,函数<code>is_boolean/1</code>能被用来检查一<br>
个值是不是布尔值:</p>
<blockquote>
<p>注意:Elixir中的函数定义是包括函数名和参数个数。例如 <code>is_boolean/1</code> 表示函数名是is_boolean并有一个函<br>
数参数的函数。<code>is_boolean/2</code> 是另一个新的函数,虽然有相同的名字,但是参数个数不一样。</p>
</blockquote>
<pre><code class="lang-iex">iex> is_boolean(true)
true
iex> is_boolean(1)
false
</code></pre>
<p>你也可以用<code>is_integer/1</code>来检查参数是否是整数,用<code>is_float/1</code>检查参数是否是浮点数或者用<code>is_number/1</code>检查参数是否整型或者浮点数。</p>
<blockquote>
<p>注意:在任何时候你都可以在控制台中键入<code>h</code>来获取帮助信息。<code>h函数</code>也能被用来访问函数的文档。例如,输入<code>h is_integer/1</code> 会打印出函数<code>is_integer/1</code>的文档。它也能用在操作符和其他的结构上(试试<code>h ==/2</code>)。</p>
</blockquote>
<h3>2.3 原子 Atoms</h3>
<p>一个原子的值就是它本身,别的语言也有称符号的(symbol)</p>
<pre><code class="lang-iex">iex> :hello
:hello
iex> :hello == :world
false
</code></pre>
<p>事实上,布尔值<code>true</code>和<code>false</code>就是原子:</p>
<pre><code class="lang-iex">iex> true == :true
true
iex> is_atom(false)
true
</code></pre>
<h3>2.4 字符串 Strings</h3>
<p>在Elixir中字符串必须用<code>双引号</code>来表达,并且用UTF-8来编码:</p>
<pre><code class="lang-iex">iex> "hellö"
"hellö"
</code></pre>
<p>Elixir也支持字符串内嵌写法:</p>
<pre><code class="lang-iex">iex> "hellö #{:world}"
"hellö world"
</code></pre>
<p>字符串可以包括换行,也可以使用转义字符引入换行:</p>
<pre><code class="lang-iex">iex> "hello
...> world"
"hello\nworld"
iex> "hello\nworld"
"hello\nworld"
</code></pre>
<p>你能用IO模块中的函数<code>IO.puts/1</code>来打印字符串:</p>
<pre><code class="lang-iex">iex> IO.puts "hello\nworld"
hello
world
:ok
</code></pre>
<p>注意:<code>IO.puts/1</code>函数在打印完成后返回原子<code>:ok</code><br>
字符串在Elixir内部是用二制类型(字节序列)来的表示:</p>
<pre><code class="lang-iex">iex> is_binary("hellö")
true
</code></pre>
<p>我们也可以获取字符串有多少个字节:</p>
<pre><code class="lang-iex">iex> byte_size("hellö")
6
</code></pre>
<p>字符串hellö的字节数书6,即使它只有5个字符。因为字符 "ö" 在UTF-8编码中占用了两个字节。可以使用<br><code>String.length/1</code>获取字符串的字符数。</p>
<pre><code class="lang-iex">iex> String.length("hellö")
5
</code></pre>
<p><a rel="nofollow" href="http://elixir-lang/docs/stable/elixir/String.html">String module</a> 模块包含了一系列unicode标准中定义胡函数。</p>
<pre><code class="lang-iex">iex> String.upcase("hellö")
"HELLÖ"
</code></pre>
<p>请牢记<code>单引号</code>和<code>双引号</code> 是不一样的,在Elixir中代表了不同的类型。</p>
<pre><code class="lang-iex">iex> 'hellö' == "hellö"
false
</code></pre>
<p>我们会在"Binaries, strings and char lists" 这一章介绍更多对unicode的支持和单引号和双引号之间的不同。</p>
<h3>2.5 匿名函数 Anonymous functions</h3>
<p>使用<code>fn</code>和<code>end</code>关键字定义匿名函数:</p>
<pre><code class="lang-iex">iex> add = fn a, b -> a + b end
#Function<12.71889879/2 in :erl_eval.expr/5>
iex> is_function(add)
true
iex> is_function(add, 2)
true
iex> is_function(add, 1)
false
iex> add.(1, 2)
3
</code></pre>
<p>在Elixir中函数是“第一等公民”,这意味着它们能被想整数和字符串一样当成参数传给别的函数。在上面的例子里,我们把变量<code>add</code> 指向的函数传给了另一个函数<code>is_function/1</code>,并且返回了结果<code>true</code>。我们也能用<code>is_function/2</code>来检查一个函数的元数(arity,参数数量)</p>
<p>注意当调用一个匿名函数时,在指向这个匿名函数的变量名和圆括号之间需要有一个点号(.)</p>
<p>匿名函数本身也是一个闭包,因此它们能够访问在被定义时的同作用域的其他变量:</p>
<pre><code class="lang-iex">iex> add_two = fn a -> add.(a, 2) end
#Function<6.71889879/1 in :erl_eval.expr/5>
iex> add_two.(2)
4
</code></pre>
<p>记住函数内的赋值不会影响它周围的环境:</p>
<pre><code class="lang-iex">iex> x = 42
42
iex> (fn -> x = 0 end).()
0
iex> x
42
</code></pre>
<h3>2.6 列表 (Linked) Lists</h3>
<p>Elixir用方括号来表示一个列表,列表内元素可以是任意类型:</p>
<pre><code class="lang-iex">iex> [1, 2, true, 3]
[1, 2, true, 3]
iex> length [1, 2, 3]
3
</code></pre>
<p>两个列表可以使用列表添加 <code>++/2</code>和移除/2`操作符进行合并和移除</p>
<pre><code class="lang-iex">iex> [1, 2, 3] ++ [4, 5, 6]
[1, 2, 3, 4, 5, 6]
iex> [1, true, 2, false, 3, true] -- [true, false]
[1, 2, 3, true]
</code></pre>
<p>贯穿整个教程,我们会不停地谈到列表的头(head)和尾(tail)。头是列表中的第一个元素,尾是剩下的。它们可以被分别用函数<code>hd/1</code>和<code>tl/1</code>得到。让我们创建一个列表,试着获取头和尾:</p>
<pre><code class="lang-iex">iex> list = [1,2,3]
iex> hd(list)
1
iex> tl(list)
[2, 3]
</code></pre>
<p>尝试获取空列表的头或尾会导致错误:</p>
<pre><code class="lang-iex">iex> hd []
** (ArgumentError) argument error
</code></pre>
<h3>2.7 元组Tuples</h3>
<p>Elixir用花括号来表示元组。和列表一样,元组能包含任何类型的元素:</p>
<pre><code class="lang-iex">iex> {:ok, "hello"}
{:ok, "hello"}
iex> size {:ok, "hello"}
2
</code></pre>
<p>元组中在内存中的存储是连续的。也就是说,用索引来访问元组中的元素或者获取元组的大小这样的操作是非常快的。(元组的索引是从0开始的):</p>
<pre><code class="lang-iex">iex> tuple = {:ok, "hello"}
{:ok, "hello"}
iex> elem(tuple, 1)
"hello"
iex> tuple_size(tuple)
2
</code></pre>
<p>也可以使用<code>set_elem/3</code>函数给指定索引设置新值:</p>
<pre><code class="lang-iex">iex> tuple = {:ok, "hello"}
{:ok, "hello"}
iex> set_elem(tuple, 1, "world")
{:ok, "world"}
iex> tuple
{:ok, "hello"}
</code></pre>
<p>注意<code>set_elem/3</code>返回一个新的元组。因为在Elixri中的数据类型都是不可变的,所以旧的元组并没有变化。变量不可变带来的一个好处是,它使得Elixir的代码变得相对简单,因为你不需要担心有一些代码在特别的地方修改了你的数据。</p>
<p>同时,不可变量也对避免一些常见的问题有帮助,比如并行代码中的竞态条件, 当两个以上的实体在同一时间试图修<br>
改同一个数据结构。</p>
<h3>2.8 列表还是元组</h3>
<p>列表和元组的不同之处在哪里?</p>
<p>列表在内存中是以链表的形式存储。这意味着列表中的每一个元素都指向下一个元素,直到到达列表的最后。我们把每一个列表单位叫做一个<strong>cons单元</strong>.</p>
<pre><code class="lang-iex">iex> list = [1|[2|[3|[]]]]
[1, 2, 3]
</code></pre>
<p>这也意味着获取一个列表的长度是一个线性操作:我们必须遍历整个列表来弄清楚列表的大小。把新元素插入列表头部的方式来更新列表是一个比较快的操作:</p>
<pre><code class="lang-iex">iex> [0] ++ list
[0, 1, 2, 3]
iex> list ++ [4]
[1, 2, 3, 4]
</code></pre>
<p>在上面的例子中,第一个操作是比较块的,因为我们只是加了一个指向<code>list</code>的新的cons单元。第二个例子就比较慢了,因为添加一个新的元素到尾部我们必须重建整个列表。</p>
<p>从另一方面来说,元组在内存的存储是连续的。这意味着获取元组的大小或通过索引来访问元素是非常快的。然而,修改或增减元组中的元素是非常昂贵的操作,因为它需要在内存中复制整个元组。</p>
<p>这些性能特征决定了这些数据结构的使用场景。一个常见的例子是函数返回一个元组来表示附加的信息。比如,<code>File.read/1</code>用来读取一个文件的内容,它返回的就是一个元组:</p>
<pre><code class="lang-iex">iex> File.read("path/to/existing/file")
{:ok, "... contents ..."}
iex> File.read("path/to/unknown/file")
{:error, :enoent}
</code></pre>
<p><code>File.read/1</code>读取的路径存在的话,返回的元组第一个元素是原子<code>:ok</code>,第二个元素是文件的内容。否则,会返回一个元组,元组内容是<code>:error</code>和错误原因。</p>
<p>大多数的时候,Elixir会引导你做正确的事。例如,有一个<code>elem/2</code>函数来访问元组内的元素,但列表没有内建此函数</p>
<pre><code class="lang-iex">iex> tuple = {:ok, "hello"}
{:ok, "hello"}
iex> elem(tuple, 1)
"hello"
</code></pre>
<p>在Elixir中,当我们计算一个数据结构的大小时,需要遵循一个简单的原则:当这个操作所需的时间是一个常量时间(也就是说,这个数值是事先已经计算好了的)的时候,函数应该用<code>size</code>来命名,如果需要显示的计算应该用<code>length</code>。</p>
<p>例如,迄今我们遇到过4个获取长度的函数:<code>byte_size/1</code>(获取字符串中的字节数),<code>tuple_size/1</code>(获取元组的大小),<code>length/1</code>(获取列表的大小),<code>String.length/1</code>(获取字符串中的字符数)。可见,当我们用<code>byte_size/1</code>去获得字符串中的字节数的时候,这个操作是相当廉价的,但当我们用<code>String.length/1</code>得到其中的unicode字符的数量时是非常昂贵的操作,因为我们需要历遍整个字符串。</p>
<p>Elixir同时也支持其他的一些数据类型,<code>Port</code>,<code>Reperence</code>和<code>PID</code>(用于进程间的通讯)。当讲到进程相关的章节的时候,我们会具体谈到它们。</p>