Home »
29
Sat

Thrift TNonblockingServer的bug?

最近老写一些无聊的代码,大部分都是客户端需要点什么数据到服务器查一下然后返回这样的,用asio写的话比较繁琐,于是就换thrift了。然而一用就踩到了一个bug,google了许久没有找到解决方法,只看到了不少人也遇到了这个问题....折腾了一下午终于搞定了,简单记一下解决方法,希望有人再遇到这个bug可以看到这里就不用浪费时间了。

thrift的服务器端共有四个服务器类,TThreadedServer,TSimpleServer,TThreadPoolServer,TNonblockingServer,我i中TSimpleServer就是个测试用的,一次只能处理一个客户端,第二个客户端连接后会一直阻塞直到前一个断开连接才能用。TThreadedServer看起来像是一个链接一个线程,而TThreadPoolServer看起来像是用了线程池的一个链接一个线程(之所以说看起来像是因为只是通过名字判断的,有错误请指正),都严重的不符合我等胃口,只有TNonblockingServer看起来比较顺眼,于是就按照http://wiki.apache.org/thrift/ThriftUsageC%2B%2B里面介绍的的用法把tutorial中的TSimpleServer替换成了TNonblockingServer,但是一运行就挂了,触发了个断言,google了一顿也没找到解决方法。我用的代码是0.9.2的版本,我想git上最新的代码也许会解决了这个bug,但是更新下来问题依旧.....自己跟踪了一下发现是一个getaddrinfo失败了,失败的原因竟然是没有调用WSAStartup,
1.png
WTF,一个网络应用在windows上竟然不调用WSAStartup....果断查找了一下别的server怎么调用的WSAStartup,结果发现其他的server都是从TServerFramework继承的,在调用serve()函数时会调用TServerFramework::server(),而TServerFramework::server()会调用TWinsockSingleton::create()来WSAStartup,这就好解决了,一切还是按照官网上的那个example来写,但是在调用server.serve()之前先判断是不是windows平台,如果是就调用一次TWinsockSingleton::create(),问题解决.

修改之后的server端的完整代码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <thrift/concurrency/ThreadManager.h>
#include <thrift/concurrency/PlatformThreadFactory.h>
#include <thrift/concurrency/StdThreadFactory.h>
#include <thrift/concurrency/BoostThreadFactory.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/server/TThreadPoolServer.h>
#include <thrift/server/TThreadedServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransportUtils.h>
#include <thrift/TToString.h>
#include <thrift/server/TNonblockingServer.h>

#include <boost/make_shared.hpp>

#include <iostream>
#include <stdexcept>
#include <sstream>

#include "../gen-cpp/Calculator.h"

using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::concurrency;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace apache::thrift::server;

using namespace tutorial;
using namespace shared;

class CalculatorHandler : public CalculatorIf
{
public:
  CalculatorHandler() {}

  void ping() { cout << "ping()" << endl; }

  int32_t add(const int32_t n1, const int32_t n2)
  {
    cout << "add(" << n1 << ", " << n2 << ")" << endl;
    return n1 + n2;
  }

  int32_t calculate(const int32_t logid, const Work& work)
  {
    cout << "calculate(" << logid << ", " << work << ")" << endl;
    int32_t val;

    switch (work.op)
    {
    case Operation::ADD:
      val = work.num1 + work.num2;
      break;
    case Operation::SUBTRACT:
      val = work.num1 - work.num2;
      break;
    case Operation::MULTIPLY:
      val = work.num1 * work.num2;
      break;
    case Operation::DIVIDE:
      if (work.num2 == 0)
      {
        InvalidOperation io;
        io.whatOp = work.op;
        io.why = "Cannot divide by 0";
        throw io;
      }
      val = work.num1 / work.num2;
      break;
    default:
      InvalidOperation io;
      io.whatOp = work.op;
      io.why = "Invalid Operation";
      throw io;
    }

    SharedStruct ss;
    ss.key = logid;
    ss.value = to_string(val);

    log[logid] = ss;

    return val;
  }

  void getStruct(SharedStruct& ret, const int32_t logid)
  {
    cout << "getStruct(" << logid << ")" << endl;
    ret = log[logid];
  }

