类别归档:Web开发

RSS feed of Web开发

最后更新于 .

上一篇文章《更简洁的C++数据库访问框架-soci》介绍了soci,但也同时提到了一个缺点---insert/update/delete操作的时候,没有返回影响了多少行。 另外还有一个隐性的原因,soci的代码大量使用了异常,而项目中要求底层代码是严禁抛出异常的(其实google也是有这个要求的)。 基于以上两个原因,所以最终没有把soci用在生产环境。 但是又不想每次都重复去调用那些繁琐的MySQL API,所以这次又重新对MySQL API做了一层C++的封装。 其实相信每个和mysql打过交道的程序员都应该会尝试去封装一套mysql的接口,这一次的封装已经记不清是我第几次了,但是每一次我希望都能做的比上次更好,更容易使用。 先来说一下这次的封装,遵守了几个原则,其中部分思想是从python借鉴过来的:

  • 1.简单
  • 简单,意味着不为了微小的效率提升,而去把接口搞的复杂。因为本身数据库存储效率的瓶颈并不是那一两次内存copy,代码中随处可以看到以这个为依据的设计。
  • 2.低学习成本
  • 使用一套新库通常意味着投入学习成本,而这次的封装并没有像django那样实现一套完整的模型系统,也没有做soci那样的语法分析器,我选择最简单易懂的方式:做sql语句拼接器,所以对习惯了使用原生mysql api的朋友,学习成本很低
  • 3.模块化
  • 代码实际包括了两个模块,一个是mysql client端的封装,一个是sql的拼接器,这两个模块是完全独立的,调用者可以任意组合或者独立使用。
  • 4.尽量使用STL以及模板,简化代码编写
  • 最大的特点就是大量使用了stringstream进行类型转化 ...

最后更新于 .

一直以来,笔者都在不停寻找一种更人性化的数据库访问方式(并不是说默认的方式不好,而是有时候的确在模块化设计中不太方便)。 后来有幸在php中找到codeigniter的ActiveReord,详细参考这篇文章: 抽离CodeIgniter的数据库访问类! 然而c++却始终用着最原始的方式,昨天趁着项目要用的机会,在网上搜索了好久,总算让我找到两套c++的数据库访问框架:

两套代码我都拿下来看了一下,litesql实现了一套完整的代码自动生成,功能强大,但是也很重;soci相对要轻量,但是同样也实现了数据结构到数据库表的映射。本人还是比较喜欢轻量的东西,所以最终选择了soci。经过这两天的试用,感觉非常不错。 官方的文档也很详细,所以这里就用我写的单元测试代码来做一下简单的讲解:

首先创建库表:

create database soci;
CREATE TABLE `tb_test` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(32) default "",
  `sex` int(11) default 0,
  PRIMARY KEY  (`id`),
  UNIQUE ...

最后更新于 .

一.string中find_first_of的误用

STL中提供的string可以说极大方便了对字符串的操作,但是很多函数由于样子上很相似,所以导致很容易理解错误,find_first_of和find就是一个很好的例子。 我们先来看一下string提供的查找相关的函数列表:

find_first_of() 查找第一个与value中的某值相等的字符
find_first_not_of() 查找第一个与value中的所有值都不相等的字符
find_last_of() 查找最后一个与value中的某值相等的字符
find_last_not_of() 查找最后一个与value中的所有值都不相等的字符
rfind() 查找最后一个与value相等的字符(逆向查找)

如此简洁的说明,其实完全没有把他们最重要的区别描述出来,请务必记住: 对于find和rfind:

  • 匹配的是整个被查找串

对于find_first_of,find_first_not_of,find_last_of,find_last_not_of:

  • 匹配的是被查找串中的任意字符

我们来测试一下:

#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <set>
#include <map>
using namespace std;
int main(int argc, const char *argv[])
{
    string src = "vimer.cn ...

最后更新于 .

接着上一篇文章: 有限状态机的C++实现(1)-epoll状态机,我们今天来介绍更复杂和深入的部分。 为什么会在标题中提到bayonet这个开源项目呢?笔者本人一直想要写一套架构优美、功能完善的异步server框架,也看过很多朋友、同事实现的版本,虽然功能上基本能满足需求,但是架构上我却始终觉得是有瑕疵的,直到后来和同事讨论,发现可以让一个客户端请求的到来作为一个session,而之后的每一次与其他server的交互都可以看作是一次状态转化,才感觉架构比较合理了。 简单来说即,一个session从开始到介绍会经历两种状态机的变化:

  • 1.业务逻辑层面的状态变化,例如先验证登录态,再验证权限,再获取用户资料
  • 2.每一个与其他server交互的socket自身的状态变化,如recv、send、等,而socket的状态变化会触发逻辑层的状态变化。

按照这种思路,目前的代码开发已经完成了70%,即可以正常的进行一个session的开始和结束,主要还缺一些细节的代码,比如超时的检测及超时之后的处理,健全的统计之类。好了,我们来用vs看一下代码的整体类图(图压缩比较严重,请单击后查看):

1

每个类的用处已经在途中简单说明了,这里就不再赘述,我们重点来看一下用这个框架来实现一个逻辑server时需要做哪些事情。 svr2目录下的main.cpp即实现了一个最简单的server,我们按部分来看其实现:

1.逻辑层状态的定义

class CAppFsmLogic1 : public CAppFsmBase ...

最后更新于 .

最近有一个server在重启的时候总要花费5分钟左右来加载配置文件,导致外网服务不可用,今天和几个同事一起研究了一下,总算找到了问题所在. 抽象出代码如下:

#include <sys/time.h>
#include <stdio.h>
#include <memory.h>
#include <map>
#include <string>

#if 0
#include <hash_map.h>
#else
#include <tr1/unordered_map>
#define hash_map std::tr1::unordered_map
#endif

using namespace std;

class CTimer
{
public:
    CTimer()
    {
        memset(&tpStart, 0, sizeof(tpStart));
        memset(&tpEnd, 0, sizeof(tpEnd));
    }
    void Begin()
    {
        gettimeofday(&tpStart ...

最后更新于 .

之前公司的同事写了一个基于epoll的网络服务器,其中涉及到socket状态的转化(如等待接收,接收中,接收完成等),以及socket之间的转化(如验证完ip权限之后,验证完登录态),可见是一个多层次的状态机。 但是在原来的实现中却并没有使用状态模式,导致整个逻辑非常复杂,状态之间的跳转也很难把握。本系列的文章将会通过状态模式来重构整套代码。 状态机模式本身这里就不做详细介绍了,读者可以google一下,笔者在仔细对比过《设计模式之禅》,《研磨设计模式》以及游戏中NPC状态机的实现之后,抽象了如下的一套接口.

/*=============================================================================
#  Author:          dantezhu - https://www.vimer.cn
#  Email:           zny2008@gmail.com
#  FileName:        interfaces.h
#  Description:     公共接口
#  Version:         1.0
#  LastChange:      2011-01-19 23:24:33
#  History:         
=============================================================================*/
#ifndef _INTERFACES_H_
#define _INTERFACES_H_
#include <iostream>
#include <map>
using namespace std ...

最后更新于 .

最近遇到了几个C++问题,在这里总结一下,希望可以避免其他朋友犯同样的错误。

一.隐式转换引发的血案

我们直接来看一段代码:

#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
using namespace std;

void a(bool input)
{
    cout<<"I amd first"<<endl;
    cout<<input<<endl;
}

void a(const string &input)
{
    cout<<"I amd second"<<endl;
    cout<<input<<endl;
}

int main(int argc,char **argv)
{
    a("str");  // 是调用第二个a函数吗?
    a(string("str"));
    return 0 ...

最后更新于 .

最近花了大力气在做openapi的优化,使其尽量柔性可用,借此也有些想法想和大家分享一下。 柔性服务,google一下,在网上并没有这样一个标准的概念,所以应该是公司自己取的一个名字。但是这种概念,相信大家都应该很容易能明白,即:

最大程度的保证关键服务的可用性

通俗点来说,一个人不能走路了,他起码可以说话,不能说话了,起码可以点头,头都不能点了,起码得能活着,即心脏还在跳动。这就是柔性。 对应互联网服务来说就是要实现两点:

1.要尽可能成功返回关键数据
2.要尽可能正常接收请求,不能堵死

笔者总结了一下,只要CGI满足其中一个或几个特点,就可以考虑使用柔性服务:

1.在整个CGI的执行过程中,存在关键路径和非关键路径
2.CGI中存在循环调用接口,导致执行时间不确定

我们分上面两种特点来看: 对于第一种,我们举一个简单的例子,比如有一个CGI,做了两件事情分别是:

1.验证登录态
2.获取用户信息

很明显可以看出,验证登录态这个接口是关键路径,而获取用户信息这个接口是非关键的。所以按照柔性服务的定义,当获取用户信息接口失败时,起码还应该返回登录成功。 但是这个时候毕竟还是要区分出完全成功和部分成功的,所以我们可以定义返回码如下(目前腾讯社区开放平台的openapi就是如下定义):

ret==0 ...

最后更新于 .

今天在工作上遇到一个问题,觉得很有代表性,特抽象如下: 通过设计模式的角度来说,就是模板方法,已经有一个基类,需要定义很多子类来实现其方法。 但是类名都只有一部分不同,且构造函数的入参也只有一部分不同。 如代码:

#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
using namespace std;
class EMA
{
    public:
        EMA(string a,string b,string c,string d,string e)
        {
            cout <<a<<","<<b<<","<<c<<","<<d<<","<<e<<endl;
        }
};
class EMA4CGI_1ST:public EMA{
    public:
        EMA4CGI_1ST():EMA(
                "app_mng.1ST_EMA_AVG",
                "app_mng.1ST_EMA_HWM",
                "app_mng ...

最后更新于 .

对于python,这几天一直有两个问题在困扰我:

  • 1.python中没办法直接取得当前的行号和函数名。这是有人在论坛里提出的问题,底下一群人只是在猜测python为什么不像__file__一样提供__line__和__func__,但是却最终也没有找到解决方案。
  • 2.如果一个函数在不知道自己名字的情况下,怎么才能递归调用自己。这是我一个同事问我的,其实也是获取函数名,但是当时也是回答不出来。

但是今晚!所有的问题都有了答案。 一切还要从我用python的logging模块说起,logging中的format中是有如下选项的:

%(name)s            Name of the logger (logging channel)
%(levelno)s         Numeric logging level for the message (DEBUG, INFO,
                    WARNING, ERROR, CRITICAL)
%(levelname)s       Text logging level for the message ("DEBUG", "INFO",
                    "WARNING", "ERROR", "CRITICAL")
%(pathname)s        Full pathname ...