Tmux应用

Tmux是一个键盘驱动的终端分屏工具,可以替代Linux下的screen。当然,如果是在Mac下使用的话,它的核心功能(例如window,分屏等)也是可以被iTerm2替代的,不过好在Tmux在*nix操作系统中足够通用,用包管理工具(apt-get, brew等)即可安装,所以了解并熟练使用它,还是能为平时终端下的工作节省不少时间的。我从几年前开始使用,但实际上也一直没有形成依赖,因为前面说过了它的可替代性。网上有大堆的所谓Tmux技巧基本上是来自于一本叫『tmux: Productive Mouse-Free Development』的小书,各大网盘也有其电子版下载,抽空通读了一遍,根据示例一步一步个性化Tmux的设置,了解了之前不知道的用法和配置细节。

首先是Tmux的用法简介。

一. session: 会话,Tmux是一个C/S架构的工具,一个会话可以认为是C端和S端一次交互的上下文。我们的所有操作都属于某个session,session可以长时间存在,也可以临时退出再重进。我们可用通过session来区分不同的工作空间,比如本地操作开一个session,远程SSH操作开一个session,又或者SSH生产环境机器开一个session,SSH测试环境机器开一个session。以下是针对session的一些常用操作。

1. open session

1
2
3
$ tmux new-session -s basic
或者
$ tmux new -s basic

-s参数表示session名称,如果不加-s参数,那么Tmux默认会新建一个以数字(下标从0开始)命名的session,并默认打开一个window。打开一个session后,后续的所有控制Tmux本身的快捷键都需要加前缀,默认是Ctrl+b,以下把前缀按键称为Prefix。

2. detach session
想要暂时离开Tmux,回到终端环境时,可以通过快捷键Prefix+d (d for detach)。要注意的时,即使是detach的状态,Tmux中在运行的程序还会继续运行。想要回到Tmux session时,只需执行:

1
$ tmux attach -t basic

-t参数可以指定要attach的session。

3. list session
终端中执行tmux ls (ls for list session)可以列出当前有多少个session。如果已经在session中,执行Prefix+s (s for session)可以列出当前有多少个session,并且可通过上、下键选择要进入的session。

4. kill session
要真正关闭一个session,可以在终端下执行命令tmux kill-session -t basic,其中-t参数表示session名称。

二. window
如果说session是个不可见的东西,那么window就是我们输入、执行命令的地方。一个session可以包含多个window。把window类比成iTerm2中的标签应该就理解了。

1. 创建window
在创建session的时候默认会创建一个以"数字下标+bash"命名的window,并且名称随着bash中执行的不同命令而变化。在新建session时可以通过-n参数指定默认打开的window名称,比如通过tmux new -s basic -n win命名一个win名称的window。也可以随时通过Prefix+,来修改window名称。

2. 切换window
类似标签,我们可以通过一些快捷键在同一个session下的多个window之间切换。比如:

Prefix+p (p for previous):切换到上一个window。
Prefix+n (n for next): 切换到下一个window。
Prefix+0: 切换到0号window,依次类推,1、2、3...
Prefix+w (w for windows): 列出当前session所有window,通过上、下键可以选择切换到指定window。

3. 关闭window
Prefix+&: 关闭当前window。

三. pane
一个window可以切割成多个pane,也就是所谓的分屏,算是Tmux的核心功能之一。

1. 分屏
Prefix+%: 垂直分屏,用一条垂线把当前窗口分成左右两屏。
Prefix+": 水平分屏,用一条水平线把当前窗口分成上下两屏。

2. 切换pane
默认情况下,被选中(激活状态下)的pane会被绿色边框高亮突显出来。
Prefix+o: 依次切换当前窗口下的各个pane。
Prefix+Up|Down|Left|Right: 根据按箭方向选择切换到某个pane。
Prefix+Space(空格键): 对当前窗口下的所有pane重新排列布局,每按一次,换一种样式。
Prefix+z: 最大化当前pane。再按一次后恢复。

3. 关闭pane
Prefix+x: 关闭当前使用中的pane。