  void zip() { cout << "zip()" << endl; }

protected:
  map<int32_t, SharedStruct> log;
};

/*
  CalculatorIfFactory is code generated.
  CalculatorCloneFactory is useful for getting access to the server side of the
  transport.  It is also useful for making per-connection state.  Without this
  CloneFactory, all connections will end up sharing the same handler instance.
*/
class CalculatorCloneFactory : virtual public CalculatorIfFactory
{
 public:
  virtual ~CalculatorCloneFactory() {}
  virtual CalculatorIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo)
  {
    boost::shared_ptr<TSocket> sock = boost::dynamic_pointer_cast<TSocket>(connInfo.transport);
    cout << "Incoming connection\n";
    cout << "\tSocketInfo: "  << sock->getSocketInfo() << "\n";
    cout << "\tPeerHost: "    << sock->getPeerHost() << "\n";
    cout << "\tPeerAddress: " << sock->getPeerAddress() << "\n";
    cout << "\tPeerPort: "    << sock->getPeerPort() << "\n";
    return new CalculatorHandler;
  }
  virtual void releaseHandler( ::shared::SharedServiceIf* handler)
  {
    delete handler;
  }
};

int main()
{
/*   TThreadedServer server(
    boost::make_shared<CalculatorProcessorFactory>(boost::make_shared<CalculatorCloneFactory>()),
    boost::make_shared<TServerSocket>(9090), //port
    boost::make_shared<TBufferedTransportFactory>(),
    boost::make_shared<TBinaryProtocolFactory>());

 
  // if you don't need per-connection state, do the following instead
  TThreadedServer server(
    boost::make_shared<CalculatorProcessor>(boost::make_shared<CalculatorHandler>()),
    boost::make_shared<TServerSocket>(9090), //port
    boost::make_shared<TBufferedTransportFactory>(),
    boost::make_shared<TBinaryProtocolFactory>());
  */

  /**
   * Here are some alternate server types...

  // This server only allows one connection at a time, but spawns no threads
  TSimpleServer server(
    boost::make_shared<CalculatorProcessor>(boost::make_shared<CalculatorHandler>()),
    boost::make_shared<TServerSocket>(9090),
    boost::make_shared<TBufferedTransportFactory>(),
    boost::make_shared<TBinaryProtocolFactory>());

  const int workerCount = 4;

  boost::shared_ptr<ThreadManager> threadManager =
    ThreadManager::newSimpleThreadManager(workerCount);
  threadManager->threadFactory(
    boost::make_shared<PlatformThreadFactory>());
  threadManager->start();

  // This server allows "workerCount" connection at a time, and reuses threads
  TThreadPoolServer server(
    boost::make_shared<CalculatorProcessorFactory>(boost::make_shared<CalculatorCloneFactory>()),
    boost::make_shared<TServerSocket>(9090),
    boost::make_shared<TBufferedTransportFactory>(),
    boost::make_shared<TBinaryProtocolFactory>(),
    threadManager);
  */

  const int workerCount = 4;

  boost::shared_ptr<ThreadManager> threadManager =
      ThreadManager::newSimpleThreadManager(workerCount);
  threadManager->threadFactory(
      boost::make_shared<BoostThreadFactory>());
  threadManager->start();

#ifdef _WIN32
  TWinsockSingleton::create();
#endif // _WIN32

  TNonblockingServer server(
      boost::make_shared<CalculatorProcessor>(boost::make_shared<CalculatorHandler>()),
      boost::make_shared<TBinaryProtocolFactory>(),
      9090,
      threadManager);
//    TNonblockingServer server(boost::make_shared<CalculatorProcessor>(boost::make_shared<CalculatorHandler>()), 9090);

  cout << "Starting the server..." << endl;
  server.serve();
  cout << "Done." << endl;
  return 0;
}

注意一下创建server之前的
#ifdef _WIN32
TWinsockSingleton::create();
#endif // _WIN32

其实这段代码只要是放在server.serve();之前就可以了。其实TWinsockSingleton::create()里面的代码非常简单,就是调用了一下WSAStartup判断了一下车公公了没,没成功就抛异常。

14
Thu

用asio撸了一个简单的Actor模型

