wenfengsun

Archive for September 2011

facebook将他们的thrift开源,thrift是一个用于不同系统间通信的协议,它大大提高了系统间数据通信的效率。more about thrift

thrift为linux和windows都提供了相应的gen命令,安装之后只需要调用thrift命令就可以生产对应语言的程序。
thrift生成的java源文件主要是描述object和interface,实现接口的代码需要自己实现;
下面是一个简单的例子,通过定义一个message,和一个获取message对象的接口,来实现client获取数据对象的结果。
首先要定义thrift脚本,thrift的基本介绍:

基本名词

  • Types: 为了满足多语言平台的要求,需要提供基本数据类型来进行转换。比如在C++的Map和Python的Dict之间能够相互转换。
  • Transport: 对于每一种语言,都应该有一个抽象的公共层来完成对应的双向数据传输。
  • Protocal: 数据需要有一种方式来使用对应的传输层的code,而不用关心传输层的具体实现细节。
  • Versioning:数据需要有自己的版本号来实现对应的健壮性。
  • Processing : 产生code来完成RPC调用。

类型
1. Goals:

  • 1. 使用最基本的数据类型,不管上层使用怎么样的编程语言。
  • 2. 不使用动态数据类型,也不需要开发者写对象序列化或者传输的代码(已经封装)。
  • 3. IDL 用来告诉代码生成器如何在多语言之上安全地传输数据。

2. Base Types:

  • 1. 选取了大多数语言中都存在的基本数据类型,忽略了一些语言中特别的类型。(无符号类型), 所有的基本
  • 类型都是有符号的。
  • 2. 基本类型包括bool, byte,i16,i32,i64,double,string。分别对应java中的boolean,byte,short,int,long,double,String

3. Structs:

  • 1. 提供了一种common的对象来代表面向对象语言中的对象类型,每一个filed都有一个标志符。
  • 2. 每个filed必须有一个标志符和一个给定的默认值。标志符如果没有给定的话,会自动分配一个,但是强烈建议给定标志符,为了方便版本控制。

4. Container:

  • 1. List, set, Map。其中List被翻译成STL中的vector, 或者Java中的ArrayList,set和map类似。
  • 2. 要求对应的基本语言类型支持对应的Thrift的基本数据类型。
  • 3. 在每一种目标语言中,对于每一种定义的类型,生成两个接口,read和write,用于序列化和传输对应的数据结构。

5. Exceptions:

  • 1. 使用Exception关键字来代替对应的structs关键字,得到对应的基本类。
  • 2. 生成的对象继承自每一种语言中的对应的Exception的基类。

6. Services:

  • 1. 提供了服务端的接口调用,通过代码生成器会生成Client端和Server端的桩函数接口,共上层调用使用。
  • 2. 在每一个函数前面可以加上async关键字,这个关键字能够实现对应的一部方法。
  • 3. 一个纯void函数也会返回一个response,用来保证该操作在服务端已经被执行。
  • 4. async call只保证消息在传输层是没有问题的,因此,在使用async的时候需要保证当前的使用环境对于丢包是可以接受的。

3. Transport
1. 接口

  • 1. 传输层用来促进数据传输。一个关键的设计是Thrift将数据传输层和代码生成器分开。
  • 2. 由于抽象的IO炒作产生的tradoff相比起实际的IO操作显得微不足道。
  • 3. 需要满足的基本功能:如何读写数据,不管是在网络中,还是共享内存,还是从磁盘文件中读写。
  • 4. 提供的接口如下:open, close,isOpen, read, write, flush。还有一些用于batch操作的接口
  • 5. 对于TServerTransport,还有接口用来创建对应的Transport对象。如open, listen, accept, close。

2. 实现

  • 1. TSocket 类对于TCP/IP的流式套接字实现了一个通用,简单的接口。
  • 2. TFileTransport类实现了一个抽象接口,用于将磁盘文件导入到数据流中。

3. Utilities

  • 1. 网络传输层设计的支持简单的扩展,比如组合,TBufferdTransport实现在传输层实现了buffer的功能,TFrameTransport实现了数据头定长大小,用于Noblocking传输。TMemoryBuffer允许读和写直接在进程的堆顶完成。

4. Protocal
1. 接口

  • 1. 另外一块主要的部分是thrift中将数据结构和传输层分离,thrift定义了一些传输时的基本类型,但是没有强制定义需要实现的编码格式,只要能被代码生成器识别就好。
  • 2. thrift协议的接口简单明了,支持两种功能:双向有序的消息,基本类型,容器,结构的编码。

