Fork me on GitHub
基于Node的代码生成器 之 入门教程

基于Node的代码生成器 之 入门教程

背景

为了方便团队和其它朋友使用我开发的这款代码生成器,特此写一篇入门教程。

环境要求

Node:http://nodejs.org/

.Net4.5:http://www.microsoft.com/zh-cn/download/details.aspx?id=30653

happy.node:http://happy.codeplex.com/SourceControl/latest

目录结构

模板文件和最终生成的代码文件

生成程序

app.js

复制代码
 1 var _ = require('underscore');
 2 
 3 var DatabaseSchemaReader = require('happy.database.schem.reader').DatabaseSchemaReader;
 4 var CodeGenerator = require('happy.code.generator').CodeGenerator;
 5 
 6 var project = {
 7     projectDirectory: './outputs', //输出目录
 8     rootNamespace: 'AutoGenerateWcfService', //根命名空间
 9     connectionString: 'Data Source=192.168.0.14\\SQLEXPRESS;Initial Catalog=Tenoner3.0;Persist Security Info=True;User ID=sa;Password=woshishui2010;', //连接字符串
10     openTables: ['HrEmployees'] //要生成的表,也可以全部生成。
11 };
12 
13 //和数据库无关的代码生成
14 CodeGenerator.executeTemplateDirectory('./templates/commons', project);
15 
16 //和数据库相关的代码生成
17 var schemaReader = DatabaseSchemaReader.createSqlClientReader(project.connectionString);
18 schemaReader.readAll(function (error, schema) {
19     if (error) {
20         console.log(error)
21 
22         return;
23     }
24 
25     _.each(schema.tables, function (table) {
26         if (_.contains(project.openTables, table.name)) {
27             var ejsOptions = _.extend({
28                 table: table,
29                 baseNamespace: 'Test'
30             }, project);
31 
32             CodeGenerator.executeTemplateDirectory('./templates/tables', ejsOptions);
33         }
34     });
35 
36     console.log('success!')
37 });
复制代码

常见问题

  1. 如何学习模板语法?
    语法采用的是ejs模板的语法,不懂请参考:https://github.com/visionmedia/ejs,非常简单,十分钟学不会找我。
  2. 如何详细学习API呢?

  3. 如何指定输出的文件路径呢?
  4. 如何设置只生成一次呢?如果文件已经生成,就不要生成了。
  5. 如何扩展呢?
    随便使用了,有问题可以随时交流。

备注

写的还是比较简单,作为一个开始吧!工具里自带了一些demo,也可以作为更好的入门教程使用。

 

在上一篇“为什么程序越优化越慢?”里,详述了程序指令在cache里发生冲突后另运行效率变的完全不可预测的问题,并提出了两种将不老实的cache变乖的良方。本以为cache已经完全被驯服了,没过多久,已经优化到23s的程序,在我修改了一段代码后,又掉回了30s,调出逻辑分析仪查看主存sram的读取和写入信号线,在运行关键程序段时,cache竟然又不乖了,导致CPU频繁的访问主存,拉低了效率。

 
图一
 
不过这次访问的主存地址不在程序指令区,而是数据区域。在编译的map文件里,0x20FE8对应的是一个全局变量guiTime。
 
图二
 
guiTime此变量确实有在关键函数里频繁使用到,但是数据cache总共有2KB的空间,而程序所有的全局变量不到1KB。理论上说,运行时,这些全局变量都应该存在数据cache里,不应该去频繁访问主存呀。
 
再看一看程序,guiTime被定义为volatile类型。
 
volatile  uint32  guiTime
 
“为了提高存取速度,编译器会将一些变量存放到一个寄存器内,下次使用的时候直接从寄存器里读取,当原变量被外界(中断,其他线程)改变后,寄存器里的值是不会变的,这样程序里用的寄存器值和实际的变量值就不一致了,错误随之而来。将变量定义成volatile类型,就是为了避免这种错误,每次使用数据都从原始地址读取,而不是寄存器。”
 