图灵社区文章地址:http://www.ituring.com.cn/article/198241
前几天买了本七周七并发模型,看了里面关于Actor模型的介绍后觉得Actor这东西其实挺简单的,平时用asio写网络应用,每个session直接也是通过投递异步事件来互相交流的,本质上每个session类也就是一个Actor,看来着东西平时一直在用,只是不知道叫这么个名字罢了,再生搬硬套一个Actor模型感觉毫无意义,于是用asio写了个简单的比较通用的Actor模型。

阅读剩余部分...

13
Wed

解决windows上静态链接cairo不显示东西的问题

今天闲着没事把cairo静态编译了一把,但是没想到静态链接后用不起来,本来用cairo画图好好的现在却变成了白板一张..调试了一下看是进入了cairo_win32_surface_create这个函数之后就没有返回,不过仔细想了下调用dll没问题但是静态链接就有问题,八成是在加载dll的时候初始化了什么东西,但是static链接没有初始化,那手动初始化一下应该就可以了。于是打开notepad++在cairo源码中搜DllEntry,发现果然有初始化一个mutex

BOOL WINAPI
DllMain (HINSTANCE hinstDLL,
         DWORD     fdwReason,
         LPVOID    lpvReserved)
{
    switch (fdwReason) {
        case DLL_PROCESS_ATTACH:
            CAIRO_MUTEX_INITIALIZE ();
            break;

        case DLL_PROCESS_DETACH:
            CAIRO_MUTEX_FINALIZE ();
            break;
    }

    return TRUE;
}

那事情就好说了,在程序开始的地方调用CAIRO_MUTEX_INITIALIZE (),结束的地方调用CAIRO_MUTEX_FINALIZE (),重新编译,一切正常。

另外还发现了一个方法,在cairo_win32_surface_create之前调用一遍cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0)也能解决问题,原因不知道...不过这不是正常的解决方法,而且还要费劲去释放这个surface,没什么好处。

25
Wed

静态编译Qt5.4.1和Qt WebKit

图灵社区文章地址:http://www.ituring.com.cn/article/195148
WebKit是个好东西,做爬虫、显示网页还是想用HTML来做桌面应用的界面都可以用他,不过一直以来都觉得自己编译webkit费力不讨好,所以都是用的qt官方编译好的,至于静态编译webkit,我之前一直以为是根本不可能的。后来看qt forum上说其实是可以的,不过是因为开源协议的问题qt默认的静态编译只是把webkit给排除了,自己编译一下webkit就好了,既然如此,那我就当小白试一下。不过最后发现静态编译webkit其实也不难,就是比较麻烦,占用了几十个G的硬盘历时一天终于把这玩意编译完了。所以简单记一下编译的流程,方便大家。不过得提醒大家闭源项目静态链接qt和webkit是违反LGPL协议的,除非你把你的程序以GPL协议开源,要不就是违法的。

阅读剩余部分...

15
Sat

脚本新选择——用C做脚本

图灵社区文章地址:http://www.ituring.com.cn/article/129090
很早之前就知道了有tcc这么个玩意,不过当时对这玩意兴趣不大,因为他官网上主要是在说tcc的编译速度很快甩GCC好几条街之类的,当时觉得似乎意义不是很大就没再关心过,可是没想到这个小玩意给了我不少的惊讶,之前闲着么事逛陈皓的blog发现它能把C当脚本使,感觉有点惊讶,不过觉得C比起bash或者python什么的毫无优势可言,于是还是只当是个玩具。再后来因为项目需要用脚本来实现一些逻辑经常需要改动的地方,就一直在用lua+luabridge来搞的,不过习惯了类C语言的语法,总觉得lua的语法很别扭,于是想换一个,思来想去的,考虑过python,考虑过V8搞js,可惜python和V8,要么比较大,一堆没用的库;要么编译麻烦,还没什么基础库。正烦艹中想到了tcc,记得当时用了下他是可以调用C的标准库,也可以调用系统API,而且还是C语言的,正合我意。

当时还想是要自己改tcc的源码,搞出来接口供我的程序调用,没想到又仔细看了下tcc发现人家本来就提供了个libtcc,是可以直接拿来用的,这下可把我乐坏了,于是就开始学习这玩意怎么用。到现在用libtcc当脚本引擎也快半年了,感觉相当不错,于是写一篇文章来介绍下。

阅读剩余部分...