Rest – 架构风格与基于网络的软件架构设计 – 总结

简介

本文内容是博主的一篇读书笔记。读的书是Roy Thomas Fielding的博士论文 Architectrual Styles and the Design of Network-based Software Architectures (《架构风格与基于网络的软件架构设计》)。

为什么想读这篇论文?主要因为REST架构风格和Restful的框架,在Web系统设计中越来越流行。 Rest这个词,在软件研发人员之间,尤其是博主周围同事、同学之间,不断被提起,可见其影响之大。学习追踪了一下Rest,知道Rest架构风格最早由Fielding在他的博士论文中提出,于是就开始读这篇论文。

本博客可以成为一篇论文的导读,想阅读但未读 Fielding博士这篇论文的读者,希望本博客能给您帮助。

阅读此论文前的一点准备和对论文的一点小总结

Fielding博士的论文写成的时间在2000年,猜测Rest的概念形成时间比2000年还早。

而本博客写于2015年末,与论文写成已有十几年间隔,互联网领域里,无论是行业还是技术,发展太快,好多东西估计也让Fielding难以预料。2000年时,有很多概念并不明朗,好多软件还没有出现,因此论文中有一些东西,让现代的人看很具体,但论文中却写的并不清楚。

读Fielding博士的论文最好立足于2000年的环境下,从现在(2015年)的角度看论文里的内容,论文里有超前也有滞后的内容,有技术领域里早已被人熟知的概念,也有经典不变的原理和原则。

所以,最好用选择性的并略带批判的观点去看这篇论文,从中找寻具有价值的内容,并鉴别已经过时的内容。

论文目录及各章内容简单总结

第一章 软件架构

软件架构中,被用到的一些概念、术语。

第二章 基于网络应用的架构

话题范围缩小,关注基于网络应用的架构。谈及7个方面的架构属性,可作为评估一种架构优劣的7个维度。

第三章 基于网络的架构风格

话题范围进一步缩小,聚焦基于网络的架构风格。列举了很多种架构风格,并用上一章的提到的7个维度,对它们进行简单评估。

第四章 设计Web架构:问题与洞察力

提出Web系统的架构风格需求,比如需要满足容量大、可伸缩性强。(后面提出Rest风格正是满足了这些需求。)

第五章 表述性状态转移

博主认为,这是这篇论文的核心内容。

主要讲了Rest风格的具体内容,从5个方面约束和规范Web架构。

在描述Rest风格的时候,论文用了一个很特别的例子,这个例子是讲述了一个架构,从无到有,再从简单到复杂演化的过程。过程中,这个架构每一步变化都使用Rest风格来约束它,让架构表现出了很好的可伸缩性。论文中称这是Rest风格的推导过程。

Rest 风格的约束

  • 无状态, 请求的上下文无关
  • 缓存约束,缓存约束于客户端
  • 统一接口,Rest 风格核心特征
  • 分层系统,添加中间层和中间层约束。(类似于现在的Proxy 和 Proxy+缓存,比如nginx、nginx+cache、 msyql proxy、file proxy)
  • 按需代码。按照现在的理解则是,让B/S架构,替换掉以前的C/S架构。

第六章 经验与评估

这一章内容有些杂乱。

论文具有局限性的内容

  • Cookie 违反了Rest (6.3.4.2)
  • 会话状态全部保存在客户端
  • 缓存约束于客户端, 可靠性降低
  • 特别适合分布式超媒体系统

问题总结

  • Web应用程序的无状态性

    http协议作为技术背景的web应用程序请求——应答模式是无状态的

    web应用是有状态的,session、cookies等状态机制+其他辅助的机制(违反Rest)

  • 经常被谈及的Rest风格的URL是Rest定义的吗?

Rest风格是抽象的,而经常被谈及Rest风格的URL,只是遵从Rest风格的一种具体实现,Feilding博士的这篇论文里并没有具体去谈。

  • Rest 风格推荐HTTP中的GET,POST,PUT,DELETE对资源进行访问和控制是吗

此篇论文里并没有具体去写这个问题

后记

博主第一次听人提起 Rest 是 2012年时,当时博主刚大学毕业参加工作,在公司里听前辈工程师们谈起Rest。可惜博主3年后才开始主动去学习有关Rest的内容。

虽然读完了Feilding博士的论文,但是感觉Rest的内容好像不仅有这些,可能是其他人的补充,再去学习下。

2015年12月14日

判断平面上点和不规则多边形位置关系算法

本文列举5中计算点和不规则多边形位置关系的算法。

计算机算法实现方案,最好的是弧长法。

射线法 (铅垂线法、水平线法)

射线法是使用最广泛的算法,这是由于相比较其他算法而言,它不但可以正确使用在凹多边形上,而且不需要考虑精度误差问题。

该算法思想是从点出发向右水平做一条射线,计算该射线与多边形的边的相交点个数,当点不在多边形边上时,如果是奇数,那么点就一定在多边形内部,否则,在外部。