上述是对volatile类型一种被广泛认可的解读,注意红色的标注“原始地址”,这是否意味着定义成volatile类型的变量,使用时,都会无视cache,而去访问主存?抱着强烈的好奇心,做了一个小测试,去掉了针对guiTime的volatile定义,测试关键程序的运行时间———还是30s,从逻辑分析仪上仍能看到CPU的频繁访问0x20FE8地址开始的主存。
 
看来不是volatile的问题,至于"每次都要从原始地址读取"里的原始地址到底是哪里,先搁一搁,解决问题要紧。
 
回头看一看图二,CPU在读完0x20FE8开始的8个32位数据后,立刻又向0x3FFE8开始的地址写入了8个32位数据,而在map文件里,0x3FFE8指向的是程序的stack栈区域,光凭这些暂时还找不到问题的根结。
 
在逻辑分析仪的数据包里,又发现了下面一段波形,图三里,CPU干了两件事,从0x3FFE8地址开始读取8个数据,然后向0x20FE8地址写入8个数据,刚好和图二里干的事情相反。CPU没事在0x20FE8和0x3FFE8两个地址之间导来导去是在干嘛呢? 而且两个地址的末尾都是FE8,一个假象在脑子了蹦了出来-------难道是两个地址在cache里发生冲突了?(10分钟内,不能从"两个地址末尾都是FE8"这条线索联想到cache冲突的同学,请先面壁好好反省cache原理,想不起来的请先阅读前文“为什么程序越优化越慢”)
 
图三
 
根据直接映射cache的散列函数:cache_addr = ram_addr  mod  cache_size,0x3FFE8和0x20FE8在2KB的cache里的散列地址都是0x7E8,两个地址上存放的数据肯定会冲突,导致频繁的读取主存。那为什么guiTime变量会和看似毫无关联的栈区数据冲突呢?
 
回想了下堆栈的作用,恍然大悟。函数调用时,返回地址等一些现场信息都会存入栈中,调用结束后,出栈取出返回地址,PC指针跳转到返回地址继续运行。如果函数被频繁调用,为了减少存取消耗,栈区地址会被直接映射到cache的对应空间,这样函数在调用时,现场信息的存取都将直接在cache里操作,而不再是主存的栈区。但是,如果一个函数的现场信息和要使用到的全局变量,在cache里的映射地址完全相同,悲剧就发生了。
 
假设,Key函数在调用时,现场返回信息先存在cache的0x7F8上,当Key函数在执行过程中要用到一个全局变量guiTime,CPU先到guiTime对应的cache地址0x7F8上寻找,发现没有存储这个变量,于是就从主存0x20FE8读入guiTime,并同时准备将其覆盖到cache的0x7F8上,以备日后使用。但是0x7F8之前存储的是非常重要的函数现场信息,如果这些信息缺失,CPU将不知程序去向——直接跑飞,所以在覆盖之前,CPU会将cache里的现场信息存入主存0x3FFE8开始的真实栈区,到此,已经重现了图二的一幕。函数执行结束后,CPU需要返回地址给以指明一条出路。CPU先到cache的0x7F8上搜寻,寻找无果后,从主存的栈区取回返回地址继续跑路,同时准备将现场信息覆盖到cache的0x7F8上,避免以后重复读取主存,但此时0x7F8上存储的是全局变量guiTime,不能被随意篡改,否则就不能称为"全局变量"了,无奈的解决办法是在覆盖前,将guiTime再写回原始的主存地址,至此,图三的波形也再现了。如果Key函数被频繁调用,图二和图三的波形将频繁交替显现,运行时间就在这交替之间白白的浪费了。
 
解决办法还得从cache的散列函数入手,但是和前文遇到的情况略有不同,栈区的地址一般都从主存的末尾开始,是一个相当大的数值0x3FFFF,和数据所在区域0x20FE8的跨度太大了,远远超过了cache_size的2KB,而且嵌入式系统里也没法提供这么大的cache_size来消除冲突。但是注意,栈区所在区域虽然很大,但是真正使用到的地址是非常有限的,以本文为例,总共使用了0x3FFFF到0x3FFE8的24个地址,对应到cache的映射地址从0x7FF到0x7E8,所以只要数据区域的在cache的映射空间避开这段地址,就可以避免冲突了。前文所述的第二种方法在此就可派上用处了,比如在bsp里,新增一个从0x1000到0x17E7的段.DataCache,然后将程序里的所有的全局变量强制分布到这个段里,这样数据和栈永远也不会冲突了。
 
