友善交流技术...

发布新日志

  • java 带证书的https请求

    2016-08-26 16:32:02

    近来需要通过JAVA来实现https请求,普通的忽略证书的https很容易通过,但是带证书的,需要密码的请求,java自带的也可以实现,不过通过网上找的方法一个一个来实,我靠,没有一个能实现的,报了很多的错误如:握手失败的,接收数据失败的。。。。

    最后看到一个文章,是通过apache的SSL客户端来实现的,很方便的,今天记录下来,为以后遇到麻烦的朋友们,解决自己的问题。我是捣鼓了2两天啊。


    java自己带的SSL不太好使用,建议使用apache的SSL请求客户端

    1.下载jar not-yet-commons-ssl-0.3.17.jar 导入项目中 也可以通过pom.xml来实现的

    <!-- https://mvnrepository.com/artifact/org.apache.servicemix.bundles/org.apache.servicemix.bundles.not-yet-commons-ssl -->
    <dependency>
        <groupId>org.apache.servicemix.bundles</groupId>
        <artifactId>org.apache.servicemix.bundles.not-yet-commons-ssl</artifactId>
        <version>0.3.11_1</version>
    </dependency>


    2.导入证书:
       
         1. beta-zh-pc3.client.p12 这个是由安装证书的人提供的。(只要这个证书就可以完成的。上面的jssecacerts 可以不要的)
    2.jssecacerts ,可以通过InstallCert.java 工具生成的(只要输入url地址就可以的如:openapi.xxxx.com,这是在JAVA自带的工具实现时需要的,可以不要jssecacerts)
     
    3.代码的实现如下

     SSLClient client = new SSLClient();
    client.addTrustMaterial(TrustMaterial.DEFAULT);
    client.addTrustMaterial(new KeyMaterial("d:\\ssl\\beta-xxx.client.p12", "xxxxxx".toCharArray()));
    client.setCheckHostname(true); // default setting is "true" for
    client.setCheckExpiry(false); // default setting is "true" for client.setCheckCRL(true); // default setting is "true" for SSLClient
    client.setWantClientAuth(true);
    // Let's load a client certificate (max: 1 per SSLClient instance).
    client.setKeyMaterial(new KeyMaterial("D:\\truststore\\beta-xxx.client.p12", "xxxxxx".toCharArray()));
    URL url = new URL(reqUrl);
    HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
    https.setSSLSocketFactory(client);
    /* 发送数据 */
    https.setDoOutput(true);
    https.setDoInput(true);
    https.setRequestMethod("POST"); //
    https.setRequestProperty("Accept", contType); //
    https.setRequestProperty("Content-Type", contType); //
    https.connect();
    OutputStreamWriter ut = new OutputStreamWriter(https.getOutputStream(), "UTF-8"); //
    out.append(data);
    out.flush();
    out.close();
    /* 接收数据 */
    in = new BufferedReader(new InputStreamReader(
    https.getInputStream()));
    String line;
    while ((line = in.readLine()) != null) {
    result += line + "\n";
    }
    int code=https.getResponseCode();
    System.out.println(">>>>:Post CODE: "  + code);
  • dubbo 客户端链接远程provide 配置及原码

    2016-06-17 17:46:59

     这几天搞了下dubbo服务器的链接配置,目前可以通过客户端来通过配置,链接服务器的interface , 从而实现对程序的接口测试

    具体的实现如下

    1.搭建环境 (spring+dubbo+zookeeper) 其实就一部分jar
     
       pom.xml
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.dubbo.demo</groupId>
        <artifactId>DubboClientDemo</artifactId>
        <version>0.0.1</version>
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.6</source>
                        <target>1.6</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>

        <dependencies>
     
          
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>dubbo</artifactId>
                <version>2.5.3</version>
            </dependency>

            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.4.6</version>
            </dependency>

            <dependency>
                <groupId>com.github.sgroschupf</groupId>
                <artifactId>zkclient</artifactId>
                <version>0.1</version>
            </dependency>

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>4.1.6.RELEASE</version>
            </dependency>
        </dependencies>
    </project>

      另外服务器端的jar包,也需要导入的.

     2. 搭建客户端代码
      useProvide.java 代码如下
     
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import com.xxx.customer.model.User;
    import com.xxx.customer.service.UserService;
    public class useProvide {
        public static void main(String[] args) throws Exception {      
            long t;
            System.out.println(t=System.currentTimeMillis());   
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[] { "classpath*:config/useServiceDubbo.xml" });          
            UserService us =    (UserService) applicationContext.getBean("userService");
            User user = us.findUserById((long)214945);
            System.out.println(user.getEmail());
            System.out.println(user.getMobile());
            System.out.println("Total time: " + (System.currentTimeMillis()-t));
          
        }

    }
     
    3.配置 xml
       3.1 如果直接链接服务provide 参考如下配置
       useServiceDubbo.xml
      
       <dubbo:reference
            id="userService"
            interface="com.xxx.customer.service.UserService"
            url="dubbo://192.168.6.1:12010/com.xxx.customer.service.UserService"
            version="1.0.0" />
      3.2 如果直接链接zookeeper 可能参考如下配置
       
        <!-- 使用dubbo -->
        <dubbo:monitor protocol="registry" />      
        <dubbo:application name="xxxx-customer" />
     
        <dubbo:registry id="registry"  protocol="registry"  address="zookeeper://zk01.xxxx.com:2181" />
        
        <dubbo:consumer check="false" />
        <dubbo:protocol name="dubbo" port="12010" />  
        <dubbo:reference id="userServiceImpl" interface="com.xxx.customer.service.UserService" version="1.0.0"/>
        <dubbo:reference id="shareUserService" interface="com.xxx.customer.service.ShareUserService" version="1.0.0"/>

    4. 运行代码 
      useProvide.java
       执行结果为:
        2016-06-17 17:38:42,748 INFO       
        1466156323097
        test1@163.com
        13503000001
        Total time: 316 


    最后说明你的代码运行成功了. 
      
     
      

  • mysql处理高并发 防止库存超卖

    2015-09-01 15:37:55

    mysql处理高并发 防止库存超卖

     
    先来就库存超卖的问题作描述:一般电子商务网站都会遇到如团购、秒杀、特价之类的活动,而这样的活动有一个共同的特点就是访问量激增、上千甚至上万人抢购一个商品。库存肯定是很有限的,如何控制库存不让出现超买,以防止造成不必要的损失是众多电子商务网站程序员头疼的问题,这同时也是最基本的问题。

    从技术方面剖析,很多人肯定会想到事务,但是事务是控制库存超卖的必要条件,但不是充分必要条件。

    举例:

    总库存:4个商品

    请求人:a、1个商品 b、2个商品 c、3个商品

    程序如下:

    beginTranse(开启事务)

    try{

        $result = $dbca->query('select amount from s_store where postID = 12345');

        if(result->amount > 0){

            //quantity为请求减掉的库存数量

            $dbca->query('update s_store set amount = amount - quantity where postID = 12345');

        }

    }catch($e Exception){

        rollBack(回滚)

    }

    commit(提交事务)

    以上代码就是我们平时控制库存写的代码了,大多数人都会这么写,看似问题不大,其实隐藏着巨大的漏洞。数据库的访问其实就是对磁盘文件的访问,数据库中的表其实就是保存在磁盘上的一个个文件,甚至一个文件包含了多张表。例如由于高并发,当前有三个用户a、b、c三个用户进入到了这个事务中,这个时候会产生一个共享锁,所以在select的时候,这三个用户查到的库存数量都是4个,同时还要注意,mysql innodb查到的结果是有版本控制的,再其他用户更新没有commit之前(也就是没有产生新版本之前),当前用户查到的结果依然是就版本;

    然后是update,假如这三个用户同时到达update这里,这个时候update更新语句会把并发串行化,也就是给同时到达这里的是三个用户排个序,一个一个执行,并生成排他锁,在当前这个update语句commit之前,其他用户等待执行,commit后,生成新的版本;这样执行完后,库存肯定为负数了。但是根据以上描述,我们修改一下代码就不会出现超买现象了,代码如下:

    beginTranse(开启事务)

    try{

        //quantity为请求减掉的库存数量
        $dbca->query('update s_store set amount = amount - quantity where postID = 12345');

        $result = $dbca->query('select amount from s_store where postID = 12345');

        if(result->amount < 0){

           throw new Exception('库存不足');

        }

    }catch($e Exception){

        rollBack(回滚)

    }

    commit(提交事务)


    另外,更简洁的方法:

    beginTranse(开启事务)

    try{

        //quantity为请求减掉的库存数量
        $dbca->query('update s_store set amount = amount - quantity where amount>=quantity and postID = 12345');

    }catch($e Exception){

        rollBack(回滚)

    }

    commit(提交事务)

    =====================================================================================

    1、在秒杀的情况下,肯定不能如此高频率的去读写数据库,会严重造成性能问题的
    必须使用缓存,将需要秒杀的商品放入缓存中,并使用锁来处理其并发情况。当接到用户秒杀提交订单的情况下,先将商品数量递减(加锁/解锁)后再进行其他方面的处理,处理失败在将数据递增1(加锁/解锁),否则表示交易成功。
    当商品数量递减到0时,表示商品秒杀完毕,拒绝其他用户的请求。


    2、这个肯定不能直接操作数据库的,会挂的。直接读库写库对数据库压力太大,要用缓存。
    把你要卖出的商品比如10个商品放到缓存中;然后在memcache里设置一个计数器来记录请求数,这个请求书你可以以你要秒杀卖出的商品数为基数,比如你想卖出10个商品,只允许100个请求进来。那当计数器达到100的时候,后面进来的就显示秒杀结束,这样可以减轻你的服务器的压力。然后根据这100个请求,先付款的先得后付款的提示商品以秒杀完。


    3、首先,多用户并发修改同一条记录时,肯定是后提交的用户将覆盖掉前者提交的结果了。

    这个直接可以使用加锁机制去解决,乐观锁或者悲观锁。
    乐观锁,就是在数据库设计一个版本号的字段,每次修改都使其+1,这样在提交时比对提交前的版本号就知道是不是并发提交了,但是有个缺点就是只能是应用中控制,如果有跨应用修改同一条数据乐观锁就没办法了,这个时候可以考虑悲观锁。
    悲观锁,就是直接在数据库层面将数据锁死,类似于oralce中使用select xxxxx from xxxx where xx=xx for update,这样其他线程将无法提交数据。
    除了加锁的方式也可以使用接收锁定的方式,思路是在数据库中设计一个状态标识位,用户在对数据进行修改前,将状态标识位标识为正在编辑的状态,这样其他用户要编辑此条记录时系统将发现有其他用户正在编辑,则拒绝其编辑的请求,类似于你在操作系统中某文件正在执行,然后你要修改该文件时,系统会提醒你该文件不可编辑或删除。

    4、不建议在数据库层面加锁,建议通过服务端的内存锁(锁主键)。当某个用户要修改某个id的数据时,把要修改的id存入memcache,若其他用户触发修改此id的数据时,读到memcache有这个id的值时,就阻止那个用户修改。

    5、实际应用中,并不是让mysql去直面大并发读写,会借助“外力”,比如缓存、利用主从库实现读写分离、分表、使用队列写入等方法来降低并发读写。



     
  • jprofile 的使用与安装

    2015-08-25 14:36:04

    项目的需求: 要求分析接口的性能,分析接口慢的原因,当然分析的手段有很多的,打印输出日志是一个常用的方法,不过这个方法要修改代码; 那有没有不修改代码可以分析出耗费CPU及慢的函数呢? 答案是肯定的,今天推荐一个工具jprofile 来分析性能问题如下

       1. 下载jprofile工具
        jprofiler_linux_9_0_2.rpm
       2. 安装jprofile

        rpm -ivh jprofiler_linux_9_0_2.rpm
       
       3.设置环境变量
       

        JPROFILER_HOME=/opt/jprofiler9/bin/linux-x64

        export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JPROFILER_HOME


       4.设置启动参数 jvm  参数设置

       

         -agentpath:/opt/jprofiler9/bin/linux-x64/libjprofilerti.so=port=8849,nowait

       5.启动客户端的jprofile

       6.监控jprofile结果如下
     
       
  • MongoDB 查询的性能

    2015-08-12 09:45:01

    近来无事,想研究一下mongodb ,都说性能很好的,果真是如些吗? 所以做了几个小的实现如下

    1. 创建表
        db.createCollection(“userInfo”) 创建表
    2. 插入数据100w
     
    3. 查询数据,具体的代码如下
              public  String test() throws UnknownHostException {
            @SuppressWarnings("deprecation")
            Long sTime = System.currentTimeMillis(); 
                Mongo mg = new Mongo("192.168.0.177", 27017);
            DB db = mg.getDB("test");
            DBCollection users = db.getCollection("userInfo");      
              //查询所有的数据
            DBCursor cur = (DBCursor) users.find(new BasicDBObject("database", "mkyongDB400000"));
            String str=cur.next().toString();
            Long eTime = System.currentTimeMillis();
           
            System.out.println("Time = " + (eTime-sTime));     
           
            cur.close();
            mg.close();
            return  str;
    }
    4.场景设计

      4.1 测试场景1
           测试结果: 查询1万次 ,链接1w次,有索引 查询速度平均值是:15毫秒

     4.2 测试场景2:
              

          测试结果: 查询1万 次,链接1次,有索引 查询速度平均值是:8毫秒
    4.3 测试场景3:
      
         测试结果: 50v并发,执行1000次, 有索引,查询速度平均值是:200毫秒

    4.4 测试场景4:
     
        测试结果:  查询1w次,链接1w次,无索引,查询速度平均值是: 500毫秒

    从这个意义来讲索引对mongodb的重要性.  个人感觉这种速度比较mysql一次查询快多了.
        
  • MongoDB与内存(转)

    2015-08-12 09:43:39

    但凡初次接触MongoDB的人,无不惊讶于它对内存的贪得无厌,至于个中缘由,我先讲讲Linux是如何管理内存的,再说说MongoDB是如何使用内存的,答案自然就清楚了。

    据说带着问题学习更有效,那就先看一个MongoDB服务器的top命令结果:

    shell> top -p $(pidof mongod)

    Mem:  32872124k total, 30065320k used,  2806804k free,   245020k buffers

    Swap:  2097144k total,      100k used,  2097044k free, 26482048k cached

    VIRT  RES  SHR %MEM

    1892g  21g  21g 69.6

    这台MongoDB服务器有没有性能问题?大家可以一边思考一边继续阅读。

    先讲讲Linux是如何管理内存的

    在 Linux里(别的系统也差不多),内存有物理内存和虚拟内存之说,物理内存是什么自然无需解释,虚拟内存实际是物理内存的抽象,多数情况下,出于方便性 的考虑,程序访问的都是虚拟内存地址,然后操作系统会通过Page Table机制把它翻译成物理内存地址,详细说明可以参考Understanding Memory和Understanding Virtual Memory,至于程序是如何使用虚拟内存的,可以参考Playing with Virtual Memory,这里就不多费口舌了。

    很多人会把虚拟内存和Swap混为一谈,实际上Swap只是虚拟内存引 申出的一种技术而已:操作系统一旦物理内存不足,为了腾出内存空间存放新内容,就会把当前物理内存中的内容放到交换分区里,稍后用到的时候再取回来,需要 注意的是,Swap的使用可能会带来性能问题,偶尔为之无需紧张,糟糕的是物理内存和交换分区频繁的发生数据交换,这被称之为Swap颠簸,一旦发生这种 情况,先要明确是什么原因造成的,如果是内存不足就好办了,加内存就可以解决,不过有的时候即使内存充足也可能会出现这种问题,比如MySQL就有可能出 现这样的情况,一个可选的解决方法是限制使用Swap:

    shell> sysctl -w vm.swappiness=0

    查看内存情况最常用的是free命令:

    shell> free -m

                total       used       free     shared    buffers     cached

    Mem:         32101      29377       2723          0        239      25880

    -/+ buffers/cache:       3258      28842

    Swap:         2047          0       2047

    新 手看到used一栏数值偏大,free一栏数值偏小,往往会认为内存要用光了。其实并非如此,之所以这样是因为每当我们操作文件的时候,Linux都会尽 可能的把文件缓存到内存里,这样下次访问的时候,就可以直接从内存中取结果,所以cached一栏的数值非常的大,不过不用担心,这部分内存是可回收的, 操作系统的虚拟内存管理器会按照LRU算法淘汰冷数据。还有一个buffers,也是可回收的,不过它是保留给块设备使用的。

    知道了原理,我们就可以推算出系统可用的内存是free + buffers + cached:

    shell> echo $((2723 + 239 + 25880))

    28842

    至于系统实际使用的内存是used – buffers – cached:

    shell> echo $((29377 - 239 - 25880))

    3258

    除了free命令,还可以使用sar命令:

    shell> sar -r

    kbmemfree kbmemused  %memused kbbuffers  kbcached

     3224392  29647732     90.19    246116  26070160

    shell> sar -W

    pswpin/s pswpout/s

       0.00      0.00

    希望你没有被%memused吓到,如果不幸言中,重读本文。

    再说说MongoDB是如何使用内存的

    目 前,MongoDB使用的是内存映射存储引擎,它会把数据文件映射到内存中,如果是读操作,内存中的数据起到缓存的作用,如果是写操作,内存还可以把随机 的写操作转换成顺序的写操作,总之可以大幅度提升性能。MongoDB并不干涉内存管理工作,而是把这些工作留给操作系统的虚拟内存管理器去处理,这样做 的好处是简化了MongoDB的工作,但坏处是你没有方法很方便的控制MongoDB占多大内存,幸运的是虚拟内存管理器的存在让我们多数时候并不需要关 心这个问题。

    MongoDB的内存使用机制让它在缓存重建方面更有优势,简而言之:如果重启进程,那么缓存依然有效,如果重 启系统,那么可以通过拷贝数据文件到/dev/null的方式来重建缓存,更详细的描述请参考:Cache Reheating – Not to be Ignored。

    有时候,即便MongoDB使用的是64位操作系统,也可能会遭遇OOM问题,出现这种情况,多半是因为限制了内存的大小所致,可以这样查看当前值:

    shell> ulimit -a | grep memory

    多数操作系统缺省都是把它设置成unlimited的,如果你的操作系统不是,可以这样修改:

    shell> ulimit -m unlimited

    shell> ulimit -v unlimited

    注:ulimit的使用是有上下文的,最好放在MongoDB的启动脚本里。

    有时候,MongoDB连接数过多的话,会拖累性能,可以通过serverStatus查询连接数:

    mongo> db.serverStatus().connections

    每个连接都是一个线程,需要一个Stack,Linux下缺省的Stack设置一般比较大:

    shell> ulimit -a | grep stack

    stack size              (kbytes, -s) 10240

    至于MongoDB实际使用的Stack大小,可以用如下命令确认(单位:K):

    shell> cat /proc/$(pidof mongod)/limits | grep stack | awk -F 'size' '{print int($NF)/1024}'

    如果Stack过大(比如:10240K)的话没有意义,简单对照命令结果中的Size和Rss:

    shell> cat /proc/$(pidof mongod)/smaps | grep 10240 -A 10

    所有连接消耗的内存加起来会相当惊人,推荐把Stack设置小一点,比如说1024:

    shell> ulimit -s 1024

    注:从MongoDB1.8.3开始,MongoDB会在启动时自动设置Stack。

    有时候,出于某些原因,你可能想释放掉MongoDB占用的内存,不过前面说了,内存管理工作是由虚拟内存管理器控制的,幸好可以使用MongoDB内置的closeAllDatabases命令达到目的:

    mongo> use admin

    mongo> db.runCommand({closeAllDatabases:1})

    另外,通过调整内核参数drop_caches也可以释放缓存:

    shell> sysctl -w vm.drop_caches=1

    平时可以通过mongo命令行来监控MongoDB的内存使用情况,如下所示:

    mongo> db.serverStatus().mem:

    {

       "resident" : 22346,

       "virtual" : 1938524,

       "mapped" : 962283

    }

    还可以通过mongostat命令来监控MongoDB的内存使用情况,如下所示:

    shell> mongostat

    mapped  vsize    res faults

     940g  1893g  21.9g      0

    其中内存相关字段的含义是:

    mapped:映射到内存的数据大小

    visze:占用的虚拟内存大小

    res:占用的物理内存大小

    注:如果操作不能在内存中完成,结果faults列的数值不会是0,视大小可能有性能问题。

    在 上面的结果中,vsize是mapped的两倍,而mapped等于数据文件的大小,所以说vsize是数据文件的两倍,之所以会这样,是因为本例 中,MongoDB开启了journal,需要在内存里多映射一次数据文件,如果关闭journal,则vsize和mapped大致相当。

    如果想验证这一点,可以在开启或关闭journal后,通过pmap命令来观察文件映射情况:

    shell> pmap $(pidof mongod)

    到底MongoDB配备多大内存合适?宽泛点来说,多多益善,如果要确切点来说,这实际取决于你的数据及索引的大小,内存如果能够装下全部数据加索引是最佳情况,不过很多时候,数据都会比内存大,比如本文所涉及的MongoDB实例:

    mongo> db.stats()

    {

       "dataSize" : 1004862191980,

       "indexSize" : 1335929664

    }

    本 例中索引只有1G多,内存完全能装下,而数据文件则达到了1T,估计很难找到这么大内存,此时保证内存能装下热数据即可,至于热数据是多少,取决于具体的 应用,你也可以通过观察faults的大小来判断当前内存是否能够装下热数据,如果faults持续变大,就说明当前内存已经不能满足热数据的大小了。如 此一来内存大小就明确了:内存 > 索引 + 热数据,最好有点富余,毕竟操作系统本身正常运转也需要消耗一部分内存。

    关于MongoDB与内存的话题,大家还可以参考官方文档中的相关介绍。

  • 性能测试_自动化监控_收集数据_java代码

    2015-03-23 09:01:56

    工作中有时间会遇到大量开启监控的工作.如果机器数量比较多,人的操作时间可能就会耗费过多,所以目前来看,写脚本来实现监控是最好的解决办法.
      下面的是通过java 来实现SSH 登录:
        上传监控脚本
        执行命令
        下载监控结果.
      三个功能... 满足了自动化监控的基本需求.

      当然也可以写成exe的监控脚本,等有时间了再详细完善一下.
    package TT;

    import java.lang.reflect.Array;

    import ch.ethz.ssh2.Connection;
    import ch.ethz.ssh2.ConnectionInfo;
    import ch.ethz.ssh2.SCPClient;
    import ch.ethz.ssh2.Session;

    /**
     *
     * @author chenshu
     */
    public class Main {

        String IpArr[][] = {
                { "10.168.31.69", "xxxxx", "xxxxxx" },
                { "10.168.32.37", "xxxxxx", "xxxxxx" },
                { "10.168.32.38", "xxxxxx", "xxxxxx" },
                { "10.168.31.11", "xxxxxx","xxxxxx" },
                { "10.168.31.12", "xxxxxx","xxxxxx" },
                { "10.168.31.18", "xxxxxx","xxxxxx" },
                { "10.168.31.19", "xxxxxx","xxxxxx" },
                };
        
        public static void main(String[] args) {
            Main m = new Main();
            // m.GetFile();
            //m.putFile();
             m.ExecCmd();
        }

        public void ExecCmd() {
           
            //String cmd = "mv nmon_x86_64_rhel54 nmon";
             String cmd="chmod 777 nmon_x86_64_rhel54";
            // String cmd="./nmon_x86_64_rhel54 -f -s5 -c5000";
            for (int i = 0; i < IpArr.length; i++) {
                System.out.println("-------ExecCmd 执行 -----" + i );
                try {

                    Connection con = new Connection(IpArr[i][0]);
                    ConnectionInfo info = con.connect();
                    boolean result = con.authenticateWithPassword(IpArr[i][1],IpArr[i][2]);
                    Session session = con.openSession();
                    session.execCommand(cmd);
                    session.close();
                    System.out.println("IPaddr" + IpArr[i][0]);
                    System.out.println("userName" + IpArr[i][1]);
                    System.out.println("passwd" + IpArr[i][2]);
                } catch (Exception ex) {
                    System.out.println(ex.getLocalizedMessage());
                }
            }
        }

        public void putFile() {
         

            for (int i = 0; i < IpArr.length; i++) {   
                System.out.println("-------putFile 执行 -----" + i );
                try {                   
                    // 创建链接
                    Connection con = new Connection(IpArr[i][0]);
                    ConnectionInfo info = con.connect();
                    boolean result = con.authenticateWithPassword(IpArr[i][1],IpArr[i][2]);
                    Session session = con.openSession();             
                    // 创建scp客户端
                    SCPClient scp = con.createSCPClient();
                    // 上传文件
                    scp.put("D:\\nmon\\nmonfile\\nmon_x86_64_rhel54", "./");
                    scp.wait(10000);
                    con.close();
                    System.out.println("IPaddr: " + IpArr[i][0]  );
                    System.out.println("userName: " +IpArr[i][1]  );
                    System.out.println("passwd: " +IpArr[i][2] );

                } catch (Exception ex) {
                    System.out.println(ex.getLocalizedMessage());
                }
            }

        }

        public void GetFile() {
           
            // IP列表
            for (int i = 0; i < IpArr.length; i++) {
                System.out.println("-------GetFile 执行 -----" + i );
                try {
                    // 创建链接               
                    Connection con = new Connection(IpArr[i][0]);
                    ConnectionInfo info = con.connect();
                    boolean result = con.authenticateWithPassword(IpArr[i][1],IpArr[i][2]);
                    Session session = con.openSession();
                    // 创建scp客户端
                    SCPClient scp = con.createSCPClient();
                    // 下载文件
                    scp.get("*.nmon", "D:\\nmon");
                    scp.wait(10000);
                    System.out.println("IPaddr: " + IpArr[i][0]  );
                    System.out.println("userName: " +IpArr[i][1]  );
                    System.out.println("passwd: " +IpArr[i][2] );

                } catch (Exception ex) {
                    System.out.println(ex.getLocalizedMessage());
                }
            }
        }

    }

  • loadrunner 三种post请求

    2015-02-12 13:25:07

    测试中经常会遇到不知道使用那种类型的post请求格式. 那么怎么办呢?

     Loadrunner三种post格式的请求

    1  web_custom_request

    int web_custom_request( const char *RequestName, <List of Attributes>,
    [EXTRARES, <List of Resource Attributes>,] LAST ); 

     第一种: 自定义http格式的请求,可以是任何的方式或是body 如下:

    web_custom_request("xxxxx",

                                 "URL=http://xxxx.xxxx.xxx",

                                 "Method=POST",

                                 "Resource=0",

                                 "Snapshot=t10.inf",

                                 "Mode=HTML",

                                 "EncType=application/x-www-form-urlencoded",

                                 "Body={\"sign\": \"{sign}\",\"token\": \"68C116cd449034db04C8ff2B7271B345\",\"time\": \"123456\",\"params\": {\"city_id\": \"2419\",\"advert_id\": \"2139\",\"offset\": \"0\",\"pagesize\": \"10\"}}",LAST);

     

    2  web_submit_data 

    int web_submit_data( const char *StepName, const char *Action, <List of Attributes>, ITEMDATA, <List of data>, [ EXTRARES, <List of Resource Attributes>,] LAST ); 

    第二种提交:postget请求. 

    web_submit_data("pay_check.php",

             "Action=http://buyinterface.{url}/v1/pay_check.php",

                                 "Method=POST",

                                 "RecContentType=text/html",

                                 "Snapshot=t7.inf",

                                 "Mode=HTML",

                                 ITEMDATA,

                                 "Name=my_id", "Value={myid}", ENDITEM,

                                 "Name=trade_no", "Value={trano_1}", ENDITEM,      

                                 LAST);

    3  web_submit_form.

    int web_submit_form( const char *StepName, <List of Attributes>, <List of Hidden Fields>, ITEMDATA, <List of Data Fields>, [ EXTRARES, <List of Resource Attributes>,] LAST );

    第三种: 只支持post请求

    web_submit_form("db2net.exe",

        ITEMDATA,

        "name=library.TITLE", "value=Practical UNIX Security", ENDITEM,

        "name=library.AUTHOR_S_", "value=Garfinkel", ENDITEM,

        "name=library.SUBJECTS", "value=", ENDITEM,

        LAST );

     遇到不知道怎么办时,最好问下开发,支持什么格式.


     
     
  • loadrunner 乱码解决

    2015-02-12 13:10:03

    loadrunner 乱码解决办法:

    1.日志输出为乱码的解决办法 如下面的代码

     背景:有时间测试时,想知道服务器返回的结果是什么?但是乱码,这时候建议进行换码

    //获取返回结果

    web_reg_save_param("cont","LB=","RB=","Search=body",LAST);

                  web_custom_request("xxxxx",                        

                     "URL=http://xxxx.xxxx.xxx",

                                 "Method=POST",

                                 "Resource=0",

                                 "Snapshot=t10.inf",

                                 "Mode=HTML",

                                 "EncType=application/x-www-form-urlencoded",

                                 "Body={\"sign\": \"{sign}\",\"token\": \"68C116cd449034db04C8ff2B7271B345\",\"time\": \"123456\",\"params\": {\"city_id\": \"2419\",\"advert_id\": \"2139\",\"offset\": \"0\",\"pagesize\": \"10\"}}",LAST);

     

     //将结果换码成UTF-8 ,输出返回的结果

         lr_convert_string_encoding(lr_eval_string("{cont}"),"utf-8",NULL,"temp");

             lr_error_message("%s",lr_eval_string("{temp}"));

    2.解决输入参数为汉字的办法:

       背景: 如果输入参数是汉字时,直接输入会报错的,需要转码.如下面

      

        //定义字符数组

    char tmp[1024] ;

    //{key} 自己要生成的参数: 里面是汉字, 通过lr_convert_string_encoding 转化成UTF8

             lr_convert_string_encoding(lr_eval_string("{key}"),LR_ENC_SYSTEM_LOCALE,LR_ENC_UTF8,"str");

      

             strcpy(tmp,lr_eval_string("{str}"));

             lr_log_message("str is %s",tmp);

             lr_save_string(tmp,"keyword");

        // 参数输入{keyword} 就可以直接使用了.
        //使用举例
       web_url("search",
              "url=http://www.xxx.com/search?keywor={keyword}",
               LAST);



     

  • 造成TPS波动的原因分析

    2015-02-06 22:22:14

    测试中遇到谁多的TPS波动的问题下面将经验分享一下

    1. 有规律的波动,这样的问题,可能原因如下

      (1)程序代码的原因:
       如30分钟将重构一次索引,就有可能造成这个时间点的波动,当时测试一个项目,java-lucene, 全文检索,就出现这样的问题,当时的问题是由于内存不足造成的异常波动 ;
        定时执行脚本任务,造成的有规律波动.
        业务规则造成的波动 ,比如一秒只接受10个请求,其它请求拒绝. 

       (2)日志输出造成的波动 : 
       
       有时间有规律的小幅度波动,可能是日志写硬盘造成的,可以结合**的结果I/O 使用情况与分析波动的关系.

        (3)redis持久化造成的波动 :  
      
        持久化时,会将内存的数据写入硬盘中,这时如果很有规律的波动,也有可能是redis 有规律的写入硬盘造成硬盘那个点无法提供服务造成的波动.

     2. 无规律波动可能原因

        (1)网络造成的波动:

        网络不稳定造成的波动,也是不太好排的,不过loadrunner提供接口下载的时间,可以分析网络的原因

        (2)本地打开thinktime 时间 造成的异常波动 

          loadrunner打开thinktime造成的异常波动 

         (3)本地写日志
         在测试时,如果loadrunner打开本地写日志,本地硬盘如果性能不太好,可能会造成波动 . 这个问题已经遇到过了,害的我折腾了两天的时间才搞定这个问题.

         (4)部分服务器down了

         测试中发现线上一台服务器处于假死状态,这时候造成了请求异常. 不过一般这样的错误都是链接失败之类的,还是比较好分析出来的

         (5)服务器假死
         运行一个时间后,处于假死状态,这时候,可能所有的请求都会失败的...
         线程死锁也有可能会造成 所有的请求等待的问题. 
     
         (6) 黑白名单的问题
      
           IP请求次数过多,造成访问的异常;
           一个用户1秒请求次数太多

     上面的问题都有可能会造成波动.... 在测试中要一个个的排除来,很难的,最好先与开发多沟通... 有时间问题就定位了.
            
  • mysql查看当前运行状态(转)

    2015-01-21 14:59:35

    mysql查看当前运行状态

    show processlist;只列出前100条,如果想全列出请使用show full processlist;
    mysql> show processlist;命令: show status;

    状态名 作用域 详细解释
    Aborted_clients Global 由于客户端没有正确关闭连接导致客户端终止而中断的连接数
    Aborted_connects Global 试图连接到MySQL服务器而失败的连接数
    Binlog_cache_disk_use Global 使用临时二进制日志缓存但超过binlog_cache_size值并使用临时文件来保存事务中的语句的事务数量
    Binlog_cache_use Global 使用临时二进制日志缓存的事务数量
    Bytes_received Both 从所有客户端接收到的字节数。
    Bytes_sent Both 发送给所有客户端的字节数。
    com*   各种数据库操作的数量
    Compression Session 客户端与服务器之间只否启用压缩协议
    Connections Global 试图连接到(不管是否成功)MySQL服务器的连接数
    Created_tmp_disk_tables Both 服务器执行语句时在硬盘上自动创建的临时表的数量
    Created_tmp_files Global mysqld已经创建的临时文件的数量
    Created_tmp_tables Both 服务器执行语句时自动创建的内存中的临时表的数量。如果Created_tmp_disk_tables较大,你可能要增加tmp_table_size值使临时 表基于内存而不基于硬盘
    Delayed_errors Global 用INSERT DELAYED写的出现错误的行数(可能为duplicate key)。
    Delayed_insert_threads Global 使用的INSERT DELAYED处理器线程数。
    Delayed_writes Global 写入的INSERT DELAYED行数
    Flush_commands Global 执行的FLUSH语句数。
    Handler_commit Both 内部提交语句数
    Handler_delete Both 行从表中删除的次数。
    Handler_discover Both MySQL服务器可以问NDB CLUSTER存储引擎是否知道某一名字的表。这被称作发现。Handler_discover说明通过该方法发现的次数。
    Handler_prepare Both A counter for the prepare phase of two-phase commit operations.
    Handler_read_first Both 索引中第一条被读的次数。如果较高,它建议服务器正执行大量全索引扫描;例如,SELECT col1 FROM foo,假定col1有索引。
    Handler_read_key Both 根据键读一行的请求数。如果较高,说明查询和表的索引正确。
    Handler_read_next Both 按照键顺序读下一行的请求数。如果你用范围约束或如果执行索引扫描来查询索引列,该值增加。
    Handler_read_prev Both 按照键顺序读前一行的请求数。该读方法主要用于优化ORDER BY … DESC。
    Handler_read_rnd Both 根据固定位置读一行的请求数。如果你正执行大量查询并需要对结果进行排序该值较高。你可能使用了大量需要MySQL扫描整个表的查询或你的连接没有正确使用键。
    Handler_read_rnd_next Both 在数据文件中读下一行的请求数。如果你正进行大量的表扫描,该值较高。通常说明你的表索引不正确或写入的查询没有利用索引。
    Handler_rollback Both 内部ROLLBACK语句的数量。
    Handler_savepoint Both 在一个存储引擎放置一个保存点的请求数量。
    Handler_savepoint_rollback Both 在一个存储引擎的要求回滚到一个保存点数目。
    Handler_update Both 在表内更新一行的请求数。
    Handler_write Both 在表内插入一行的请求数。
    Innodb_buffer_pool_pages_data Global 包含数据的页数(脏或干净)。
    Innodb_buffer_pool_pages_dirty Global 当前的脏页数。
    Innodb_buffer_pool_pages_flushed Global 要求清空的缓冲池页数
    Innodb_buffer_pool_pages_free Global 空页数。
    Innodb_buffer_pool_pages_latched Global 在InnoDB缓冲池中锁定的页数。这是当前正读或写或由于其它原因不能清空或删除的页数。
    Innodb_buffer_pool_pages_misc Global 忙的页数,因为它们已经被分配优先用作管理,例如行锁定或适用的哈希索引。该值还可以计算为 Innodb_buffer_pool_pages_total – Innodb_buffer_pool_pages_free – Innodb_buffer_pool_pages_data。
    Innodb_buffer_pool_pages_total Global 缓冲池总大小(页数)。
    Innodb_buffer_pool_read_ahead_rnd Global InnoDB初始化的“随机”read-aheads数。当查询以随机顺序扫描表的一大部分时发生。
    Innodb_buffer_pool_read_ahead_seq Global InnoDB初始化的顺序read-aheads数。当InnoDB执行顺序全表扫描时发生。
    Innodb_buffer_pool_read_requests Global InnoDB已经完成的逻辑读请求数。
    Innodb_buffer_pool_reads Global 不能满足InnoDB必须单页读取的缓冲池中的逻辑读数量。
    Innodb_buffer_pool_wait_free Global 一般情况,通过后台向InnoDB缓冲池写。但是,如果需要读或创建页,并且没有干净的页可用,则它还需要先等待页面清空。该计数器对等待实例进行记数。如果已经适当设置缓冲池大小,该值应小。
    Innodb_buffer_pool_write_requests Global 向InnoDB缓冲池的写数量。
    Innodb_data_fsyncs Global fsync()操作数。
    Innodb_data_pending_fsyncs Global 当前挂起的fsync()操作数。
    Innodb_data_pending_reads Global 当前挂起的读数。
    Innodb_data_pending_writes Global 当前挂起的写数。
    Innodb_data_read Global 至此已经读取的数据数量(字节)。
    Innodb_data_reads Global 数据读总数量。
    Innodb_data_writes Global 数据写总数量。
    Innodb_data_written Global 至此已经写入的数据量(字节)。
    Innodb_dblwr_pages_written Global 已经执行的双写操作数量
    Innodb_dblwr_writes Global 双写操作已经写好的页数
    Innodb_log_waits Global 我们必须等待的时间,因为日志缓冲区太小,我们在继续前必须先等待对它清空
     
    Innodb_log_write_requests Global 日志写请求数。
    Innodb_log_writes Global 向日志文件的物理写数量。
    Innodb_os_log_fsyncs Global 向日志文件完成的fsync()写数量。
    Innodb_os_log_pending_fsyncs Global 挂起的日志文件fsync()操作数量。
    Innodb_os_log_pending_writes Global 挂起的日志文件写操作
    Innodb_os_log_written Global 写入日志文件的字节数。
    Innodb_page_size Global 编译的InnoDB页大小(默认16KB)。许多值用页来记数;页的大小很容易转换为字节。
    Innodb_pages_created Global 创建的页数。
    Innodb_pages_read Global 读取的页数。
    Innodb_pages_written Global 写入的页数。
    Innodb_row_lock_current_waits Global 当前等待的待锁定的行数。
    Innodb_row_lock_time Global 行锁定花费的总时间,单位毫秒。
    Innodb_row_lock_time_avg Global 行锁定的平均时间,单位毫秒。
    Innodb_row_lock_time_max Global 行锁定的最长时间,单位毫秒。
    Innodb_row_lock_waits Global 一行锁定必须等待的时间数。
    Innodb_rows_deleted Global 从InnoDB表删除的行数。
    Innodb_rows_inserted Global 插入到InnoDB表的行数。
    Innodb_rows_read Global 从InnoDB表读取的行数。
    Innodb_rows_updated Global InnoDB表内更新的行数。
    Key_blocks_not_flushed Global 键缓存内已经更改但还没有清空到硬盘上的键的数据块数量。
    Key_blocks_unused Global 键缓存内未使用的块数量。你可以使用该值来确定使用了多少键缓存
    Key_blocks_used Global 键缓存内使用的块数量。该值为高水平线标记,说明已经同时最多使用了多少块。
    Key_read_requests Global 从缓存读键的数据块的请求数。
    Key_reads Global 从硬盘读取键的数据块的次数。如果Key_reads较大,则Key_buffer_size值可能太小。可以用Key_reads/Key_read_requests计算缓存损失率。
    Key_write_requests Global 将键的数据块写入缓存的请求数。
    Key_writes Global 向硬盘写入将键的数据块的物理写操作的次数。
    Last_query_cost Session 用查询优化器计算的最后编译的查询的总成本。用于对比同一查询的不同查询方案的成本。默认值0表示还没有编译查询。 默认值是0。Last_query_cost具有会话范围。
    Max_used_connections Global 服务器启动后已经同时使用的连接的最大数量。
    ndb*   ndb集群相关
    Not_flushed_delayed_rows Global 等待写入INSERT DELAY队列的行数。
    Open_files Global 打开的文件的数目。
    Open_streams Global 打开的流的数量(主要用于记录)。
    Open_table_definitions Global 缓存的.frm文件数量
    Open_tables Both 当前打开的表的数量。
     
    Opened_files Global 文件打开的数量。不包括诸如套接字或管道其他类型的文件。 也不包括存储引擎用来做自己的内部功能的文件。
    Opened_table_definitions Both 已经缓存的.frm文件数量
    Opened_tables Both 已经打开的表的数量。如果Opened_tables较大,table_cache 值可能太小。
    Prepared_stmt_count Global 当前的预处理语句的数量。 (最大数为系统变量: max_prepared_stmt_count)
    Qcache_free_blocks Global 查询缓存内自由内存块的数量。
    Qcache_free_memory Global 用于查询缓存的自由内存的数量。
    Qcache_hits Global 查询缓存被访问的次数。
    Qcache_inserts Global 加入到缓存的查询数量。
    Qcache_lowmem_prunes Global 由于内存较少从缓存删除的查询数量。
    Qcache_not_cached Global 非缓存查询数(不可缓存,或由于query_cache_type设定值未缓存)。
    Qcache_queries_in_cache Global 登记到缓存内的查询的数量。
    Qcache_total_blocks Global 查询缓存内的总块数。
    Queries Both 服务器执行的请求个数,包含存储过程中的请求。
    Questions Both 已经发送给服务器的查询的个数。
    Rpl_status Global 失败安全复制状态(还未使用)。
    Select_full_join Both 没有使用索引的联接的数量。如果该值不为0,你应仔细检查表的索引
    Select_full_range_join Both 在引用的表中使用范围搜索的联接的数量。
    Select_range Both 在第一个表中使用范围的联接的数量。一般情况不是关键问题,即使该值相当大。
    Select_range_check Both 在每一行数据后对键值进行检查的不带键值的联接的数量。如果不为0,你应仔细检查表的索引。
    Select_scan Both 对第一个表进行完全扫描的联接的数量。
    Slave_heartbeat_period Global 复制的心跳间隔
    Slave_open_temp_tables Global 从服务器打开的临时表数量
    Slave_received_heartbeats Global 从服务器心跳数
    Slave_retried_transactions Global 本次启动以来从服务器复制线程重试次数
    Slave_running Global 如果该服务器是连接到主服务器的从服务器,则该值为ON。
    Slow_launch_threads Both 创建时间超过slow_launch_time秒的线程数。
    Slow_queries Both 查询时间超过long_query_time秒的查询的个数。
    Sort_merge_passes Both 排序算法已经执行的合并的数量。如果这个变量值较大,应考虑增加sort_buffer_size系统变量的值。
    Sort_range Both 在范围内执行的排序的数量。
    Sort_rows Both 已经排序的行数。
    Sort_scan Both 通过扫描表完成的排序的数量。
    ssl*   ssl连接相关
    Table_locks_immediate Global 立即获得的表的锁的次数。
    Table_locks_waited Global 不能立即获得的表的锁的次数。如果该值较高,并且有性能问题,你应首先优化查询,然后拆分表或使用复制。
    Threads_cached Global 线程缓存内的线程的数量。
    Threads_connected Global 当前打开的连接的数量。
    Threads_created Global 创建用来处理连接的线程数。如果Threads_created较大,你可能要增加thread_cache_size值。缓存访问率的计算方法Threads_created/Connections。
    Threads_running Global 激活的(非睡眠状态)线程数。
    Uptime Global 服务器已经运行的时间(以秒为单位)。
    Uptime_since_flush_status Global 最近一次使用FLUSH STATUS 的时间(以秒为单位)。



  • Linux下Fork与Exec使用(转)

    2014-09-20 10:09:58

    Linux下Fork与Exec使用

    一、引言

      对于没有接触过Unix/Linux操作系统的人来说,fork是最难理解的概念之一:它执 行一次却返回两个值。fork函数是Unix系统最杰出的成就之一,它是七十年代UNIX早期的开发者经过长期在理论和实践上的艰苦探索后取得的成果,一 方面,它使操作系统在进程管理上付出了最小的代价,另一方面,又为程序员提供了一个简洁明了的多进程方法。与DOS和早期的Windows不 同,Unix/Linux系统是真正实现多任务操作的系统,可以说,不使用多进程编程,就不能算是真正的Linux环境下编程。

      多线程程序设计的概念早在六十年代就被提出,但直到八十年代中期,Unix系统中才引入多线程机制,如今,由于自身的许多优点,多线程编程已经得到了广泛的应用。

    下面,我们将介绍在Linux下编写多进程和多线程程序的一些初步知识。

    二、多进程编程

    什么是一个进程?进程这个概念是针对系统而不是针对用户的,对用户来说,他面对的概念是程序。当 用户敲入命令执行一个程序的时候,对系统而言,它将启动一个进程。但和程序不同的是,在这个进程中,系统可能需要再启动一个或多个进程来完成独立的多个任 务。多进程编程的主要内容包括进程控制和进程间通信,在了解这些之前,我们先要简单知道进程的结构。

      2.1 Linux下进程的结构

      Linux下一个进程在内存里有三部分的数据,就是"代码段"、"堆栈段"和"数据段"。其实学过汇编语言的人一定知道,一般的CPU都有上述三种段寄存器,以方便操作系统的运行。这三个部分也是构成一个完整的执行序列的必要的部分。

      "代码段",顾名思义,就是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程 序,那么它们就可以使用相同的代码段。"堆栈段"存放的就是子程序的返回地址、子程序的参数以及程序的局部变量。而数据段则存放程序的全局变量,常数以及 动态数据分配的数据空间(比如用malloc之类的函数取得的空间)。这其中有许多细节问题,这里限于篇幅就不多介绍了。系统如果同时运行数个相同的程 序,它们之间就不能使用同一个堆栈段和数据段。  

      2.2 Linux下的进程控制

    在传统的Unix环境下,有两个基本的操作用于创建和修改进程:函数fork( )用来创建一个新的进程,该进程几乎是当前进程的一个完全拷贝;函数族exec( )用来启动另外的进程以取代当前运行的进程。Linux的进程控制和传统的Unix进程控制基本一致,只在一些细节的地方有些区别,例如在Linux系统 中调用vfork和fork完全相同,而在有些版本的Unix系统中,vfork调用有不同的功能。由于这些差别几乎不影响我们大多数的编程,在这里我们 不予考虑。

      2.2.1 fork()

      fork在英文中是"分叉"的意思。为什么取这个名字呢?因为一个进程在运行中,如果使用了fork,就产生了另一个进程,于是进程就"分叉"了,所以这个名字取得很形象。下面就看看如何具体使用fork,这段程序演示了使用fork的基本框架:

     1void main()
     2{
     3    int i;
     4    if ( fork() == 0 ) 
     5    {
     6       /* 子进程程序 */
     7       for ( i = 1; i <1000; i ++ ) 
     8          printf("This is child process\n");
     9    }
    10    else 
    11    {
    12       /* 父进程程序*/
    13       for ( i = 1; i <1000; i ++ ) 
    14       printf("This is process process\n");
    15    }
    16}

      程序运行后,你就能看到屏幕上交替出现子进程与父进程各打印出的一千条信息了。如果程序还在运行中,你用ps命令就能看到系统中有两个它在运行了。

      那么调用这个fork函数时发生了什么呢?fork函数启动一个新的进程,前面我们说过,这 个进程几乎是当前进程的一个拷贝:子进程和父进程使用相同的代码段;子进程复制父进程的堆栈段和数据段。这样,父进程的所有数据都可以留给子进程,但是, 子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。它们再要交 互信息时,只有通过进程间通信来实现,这将是我们下面的内容。既然它们如此相象,系统如何来区分它们呢?这是由函数的返回值来决定的。对于父进程, fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零。在操作系统中,我们用ps函数就可以看到不同的进程号,对父进程而言,它的进程 号是由比它更低层的系统调用赋予的,而对于子进程而言,它的进程号即是fork函数对父进程的返回值。在程序设计中,父进程和子进程都要调用函数 fork()下面的代码,而我们就是利用fork()函数对父子进程的不同返回值用if...else...语句来实现让父子进程完成不同的功能,正如我 们上面举的例子一样。我们看到,上面例子执行时两条信息是交互无规则的打印出来的,这是父子进程独立执行的结果,虽然我们的代码似乎和串行的代码没有什么 区别。

      读者也许会问,如果一个大程序在运行中,它的数据段和堆栈都很大,一次fork就要复制一 次,那么fork的系统开销不是很大吗?其实UNIX自有其解决的办法,大家知道,一般CPU都是以"页"为单位来分配内存空间的,每一个页都是实际物理 内存的一个映像,象INTEL的CPU,其一页在通常情况下是 4086字节大小,而无论是数据段还是堆栈段都是由许多"页"构成的,fork函数复制这两个段,只是"逻辑"上的,并非"物理"上的,也就是说,实际执 行fork时,物理空间上两个进程的数据段和堆栈段都还是共享着的,当有一个进程写了某个数据时,这时两个进程之间的数据才有了区别,系统就将有区别的" 页"从物理上也分开。系统在空间上的开销就可以达到最小。

      下面演示一个足以"搞死"Linux的小程序,其源代码非常简单:

    1void main()
    2{
    3   for( ; ; )
    4   {
    5     fork();
    6   }
    7}

      这个程序什么也不做,就是死循环地fork,其结果是程序不断产生进程,而这些进程又不断产生新的进程,很快,系统的进程就满了,系统就被这么多不断产生 的进程"撑死了"。当然只要系统管理员预先给每个用户设置可运行的最大进程数,这个恶意的程序就完成不了企图了。

     

    2.2.2 exec( )函数族

    下面我们来看看一个进程如何来启动另一个程序的执行。在Linux中要使用exec函数族。系统 调用execve()对当前进程进行替换,替换者为一个指定的程序,其参数包括文件名(filename)、参数列表(argv)以及环境变量 (envp)。exec函数族当然不止一个,但它们大致相同,在 Linux中,它们分别是:execl,execlp,execle,execv,execve和execvp,下面我只以execlp为例,其它函数究 竟与execlp有何区别,请通过manexec命令来了解它们的具体情况。

      一个进程一旦调用exec类函数,它本身就"死亡"了,系统把代码段替换成新的程序的代码, 废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序 了。(不过exec类函数中有的还允许继承环境变量之类的信息。)

    那么如果我的程序想启动另一程序的执行但自己仍想继续运行的话,怎么办呢?那就是结合fork与exec的使用。下面一段代码显示如何启动运行其它程序:

     1#include <errno.h>
     2#include <stdio.h>
     3#include <stdlib.h>
     4
     5char command[256];
     6void main()
     7{
     8   int rtn; /*子进程的返回数值*/
     9   while(1) {
    10       /* 从终端读取要执行的命令 */
    11       printf( ">" );
    12       fgets( command, 256, stdin );
    13       command[strlen(command)-1= 0;
    14       if ( fork() == 0 ) {/* 子进程执行此命令 */
    15          execlp( command, NULL );
    16          /* 如果exec函数返回,表明没有正常执行命令,打印错误信息*/
    17          perror( command );
    18          exit( errno );
    19       }
    20       else {/* 父进程, 等待子进程结束,并打印子进程的返回值 */
    21          wait ( &rtn );
    22          printf( " child process return %d\n", rtn );
    23       }
    24   }
    25}

      此程序从终端读入命令并执行之,执行完成后,父进程继续等待从终端读入命令。熟悉DOS和 WINDOWS系统调用的朋友一定知道DOS/WINDOWS也有exec类函数,其使用方法是类似的,但DOS/WINDOWS还有spawn类函数, 因为DOS是单任务的系统,它只能将"父进程"驻留在机器内再执行"子进程",这就是spawn类的函数。WIN32已经是多任务的系统了,但还保留了 spawn类函数,WIN32中实现spawn函数的方法同前述 UNIX中的方法差不多,开设子进程后父进程等待子进程结束后才继续运行。UNIX在其一开始就是多任务的系统,所以从核心角度上讲不需要spawn类函 数。

      在这一节里,我们还要讲讲system()和popen()函数。system()函数先调 用fork(),然后再调用exec()来执行用户的登录 shell,通过它来查找可执行文件的命令并分析参数,最后它么使用wait()函数族之一来等待子进程的结束。函数popen()和函数 system()相似,不同的是它调用pipe()函数创建一个管道,通过它来完成程序的标准输入和标准输出。这两个函数是为那些不太勤快的程序员设计 的,在效率和安全方面都有相当的缺陷,在可能的情况下,应该尽量避免。

     

     2.3 Linux下的进程间通信

      详细的讲述进程间通信在这里绝对是不可能的事情,而且笔者很难有信心说自己对这一部分内容的 认识达到了什么样的地步,所以在这一节的开头首先向大家推荐著名作者Richard Stevens的著名作品:《Advanced Programming in the UNIX Environment》,它的中文译本《UNIX环境高级编程》已有机械工业出版社出版,原文精彩,译文同样地道,如果你的确对在Linux下编程有浓 厚的兴趣,那么赶紧将这本书摆到你的书桌上或计算机旁边来。说这么多实在是难抑心中的景仰之情,言归正传,在这一节里,我们将介绍进程间通信最最初步和最 最简单的一些知识和概念。

      首先,进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信 息,事实上,在很多应用系统里,都使用了这种方法。但一般说来,进程间通信(IPC:InterProcess Communication)不包括这种似乎比较低级的通信方法。Unix系统中实现进程间通信的方法很多,而且不幸的是,极少方法能在所有的Unix系 统中进行移植(唯一一种是半双工的管道,这也是最原始的一种通信方式)。而Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信 方法:管道、消息队列、共享内存、信号量、套接口等等。下面我们将逐一介绍。

      2.3.1 管道

      管道是进程间通信中最古老的方式,它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。

      无名管道由pipe()函数创建:

      #include <unistd.h>

      int pipe(int filedis[2]);

      参数filedis返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。下面的例子示范了如何在父进程和子进程间实现通信。

     1 #define INPUT 0
     2 #define OUTPUT 1
     3 
     4 void main() {
     5    int file_descriptors[2];
     6    /*定义子进程号 */
     7    pid_t pid;
     8    char buf[256];
     9    int returned_count;
    10    /*创建无名管道*/
    11    pipe(file_descriptors);
    12    /*创建子进程*/
    13    if((pid = fork()) == -1) {
    14       printf("Error in fork\n");
    15       exit(1);
    16    }
    17    /*执行子进程*/
    18    if(pid == 0) {
    19       printf("in the spawned (child) process...\n");
    20       /*子进程向父进程写数据,关闭管道的读端*/
    21       close(file_descriptors[INPUT]);
    22       write(file_descriptors[OUTPUT], "test data", strlen("test data"));
    23       exit(0);
    24    } else {
    25       /*执行父进程*/
    26       printf("in the spawning (parent) process...\n");
    27       /*父进程从管道读取子进程写的数据,关闭管道的写端*/
    28       close(file_descriptors[OUTPUT]);
    29       returned_count = read(file_descriptors[INPUT], buf, sizeof(buf));
    30       printf("%d bytes of data received from spawned process: %s\n",
    31       returned_count, buf);
    32    }
    33 }

     在Linux系统下,有名管道可由两种方式创建:命令行方式mknod系统调用和函数mkfifo。下面的两种途径都在当前目录下生成了一个名为myfifo的有名管道:

      方式一:mkfifo("myfifo","rw");

      方式二:mknod myfifo p

      生成了有名管道后,就可以使用一般的文件I/O函数如open、close、read、write等来对它进行操作。下面即是一个简单的例子,假设我们已经创建了一个名为myfifo的有名管道。

     1/* 进程一:读有名管道*/
     2#include <stdio.h>
     3#include <unistd.h>
     4void main() {
     5    FILE * in_file;
     6    int count = 1;
     7    char buf[80];
     8    in_file = fopen("mypipe""r");
     9    if (in_file == NULL) {
    10        printf("Error in fdopen.\n");
    11        exit(1);
    12    }
    13    while ((count = fread(buf, 180, in_file)) > 0)
    14        printf("received from pipe: %s\n", buf);
    15    fclose(in_file);
    16}
    17/* 进程二:写有名管道*/
    18#include <stdio.h>
    19#include <unistd.h>
    20void main() {
    21    FILE * out_file;
    22    int count = 1;
    23    char buf[80];
    24    out_file = fopen("mypipe""w");
    25    if (out_file == NULL) {
    26        printf("Error opening pipe.");
    27        exit(1);
    28    }
    29    sprintf(buf,"this is test data for the named pipe example\n");
    30    fwrite(buf, 180, out_file);
    31    fclose(out_file);
    32}
    33

     

      2.3.2 消息队列

        消息队列用于运行于同一台机器上的进程间通信,它和管道很相似,事实上,它是一种正逐渐被淘汰的通信方式,我们可以用流管道或者套接口的方式来取代它,所以,我们对此方式也不再解释,也建议读者忽略这种方式。

      2.3.3 共享内存

      共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。 通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系 统带来额外的开销,但在现实中并不常用,因为它控制存取的将是实际的物理内存,在Linux系统下,这只有通过限制Linux系统存取的内存才可以做到, 这当然不太实际。常用的方式是通过shmXXX函数族来实现利用共享内存进行存储的。

      首先要用的函数是shmget,它获得一个共享存储标识符。

      #include <sys/types.h>

      #include <sys/ipc.h>

      #include <sys/shm.h>

      int shmget(key_t key, int size, int flag);

      这个函数有点类似大家熟悉的malloc函数,系统按照请求分配size大小的内存用作共享 内存。Linux系统内核中每个IPC结构都有的一个非负整数的标识符,这样对一个消息队列发送消息时只要引用标识符就可以了。这个标识符是内核由IPC 结构的关键字得到的,这个关键字,就是上面第一个函数的 key。数据类型key_t是在头文件sys/types.h中定义的,它是一个长整形的数据。在我们后面的章节中,还会碰到这个关键字。

      当共享内存创建后,其余进程可以调用shmat()将其连接到自身的地址空间中。

      void *shmat(int shmid, void *addr, int flag);

      shmid为shmget函数返回的共享存储标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地址,进程可以对此进程进行读写操作。

      使用共享存储来实现进程间通信的注意点是对数据存取的同步,必须确保当一个进程去读取数据 时,它所想要的数据已经写好了。通常,信号量被要来实现对共享存储数据存取的同步,另外,可以通过使用shmctl函数设置共享存储内存的某些标志位如 SHM_LOCK、SHM_UNLOCK等来实现。

      2.3.4 信号量

      信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。一般说来,为了获得共享资源,进程需要执行下列操作:

      (1) 测试控制该资源的信号量。

      (2) 若此信号量的值为正,则允许进行使用该资源。进程将进号量减1。

      (3) 若此信号量为0,则该资源目前不可用,进程进入睡眠状态,直至信号量值大于0,进程被唤醒,转入步骤(1)。

      (4) 当进程不再使用一个信号量控制的资源时,信号量值加1。如果此时有进程正在睡眠等待此信号量,则唤醒此进程。

      维护信号量状态的是Linux内核操作系统而不是用户进程。我们可以从头文件/usr /src/linux/include /linux /sem.h 中看到内核用来维护信号量状态的各个结构的定义。信号量是一个数据集合,用户可以单独使用这一集合的每个元素。要调用的第一个函数是semget,用以获 得一个信号量ID。

      #include <sys/types.h>

      #include <sys/ipc.h>

      #include <sys/sem.h>

      int semget(key_t key, int nsems, int flag);

      key是前面讲过的IPC结构的关键字,它将来决定是创建新的信号量集合,还是引用一个现有 的信号量集合。    nsems是该集合中的信号量数。如果是创建新集合(一般在服务器中),则必须指定nsems;如果是引用一个现有的信号量集合 (一般在客户机中)则将nsems指定为0。

      semctl函数用来对信号量进行操作。

      int semctl(int semid, int semnum, int cmd, union semun arg);

      不同的操作是通过cmd参数来实现的,在头文件sem.h中定义了7种不同的操作,实际编程时可以参照使用。

    semop函数自动执行信号量集合上的操作数组。

      int semop(int semid, struct sembuf semoparray[], size_t nops);

      semoparray是一个指针,它指向一个信号量操作数组。nops规定该数组中操作的数量。

      下面,我们看一个具体的例子,它创建一个特定的IPC结构的关键字和一个信号量,建立此信号量的索引,修改索引指向的信号量的值,最后我们清除信号量。在下面的代码中,函数ftok生成我们上文所说的唯一的IPC关键字。

     1#include <stdio.h>
     2#include <sys/types.h>
     3#include <sys/sem.h>
     4#include <sys/ipc.h>
     5void main() {
     6    key_t unique_key; /* 定义一个IPC关键字*/
     7    int id;
     8    struct sembuf lock_it;
     9    union semun options;
    10    int i;
    11
    12    unique_key = ftok("."'a'); /* 生成关键字,字符'a'是一个随机种子*/
    13    /* 创建一个新的信号量集合*/
    14    id = semget(unique_key, 1, IPC_CREAT | IPC_EXCL | 0666);
    15    printf("semaphore id=%d\n", id);
    16    options.val = 1/*设置变量值*/
    17    semctl(id, 0, SETVAL, options); /*设置索引0的信号量*/
    18
    19    /*打印出信号量的值*/
    20    i = semctl(id, 0, GETVAL, 0);
    21    printf("value of semaphore at index 0 is %d\n", i);
    22
    23    /*下面重新设置信号量*/
    24    lock_it.sem_num = 0/*设置哪个信号量*/
    25    lock_it.sem_op = -1/*定义操作*/
    26    lock_it.sem_flg = IPC_NOWAIT; /*操作方式*/
    27    if (semop(id, &lock_it, 1== -1) {
    28        printf("can not lock semaphore.\n");
    29        exit(1);
    30    }
    31
    32    i = semctl(id, 0, GETVAL, 0);
    33    printf("value of semaphore at index 0 is %d\n", i);
    34
    35    /*清除信号量*/
    36    semctl(id, 0, IPC_RMID, 0);
    37}

     

      2.3.5 套接口

      套接口(socket)编程是实现Linux系统和其他大多数操作系统中进程间通信的主要方 式之一。我们熟知的WWW服务、FTP服务、TELNET服务等都是基于套接口编程来实现的。除了在异地的计算机进程间以外,套接口同样适用于本地同一台 计算机内部的进程间通信。关于套接口的经典教材同样是 Richard Stevens编著的《Unix网络编程:联网的API和套接字》,清华大学出版社出版了该书的影印版。它同样是Linux程序员的必备书籍之一。

      关于这一部分的内容,可以参照本文作者的另一篇文章《设计自己的网络蚂蚁》,那里由常用的几 个套接口函数的介绍和示例程序。这一部分或许是Linux进程间通信编程中最须关注和最吸引人的一部分,毕竟,Internet 正在我们身边以不可思议的速度发展着,如果一个程序员在设计编写他下一个程序的时候,根本没有考虑到网络,考虑到Internet,那么,可以说,他的设 计很难成功。

     

      3 Linux的进程和Win32的进程/线程比较

      熟悉WIN32编程的人一定知道,WIN32的进程管理方式与Linux上有着很大区别,在UNIX里,只有进程的概念,但在WIN32里却还有一个"线程"的概念,那么Linux和WIN32在这里究竟有着什么区别呢?

      WIN32里的进程/线程是继承自OS/2的。在WIN32里,"进程"是指一个程序,而" 线程"是一个"进程"里的一个执行"线索"。从核心上讲,WIN32的多进程与Linux并无多大的区别,在WIN32里的线程才相当于Linux的进 程,是一个实际正在执行的代码。但是,WIN32里同一个进程里各个线程之间是共享数据段的。这才是与Linux的进程最大的不同。

      下面这段程序显示了WIN32下一个进程如何启动一个线程。

     1 int g;
     2 DWORD WINAPI ChildProcess( LPVOID lpParameter ){
     3     int i;
     4     for ( i = 1; i <1000; i ++) {
     5         g ++;
     6         printf( "This is Child Thread: %d\n", g );
     7     }
     8     ExitThread( 0 );
     9 };
    10 
    11 void main()
    12 {
    13     int threadID;
    14     int i;
    15     g = 0;
    16     CreateThread( NULL, 0, ChildProcess, NULL, 0&threadID );
    17     for ( i = 1; i <1000; i ++) {
    18         g ++;
    19         printf( "This is Parent Thread: %d\n", g );
    20     }
    21 }

    在WIN32下,使用CreateThread函数创建线程,与Linux下创建进程不同,WIN32线程不是从创建处开始运行的,而是由 CreateThread指定一个函数,线程就从那个函数处开始运行。此程序同前面的UNIX程序一样,由两个线程各打印1000条信息。 threadID是子线程的线程号,另外,全局变量g是子线程与父线程共享的,这就是与Linux最大的不同之处。大家可以看出,WIN32的进程/线程 要比Linux复杂,在Linux要实现类似WIN32的线程并不难,只要fork以后,让子进程调用ThreadProc函数,并且为全局变量开设共享 数据区就行了,但在WIN32下就无法实现类似fork的功能了。所以现在WIN32下的C语言编译器所提供的库函数虽然已经能兼容大多数 Linux/UNIX的库函数,但却仍无法实现fork。

     

    对于多任务系统,共享数据区是必要的,但也是一个容易引起混乱的问题,在WIN32下,一个程序员很容易忘记线程之间的数据是共享的这一情况,一个 线程修改过一个变量后,另一个线程却又修改了它,结果引起程序出问题。但在Linux下,由于变量本来并不共享,而由程序员来显式地指定要共享的数据,使 程序变得更清晰与安全。

    至于WIN32的"进程"概念,其含义则是"应用程序",也就是相当于UNIX下的exec了。

    Linux也有自己的多线程函数pthread,它既不同于Linux的进程,也不同于WIN32下的进程,关于pthread的介绍和如何在Linux环境下编写多线程程序我们将在另一篇文章《Linux下的多线程编程》中讲述。

  • java socket 传递对象时,找不到类文件解决办法

    2014-09-04 23:03:43

    现象描述
        开发两个工程如: client  ; server  ,实现客户端与服务器之间可以通信,并要求 client 可以传递对象参数给服务器,对像类文件为:user ; 在两个工程中都存在这个类文件。

    遇到问题:
        可以是当运行时,提示如下的错误信息:
        java socket java.lang.ClassNotFoundException com.client.comm .user  提示找不到

    解决办法:
     
       客户端与server两个工程中,将user类文件 保存的路径不一致造成的
       如工程:client user类路径为: com.client.comm
              server user类路径为: com.server.comm

       将上面路径统一修改成:com.chat.comm  这样就可以解决了。

       另外要参加 java.io.Serializable 解决序列化的问题。 

      
      
  • Between and 索引使用情况-mysql神奇的现象

    2014-04-09 16:47:55

    Between and 有条件的使用索引,做了如下的测试

    1.表的结构如下

    Create Table


    CREATE TABLE `city` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` char(30) DEFAULT NULL,
      `subcity` int(11) DEFAULT NULL,
      `tdate` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
      `cont` varchar(2000) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `name_idx` (`name`)
    ) ENGINE=MyISAM AUTO_INCREMENT=81 DEFAULT CHARSET=utf8

    2. 测试结果如下

    测试场景1

    表数据量是10条:

    SELECT * FROM city WHERE id  BETWEEN 1 AND 5; 5/10使用索引

    SELECT * FROM city WHERE id  BETWEEN 1 AND 6; 不使用索引

    测试场景2

    表数据量是条14

    SELECT * FROM city WHERE id  BETWEEN 1 AND 5 ; 5/14使用索引

    SELECT * FROM city WHERE id  BETWEEN 1 AND 6; 不使用索引  

    测试场景3

    表数据量是条50

    SELECT * FROM city WHERE id  BETWEEN 1 AND 11; 11/50 使用索引

    SELECT * FROM city WHERE id  BETWEEN 1 AND 12;不使用索引

    测试场景4

    表数据量是条1000

    SELECT * FROM city WHERE id  BETWEEN 1 AND 179; 179/1000 使用索引

    SELECT * FROM city WHERE id  BETWEEN 1 AND 180;不使用索引

    测试场景5

    表数据量是条10000

    SELECT * FROM city WHERE id  BETWEEN 1 AND 9000; 使用索引

    SELECT * FROM city WHERE id  BETWEEN 1 AND 10000; 使用索引

    测试场景6

    表数据量是条100000

    SELECT * FROM city WHERE id  BETWEEN 1 AND 100000; 使用索引

    SELECT * FROM city WHERE id  BETWEEN 1 AND 90000; 使用索引


     3.个人的结果分析

      1)如果表数据量小14条时,取5条时就会使用索引,否则就使用全表描述

      2)如果表数据量在1000左右时,取出超过18%时,就不会使用索引

      3)如果表数据量超地10000时,只要使用between and 都使用索引

     以上是基于测试结果分析出来的,不知道正确与否?有时间要看一下源代码才行确定的上面的分析正确性的.贴出来,希望大家拍砖!

  • nginx 502错误解决办法

    2014-04-01 10:51:21

    前几天遇到大量的502错误,开始是一头雾水,不知道怎么来解决,下面将我个人的解决办法介绍如下
    (1)PHP调整执行参数
        max_execution_time=60 调整成 max_execution_time=3600
        max_input_time=60    调整成 max_input_time=-1 
        default_socket_timeout=60 调整成 3600
    重新启动php 问题没有解决

    (2)修改nginx参数nginx.conf
    worker_connections 65536;
        fastcgi_connect_timeout 3000;
    fastcgi_send_timeout 3000;
    fastcgi_read_timeout 3000;
    重新启动nginx 问题没有解决
      
    (3)修改PHP加载函数
    将入口api.php的一个页面加载方法调整一下如下:
    require_once 修改成 include_once
    问题解决了.

    总结如下:经过查看资源了解如下:

    require_once 套用一个文件,只能套用一次,如果文件不存在,会中断程序执行。
    include_once 套用一个文件,只能套用一次,如果文件不存,只是一个提示,然后继续执行。
  • php操作xml - simplexml使用方法

    2014-04-01 10:37:54

     其它php操作xml有三种方法:dom,xpath,simplexml,我只使用其中一个方法:simplexml.

      近来学习php操作xml的知识,将学习的知识总结一下,不费话,直接上代码.

    1.实例 Dbconf.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <mysql>

    <ip>localhost</ip>

    <dbname>test</dbname>

    <user>root</user>

    <passwd></passwd>

    </mysql>

     2.测试代码 Db.php


    //操作上面的dbconf.php 

    <?php

    //加载配置文件

    $xml=simplexml_load_file('dbconf.xml');

    //查询节点名

    $ip=(string) $xml->ip;

    $dbname=(string) $xml->dbname;

    $user=(string) $xml->user;

    $passwd=(string) $xml->passwd;

     

    //循环获取子

    foreach($xml->children() as $child){

      echo $child->getName().'----';

    }

     

    //增加字段

    $xml->addchild("charset","utf8");

     

    //增加字段及属性

    $xml->addchild("type","mysql");

    $xml->type->addattribute("name","mysql");

     

    //获取属性

    $names = $xml->type->attributes();

     

    foreach ($names as $a=>$b){

        echo $a . '==' . $b ;   

    }

     

    //删除字段

    unset($xml->charset);

    //删除属性

    unset($names['name']);

    //修改字段内容

    $xml->passwd='';

     

    //保存字段内容到文件中

    $xml->asXML('dbconf.xml');

    echo 'Sucess !' ;

    ?>

  • 性能测试- xhprof深层次分析性能问题

    2013-12-25 15:39:44

      这几天刚换了一个新的工作,刚到一个公司就要表现一下.所以想通过xhprof来分析PHP页面中函数调用存在的问题.
      费话不多说,下面讲一下xhprof使用过程及发现的问题
     

    1 安装xhprof

     

    wget http://pecl.php.net/get/xhprof-0.9.2.tgz
    tar zxf xhprof-0.9.2.tgz
    cd xhprof-0.9.2
    cp -r xhprof_html xhprof_lib /www/www.hx.com/xhprof/
    cd extension/
    /usr/local/webserver/php/bin/phpize
    ./configure  –with-php-config=/usr/local/webserver/php/bin/php-config
    make && make install

    2 安装完提示:

     

    Installing shared extensions:     /usr/local/webserver/php/lib/php/extensions/no-debug-non-zts-20060613/

    3   php.ini中添加

    extension_dir = "/usr/local/webserver/php/lib/php/extensions/no-debug-non-zts-20060613/"
    这句我原来就有了,就这用添加下面两句
    extension=xhprof.so
    xhprof.output_dir=/www/logs/xhprof

    分析日志输出在/www/logs/xhprof目录。

    4   重新加载php配置文件和重启web

     

    /ect/init.d/php-fpm1 reload
    /ect/init.d//nginx -s reload

    刷新phpinfo页面,看到输出中有了xhprof信息。


    5   安装graphviz,一个画图工具

     

    wget http://www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.24.0.tar.gz
    tar zxf graphviz-2.24.0.tar.gz
    cd graphviz-2.24.0
    ./configure
    make && make install


    6 程序试例

     

    头部:
    xhprof_enable();
    //xhprof_enable(XHPROF_FLAGS_NO_BUILTINS);
    不记录内置的函数
    //xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);  
    同时分析CPUMem的开销
    $xhprof_on = true;

    代码:
      放置你要测试的代码.

    尾部:
    if($xhprof_on){
    $xhprof_data = xhprof_disable();
    $xhprof_root = '/share/web/xhprof/';
    include_once $xhprof_root."xhprof_lib/utils/xhprof_lib.php";
    include_once $xhprof_root."xhprof_lib/utils/xhprof_runs.php";
    $xhprof_runs = new XHProfRuns_Default();
    $run_id = $xhprof_runs->save_run($xhprof_data, "hx");
    echo '<a href="
    http://10.168.1.1:80/xhprof/xhprof_html/index.php?run='.$run_id.'&source=hx" target="_blank">函数调用情况统计</a>';
    }
    7. 上图片查看效果
     

  • time_wait 过多解决办法

    2013-12-16 11:09:31

     这几天做性能测试通过监控发现大量的time_wait状态的链接
    监控现象:
          1 FIN_WAIT2
         80 ESTABLISHED
      50486 TIME_WAIT 
    造成的结果
      web无再可以使用的链接,去链接DB,造成大量的请求失败.

    解决办法:

     net.ipv4.tcp_max_tw_buckets = 5000 #本参数可以控制TIME_WAIT数量
     net.ipv4.tcp_tw_reuse = 1
     net.ipv4.tcp_tw_recycle = 1
     net.ipv4.tcp_fin_timeout = 5

    遗留问题
     
     net.ipv4.tcp_max_tw_buckets = 5000 如果设置太小会造成报错的.
    Dec 13 17:01:52 web-adc-31-69 kernel: TCP: time wait bucket table overflow
    Dec 13 17:01:52 web-adc-31-69 kernel: TCP: time wait bucket table overflow
    Dec 13 17:01:57 web-adc-31-69 kernel: __ratelimit: 5243 callbacks suppressed
     目前还不知道有什么办法可以解决本问题的.

  • 浅析innodb_support_xa与innodb_flush_log_at_trx_commit (转载)

    2013-12-11 16:49:36

    浅析innodb_support_xa与innodb_flush_log_at_trx_commit

    分类: mysql 356人阅读 评论(0) 收藏 举报

            很久以前对innodb_support_xa存在一点误解,当初一直认为innodb_support_xa只控制外部xa事务,内部的xa事务是 mysql内部进行控制,无法人为干预(这里说的内部xa事务主要是指binlog与innodb的redo log保持一致性所采用的内部xa事务)。直到前阵子在微博上看到有人讨论mysql数据安全时才仔细去手册上查看了关于 innodb_support_xa的解释,这几天又与同事再次讨论了这个问题,于是想着还是将其记录下来。先看官方手册上对 innodb_support_xa的解释:

    “EnablesInnoDBsupport for two-phase commit in XA transactions, causing an extra disk flush for transaction preparation. This set-ting is the default. The XA mechanism is used internally and is essential for any server that has its binary log turned on and is accepting changes to its data from more than one thread. If you turn it off, transactions can be written to the binary log in a different order from the one in which the live database is committing them. This can produce different data when the binary log is replayed in disaster recovery or on a replication slave. Do not turn it off on a replication master server unless you have an unusual setup where only one thread is able to change data.”

    从官方解释来看,innodb_support_xa的作用是分两类:第一,支持多实例分布式事务(外部xa事务),这个一般在分布式数据库环境中 用得较多。第二,支持内部xa事务,说白了也就是说支持binlog与innodb redo log之间数据一致性。今天的重点是讨论第二类内部xa事务。

            首先我们需要明白为什么需要保持binlog与redo log之间数据一致性,这里分两个方面来解释:

    第一,保证binlog里面存在的事务一定在redo log里面存在,也就是binlog里不会比redo log多事务(可以少,因为redo log里面记录的事务可能有部分没有commit,这些事务最终可能会被rollback)。先来看这样一个场景(后面的场景都是假设binlog开 启):在一个AB复制环境下主库crash,然后进行crash recovery,此时如果binlog里面的的事务信息与redo log里面的信息不一致,那么就会出现主库利用redo log进行恢复后,然后binlog部分的内容复制到从库去,然后出现主从数据不一致状态。所以需要保证binlog与redo log两者事务一致性。

    第二,保证binlog里面事务顺序与redo log事务顺序一致性。这也是很重要的一点,假设两者记录的事务顺序不一致,那么会出现类似于主库事务执行的顺序是ta, tb, tc,td,但是binlog里面记录的是ta,tc, tb, td,binlog复制到从库后导致主从的数据不一致。当然也由于当初蹩脚的设计导致BGC被打破,这里就不详说了。

            为了达到上面说的两点,mysql是怎么来实现的呢?没错,答案是内部xa事务(核心是2pc)。现在mysql内部一个处理流程大概是这样:

    1. prepare ,然后将redo log持久化到磁盘

    2. 如果前面prepare成功,那么再继续将事务日志持久化到binlog

    3. 如果前面成功,那么在redo log里面写上一个commit记录

    那么假如在进行着三步时又任何一步失败,crash recovery是怎么进行的呢? 此时会先从redo log将最近一个检查点开始的事务读出来,然后参考binlog里面的事务进行恢复。如果是在1 crash,那么自然整个事务都回滚;如果是在2 crash,那么也会整个事务回滚;如果是在3 crash(仅仅是commit记录没写成功),那么没有关系因为2中已经记录了此次事务的binlog,所以将这个进行commit。所以总结起来就是 redo log里凡是prepare成功,但commit失败的事务都会先去binlog查找判断其是否存在(通过XID进行判断,是不是经常在binlog里面 看到Xid=xxxx?这就是xa事务id),如果有则将这个事务commit,否则rollback。

            在这三个步骤中因为持久化需求每一步都需要fsync,但是如果真的每一步都需要fsync,那么sync_binlog与 innodb_flush_log_at_trx_commit两个参数的意义又在哪?这里还没理得很清楚,希望自己以后补上来或是谁帮忙解答一下。

            前面已经解释完了通过内部xa事务来保证binlog里记录的事务不会比redo log多(也可以间接的理解为binlog一定只记录提交事务),这么做的原因是为了crash recovery后主从保持一致性。接下来解释目前是怎么来保证binlog与redo log之间顺序一致的。

            为什么要保证binlog里事务与redo log里事务顺序一致性原因前面已经解释过。为了保证这一点带来的问题相信了解过BGC的朋友都知道----臭名昭著的 prepare_commit_mutex,没错就是它导致了正常情况下无法实现BGC,原理是什么?在每次进行xa事务时,在prepare阶段事务先 拿到一个全局的prepare_commit_mutex, 然后执行前面说的持久化(fsync)redo log与binlog,然后等fsync完了之后再释放prepare_commit_mutex,这样相当于串行化的效果虽然保证了binlog与 redo log之间顺序一致性,但是却导致每个事务都需要一个fsync操作,而大家都知道在一次持久化的过程中代价最大的操作就是fsync了,而想 write()这些不落地的操作代价相对来说就很小。所以BGC得核心在于很多事务需要的fsync合并成一个fsync去做。

             说了这么多就只为了解释innodb_support_xa=1的价值在哪,但是刚才也说了由于xa事务中需要多次fsync,所以开启后会对性能有一 定影响。从percona博客上看到06年他们测试时开启后tps下降一半,但是我实际用mysql-5.5.12+sysbench-0.5+10块 SAS(raid 10)测试结果性能下面没那么明显。在oltp模式下tps几乎没差别,不过它默认读写比例是4:1,后来换成纯update测试,开始xa事务性能下降 也仅仅是5%左右,没有传说中那么大的差别。所以我怀疑可能的原因有两个:第一,现在的mysql性能相对于06有了较大提升;第二,我测试的机器较好 (10块SAS盘做raid10),这样即使开启了xa事务,需要较多的fsync,但是由于存储方面能抗住,所以没有体现出太大的劣势。

            接下来顺便谈一下innodb_flush_log_at_trx_commit意义以及合理设置。 innodb_flush_log_at_trx_commit有0、1、2三个值分别代表不同的使redo log落地策略。0表示每秒进行一次flush,但是每次事务commit不进行任何操作(每秒调用fsync使数据落地到磁盘,不过这里需要注意如果底 层存储有cache,比如raid cache,那么这时也不会真正落地,但是由于一般raid卡都带有备用电源,所以一般都认为此时数据是安全的)。1代表每次事务提交都会进行 flush,这是最安全的模式。2表示每秒flush,每次事务提交时不flush,而是调用write将redo log buffer里面的redo log刷到os page cache。

            那现在来比较三种策略的优劣势:1由于每次事务commit都会是redo log落地所以是最安全的,但是由于fsync的次数增多导致性能下降比较厉害。0表示每秒flush,每次事务提交不进行任何操作,所以mysql crash或者os crash时会丢失一秒的事务。2相对于0来说了多了每次事务commit时会有一次write操作,此时数据虽然没有落地到磁盘但是只要没有 os crash,即使mysql crash,那么事务是不会丢失的。2相对于0来说会稍微安全一点点。

            所以关于这两个参数,我的建议是主库开始innodb_support_xa=1,从库不开(因为从库一般不会记binlog),数据一致性还是很重要 的。而对于innodb_flush_log_at_trx_commit,除非是对数据很重要,不能丢事务,否则我建议设置成2。我看到有些公司设置成 0。其实我个人认为都设置成0了就没有多少理由不设置成2,因为2带来的性能损耗是每个事务一个write操作,write操作的开销相对于fsync还 是小很多的,但是这点开销换来了即使mysql挂掉事务依然不会丢的好处。

  • mysqltuner 及mysqlreport 性能测试的监控及分析工具

    2013-11-15 16:41:49

    mysqltuner and mysqlreport 工具使用

    1.2     mysqltuner.pl

      本工具建议定期运行,发现目前MYSQL数据库存在的问题及修改相关的参数.

    1.2.1  安装

    直接下载

    [root@even ~]# wget  http://mysqltuner.pl/mysqltuner.pl

    [root@even ~]# chmod +x mysqltuner.pl

    [root@even ~]# cp mysqltuner.pl /home/mysql/

    1.2.2  生成的报告如下

    [root@xx xxx]# ./mysqltuner.pl

     

     >>  MySQLTuner 1.0.0 - Major Hayden <major@mhtx.net>

     >>  Bug reports, feature requests, and downloads at http://mysqltuner.com/

     >>  Run with '--help' for additional options and output filtering

    [!!] Successfully authenticated with no password - SECURITY RISK!

     

    -------- General Statistics --------------------------------------------------

    [--] Skipped version check for MySQLTuner script

    [OK] Currently running supported MySQL version 5.1.57-community-log

    [OK] Operating on 64-bit architecture

     

    -------- Storage Engine Statistics -------------------------------------------

    [--] Status: -Archive -BDB -Federated +InnoDB -ISAM -NDBCluster

     

     

    [--] Data in MyISAM tables: 18G (Tables: 3395)

    [--] Data in MRG_MYISAM tables: 218M (Tables: 11)

    [--] Data in InnoDB tables: 3G (Tables: 449)

    [--] Data in MEMORY tables: 95M (Tables: 15)

    [!!] Total fragmented tables: 188

     

    -------- Performance Metrics -------------------------------------------------

    [--] Up for: 8d 0h 22m 0s (7B q [10K qps], 3M conn, TX: 6131B, RX: 2280B)

    [--] Reads / Writes: 62% / 38%

    [--] Total buffers: 1.5G global + 96.2M per thread (1000 max threads)

    [!!] Maximum possible memory usage: 95.5G (1226% of installed RAM)

    [OK] Slow queries: 0% (8K/7B)

    [!!] Highest connection usage: 100%  (1001/1000)

    [OK] Key buffer size / total MyISAM indexes: 512.0M/6.5G

    [OK] Key buffer hit rate: 100.0% (6B cached / 301K reads)

    [OK] Query cache efficiency: 99.2% (7B cached / 7B selects)

    [!!] Query cache prunes per day: 6074

    [OK] Sorts requiring temporary tables: 0% (2 temp sorts / 3M sorts)

    [!!] Joins performed without indexes: 2029

    [!!] Temporary tables created on disk: 33% (2M on disk / 7M total)

    [OK] Thread cache hit rate: 99% (1K created / 3M connections)

    [!!] Table cache hit rate: 0% (2K open / 222K opened)

    [OK] Open file limit used: 35% (2K/8K)

    [OK] Table locks acquired immediately: 97% (226M immediate / 230M locks)

    [!!] InnoDB data size / buffer pool: 3.9G/256.0M

     

    -------- Recommendations -----------------------------------------------------

    General recommendations:

        Run OPTIMIZE TABLE to defragment tables for better performance

        Reduce your overall MySQL memory footprint for system stability

        Reduce or eliminate persistent connections to reduce connection usage

        Increasing the query_cache size over 128M may reduce performance

        Adjust your join queries to always utilize indexes

        Temporary table size is already large - reduce result set size

        Reduce your SELECT DISTINCT queries without LIMIT clauses

        Increase table_cache gradually to avoid file descriptor limits

    Variables to adjust:

      *** MySQL's maximum memory usage is dangerously high ***

      *** Add RAM before increasing MySQL buffer variables ***

        max_connections (> 1000)

        wait_timeout (< 3600)

        interactive_timeout (< 3600)

        query_cache_size (> 512M) [see warning above]

        join_buffer_size (> 32.0M, or always use indexes with joins)

        table_cache (> 2000)

    innodb_buffer_pool_size (>= 3G)

     

    分析本报告:

    (1)   链接数要求1000,每一个链接需要的内存是95M左右,需要总的内存是96g,目前本机的内存只有8G Highest connection usage: 100%  (1001/1000)

    建议链接数大于1000,最大值是1001

    (2)   Key buffer size / total MyISAM indexes: 512.0M/6.5G  建议调整成6.5G左右

    (3)   Temporary tables created on disk: 33% (2M on disk / 7M total)  说明了需要调整tmp_table_size大小的

    (4)   系统整体建议的参数为:

        max_connections (> 1000)

        wait_timeout (< 3600)

        interactive_timeout (< 3600)

        query_cache_size (> 512M) [see warning above]

        join_buffer_size (> 32.0M, or always use indexes with joins)

        table_cache (> 2000)

    innodb_buffer_pool_size (>= 3G

    1.1     Mysqlreport

    1.1.1  安装

    下载地址

    http://hackmysql.com/mysqlreport

    Chmod 777 mysqlreport

    ./mysqlreport

     

    1.1.2  使用

    生成的报告如下

    [root@xxx xxx]# ./mysqlreport --flush-status

    Use of uninitialized value in multiplication (*) at ./mysqlreport line 829.

    Use of uninitialized value in formline at ./mysqlreport line 1227.

    MySQL 5.1.57-community-  uptime 8 0:40:50       Wed Nov 13 10:08:06 2013

     

    __ Key _________________________________________________________________

    Buffer used    27.52M of 512.00M  %Used:   5.37

      Current      97.83M            %Usage:  19.11

    Write hit      99.55%

    Read hit      100.00%

     

    __ Questions ___________________________________________________________

    Total           7.13G   10.3k/s

      QC Hits       7.03G   10.1k/s  %Total:  98.58

      Com_          6.82G    9.8k/s           95.69

      -Unknown      6.81G    9.8k/s           95.52

      DMS          85.78M   123.7/s            1.20

      COM_QUIT      3.38M     4.9/s            0.05

    Slow 1 s        8.44k     0.0/s            0.00  %DMS:   0.01  Log:  ON

    DMS            85.78M   123.7/s            1.20

      SELECT       53.86M    77.6/s            0.76         62.79

      DELETE       19.56M    28.2/s            0.27         22.81

      INSERT        9.83M    14.2/s            0.14         11.46

      UPDATE        1.64M     2.4/s            0.02          1.91

      REPLACE     883.42k     1.3/s            0.01          1.03

    Com_            6.82G    9.8k/s           95.69

      admin_comma   6.81G    9.8k/s           95.52

      set_option    5.99M     8.6/s            0.08

      change_db     3.19M     4.6/s            0.04

     

    __ SELECT and Sort _____________________________________________________

    Scan            3.93M     5.7/s %SELECT:   7.31

    Range           8.95M    12.9/s           16.61

    Full join       2.03k     0.0/s            0.00

    Range check         0       0/s            0.00

    Full rng join  48.99k     0.1/s            0.09

    Sort scan       2.17M     3.1/s

    Sort range    947.47k     1.4/s

    Sort mrg pass       2     0.0/s

     

    __ Query Cache _________________________________________________________

    Memory usage  215.91M of 512.00M  %Used:  42.17

    Block Fragmnt  15.00%

    Hits            7.03G   10.1k/s

    Inserts         5.77M     8.3/s

    Insrt:Prune  118.57:1     8.3/s

    Hit:Insert  1217.91:1

     

    __ Table Locks _________________________________________________________

    Waited          4.68M     6.7/s  %Total:   2.02

    Immediate     226.55M   326.6/s

     

    __ Tables ______________________________________________________________

    Open             2000 of 2000    %Cache: 100.00

    Opened        241.76k     0.3/s

     

    __ Connections _________________________________________________________

    Max used         1001 of 1000      %Max: 100.10

    Total           3.38M     4.9/s

     

    __ Created Temp ________________________________________________________

    Disk table      2.45M     3.5/s

    Table           4.85M     7.0/s    Size: 256.0M

    File            2.06k     0.0/s

     

    __ Threads _____________________________________________________________

    Running             2 of  781

    Cached             21 of  500      %Hit:  99.96

    Created         1.28k     0.0/s

    Slow                0       0/s

     

    __ Aborted _____________________________________________________________

    Clients         8.91k     0.0/s

    Connects        2.39k     0.0/s

     

    __ Bytes _______________________________________________________________

    Sent            6.14T    8.9M/s

    Received        2.29T    3.3M/s

     

    __ InnoDB Buffer Pool __________________________________________________

    Usage         256.00M of 256.00M  %Used: 100.00

    Read hit      100.00%

    Pages

      Free              0            %Total:   0.00

      Data         16.13k                     98.45 %Drty:   0.00

      Misc            254                      1.55

      Latched                                  0.00

    Reads           5.26G    7.6k/s

      From file    60.56k     0.1/s            0.00

      Ahead Rnd      1001     0.0/s

      Ahead Sql      1242     0.0/s

    Writes         10.89M    15.7/s

    Flushes       163.88k     0.2/s

    Wait Free           0       0/s

     

    __ InnoDB Lock _________________________________________________________

    Waits             158     0.0/s

    Current             0

    Time acquiring

      Total        401132 ms

      Average        2538 ms

      Max           21434 ms

     

    __ InnoDB Data, Pages, Rows ____________________________________________

    Data

      Reads        71.09k     0.1/s

      Writes        1.37M     2.0/s

      fsync         1.30M     1.9/s

      Pending

        Reads           0

        Writes          0

        fsync           0

     

    Pages

      Created       8.49k     0.0/s

      Read        169.24k     0.2/s

      Written     163.88k     0.2/s

     

    Rows

      Deleted     237.02k     0.3/s

      Inserted    255.62k     0.4/s

      Read          1.77G    2.5k/s

      Updated     180.99k     0.3/s

1711/9123456789>
Open Toolbar