关于Tmux的三个核心概念(session、window和pane)及其基本用法已经介绍完毕。接下来的是一些个性化配置和奇技淫巧,包括重新绑定快捷键、自定义快捷键、UI样式、鼠标支持、复制粘贴等等,这些可配置的高级功能也是Tmux受人推崇的原因。Tmux配置文件推荐放在~/.tmux.conf文件中,避免某个用户修改配置影响到其他用户,修改配置文件后要经过reload操作才会在已打开session中生效。

一. 重新绑定快捷键
Tmux的很多默认配置不够友好,需要个人重新定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unbind C-b
set -g prefix C-a
 
bind C-a send-prefix
 
bind r source-file ~/.tmux.conf \; display "tmux.conf reload!"
 
bind | split-window -h
bind - split-window -v
 
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
 
set -g base-index 1
set -g pane-base-index 1

第1-2行表示重新定义Prefix,把默认的Ctrl+b换成Ctrl+a,便于单手操作。
第4行重新定义Ctrl+a组合键,当Prefix + Ctrl+a按下后,等同于原先Ctrl+a功能,解决Ctrl+a被设置为Prefix后已有快捷键失效的问题,也就是说只要按下两次Ctrl+a,就能实现原先终端下回到行首的功能。
第6行定义新的快捷键Prefix+r,重新加载Tmux配置文件,避免每次要进入命令模式reload配置。
第8-9行重新定义分屏快捷键。使用Prefix+|代替Prefix+%实现垂直分屏,使用Prefix+-代替Prefix+"实现水平分屏。|和-的符号本身就可以表示分屏线形状,非常直观。
第11-14行重新定义上下左右方向键,遵循vi习惯。定义以后,任何需要上下左右方向键的场景都可以用hjkl替代。
第16行表示将window的起始下标设为1。因为标准键盘的0在9后面,Prefix + 0/1/2...切换不便。
第17行表示将pane的起始下标设为1。理由同上。

二. 鼠标支持

1
2
3
4
set-window-option -g mode-mouse on
set -g mouse-select-pane on
set -g mouse-resize-pane on
set -g mouse-select-window on

第1行表示启用鼠标。虽然Tmux推荐用键盘完成所有操作,但是对现代开发人员来说,纯键盘操作的习惯并非那么容易养成,因此启用鼠标配置成为标配。
第2行表示支持鼠标选择pane。
第3行表示支持鼠标调整pane大小。
第4行表示支持鼠标选择window。

三. UI样式调整

1
2
3
4
5
6
7
setw -g window-status-current-fg white
setw -g window-status-current-bg red
setw -g window-status-current-attr bright
 
set -g status-justify left
 
setw -g monitor-activity on

第1-3行表示状态栏中window标签的高亮样式,默认是绿底黑字,设置后当前window红底白字显示。
第5行表示状态栏中window列表左对齐排列。
第7行表示非当前window有内容更新时显示在状态栏。

四. 复制粘贴
默认情况下,按Prefix+[进入复制模式,按回车(Enter)退出复制模式。可以通过配置在复制模式中使用vi习惯操作:

1
setw -g mode-keys vi

在复制模式下,按空格键(Space)开始复制,按回车(Enter)完成复制,并退出模式,按Prefix+]粘贴。这些快捷键也可以通过以下配置进行修改,使操作更加靠近vi。