2. 结构

  • 1. Thrift中默认使用流式结构来传输对应的协议,这样避免了因为需要分片或者在发送前整体计算带来的性能损失。当有的场景需要使用分片,或者分片的性能优势更加明显的时候,可以使用TFramedTransport这个抽象接口类。

3. 实现

  • 1. Thrift的实现了一个空间高效的二进制协议,所有的数据都以一个扁平的二进制格式存在,整形被转换为网络字节序,字符串在头上加上它的长度,所有的消息和field 头都用它的整数序列号,忽略这些filed中的字符串名字。
  • 2. 我们没有做过多的性能优化,为了代码的整洁和高效。如果有需要,可以加入这些优化方案。

5.Versioning
1. 域标志符

  • 1. Thrift在版本控制上面是健壮的,它可以接受旧客户端过来的请求并正确处理。
  • 2. Thrift中的版本控制是通过域标志符来确定的,thrift结构中的每一项前面都有一个对应的标志符,标志符和数据类型唯一化开item。
  • 3. 为了避免冲突,自动分配的标志符从-1开始递减,人工定义的标志符只能为整数。
  • 4. 在数据是自定义的前提下,当反序列化的时候,thrift的code 生成器可以根据标识符来判断读取到的数据是否对齐,也可以跳过不能识别的域,保证兼容性。
  • 5. 域标志符也可以在参数列表中声。

2. Isset

  • 1. 用来标注必须存在于结构体中的域,如果必须存在,标注为true,这样可以当该域不存在的时候,可以通知上层调用者。

3. 场景分析
1. 4种场景,加减域/新旧client或者server。
4. Protocal/Transport Versioning
1. 实现了TProtocal的抽象接口,用来让协议的实现自己来管理版本。
6. RPC 实现
1. TProcessor
1. 核心类,抽象出client和server的基类,核心功能是处理input和output。
2. Generated code
2. 自动生成client端和server端的调用接口。
3. TServer
1. Tserver 可以有多种实现,来满足不同的需求。如单线程的TSimpleServer, 每个连接一个线程的TThreadedServer, 还有基于线程库的TThreadPoolServer.
2. 开发者可以实现自己的TServer。
下面是message的简单例子:
1. 定义message.thrift
namespace java com.company.thrift.model
struct Message {
1: i64 id,
2: string body,
3: string content,
4: i64 userId,
5: string picpath,
}

2. 定义service
namespace java com.company.thrift
include "message.thrift"
service MessageService{
message.Message getMessage(1: i64 id);
}

