返回留言板
文章标题:采用最新版本的UDP功能,实现多个平台之间的全局锁
文章作者:bluesen
发表时间:2012-3-3 14:17:35
内容:
一、基本原理:
  多个平台之间也可能需要全局锁,比如多台机器同时预测外呼,那么不能取到相同的一批号码。全局锁的实现方法是:
  在其中一台机器部署一个虚拟线路专门运行管理令牌的脚本。
  每个平台都可以运行1个客户端脚本,在需要访问独占资源时,向服务程序申请令牌,资源访问完毕后,再交回令牌。
  令牌可以按任务进行编号,即不同的任务不同的锁。
  下面的程序考虑了UDP的不可靠性,客户端在没有收到响应时会重复地发送请求。

二、服务端脚本:TokenRing.bss
tonkenPort = 7102;
hd = UdpOpen(tonkenPort);
if( hd<0 )
{
   DispInfo(0, "UdpOpenErr:"+hd);
   return(0);
}

// 请求数据包结构: cmdcid
// cmd: GET - 请求获取令牌,cid为参数
//      PUT - 请求交还令牌,cid为参数
// 响应数据包结构: Acmdcid
//   A: 0不成功,1成功
// cmd: GET - 响应get请求
//      PUT - 响应put请求
// cid: 原样返回

tokens = 0;

count = 0;
buff = "";
ip = "";
port = 0;
while(1)
{
      ret = UdpRecv(hd, ip, port, buff);
      if( ret>0 )
      {
         DispInfo(0, ret);
         DispInfo(1, port);
         DispInfo(2, buff);

         frm = ip+":"+port;
         cmd = Substr(buff, 0, 3, 0);
         cid = Substr(buff, 3, 100, 0);

         if( cmd=="GET" )  // 取令牌
         {
            val = tokens[cid];
            if( val==NULL || val==frm )
            {
               tokens[cid] = frm;
               a = "1";
            }
            else
               a = "0";
            UdpSend(hd, ip, port, a+cmd+cid);
         }
         else if( cmd=="PUT" )  // 还令牌
         {
            val = tokens[cid];
            if( val==frm )
            {
               tokens[cid] = NULL;
               a = "1";
            }
            else
               a = "0";
            UdpSend(hd, ip, port, a+cmd+cid);
         }
         else if( cmd=="EXT" )  // 退出
            break;
      }
   // Sleep(0.05);
}

OnSysQuit();
UdpClose(hd);
return(0);


三、客户端脚本和测试程序: GetToken.bss
// 获取令牌的客户端函数
tonkenIp = "127.0.0.1";
tonkenPort = 7102;
myPort = 7103+_lineNo;

function SendTokenReq(hd, cmd, cid)
{
  ret = -1;
  UdpSend(hd, tonkenIp, tonkenPort, cmd+cid);
  t1 = GetTick(0);
  while(1)
  {
     n = UdpTest(hd);
     if( n>0 )
     {
        buff = "";
        ip = "";
        port = 0;
        ret = UdpRecv(hd, ip, port, buff);
        if( ret>0 )
        {
           DispInfo(2, buff);

           a = Substr(buff, 0, 1, 0);
           cmd2 = Substr(buff, 1, 3, 0);
           cid2 = Substr(buff, 4, 100, 0);

           if( cmd2==cmd && cid2==cid )
           {
              ret = Int(a, 0);
              break;
           }
           else
              ret = -2;
           // break;
        }
     }
     else if( n<0 )
     {
        ret = -3;
        break;
     }
     t2 = GetTick(0);
     if( t2-t1>210 )
        break;
     Sleep(0.05);
  }

  return(ret);
}

function ProcToken(hd, cmd, cid)
{
  for(i=0; i<6; i++)
  {
     ret = SendTokenReq(hd, cmd, cid);
     if( ret>=0 )
        break;
  }
  return(ret);
}
// -------------------
// 暴露的接口:
// 初始化
function InitToken(port)
{
  hd = UdpOpen(port);
  if( hd<0 )
  {
     DispInfo(0, "UdpOpenErr:"+hd);
     return(-1);
  }
  return(hd);
}

// 关闭
function CloseToken(hd)
{
  if( hd )
  {
     UdpSend(hd, tonkenIp, tonkenPort, "EXT123");
     UdpClose(hd);
     hd = 0;
  }
}

// 加锁
function LoackToken(hd, cid)
{
  for(i=0; i<35; i++)
  {
     ret = ProcToken(hd, "GET", cid);
     if( ret==1 )
       break;

     Sleep(0.1);
  }
  return(ret);
}

// 解锁
function UnloackToken(hd, cid)
{
  ret = ProcToken(hd, "PUT", cid);
  return(ret);
}

// -----------------------------
// 测试脚本
hd = InitToken(myPort);
cid = "1234567890-abcdefg-hello-word";

while(1)
{
   len = KeysLen(0);
   if( len<1 )
   {
      Sleep(0.05);
      continue;
   }

   s = "";   
   PcGetKeys(s, 1, 3);
   if( s=="" )
      continue;

   if( s=="l" )  // lock
   {
      t1 = GetTick(0);
      ret = LoackToken(hd, cid);
      t2 = GetTick(0);
      DispInfo(0, ret);
      DispInfo(1, t2-t1);
   }
   else if( s=="u" )  // unlock
   {
      t1 = GetTick(0);
      ret = UnloackToken(hd, cid);
      t2 = GetTick(0);
      DispInfo(0, ret);
      DispInfo(1, t2-t1);
   }
   else if( s=="q" )  // quit
   {
      CloseToken(hd);
      break;
   }
   Sleep(0.1);
}


OnSysQuit();
CloseToken(hd);
return(0);

  

已有回复:


回复如下

标题:
    发言人:

内容: [回复] [重写]