1
2
3
4
5
6
unbind [
bind Escape copy-mode
unbind p
bind p paste-buffer
bind -t vi-copy 'v' begin-selection
bind -t vi-copy 'y' copy-selection

第1-2行表示重新绑定Escape键,Prefix+Escape为进入复制模式。
第3-4行表示重新绑定p键,Prefix+p为粘贴。
第5行表示重新绑定v键,Prefix+v为开始复制。
第6行表示重新绑定y键,Prefix+y为完成复制。

要查看当前复制的内容,可以在Prefix+:后出现的命令行中输入show-buffer,输入list-buffers可以列出所有的复制历史内容。
关于复制粘贴,更深入的话题是Tmux和系统剪贴板之间的交互,Linux可以使用xclip,Mac可以使用tmux-MacOSX-pasteboard,不过我没有试验成功,暂时可以通过ALT + 鼠标复制内容到系统剪贴板。

五. 多屏操作
默认情况下,一个window上只有一个pane被激活,接收键盘交互。但是某些场景下需要在多个pane中执行相同的操作,比如同时修改两台或更多台远程机器的nginx配置,这时候可以在分屏后按Prefix+:进入命令模式,输入set synchronize-panes,即可进入批量操作模式,要退出批量操作模式,再次输入set synchronize-panes即可。

最后上图一张:
Tmux

--EOF--

通过Spring获取properties文件属性值

Spring提供了注解@Value,用于在程序中获取properties配置文件属性值。例如:

1. applicationContext.xml中指定配置文件。

<context:property-placeholder location="classpath:xxx.properties" 
    ignore-unresolvable="true" />

当有多个配置文件时,上述配置可以配置多条。

<context:property-placeholder location="classpath:xxx.properties" 
    ignore-unresolvable="true" />
<context:property-placeholder location="classpath:yyy.properties" 
    ignore-unresolvable="true" />

2. Spring bean中使用@Value注解获取指定参数。

// xxx.properties配置项:
// server.ip=192.168.1.1
// server.port=8080

@Value("${server.ip}")
private String ip;

@Value("${server.port}")
private int port;

使用@Value注解的前提是当前对象的生命周期由Spring管理,是Spring bean,无论通过XML配置文件还是@Component、@Service等注解声明。假如一个对象的生命周期是我们程序自己管理的,比如常规用法下的new Object(),特别是做一些框架开发,经常用到Class.forName().newInstance()来实例化对象,那么想要反射为新创建对象的成员变量赋值时,如何借助Spring来获取已经解析好的properties属性值是个值得一试的探索。上述场景可以简化为:

如何在一个拥有Spring上下文的平台上,对不受Spring管理的对象使用依赖注入,达到类似@Value注解实现的功能。

思路也很简单,既然Spring已经解析过properties文件,那么通过某种手段把这些值暴露出来就可以了,EmbeddedValueResolverAware接口很适合做这件事情。Aware接口是定义一些能在Spring bean中操作Spring上下文信息的一类接口,常见的有ApplicationContextAware,可以在Spring bean中拿到ApplicationContext;BeanFactoryAware,可以在Spring bean中拿到Spring BeanFactory。这里的EmbeddedValueResolverAware也是类似功能,它定义了一个void setEmbeddedValueResolver(StringValueResolver resolver)接口方法,在bean初始化后,Spring回调setEmbeddedValueResolver()方法,将StringValueResolver对象注入到bean中,从这个对象中就能获取properties文件中的属性名称和值。用法如下:

1. 声明一个实现EmbeddedValueResolverAware接口的实例,用@Component注解声明为Spring bean,重写setEmbeddedValueResolver()方法,将StringValueResolver实例的引用保存下来,并且对外提供getPropertiesValue()方法,用于获取properties值。

@Component
public class PropertiesUtils implements EmbeddedValueResolverAware {
    
    privat StringValueResolver stringValueResolver;

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        stringValueResolver = resolver;
    }

    public String getPropertiesValue(String name){
        return stringValueResolver.resolveStringValue(name);
    }
}

2. 通过${key}作为name格式调用getPropertiesValue()方法,获取properties值。

String name = "${server.ip}";
String value = propertiesUtils.getPropertiesValue(name);

StringValueResolve解析出来的值都是String类型的,非String类型需要在拿到参数String值后自行转换。

本文只对特定场景下使用EmbeddedValueResolverAware接口借助Spring上下文从properties文件中获取参数值做了一个简单介绍,此方法有效但不唯一,供参考。

--EOF--

『大型网站技术架构:核心原理与案例分析』(六)