其中铅垂线法和水平线法是射线法的两个具体算法,更容易实现,在坐标计算中更方便。

面积法

面积法的思想是如果点在多边形内部或者边上,那么点与多边形所有边组成的三角形面积和等于多边形面积。多边形的面积公式可以用叉积计算。不过计算面积是会有一定误差的,需要设置精度的误差范围。

点线判断法

对于多边形,如果一个点它的所有边的左边,那么这个点一定在多边形内部。利用叉积正好可以判断点与给定边的关系,即点是在边的左边右边还是边上。

转角法

以被测点O为坐标原点,计算其与所有相邻两个多边形顶点点P[i]之间的角度和,所有角度相加后得到360度,即为点在多边形之内,若得到180度,则在多边形边上,0度则在多边形外。

弧长法

弧长法是改进后的转角法,解决了传统转角法的精度问题。算法思想是,以被测点O为坐标原点,将平面划分为4个象限,对每个多边形顶点P[i],计算其所在的象限,然后顺序访问多边形的各个顶点P[i],分析P[i]和P[i+1],有下列三种情况:

  • P[i+1]在P[i]的下一象限。此时弧长和加π/2;

  • P[i+1]在P[i]的上一象限。此时弧长和减π/2;

  • P[i+1]在Pi的相对象限。利用叉积f=y[i+1]x[i]-x[i+1]y[i]计算Op[i]与Op[i+1]的关系,若f=0,Op[i]与Op[i+1]共线,点在多边形边上;若f<0,Op[i+1]在Op[i]逆时针方向,弧长和减π;若f>0,Op[i+1]在Op[i]顺时针方向,弧长和加π。

由于顶点在原点和坐标轴上时需要特殊处理,为了准确性,应该在每次计算顶点时都用叉积判断P是否在当前边上,另外将π用整数代替可以避免浮点数的精度误差。

Ubuntu 使用问题汇总

SSH 连接Ubuntu反应慢

  • SSH服务默认启用了DNS反向解析的功能
  • 在/etc/ssh/sshd_config文件中修改或加入UseDNS=no 和 GSSAPIAuthentication no 两行
  • 重启sshd sudo service sshd restart

删除旧内核

uname -a  #使用这个命令可以查看当前系统使用的内核。

dpkg --get-selections|grep linux #列出当前内核,带image的则是已经安装的内核。可以用类似以下命令卸载:

sudo apt-get remove linux-image-2.6.24-11-generic #其中 linux-image-2.6.24-11-generic 为版本号,输全 。

在最新的ubuntu (15.10以上)版本中,执行sudo apt-get autoremove也可以直接卸载旧内核

如何升级 Ubuntu 版本, 比如从 Ubuntu 15.10 升级到 Ubuntu 16.04

终端执行如下命令:

sudo do-release-upgrade -d

如果命令do-release-upgrade不存在,则安装 update-manager-core :

sudo apt-get install update-manager-core

Ubuntu中安装和卸载Gnome、KDE、XFACE桌面环境

  • 安装XFACE: sudo apt-get install xubuntu-desktop
  • 安装KDE:sudo apt-get install kubuntu-desktop
  • 安装Gnome:sudo apt-get install ubuntu-desktop

Ubuntu卸载桌面环境(应备份重要文件)

  • 卸载gnome:sudo apt-get --purge remove liborbit2
  • 卸载kde:sudo apt-get --purge remove kdelibs4c2a libarts1c2a
  • 卸载xface:sudo apt-get --purge remove xfce4

给用户添加sudo权限

  • root权限编辑/etc/sudoers文件

添加一行 xxx ALL=(ALL) ALL

或者添加 xxx ALL=(ALL) NOPASSWD: ALL , 使用不用输密码

配置静态IP后无法解析域名

编辑 /etc/network/interfaces 文件, 添加配置:

dns-nameservers 114.114.114.114 114.114.115.115

update-apt-xapi占用资源过高

  • 后台任务,会定期把cpu和磁盘IO打满
  • 解决方法,直接卸载掉 apt-get autoremove --purge apt-xapian-index

升级Ubuntu版本,比如从15.04升级到16.04

先备份!

sudo apt-get update

sudo apt-get install update-manager-core

sudo do-release-upgrade

Ubuntu 无法重启、关机

此问题发生于Ubuntu 16.04 LTS版本,由内核bug导致。

解决办法是,把内核升级到最新版本。截止本文,Ubuntu 16.04 源中的最新版本是4.10.0-22

sudo apt-get install linux-image-4.10.0-22-lowlatency

sudo apt-get install linux-headers-4.10.0-22-lowlatency

最新版本内核查找方法如下:

sudo apt-cache search linux-image

Ubuntu Desktop 软件推荐

本文所列都是作者正在使用的软件,也都是个人爱好的软件。