最后还有一种终极的解决办法就是消除祸根,不用全局变量....
 
再回到文中关于volatile的解读,解决了数据和栈的冲突后,无论在全局变量前是否加volatile的定义,都不会操作主存了。在加入cache的系统后,原始地址的主存已经被映射到了cache上,所以这里“原始地址”的含义其实就是cache了。
 
cache确实是一个不安分的家伙,作风爽快,干事利落,但性格经常会反复无常,很难伺候,对它是又爱又恨,要想用好它,需要好好的持久调教。翻篇后,希望cache能听话点,别再惹事了啊。
 
 

利用Java+DOS批处理实现网站刷流量

 

     最近开了个小店,木流量更木信誉,虽然东西已经便宜到要低于进价了大哭。 在不断向广大亲朋好友推荐的同时,想身为程序猿的我应该要做些什么,于是想到能否编程刷流量,网上一大堆刷流量的软件不敢用,搞不好店就得关门,还是自己解决吧。刷流量需要解决两个题,一是怎样访问算流量;二是怎样获取多个IP来访问,因为大家知道像淘宝这样的大型商城肯定是有各种防刷机制,一个IP肯定刷不了多少,搞不好就被封了,况且一个IP刷只能增加浏览量而不能增加访客数,作用还是不大。

     于是我想到了两个方法:一是用httpclient后台执行get方法打开网页获取数据,代码如下:

 

复制代码
package com.alexia;

import java.io.IOException;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;

public class AutoVisitSite {

    public static void main(String[] args) {
        
        // 访问10次
        for(int count = 1; count < 11; count++) {
            AutoVisit(count);
        }
        
    }

    public static void AutoVisit(int count) {
        
        HttpClient httpclient = new HttpClient();

         // 设置HTTP代理IP和端口
//         httpclient.getHostConfiguration().setProxy("58.252.56.149", 8080);
        
         // 代理认证(登录名+密码)
//         UsernamePasswordCredentials creds = new UsernamePasswordCredentials("root", "123456");
        
//         httpclient.getState().setProxyCredentials(AuthScope.ANY, creds);

        // Get调用
        GetMethod method = new GetMethod("http://alexia.taobao.com/");

        method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
                new DefaultHttpMethodRetryHandler(3, false));

        try {
            int statusCode = httpclient.executeMethod(method);

            if (statusCode != HttpStatus.SC_OK) {

                System.out.println(statusCode + ": " + method.getStatusLine());

            } else {

                // System.out.println(new String(method.getResponseBody(),
                // "GBK"));
                System.out.println("访问" + count + "次!");
            }

        } catch (IOException e) {
            
            e.printStackTrace();
            
        } finally {

            // 停留3s后再关闭连接
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            method.releaseConnection();
        }
    }
}
复制代码

 

 

结果当然是不行,要是这么简单,淘宝也可以关门了。通过淘宝的实时客户访问可知,淘宝是记录每一位访客来源的,是直接访问的还是站外访问的(也就是你推广的链接处访问进来),点击站外其他可以看到具体从哪个页面链接访问进来的,如下图所示:

所以此法不通(其实早知道了,只是还是想亲身尝试下以彻底死心)。于是想到了第二种方法:通过自动打开浏览器窗口访问网站,这种方法应该是可行的,就跟你自己手动打开浏览器访问网站效果一样,而打开浏览器针对不同版本的JDK也有不同的方法,如下:

 