『大型网站技术架构:核心原理与案例分析』读书笔记系列:
(一):架构演化、模式、要素
(二):高性能架构
(三):高可用架构
(四):可伸缩架构
(五):可扩展架构
(六):安全性架构


『大型网站技术架构』(六):安全性架构

一、网站应用攻击与防御

1. XSS攻击

跨站脚本攻击(Cross Site Script, XSS)指攻击者通过篡改网页,注入恶意HTML脚本,在用户浏览网页时,控制用户浏览器进行恶意操作的一种攻击方式。

  • 反射型: 攻击者诱使用户点击一个嵌入恶意脚本的链接,达到攻击目的。
  • 持久型:攻击者提交含有恶意脚本的请求,保存在被攻击的Web站点数据库中,用户浏览网页时,恶意脚本被包含在正常的页面中,达到攻击目的。

防御手段:

  • 消毒:特殊字符('<','>'等)转义。
  • HttpOnly: 不能避免XSS,但是能避免攻击者通过XSS获取Cookie,导致敏感信息泄露。

2. 注入攻击

两种形式:SQL注入攻击、OS注入攻击。本质是将数据当做程序执行。

攻击者如何获取数据库表结构信息?

  • 开源:表结构本身公开。
  • 错误回显:服务器500错误回显到浏览器。
  • 盲注:攻击者根据页面变化情况判断SQL语句的执行情况,据此猜测数据库表结构。

防御手段:

  • 消毒:过滤数据中可能注入的SQL。
  • 参数绑定:预编译(两大好处:1.提升性能,2.防止SQL注入)

3. CSRF攻击

跨站请求伪造(Cross Site Request Forgery, CSRF)指攻击者通过跨站请求,以合法用户的身份进行非法操作。核心是利用浏览器Cookie或服务器Session策略盗取用户身份。

防御手段:识别请求者身份

  • 表单Token: 每次页面请求,服务器写入一个随机数到Cookie,下次页面表单提交,表单域携带随机数,服务器端以此判断Cookie中随机数与表单域中值是否一致。这个方法有效的前提是Cookie不泄露,否则不生效。CSRF配合XSS攻击盗取Cookie会带来严重后果。
  • 验证码:用户体验不好。
  • Referer cheker:判断Referer域中请求来源是否合法。

4. 其他攻击和漏洞

  • Error Code: 服务器端500错误异常信息直接显示在浏览器。
  • HTML注释
  • 文件上传:上传不可靠文件类型。
  • 路径遍历:URL路径部分填入相对路径,暴露服务器文件系统。

二、信息加密技术及密钥安全管理

1. 加密技术

  • 单项散列加密
  • 对称加密
  • 非对称加密

单向散列加密

通过对不同输入长度信息进行散列计算,得到固定长度输出,不可逆。

常见算法:MD5、SHA

对称加密

加密和解密使用的密钥是同一个(或者可以互相推算)。算法简单,加解密效率高,系统开销小,适合大量数据加密,如何保存密钥是个难题。

常见算法:DES、RC

非对称加密

加密和解密使用的密钥不是同一个。安全性高。

常见算法:RSA

2. 密钥安全管理

  • 密钥和算法放在一个独立的服务器上,甚至做成一个专用硬件设施,对外提供加密和解密服务,应用系统通过调用这个服务实现加解密。
  • 加解密算法放在应用系统中,密钥放在独立服务器,并且密钥分片存储,分别由专人保管。

三、信息过滤和反垃圾

  • 文本匹配:Trie算法、多级Hash表
  • 分类算法:数据挖掘、贝叶斯分类算法
  • 黑名单:Hash表(精确)、布隆过滤器(不完全精确)

四、电子商务风险控制

1. 风险

  • 账户风险
  • 买家风险
  • 卖家风险
  • 交易风险

2. 风控

  • 规则引擎:业务规则和规则处理逻辑相分离。规则处理逻辑写好后,由运营人员负责通过页面编辑规则。
  • 统计模型:数据挖掘、机器学习

--EOF--

『大型网站技术架构:核心原理与案例分析』(五)