适用Ubuntu版本 Ubuntu 15.10 、 Ubuntu 15.04

文本编辑

  • wps-office

输入法

  • 搜狗拼音输入法

    百度上搜索搜狗拼音输入法 for linux 可得到下载链接

浏览器

  • Chrome
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
sudo apt-get update
sudo apt-get install google-chrome-stable

系统工具

  • SysPeek

    sudo add-apt-repository ppa:nilarimogard/webupd8
    sudo apt-get update
    sudo apt-get install syspeek

远程桌面

  • xrdp

sudo apt-get install xrdp

下载工具

  • axel 相当于迅雷

sudo apt-get install axel

开发工具

  • vim
  • vpnc
  • git
  • Sublime Text 3

    sudo add-apt-repository ppa:webupd8team/sublime-text-3
    sudo apt-get update
    sudo apt-get install sublime-text

删除原装软件

sudo apt-get remove libreoffice-common
sudo apt-get remove unity-webapps-common  # 删除Amazon的链接
sudo apt-get remove thunderbird totem rhythmbox empathy brasero simple-scan gnome-mahjongg aisleriot gnome-mines cheese transmission-common gnome-orca webbrowser-app gnome-sudoku  landscape-client-ui-install
sudo apt-get remove onboard deja-dup

线程安全的单例模式-以C++代码为例

本文描述3种场景下的单例模式:

  • 进程体内无线程的单例模式
  • 进程体内多线程单例模式
  • 在单个线程体中的单例模式

本文所写单例模式代码都使用懒汉模式。

进程体内单例

注意问题:

  • 如果进程体中运行多个线程,则需要考虑多线程同步访问的问题。
  • 如果进程体重没有运行多个线程,则不需要考虑多线程同步访问。
  • 使用线程同步锁保证多进程同步

使用场景举例 :

  • 日志类、文件读写类
  • 资源管理类

代码示例:

进程体内没有运行多线程的单例模式,无需考虑线程同步与互斥

class Singleton {
  public:
    static Singleton* getInstance() {
        if (NULL == instance) {
          instance = new SingletonInside();
        }
        return instance;
    }
  private:
    SingletonInside(){}
    ~SingletonInside() {}
    static Singleton* instance;
};

Singleton::instance = NULL;    

进程体内运行多线程单例模式,使用系统mutex保证线程安全

class Singleton {
  public:
    static Singleton* getInstance() {
        pthread_once(&g_once_control, InitOnce);
        pthread_mutex_lock(&mutex);  // lock
        if (NULL == instance) {
          instance = new SingletonInside();
        }
        pthread_mutex_unlock(&mutex); // unlock
        return instance;
    }
  private:
    SingletonInside() {

    }
    ~SingletonInside() {
       pthread_mutex_destroy(&mutex);   // destroy lock
    }
    static void InitOnce(void) {
      pthread_mutex_init(&mutex,NULL);  // init lock
    }
    Singleton* instance;
    static pthread_once_t g_once_control;
    static pthread_mutex_t mutex;
};
Singleton::instance = NULL;
pthread_once_t Singleton::g_once_control = PTHREAD_ONCE_INIT;

单个线程体中的单例

某些资源在单个线程体内需要保持单例,即每个线程体内都保持唯一。每个线程体内,对象相互隔离,则无需考虑线程安全问题。

此种单例模式的实例需要调用系统提供的 TLS 接口,放于线程局部存储中。

使用场景举例:

  • 多路复用Socket封装类
  • 网络上下文环境类
  • 线程安全的资源

代码示例

class Singleton {
  public:
    static Singleton* getInstance() {
       pthread_once(&g_once_control, InitOnce);
       Singleton* instance = (Singleton*)pthread_getspecific(g_thread_data_key);

        if (NULL == instance) {
          instance = new SingletonInside();
          pthread_setspecific(g_thread_data_key, (void*)Singleton)
        }
        return instance;
    }
  private:
    SingletonInside() {}
    ~SingletonInside() {
       pthread_key_delete(g_thread_data_key);
    }
    static void InitOnce(void) {
      pthread_key_create(&g_thread_data_key, NULL);
    }

    static pthread_once_t g_once_control;
    static pthread_key_t g_thread_data_key;
};

pthread_once_t Singleton::g_once_control = PTHREAD_ONCE_INIT;
pthread_key_t Singleton::g_thread_data_key;

如果使用 Poco库 TreadLocal ,代码还会简洁很多,性能上也会好很多

    class Singleton {
      public:
        static Singleton* getInstance() {
            if (NULL == instance) {
              instance = new SingletonInside();
            }
            return instance;
        }
      private:
        SingletonInside() {}
        ~SingletonInside() {
           delete instance;
        }
        static Poco::ThreadLocal<Singleton> instance;
    };
Poco::ThreadLocal<singleton> Singleton::instance = NULL;

总结

  • 无论程序是多进程运行还是多线程运行,代码都要尽量兼容线程安全