分享

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

 春和秋荣 2019-06-25

早已对纯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:

 

   <filter>

       <filter-name>CORS</filter-name>

       <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>

       <init-param>

           <param-name>cors.allowOrigin</param-name>

           <param-value>http://127.0.0.1</param-value>

       </init-param>

       <init-param>

           <param-name>cors.supportedMethods</param-name>

           <param-value>GET,POST,HEAD,PUT,DELETE</param-value>

       </init-param>

       <init-param>

           <param-name>cors.supportedHeaders</param-name>

<param-value>Accept,Origin,X-Requested-With,Content-Type,Last-Modified</param-value>

       </init-param>

       <init-param>

           <param-name>cors.exposedHeaders</param-name>

           <param-value>Set-Cookie</param-value>

       </init-param>

       <init-param>

           <param-name>cors.supportsCredentials</param-name>

           <param-value>true</param-value>

       </init-param>

   </filter>

    <filter-mapping>

       <filter-name>CORS</filter-name>

       <url-pattern>/*</url-pattern>

</filter-mapping>

 

注意我写的是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 加入下列代码:

<scripttype="text/javascript">

//树缓存

var subtreeHashTabls = new HashTable();

</script>

 

在具体调用的方法里加入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也尽了很大的力。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多