『大型网站技术架构:核心原理与案例分析』读书笔记系列:
(一):架构演化、模式、要素
(二):高性能架构
(三):高可用架构
(四):可伸缩架构
(五):可扩展架构
(六):安全性架构


『大型网站技术架构』(五):可扩展架构

扩展性和伸缩性:

  • 扩展性(Extensibility): 指对现有系统影响最小的情况下,系统功能可持续扩展或提升的能力。目标是当系统新增功能时,不需要对现有系统的结构和代码进行修改。
  • 伸缩性(Scalability):指系统能够通过增加/减少自身资源规模的方式增强/减少自己计算处理事务的能力。目标是利用集群的方式增加服务器数量,提高系统的整体事务吞吐能力,实现线性伸缩性。

一、构建可扩展的网站架构

终极目标:系统间低耦合。如何分解系统的各个模块、如何定义各个模块接口、如何复用组合不同的模块构造一个完整的系统。

核心思想:模块化,并在此基础上降低模块间耦合性,提高模块复用性。

二、利用分布式消息队列降低系统耦合性

分布式消息队列通过消息对象分解系统耦合性,不同子系统处理同一个消息。

事件驱动架构

定义:事件驱动架构(Event Driven Architecture)通过在低耦合的模块之间传输事件消息,以保持模块的松散耦合,并借助事件消息的通信完成模块间合作。

典型的EDA架构比如生产者消费者模式。利用分布式消息队列的发布-订阅模式工作。生产者只需生产消息到队列,消费者从队列获取消息进行处理。新增业务,只要对某类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响。

三、利用分布式服务打造可复用的业务平台

分布式服务通过接口分解系统耦合性,不同子系统通过相同的接口描述进行服务调用。

巨无霸系统带来的问题:

  • 编译、部署困难
  • 代码分支管理困难:多个团队共同维护一份代码。
  • 数据库连接容易耗尽:数据库连接数与应用数量成正比。
  • 新增业务困难:老人不敢碰,新人不能接。

解决方法:拆分、模块独立部署

  • 纵向拆分:将大应用拆分为多个小应用。
  • 横向拆分:将复用业务拆分出来,独立部署为分布式服务,新增业务只需调用这些分布式服务,不需要依赖具体的模块代码。

Web Service与企业级分布式服务

Web Service: 服务提供者通过WSDL描述服务(接口),客户端通过WSDL生成客户端调用代码,通过SOAP协议与服务提供者通信,传输层协议可以是HTTP、SMTP、TCP等。

缺点:

  • 臃肿的注册与发现机制
  • 低效的XML序列化手段
  • 开销相对较高的HTTP远程通信
  • 复杂的部署与维护手段

大型网站分布式服务的需求

  • 服务注册与发现
  • 负载均衡:支持服务请求者使用可配置的负载均衡算法访问服务。
  • 失效转移
  • 高效的远程通信
  • 整合异构系统
  • 对应用最小侵入:适应服务架构的进化和反复(分布式或集中式部署)。
  • 版本管理:支持服务接口的多版本。
  • 实时监控

分布式服务框架设计

  • Thrift(远程服务调用框架):Facebook用它管理其分布式服务(注册、发现和调用),但是未开源基于Thrift的分布式服务框架。
  • Dubbo:阿里开源的分布式服务框架,较为成熟。

四、可扩展的数据结构

NoSQL: 宽列存储模型、ColumnFamily(列族)设计、面向列族的稀疏矩阵存储格式

五、利用开放平台建设网站生态圈

开放平台架构:

  • API接口:RESTful、Web Service、RPC等。
  • 协议转换:将API输入转成内部服务可识别的形式,将内部服务返回值封装成API格式。
  • 安全:身份识别、权限控制。
  • 审计:监控、计费。
  • 路由:将开放平台访问路由映射到具体的内部服务。
  • 流程:将一组离散的服务组织成一个上下文相关的新服务,隐藏服务细节,提供统一接口。

--EOF--

『大型网站技术架构:核心原理与案例分析』(四)