复制代码
// 在新的浏览器窗口或在已有的浏览器窗口打开指定的URL(JDK 1.6以下)
    public static void openURL1(String url){  
         try {  
          Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler "+ url);  
         }catch(Exception e){
             e.printStackTrace();
         }  
    } 
    
    // 在新的浏览器窗口或在已有的浏览器窗口打开指定的URL(JDK 1.6以上)
    public static void openURL2(String url){  
        Desktop desktop = Desktop.getDesktop();
        if (Desktop.isDesktopSupported() && desktop.isSupported(Desktop.Action.BROWSE)) {
            URI uri = URI.create("http://lanxuezaipiao.blog.163.com/");
            try {
                desktop.browse(uri);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
   } 
复制代码

 

 

但为了打开浏览器后能及时关闭浏览器以避免产生多个窗口,我选择了利用Java执行DOS批处理的方式,即第一种方式,但上述第一种方式是用默认浏览器打开,而我系统的默认浏览器是Firefox,为了能在刷流量的同时还能继续上网,我决定用IE来执行刷流量的任务,批处理文件(autoVisit.bat)如下:

cd \
C:
cd Program Files\Internet Explorer
iexplore "http://item.taobao.com/item.htm?spm=a1z10.1.w4004-1854795698.36.SVY3Wd&id=18480249494"

然后Java调用这个批处理,代码如下:

复制代码
package com.alexia;

import java.io.IOException;

/**
 * 自动刷流量
 * 
 * @author Alexia
 *
 */
public class AutoFlux {

    /**
     * 
     * @param count
     *            访问次数
     * @param seconds
     *            每次访问时间
     * @throws InterruptedException 
     */
    public static void flux(int count, int seconds) {
        Process p1 = null, p2 = null;

        while (count-- > 0) {
            try {
                // 执行批处理开始打开IE浏览器访问
                p1 = Runtime.getRuntime().exec("cmd /c autoVisit.bat");
                
                // 访问延时(打开浏览器窗口要时间的)
                try {
                    Thread.sleep(1000 * seconds);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                // 关闭IE浏览器
                p2 = Runtime.getRuntime().exec("taskkill /F /IM iexplore.exe");
                
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        
            p1.destroy();
            p2.destroy();
        }
    }

    /**
     * @param args
     * @throws InterruptedException 
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        // 访问10次,每次延时5s
        flux(2, 5);
    }

}
复制代码

你可能会想,没必要这么麻烦把,既然是执行批处理,何必需要Java来调用执行,执行运行.bat文件不就行了嘛,的确如此,因为开始不知道批处理的延时方法,现在知道了,改用批处理方式最简洁:

 

复制代码
@echo off
rem 首先找到IE浏览器的执行路径
cd \
C:
cd Program Files\Internet Explorer

rem 循环10次
for /l %%i in (1,1,10) do (
rem 打开网页并最小化窗口(不影响你当前工作)
start /min iexplore "http://item.taobao.com/item.htm?spm=a1z10.1.w4004-

1854795698.36.SVY3Wd&id=18480249494"

rem 延时5s
choice /t 5 /d y /n >nul

rem 关闭网页
taskkill /F /IM iexplore.exe
)
复制代码

 

 

上面的批处理在执行时还是会出现DOS窗口的,还是有点影响不好,所以为了不显示DOS窗口,又写了个VB脚本:

 

DIM objShell
set objShell=wscript.createObject("wscript.shell")
iReturn=objShell.Run("cmd.exe /C autoVisit.bat", 0, TRUE)

 

 

这样就好了,完全不影响当前工作,真正实现了刷流量的功能。


但还是有问题没有解决:

1. 打开IE时任务栏有显示图标,你会看到图标一下有一下没,希望能够隐藏任务栏上的IE图标(也就是后台运行吧),经过搜索只能隐藏通知区域的图标而无法隐藏任务栏的某个程序图标;

2. 最关键的问题是如何多IP访问(增加访客量),这个问题我想到用代理服务器,IE可以设置代理服务器,关键是找不到可用的免费代理服务器,下载了个代理猎手ProxyHunter,在里面搜索了很久都搜不到一个免费的不要密码的,全是下面这些结果:



自己在网上也找了很多代理IP,放到猎手里一验证还是上面那些东西,没一个可用的,所以目前还为实现多IP访问网站的功能。


PS:如果谁有办法解决上面的两个问题,一定要告诉我,感激不尽!!!联系我

 

 

 

 

 

 

 
 
 
标签: JavaDos批处理刷流量
 
 
 
 
 
标签: nioscache
posted on 2013-05-21 22:28  HackerVirus  阅读(1213)  评论(0编辑  收藏  举报