纯Java网站开发改造为nodejs混合编程



早已对纯JAVA版的网站不满了,不管是繁重的代码编写量,和无谓的3层代码编写都让我提不起兴趣。但是提到nodejs我就有兴趣来做了,原来的网站是放到云服务器上的,由于CPU和内存的限制进一步影响了网站速度和承载量。达到什么程度呢,就是3个人同时应用就会造成访问慢或卡死。

         于是我想到nodejs将原来网站重写。将来就会加速网站和体现并发数优势。很多人反对我这样做,说nodejs不适合重逻辑的部分,但好了,89%的应用都是直接从用户获得参数直接传透到数据库,为啥要写那么多代码,什么时候运行过其他计算。偶尔也是对参数进行个加减而已。为啥不将几百行代码缩减到几行。

       既然要动手就开始,准备好nodejs,在nodejs.org官网下在nodejs1.2X 安装好之后,下在javascript编辑器,或文本都行。我喜欢用文本直接编辑。

    罗列了以下几个步骤  :

1.      安装nodejs mysql   (网上很多教程注意先安装git

2.      修改java工程文件中的web,添加跨域反问,并将跨域限制为本机

3.      修改原工程jqueryajax调用,使用访问本机127.0.0.1:1337端口访问

4.      提供nodejs直接调用数据库的调用方法

5.      修改调用返回的处理

最后就是写一个工具在原网站上进行500个轮询访问的效率查询,用以鉴定以前的效率和现在效率的差别。




这棵树就是我们需要改造的原因。原来的反问原理是,通过spring->访问controller->访问helper->访问dao->访问mysql->再依次将结果json返回页面处理。


上面就是全部步骤,其实我说错了,上面还不是整个过程。因为树是存在一张表中的。其中只有id,pried,name,leve,orderid等,所以关系都是锁在同一张表里,意味着要把所有树排列好一次拿出来是可以的。只要按默认顺序将树整个解析出来即可。但目前存在客户要求树也要按顺序列出来,也就是按order指定的顺序排列,那么一次将树拿出来解析就不可以了。


因此我采用先将根结点读数据库拿出来,在生成根结点界面的时候程序回调再去查询所有子节点,并从数据库返回结果生成整棵树。


这样本来一次调用却变成了N次调用,往返于服务器之间,登陆几个用户打开几次页面我的程序基本就慢的要死。


第一步介绍:我只介绍注意事项即可,其他的请搜索网上吧,而且都有很好的文章。安装好nodejs使用npm装载mysql模块是报错的,因为没装git,使用git后才能安装,输入以下命令


npm install felixge/node-mysql


完成安装mysql


完成之后试写mysql功能简单调用一下:


var mysql  = require('mysql');




var pool  = mysql.createPool({

  connectionLimit : 30,

  host            : 'localhost',

  user            : 'root',

  password        : xxxx

});
 
pool.query('SELECT * FROM zd.alga_cs;', function(err, rows, fields) {

  if (err) throw err;

  console.log('The solution is: ', rows);

});


       调用完成后看一下你是否能读出结果,测试成功则nodejsmysql模块都装好了。


 


       第二步:修改java原来的tomcat,因为在一个页面下以前用jqueryajax调用spring对应的controller,所以现在需要改成调用nodejs本地下的一个端口。我设置为127.0.0.1:1337下来访问我定义的nodejs代码块。


       第一就直接修改了,例如将如下:


 


$.ajax({ async:false,type:"post", url:"employee.getUnDeparment.do",data:"", dataType:"text", success:function(msg){ mydata=eval("("+msg+")"); // alert(msg); $.each(mydata,function(idx,item){ unuser = item.count; });


       改成:


$.ajax({ async:false, type:"get",url:"http://127.0.0.1:1337/employee_getUnDepartment",data:"", dataType:"text", success:function(msg){ mydata=eval("("+msg+")"); // alert(msg); $.each(mydata,function(idx,item){ unuser = item.count; });


       然后就不出数据了,按下IEF12,看到提示CORS错误!nodejs写的http模块使用http:// 127.0.0.1:1337/employee_getUnDepartment是直接可以返回json串的,怎么到这里就不行了呢?!原来还需要改造一下javaweb.xml配置,和加入两个jar包才行。


       网上下载:cors-filter-2.4.jarjava-property-utils-1.9.1.jar;放入项目工程里的libs目录下,并引用这两个包。并将如下代码加到web.xml:


 


   


       CORS


       com.thetransactioncompany.cors.CORSFilter


       


           cors.allowOrigin


           http://127.0.0.1


       


       


           cors.supportedMethods


           GET,POST,HEAD,PUT,DELETE


       


       


           cors.supportedHeaders


Accept,Origin,X-Requested-With,Content-Type,Last-Modified


       


       


           cors.exposedHeaders


           Set-Cookie


       


       


           cors.supportsCredentials


           true


       


   


   


       CORS


       /*



 


注意我写的是127.0.0.1,也就是说我允许跨域到本机127.0.0.1位置。设置完了后再次调用,怎么回事,nodejs控制台已经返回了查询结果,但IE报一个ajax错误,查了之后发现如果是跨域访问,则需要返回的内容加上文件头。于是在返回结果的模块里加了头如下:


res.writeHeader(200,{

  'Access-Control-Allow-Origin' : '*'  // 先写header 否则返回无效在跨域访问时

}) ;


加上这句,返回的串就可以显示到原来的界面上。速度嘛,当然比以前块几百毫秒,但调试变简单了,Ctrl+C终止程序,按上键显示上句命令,回车就完成了再次启动nodejs程序。


而且不受以前tomcat的影响,只要程序是nodejs里的,直接关闭nodejs再启动调试,使用者基本感觉不到你在一步步调试程序,他们其他的java应用里的程序还正常执行。


 


第三步修改ajax调用为nodejs远程:


$.ajax({async:false, type:"get",url:"http://127.0.0.1:1337/node_employee_getsubtree_do", data:"citylist="+session_citylist+"&did="+id,success:function(msg){


alert(msg); }


});


 


这里有一个坑,就是type:get;如果你不注意原来用的是post的话,那么在nodejs处理比较复杂,因为post是流发送,也就是有个开始投送,到接收完毕的过程,在nodejs里需要处理开始,和回调函数,这样整个改造过程就比较麻烦了。因为没有牵扯到需要post的表单,所以直接用get,否则参数会接收不到。当然如果你用了express 的话当然可以用里面包含的接收post包装好的方法。


 


第四步提供调用subtreenodejs方法:


//调用子树

if(pathname=="/node_employee_getsubtree_do") {

  var str = arg.citylist;

  var did = arg.did;

  var subtree = "";

 pool.query('select idcity,cityName,b.departmentid,departmentName,departmentlimit,count(c.employeeId) count  from zd.city as a left outer join zd.department as b on a.idcity=b.cityId left outer join zd.employee c on b.departmentId = c.departmentId  where 1 =1 and ('+str+')  and b.pre='+ did +' and b.level=1  group by idcity,cityName,b.departmentid,departmentName,departmentlimit order by a.idcity asc,b.orderid asc ;', function(err, rows, fields) {

      if (err) throw err;
            subtree = JSON.stringify(rows);
     res.writeHeader(200,{

        'Access-Control-Allow-Origin' : '*'  // 先写header 否则返回无效在跨域访问时

      }) ;



      res.end(subtree);

     });

 

  }


这里有两个坑,不小心你就掉里面了,第一个坑就是返回json串的方法,在nodejs里把结果集合改成json是用JSON.stringifv();方法格式化结果集。第二个坑就是Header必须写在前面,否则跨域不接受数据。我写的*是允许所有操作(GET UPDATE DELETE POST等)跨域提供数据。


 


第五步,修改JAVA程序适合调用返回nodejs程序:


其实这步根本不需要做,为什么还需要这步,是因为,以前java调用dao返回结果结集的时候字段名称有大写有小写,有混合写的。但用nodejs调用后直接都是数据库里怎么写的字段名返回就是怎么写的。所以departMent有可能变成department,因此要详细核对一下,这个坑我已经掉进去过了。


 


这就是一个简单的混合程序完成了。但只是比java的快了一点点,那么怎么优化呢?下面介绍一下优化。


优化思路:


       减少数据库调用à减少ajax调用


这个大方向走,首先是否使用redis,想了半天,还是算了,只是为了优化一棵树,何必动用神器。自己搞个HashTabls算了。首先采用变量来优化基础查询,如下:


res.writeHeader(200,{

  'Access-Control-Allow-Origin' : '*'  // 先写header 否则返回无效在跨域访问时

}) ;



//判断主树是否需要缓寸

if(condmaintree!=str) {

  condmaintree = str;

  // console.log('citylist: ', str);

  pool.query('select idcity,cityName,b.departmentid,departmentName,departmentlimit,count(c.employeeId) count  from zd.city as a left outer join zd.department as b on a.idcity=b.cityId left outer join zd.employee c on b.departmentId = c.departmentId  where 1 =1 and ('+str+')  and b.level=0 group by idcity,cityName,b.departmentid,departmentName,departmentlimit  order by a.idcity asc,b.orderid asc ;', function(err, rows, fields) {

    if (err) throw err;

     console.log('读数据库! ');

    memmaintree = JSON.stringify(rows);

    res.end(memmaintree); //数组和json之间的数据转换

 });

} else {

  console.log('直接返回!');

  res.end(memmaintree);

}


 可以看出采用了nodejs全局变量condmaintree,因为所有人只有权限不同的才会需要重新加载树,所以可以这样做,改完之后只有第一次读取需要查数据库,否则直接http返回存在condmaintree里的json串。子树也是这样优化的,但字树的分支读取次数很多,需要很多全局变量,这不切合实际。怎么办,引用自己编写的HashTablenodejs版如下HashTable.js


 


 


var size = 0;

var entry = new Object();



exports.add = function (key , value)

{

    if(!this.containsKey(key))

    {

        size ++ ;

    }

    entry[key] = value;

}



exports.getValue = function (key)

{

    return this.containsKey(key) ? entry[key] : null;

}



exports.remove = function ( key )

{

    if( this.containsKey(key) && ( delete entry[key] ) )

    {

        size --;

    }

}



exports.containsKey = function ( key )

{

    return (key in entry);

}



exports.containsValue = function ( value )

{

    for(var prop in entry)

    {

        if(entry[prop] == value)

        {

            return true;

        }

    }

    return false;

}



exports.getValues = function ()

{

    var values = new Array();

    for(var prop in entry)

    {

        values.push(entry[prop]);

    }

    return values;

}



exports.getKeys = function ()

{

    var keys = new Array();

    for(var prop in entry)

    {

        keys.push(prop);

    }

    return keys;

}



exports.getSize = function ()

{

    return size;

}



exports.clear = function ()

{

    size = 0;

    entry = new Object();

}


 


调用过程如下:


var MhashTable = require('./HashTable.js');
//调用子树

if(pathname=="/node_employee_getsubtree_do") {

  var str = "";

  var citylist = arg.citylist;

  var did = arg.did;

  var subtree = "";

  if(citylist=="") {

    str += " idcity =-1";

  } else {

    str +=" idcity =";

    citylist= citylist.replace(/,/g," or idcity =");

    str+=citylist;

  }

  subtree = MhashTable.getValue(did); //获得变量如果为null则访问数据库

  if(subtree == null) {

    pool.query('select idcity,cityName,b.departmentid,departmentName,departmentlimit,count(c.employeeId) count  from zd.city as a left outer join zd.department as b on a.idcity=b.cityId left outer join zd.employee c on b.departmentId = c.departmentId  where 1 =1 and ('+str+')  and b.pre='+ did +' and b.level=1  group by idcity,cityName,b.departmentid,departmentName,departmentlimit order by a.idcity asc,b.orderid asc ;', function(err, rows, fields) {

      if (err) throw err;

      subtree = JSON.stringify(rows);

      MhashTable.add(did,subtree); //将结果存入hashtable

      console.log('哈希没找到!: ', subtree);

      res.writeHeader(200,{

        'Access-Control-Allow-Origin' : '*'  // 先写header 否则返回无效在跨域访问时

      }) ;

      res.end(subtree);

     });

  } else {

    res.writeHeader(200,{

      'Access-Control-Allow-Origin' : '*'  // 先写header 否则返回无效在跨域访问时

    }) ;

    console.log('哈希找到!: ', subtree);

    res.end(subtree);

  }

}


       这样做以后,首次60次的ajax调用确实访问了数据库,但第二次的刷新树的时候就不会调用数据库了。但这也不是最终的方法,我还是决定要去掉远程调用,那么在index.jsp框架页面里引入HashTable.js,但这个版本和nodejs用的稍微有点不同。代码如下:


HashTable.js


functionHashTable()


{


    var size = 0;


    varentry = new Object();


    this.add = function (key , value)


    {


        if(!this.containsKey(key))


        {


            size ++ ;


        }


        entry[key] = value;


    }


    this.getValue = function (key)


    {


        return this.containsKey(key) ?entry[key] : null;


    }


    this.remove = function ( key )


    {


        if( this.containsKey(key) && (delete entry[key] ) )


        {


            size --;


        }


    }


    this.containsKey = function ( key )


    {


        return (key in entry);


    }


    this.containsValue = function ( value )


    {


        for(var prop in entry)


        {


            if(entry[prop] == value)


            {


                return true;


            }


        }


        return false;


    }


    this.getValues = function ()


    {


        var values = new Array();


        for(var prop in entry)


        {


            values.push(entry[prop]);


        }


        return values;


    }


    this.getKeys = function ()


    {


        var keys = new Array();


        for(var prop in entry)


        {


            keys.push(prop);


        }


        return keys;


    }


    this.getSize = function ()


    {


        return size;


    }


    this.clear = function ()


    {


        size = 0;


        entry = new Object();


    }


}


 


改造index.jsp 加入下列代码:



//树缓存


var subtreeHashTabls = new HashTable();



 


在具体调用的方法里加入hashtable查询的过程,如下:


function getsubTree(id) {


var result = subtreeHashTabls.getValue(id);


var str = "";


if(result ==null) { //alert("远程取!");


$.ajax({async:false, type:"get",url:"http://127.0.0.1:1337/node_employee_getsubtree_do",data:"citylist="+session_citylist+"&did="+id,success:function(msg){


subtreeHashTabls.add(id,msg);// alert(msg);


}}); }


result =subtreeHashTabls.getValue(id);


var mydata=eval(result);



…}


 


经过这样的改造后,只需要读取一次树,其他时候读取树完全由内存里的HashTable读取,根本都不需要访问ajax跟服务器发生交互。

改造完毕后,我的页面首次加载比原来快1秒,再次加载快3秒,当然并发量我并没有测试,应该部署后会比原来强大许多,这就是nodejs优势,当然HashTable也尽了很大的力。




你可能感兴趣的:(java,nodejs,混合)