3. 执行生成命令
# thrift –gen java message.thrift
# thrift –gen java messageService.thrift
将生成的Message.java和messageService.java拷贝到你的工程中。
4. 编写接口实现:
public class MessageServiceImpl implements MessageService.Iface{

@Override
public Message getMessage(long id) throws TException {
Message message = new Message();
message.setId(id);
message.setBody(“Test body value”);
message.setContent(“”);
message.setPicpath(“http://www.google.com.hk/images/nav_logo86.png”);
message.setUserId(1001);
return message;
}

}
5. 编写服务器端调用
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TBinaryProtocol.Factory;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.server.TThreadPoolServer.Args;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;

import com.conpany.thrift.MessageService.Processor;

public class Server {
public void startServer() {
try {
TServerSocket serverTransport = new TServerSocket(1234);
MessageService.Processor process = new Processor(new MessageServiceImpl());
Factory portFactory = new TBinaryProtocol.Factory(true, true);
Args args = new Args(serverTransport);
args.processor(process);
args.protocolFactory(portFactory);
TServer server = new TThreadPoolServer(args);
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Server server = new Server();
server.startServer();
}
}
6. 编写客户端代码:
public class Client {
public void startClient() {
TTransport transport;
try {
transport = new TSocket("localhost", 1234);
TProtocol protocol = new TBinaryProtocol(transport);
MessageService.Client client = new MessageService.Client(protocol);
transport.open();
Message msg = client.getMessage(1020);
System.out.println(msg);
transport.close();
} catch (TTransportException e) {
e.printStackTrace();
} catch (TException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Client client = new Client();
client.startClient();
}
}
7. 启动Server,并执行客户端代码,可以看到客户端会打印一行服务器生成的message数据

Message(id:1020, body:Test body value, content:, userId:1001, picpath:http://www.google.com.hk/images/nav_logo86.png)

Advertisements

ios平台拍摄视频是有方向信息的,当手持设备在朝向非正常的方向拍摄时,会造成视频上传后处于颠倒的状态。(默认的正确方向是横向,home键在右侧)
做到自动旋转,需要两步:
1. 获取视频文件的方向信息;
2. 根据方向信息旋转;
解决这两个问题可以通过两个开源软件完成:mediainfo(http://mediainfo.sourceforge.net)和ffmpeg(http://www.ffmpeg.org)
一、安装mediainfo
下载libzen0,libmediainfo0和mediainfo三个文件,注意选择redhat的操作系统版本。
1. 安装libzen0: rpm -i libzen0-0.4.20-1.x86_64.RHEL_5.rpm
2. 安装libmediainfo0: rpm -i libmediainfo0-0.7.48-1.x86_64.RHEL_5.rpm
3. 安装mediainfo: rpm -i mediainfo-0.7.48-1.x86_64.RHEL_5.rpm
查看已安装的信息可以通过rpm -q libzen0来查看。
此时可以通过mediainfo来查看视频文件的扩展信息:
例如:mediainfo -f iphone_mov.MOV |grep Rotation
Rotation : 90.000
Rotation : 90?
这里就获取到了视频的偏转信息;
二、安装ffmpeg
可以通过svn方式直接下载源码:
svn co svn://svn.mplayerhq.hu/ffmpeg/trunk ffmpeg
然后切换到ffmpeg目录下:执行./configure –enable-gpl –enable-shared –disable-ffserver –disable-yasm 进行安装
再执行make
make install
最后运行ffmpeg,此时可能会有error信息:
ffmpeg: error while loading shared libraries: libavdevice.so.52: cannot open shared object file: No
这个是因为lib未加入默认目录。
切换到目录:
# cd /etc/ld.so.conf.d
# vi ffmpeg.conf
加入目录/usr/local/lib
执行ldconfig
再次执行ffmpeg,运行OK。
执行命令旋转:ffmpeg -vf “transpose=1” -i iphone_mov.MOV ouptup.MOV
下载到电脑运行,OK,视频已经旋转。

系统
# uname -a # 查看内核/操作系统/CPU信息
# head -n 1 /etc/issue # 查看操作系统版本
# cat /proc/cpuinfo # 查看CPU信息
# hostname # 查看计算机名
# lspci -tv # 列出所有PCI设备
# lsusb -tv # 列出所有USB设备
# lsmod # 列出加载的内核模块
# env # 查看环境变量
资源
# free -m # 查看内存使用量和交换区使用量
# df -h # 查看各分区使用情况
# du -sh # 查看指定目录的大小
# grep MemTotal /proc/meminfo # 查看内存总量
# grep MemFree /proc/meminfo # 查看空闲内存量
# uptime # 查看系统运行时间、用户数、负载
# cat /proc/loadavg # 查看系统负载
磁盘和分区
# mount | column -t # 查看挂接的分区状态
# fdisk -l # 查看所有分区
# swapon -s # 查看所有交换分区
# hdparm -i /dev/hda # 查看磁盘参数(仅适用于IDE设备)
# dmesg | grep IDE # 查看启动时IDE设备检测状况
网络
# ifconfig # 查看所有网络接口的属性
# iptables -L # 查看防火墙设置
# route -n # 查看路由表
# netstat -lntp # 查看所有监听端口
# netstat -antp # 查看所有已经建立的连接
# netstat -s # 查看网络统计信息
进程
# ps -ef # 查看所有进程
# top # 实时显示进程状态
用户
# w # 查看活动用户
# id # 查看指定用户信息
# last # 查看用户登录日志
# cut -d: -f1 /etc/passwd # 查看系统所有用户
# cut -d: -f1 /etc/group # 查看系统所有组
# crontab -l # 查看当前用户的计划任务
服务
# chkconfig –list # 列出所有系统服务
# chkconfig –list | grep on # 列出所有启动的系统服务
程序
# rpm -qa # 查看所有安装的软件包

[代码] [SQL]代码
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
3.应尽量避免在 where 子句中使用!=或操作符,否则将引擎放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
5.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.下面的查询也将导致全表扫描:
select id from t where name like ‘%abc%’
若要提高效率,可以考虑全文检索。
7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=’abc’ // oracle总有的是substr函数。
select id from t where datediff(day,createdate,’2005-11-30′)=0 //查过了确实没有datediff函数。
应改为:
select id from t where name like ‘abc%’
select id from t where createdate>=’2005-11-30′ and createdate= to_date(‘2005-11-30′,’yyyy-mm-dd’)
10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
12.不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(…)
13.很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
15.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
21.避免频繁创建和删除临时表,以减少系统表资源的消耗。
22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。
29.尽量避免大事务操作,提高系统并发能力。
30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。