『大型网站技术架构:核心原理与案例分析』读书笔记系列:
(一):架构演化、模式、要素
(二):高性能架构
(三):高可用架构
(四):可伸缩架构
(五):可扩展架构
(六):安全性架构


『大型网站技术架构』(四):可伸缩架构

“大型“定义:

  • Facebook: 大量用户及大量访问,10亿用户。
  • 腾讯: 功能复杂,产品众多,1600+种产品。
  • Google:大量服务器,100w台服务器。

一、网站架构的伸缩性设计

  • 不同功能进行物理分离实现伸缩

    单一服务器处理所有服务 -> 数据库从应用服务器分离 -> 缓存从应用服务器分离 -> 静态资源从应用服务器分离

    横向分离(分层后分离)、纵向分离(业务分割后分离)

  • 单一功能通过集群规模实现伸缩

    当一头牛拉不动车的时候,不要去寻找一头更强壮的牛,而是用两头牛来拉车。

    集群伸缩性:应用服务器集群伸缩性、数据服务器集群伸缩性(缓存数据服务器集群和存储数据服务器集群)

二、应用服务器集群的伸缩性设计

负载均衡:实现网站伸缩性,改善网站可用性。

负载均衡类型

1. HTTP重定向负载均衡

通过一台HTTP重定向服务器,返回302实现负载均衡。实践中很少见。

优点:简单
缺点:性能差(2次请求)、伸缩性有限(重定向服务器容易成为瓶颈)、被搜素引擎判为SEO作弊(302请求),

2. DNS域名解析负载均衡

在DNS服务器中配置多个A纪录。大型网站用于进行第一级负载均衡。

优点:支持基于地理位置域名解析,加快用户访问速度。
缺点:DNS多级解析,生效和失效时间久。

3. 反向代理负载均衡

利用反向代理服务器(缓存资源、安全等)进行负载均衡。应用层负载均衡。

优点:反向代理功能和负载均衡功能集成,部署简单。
缺点:反向代理服务器容易成为瓶颈。

4. IP负载均衡

在网络层通过修改请求目标地址进行负载均衡。

  • 负载均衡服务器修改目的IP的同时修改源地址,将数据包源地址设为自身IP。(SNAT)
  • 负载均衡服务器同时作为Real Server的网关服务器。(LVS/NAT模式)

优点:相比应用层负载均衡(反向代理)有更好的性能。
缺点:进出流量走负载均衡服务器,依然存在瓶颈。

5. 数据链路层负载均衡

在通信协议的数据链路层修改mac地址进行负载均衡(LVS/DR)。数据三角传输模式,流量从用户->负载均衡服务器->Real Server->用户。

目前使用最广泛。

负载均衡算法

  • 轮询(Round Robin, RR)
  • 加权轮询(Weighted Round Robin, WRR)
  • 随机(Random)
  • 最少连接(Least Connections)
  • 源地址散列(Source Hashing)

三、分布式缓存集群的伸缩性设计

目标:必须让新上线/下线的缓存服务器对整个分布式缓存集群影响最小,也就是说经过调整使整个缓存服务器集群中已经缓存的数据尽可能还被访问到。

一致性Hash:解决集群扩减容时过多节点缓存失效问题。
使用虚拟节点的一致性Hash环:避免集群扩减容造成的节点负载不均问题,通过增加一层虚拟节点与物理节点的映射来使节点增删带来的影响平均到所有节点。

四、数据存储服务器集群的伸缩性设计

数据存储服务器必须保证数据可靠存储、可用性、正确性,伸缩性设计原则与缓存不同。

1. 关系数据库集群的伸缩性设计

  • 主从读写分离
  • 业务分隔、数据分库。跨库不能Join操作、避免事务
  • 数据分片:通过分布式关系数据库访问代理,比如Amoeba、Cobar(Ali)。伸缩性:一致性Hash + 数据迁移

2. NoSQL数据库的伸缩性设计

HBase伸缩性: 依赖可分裂的HRegion及可伸缩的分布式文件系统HDFS实现。

--EOF--