今日头条Go建千亿级微服务的实践(转)

今日头条当前后端服务超过80%的流量是跑在 Go 构建的服务上。微服务数量超过100个,高峰 QPS 超过700万,日处理请求量超过3000亿,是业内最大规模的 Go 应用。

Go 构建微服务的历程

在2015年之前,头条的主要编程语言是 Python 以及部分 C 。随着业务和流量的快速增长,服务端的压力越来越大,随之而来问题频出。Python 的解释性语言特性以及其落后的多进程服务模型受到了巨大的挑战。此外,当时的服务端架构是一个典型的单体架构,耦合严重,部分独立功能也急需从单体架构中拆出来。

为什么选择 Go 语言?

Go 语言相对其它语言具有几点天然的优势:

  • 语法简单,上手快
  • 性能高,编译快,开发效率也不低
  • 原生支持并发,协程模型是非常优秀的服务端模型,同时也适合网络调用
  • 部署方便,编译包小,几乎无依赖

当时 Go 的1.4版本已经发布,我曾在 Go 处于1.1版本的时候,开始使用 Go 语言开发后端组件,并且使用 Go 构建过超大流量的后端服务,因此对 Go 语言本身的稳定性比较有信心。再加上头条后端整体服务化的架构改造,所以决定使用 Go 语言构建今日头条后端的微服务架构。

2015年6月,今日头条开始使用 Go 语言重构后端的 Feed 流服务,期间一边重构,一边迭代现有业务,同时还进行服务拆分,直到2016年6月,Feed 流后端服务几乎全部迁移到 Go。由于期间业务增长较快,夹杂服务拆分,因此没有横向对比重构前后的各项指标。但实际上切换到 Go 语言之后,服务整体的稳定性和性能都大幅提高。 Continue reading "今日头条Go建千亿级微服务的实践(转)"

etcd: 从应用场景到实现原理的全方位解读

随着CoreOS和Kubernetes等项目在开源社区日益火热,它们项目中都用到的etcd组件作为一个高可用强一致性的服务发现存储仓库,渐渐为开发人员所关注。在云计算时代,如何让服务快速透明地接入到计算集群中,如何让共享配置信息快速被集群中的所有机器发现,更为重要的是,如何构建这样一套高可用、安全、易于部署以及响应快速的服务集群,已经成为了迫切需要解决的问题。etcd为解决这类问题带来了福音,本文将从etcd的应用场景开始,深入解读etcd的实现方式,以供开发者们更为充分地享用etcd所带来的便利。 Continue reading "etcd: 从应用场景到实现原理的全方位解读"

安装gRPC开发环境

gRPC开发源码包安装

安装官方安装命令:

是安装不起的,会报:

原因是这个代码已经转移到github上面了,但是代码里面的包依赖还是没有修改,还是 google.golang.org 这种地址,

所以不能使用go get的方式安装,正确的安装方式:

Continue reading "安装gRPC开发环境"

微服务常见架构方案及基础框架

微服务(MicroServices)架构是当前互联网业界的一个技术热点,圈里有不少同行朋友当前有计划在各自公司开展微服务化体系建设,他们都有相同的疑问:

一个微服务架构有哪些技术关注点(technical concerns)?

需要哪些基础框架或组件来支持微服务架构?

这些框架或组件该如何选型? Continue reading "微服务常见架构方案及基础框架"

分布式系统的Raft算法

过去,Paxos一直是分布式协议的标准,但是Paxos难于理解,更难以实现,Google的分布式锁系统Chubby作为Paxos实现曾经遭遇到很多坑。

来自Stanford的新的分布式协议研究称为Raft,它是一个为真实世界应用建立的协议,主要注重协议的落地性和可理解性。

在了解Raft之前,我们先了解Consensus一致性这个概念,它是指多个服务器在状态达成一致,但是在一个分布式系统中,因为各种意外可能,有的服务器可能会崩溃或变得不可靠,它就不能和其他服务器达成一致状态。这样就需要一种Consensus协议,一致性协议是为了确保容错性,也就是即使系统中有一两个服务器当机,也不会影响其处理过程。

为了以容错方式达成一致,我们不可能要求所有服务器100%都达成一致状态,只要超过半数的大多数服务器达成一致就可以了,假设有N台服务器,N/2 1 就超过半数,代表大多数了。

Paxos和Raft都是为了实现Consensus一致性这个目标,这个过程如同选举一样,参选者需要说服大多数选民(服务器)投票给他,一旦选定后就跟随其操作。Paxos和Raft的区别在于选举的具体过程不同。

Continue reading "分布式系统的Raft算法"

CAP原理和BASE思想

分布式领域CAP理论
Consistency(一致性): 数据一致更新,所有数据变动都是同步的;
Availability(可用性):好的响应性能,但往往一致性要求越高的系统,可用性越低;
Partition tolerance(分区容错性):可靠性,分区之后也能够保证集群的只能正常行使,往往是分布式系统中必须保证的一点;

定理:任何分布式系统只可同时满足二点,没法三者兼顾。
忠告:架构师不要将精力浪费在如何设计能满足三者的完美分布式系统,而是应该进行取舍。

Continue reading "CAP原理和BASE思想"

JSON-RPC、XML-RPC、SOAP三者的关系

JSON-RPC规范:http://json-rpc.org/wiki/specification

XML-RPC规范:http://www.xmlrpc.com/spec

SOAP规范:http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383487

参考:http://weblog.masukomi.org/writings/xml-rpc_vs_soap.htm

三者都是为了实现RPC中的消息交换,并且都没有定义传输协议。不过为了更方便在网络中传输,而且由于HTTP的无状态性,都使得HTTP为这三者的常用的传输协议。下面例子也是基于HTTP协议的

XML-RPC和SOAP都是基于XML格式的消息交换:

XML-RPC非常简单,定义了几种基本类型、匿名结构体、匿名数组;

SOAP除了基本类型、命名结构体、命名数组以外,还可以自定义类型,能使用多态的方法调用方式

而JSON-RPC是基于JSON格式的消息交换,JSON比XML更加轻巧,并且非常容易在页面JS中使用,其他特点与XML-RPC类似

下面是使用这几种协议发送请求的例子:

XML-RPC

Xhtml代码
  1. POST /RPC2 HTTP/1.0  
  2. User-Agent: Frontier/5.1.2 (WinNT)  
  3. Host: betty.userland.com  
  4. Content-Type: text/xml  
  5. Content-length: 181  
  6.   
  7.   
  8.   
  9. <?xml version="1.0"?>  
  10. <methodCall>  
  11.    <methodName>examples.getStateName</methodName>  
  12.    <params>  
  13.       <param>  
  14.          <value><i4>41</i4></value>  
  15.          </param>  
  16.       </params>  
  17.    </methodCall>  

SOAP:

Xhtml代码
  1. POST /StockQuote HTTP/1.1  
  2. Host: www.stockquoteserver.com  
  3. Content-Type: text/xml; charset="utf-8"  
  4. Content-Length: nnnn  
  5. SOAPAction: "Some-URI"  
  6.   
  7. <SOAP-ENV:Envelope  
  8.   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"  
  9.   SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>  
  10.    <SOAP-ENV:Header>  
  11.        <t:Transaction  
  12.            xmlns:t="some-URI"  
  13.            SOAP-ENV:mustUnderstand="1">  
  14.                5  
  15.        </t:Transaction>  
  16.    </SOAP-ENV:Header>  
  17.    <SOAP-ENV:Body>  
  18.        <m:GetLastTradePrice xmlns:m="Some-URI">  
  19.            <symbol>DEF</symbol>  
  20.        </m:GetLastTradePrice>  
  21.    </SOAP-ENV:Body>  
  22. </SOAP-ENV:Envelope>  

JSON:

Javascript代码
  1. --> { "method": "echo", "params": ["Hello JSON-RPC"], "id": 1}  
  2. <-- { "result": "Hello JSON-RPC", "error": null, "id": 1} 

PHP SOAP实例讲解

一、什么是soap,什么是wsdl,为什么要用他们

SOAP是基于XML和HTTP通信协议,xml各种平台,各种语言都支持的一个种语言。http呢它得到了所有的因特网浏览器及服务器的支持。

WSDL 指网络服务描述语言 (Web Services Description Language),是一种使用 XML 编写的文档。这种文档可描述某个 Web service。它可规定服务的位置,以及此服务提供的操作。

我是做php的,你是java的,他是做.net,如果我们三个之间要进行通信,要进行数据交换,怎么办呢?我们需要一个能和我们都能通信的工具。soap,wsdl被创造出来,使得运行在不同的操作系统并使用不同的技术和编程语言的应用程序可以互相进行通信。

二、实例

如果php要使用soap的话,通常做法是,添加了一下php的soap模块,在php.ini里面加上soap.so,下面介绍一个不要添加soap.so文件,也可以实现soa

  1. <?php    
  2. //包函nusoap.php  
  3. require_once('./lib/nusoap.php');  
  4.   
  5. //创建服务端  
  6. $server = new soap_server;  
  7.   
  8. //定义客户端调用方法  
  9. $server->register('hello');  
  10.   
  11. //调用方法以及参数  
  12. function hello($name) {  
  13.     return 'Hello, ' . $name;  
  14. }  
  15.   
  16. $HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';  
  17. $server->service($HTTP_RAW_POST_DATA);  
  18. ?>    

p的方法

nusoap是php写的一个功能文件,包涵进来就可以用了,网上很多自己去搜一下吧。

1、不使用wsdl

a、服务端helloworld2.php

b、客户端hello.php

  1. <?php    
  2. //包函nusoap.php  
  3. require_once('./lib/nusoap.php');  
  4. //新建一个soap客户端,调用服务端提供的wsdl  
  5. //$client = new soapclient('http://localhost/test/hellowsdl2.php?wsdl', true);  
  6. $client = new soapclient('http://localhost/test/helloworld2.php');  
  7. //查看一下是不是报错  
  8. $err = $client->getError();  
  9. if ($err) {  
  10.     //显示错误  
  11.     echo '<h2>Constructor error</h2><pre>' . $err . '</pre>';  
  12. }  
  13.   
  14. //调用服务端的方法  
  15. $result = $client->call('hello'array('person' => "this is a test"));  
  16.   
  17. echo '<h2>Result</h2><pre>';  
  18. print_r($result);  
  19. echo '</pre>';  
  20. ?>    

2、使用wsld

a、服务器端

  1. <?php    
  2. //包函nusoap.php  
  3. require_once('./lib/nusoap.php');  
  4. //新建一个soap服务  
  5. $server = new soap_server();  
  6. //初始化支持wsdl  
  7. $server->configureWSDL('hellowsdl2''urn:hellowsdl2');  
  8. //定义数据结构来接收数据  
  9. $server->wsdl->addComplexType(  
  10.     'Person',  
  11.     'complexType',  
  12.     'struct',  
  13.     'all',  
  14.     '',  
  15.     array(  
  16.         'firstname' => array('name' => 'firstname''type' => 'xsd:string'),//后面的type定义数据的类型,这个是string  
  17.         'age' => array('name' => 'age''type' => 'xsd:int'),//后面的type定义数据的类型,这个是int  
  18.         'gender' => array('name' => 'gender''type' => 'xsd:string')//后面的type定义数据的类型,这个是string  
  19.     )  
  20. );  
  21. $server->wsdl->addComplexType(  
  22.     'SweepstakesGreeting',  
  23.     'complexType',  
  24.     'struct',  
  25.     'all',  
  26.     '',  
  27.     array(  
  28.         'greeting' => array('name' => 'greeting''type' => 'xsd:string'),  
  29.         'winner' => array('name' => 'winner''type' => 'xsd:string')  
  30.     )  
  31. );  
  32. //服务器定义的soap调用方法  
  33. $server->register('hello',                    // 方法名字hello,方法就在下面  
  34.     array('person' => 'tns:Person'),          // 客户端传来的变量  
  35.     array('return' => 'tns:SweepstakesGreeting'),    //返回参数  
  36.     'urn:hellowsdl2',                         // soap名  
  37.     'urn:hellowsdl2#hello',                   // soap的方法名  
  38.     'rpc',                                    // 使用的方式  
  39.     'encoded',                                // 编码  
  40.     'test'                                    // 存档  
  41. );  
  42. //定义上面注册过的函数hello  
  43. function hello($person) {  
  44.     $greeting = 'Hello, '.$person['firstname'].'. It is nice to meet a '.$person['age'].' year old '.$person['gender'].'.';  
  45.   
  46.     $winner =  'Scott';  
  47.     //要返回的数据  
  48.     return array(  
  49.         'greeting' => $greeting,  
  50.         'winner' => $winner  
  51.     );  
  52. }  
  53. // 请求时(试图)调用服务  
  54. $HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';  
  55. $server->service($HTTP_RAW_POST_DATA);  
  56. ?>    

b、客户端

  1. <?php  
  2. //包函nusoap.php  
  3. require_once('./lib/nusoap.php');  
  4. //新建一个soap客户端,调用服务端提供的wsdl  
  5. //$client = new soapclient('http://localhost/test/hellowsdl2.php?wsdl', true);  
  6. $client = new soapclient('http://localhost/test/helloworld2.php');  
  7. //查看一下是不是报错  
  8. $err = $client->getError();  
  9. if ($err) {  
  10.  //显示错误  
  11.  echo '<h2>Constructor error</h2><pre>' . $err . '</pre>';  
  12. }  
  13. //要向服务端要传的参数  
  14. $person = array('firstname' => 'Willi', 'age' => 22, 'gender' => 'male');  
  15.   
  16. //调用服务端的方法  
  17. $result = $client->call('hello', array('person' => $person));  
  18. //错误审核  
  19. if ($client->fault) {  
  20.  echo '<h2>Fault</h2><pre>';  
  21.  print_r($result);  
  22.  echo '</pre>';  
  23. else {  
  24.  $err = $client->getError();  
  25.  if ($err) {  
  26.  echo '<h2>Error</h2><pre>' . $err . '</pre>';  
  27.  } else {  
  28.  echo '<h2>Result</h2><pre>';  
  29.  print_r($result);  
  30.  echo '</pre>';  
  31.  }  
  32. }  
  33. //显示请求信息  
  34. echo '<h2>Request</h2>';  
  35. echo '<pre>' . htmlspecialchars($client->request, ENT_QUOTES) . '</pre>';  
  36. //显示返回信息  
  37. echo '<h2>Response</h2>';  
  38. echo '<pre>' . htmlspecialchars($client->response, ENT_QUOTES) . '</pre>';  
  39. //显示调试信息  
  40. echo '<h2>Debug</h2>';  
  41. echo '<pre>' . htmlspecialchars($client->debug_str, ENT_QUOTES) . '</pre>';  
  42. ?>  

上面二个例子不管是客户端,还是服务器端,都是用php写的,你可以试着用多种语言来写,来测试一下。不管你是用php的模块,还是用nusoap,里面具体方法就不在这多说了,手册里面都有。

 

 

 

 SOAP在这里就不用介绍了,  这里只是简单的实现一个SOAP的实例, 不多说 ,看代码吧。 soap分为server和client, 我们要使client去调用server的代码. 首先看server短的代码:

 

这个是server端的代码: server.php

<?php

    //声明一个函数add() ,并返回它的值
        function add($a,$b){
        return $a+$b;
        }

    //实例化一个SoapServer对象, 并将add函数注册成为其方法
        $server = new SoapServer(null,array('uri'=>'http://localhost/')); //指定server端代码的URI(资源标志符)
        $server->addFunction("add");
        $server->handle();
?>

然后使用client端的代码来调用server端的代码: client的代码也很简单: 如下:

这个是client端的代码 client.php

<?php

    //建立一个参数数组,存储要访问的提供soap服务的计算机的地址与程序
        $arrOptions=array(
            'uri'=>'http://localhost/',
            'location'=>'http://localhost/soap/server.php',  //注意: 这个location指定的是server端代码在服务器中的具体位置, 我的是在本地根目录下的soap目录中,
            'trace'=>true,
        );
        $soapObject = new SoapClient(null,$arrOptions); //实例化客户端对象
        echo $soapObject->add(20,30); //调用服务器端的函数add并返回值50
?>

ok, 结束了 !

PHP中使用XML-RPC构造Web Service简单入门

[  Web Service介绍 ]

Web Service就是为了异构系统的通信而产生的,它基本的思想就是使用基于XML的HTTP的远程调用提供一种标准的机制,而省去建立一种新协议的需求。 目前进行Web Service通信有两种协议标准,一种是XML-RPC,另外一种是SOAP。XML-RPC比较简单,出现时间比较早,SOAP比较复杂,主要是一些 需要稳定、健壮、安全并且复杂交互的时候使用。

PHP中集成了XML-RPC和SOAP两种协议的访问,都是集中在xmlrpc扩展当 中。另外,在PHP的PEAR中,不管是PHP 4还是PHP 5,都已经默认集成了XML-RPC扩展,而且该扩展跟xmlrpc扩展无关,能够独立实现XML-RPC的协议交互,如果没有xmlrpc扩展,建议使 用PEAR::XML-RPC扩展。

我们这里主要是以XML-RPC来简单描述Web Service的交互过程,部分内容来自PHP手册,更详细内容,建议参考手册。

[  安装xmlrpc扩展 ]

如果你的系统中没有安装xmlrpc的php扩展,那么请正确安装。

在 Windows平台下,首先把PHP安装目录下的扩展php_xmlrpc.dll放到C:\Windows或者C:\Winnt目录下,(PHP4的扩 展在C:\php\extensions目录中,PHP5的扩展在C:\php\ext目录中),同时在C:\Windows\php.ini或者C: \Winnt\php.ini中把extension=php_xmlrpc.dll前面的分号";"去掉,然后重启Web服务器后查看 phpinfo()有没有XML-RPC项目就能够确定是否已经正确安装xmlrpc扩展。

在Unix/Linux平台下,如果没有安装xmlrpc扩展,请在重新编译PHP,在configure的时候请加入 --with-xmlrpc 选项,然后查看phpinfo()看是否正常安装xmlrpc。

(注意:以下操作都是建立在xmlrpc扩张正常安装前提下,请务必正确安装。)

[  XML-RPC工作原理 ]

XML-RPC大致就是整个过程就是使用XML来进行通信。首先构造一个RPC 服务器端用来出来从RPC客户端传递过来的使用XML封装的请求,并且把处理结果通过XML的形式返回给RPC客户端,客户端就去分析XML获取自己需要的数据。

XML-RPC的服务器端必须有现成的函数提供给客户端调用,并且客户端提交的请求中的函数和方法必须和服务器端的一致,否则将无法获取所需要的结果。

下面我进行简单的代码来描述整个过程。


[  XML-RPC实践 ]

服务器端使用xmlrpc_server_create函数产生一个服务器端,然后把需要需要暴露的RPC调用接口进行注册,接受RPC客户端POST过来的XML数据,然后进行处理,处理结果通过XML的形式显示给客户端。

代码如下: rpc_server.php

  1. <?php  
  2. /** 
  3. * 函数:提供给RPC客户端调用的函数 
  4. * 参数: 
  5. * $method 客户端需要调用的函数 
  6. * $params 客户端需要调用的函数的参数数组 
  7. * 返回:返回指定调用结果 
  8. */  
  9. function rpc_server_func($method$params) {  
  10. $parameter = $params[0];  
  11.    if ($parameter == "get")  
  12.    {  
  13.        $return = ''This data by get method'';  
  14.    }  
  15.    else  
  16.    {  
  17.        $return = ''Not specify method or params'';  
  18.    }  
  19.    return $return;  
  20. }  
  21.   
  22. //产生一个XML-RPC的服务器端  
  23. $xmlrpc_server = xmlrpc_server_create();  
  24.   
  25. //注册一个服务器端调用的方法rpc_server,实际指向的是rpc_server_func函数  
  26. xmlrpc_server_register_method($xmlrpc_server"rpc_server""rpc_server_func");  
  27.   
  28. //接受客户端POST过来的XML数据  
  29. $request = $HTTP_RAW_POST_DATA;  
  30.   
  31. //执行调用客户端的XML请求后获取执行结果  
  32. $xmlrpc_response = xmlrpc_server_call_method($xmlrpc_server$request, null);  
  33.   
  34. //把函数处理后的结果XML进行输出  
  35. header(''Content-Type: text/xml'');  
  36. echo $xmlrpc_response;  
  37.   
  38. //销毁XML-RPC服务器端资源  
  39. xmlrpc_server_destroy($xmlrpc_server);  
  40. ?> 

服务器端构造好了,那么再构造我们的RPC客户端。客户端大致通过Socket访问XML-RPC服务器端的80端口,然后把需要调用的RPC接口封装到XML里,通过POST请求提交给RPC服务器端,最后获取服务器端返回结果。

代码如下:rpc_client.php

  1. <?php  
  2. /** 
  3. * 函数:提供给客户端进行连接XML-RPC服务器端的函数 
  4. * 参数: 
  5. * $host  需要连接的主机 
  6. * $port  连接主机的端口 
  7. * $rpc_server XML-RPC服务器端文件 
  8. * $request  封装的XML请求信息 
  9. * 返回:连接成功成功返回由服务器端返回的XML信息,失败返回false 
  10. */  
  11. function rpc_client_call($host$port$rpc_server$request) {  
  12.   
  13.    //打开指定的服务器端  
  14.    $fp = fsockopen($host$port);  
  15.   
  16.    //构造需要进行通信的XML-RPC服务器端的查询POST请求信息  
  17.    $query = "POST $rpc_server HTTP/1.0\nUser_Agent: XML-RPC Client\nHost: ".$host."\nContent-Type: text/xml\nContent-Length: ".strlen($request)."\n\n".$request."\n";  
  18.   
  19.    //把构造好的HTTP协议发送给服务器,失败返回false  
  20.    if (!fputs($fp$querystrlen($query)))  
  21.    {  
  22.        $errstr = "Write error";  
  23.        return false;  
  24.    }  
  25.      
  26.    //获取从服务器端返回的所有信息,包括HTTP头和XML信息  
  27.    $contents = '''';  
  28.    while (!feof($fp))  
  29.    {  
  30.        $contents .= fgets($fp);  
  31.    }  
  32.   
  33.    //关闭连接资源后返回获取的内容  
  34.    fclose($fp);  
  35.    return $contents;  
  36. }  
  37.   
  38. //构造连接RPC服务器端的信息  
  39. $host  = ''localhost'';  
  40. $port  = 80;  
  41. $rpc_server = ''/~heiyeluren/rpc_server.php'';  
  42.   
  43. //把需要发送的XML请求进行编码成XML,需要调用的方法是rpc_server,参数是get  
  44. $request = xmlrpc_encode_request(''rpc_server''''get'');  
  45.   
  46. //调用rpc_client_call函数把所有请求发送给XML-RPC服务器端后获取信息  
  47. $response = rpc_client_call($host$port$rpc_server$request);  
  48.   
  49. //分析从服务器端返回的XML,去掉HTTP头信息,并且把XML转为PHP能识别的字符串  
  50. $split = ''<?xml version="1.0" encoding="iso-8859-1"?>'';  
  51. $xml =  explode($split$response);  
  52. $xml = $split . array_pop($xml);  
  53. $response = xmlrpc_decode($xml);  
  54.   
  55. //输出从RPC服务器端获取的信息  
  56. print_r($response);  
  57.   
  58. ?>  

大致我们上面的例子就是提交一个叫做rpc_server的方法过去,参数是get,然后获取服务器端的返回,服务器端返回的XML数据是:

  1. <?xml version="1.0" encoding="iso-8859-1"?>  
  2. <methodResponse>  
  3. <params>  
  4. <param>  
  5.   <value>  
  6.    <string>This data by get method</string>  
  7.   </value>  
  8. </param>  
  9. </params>  
  10. </methodResponse> 

那么我们再通过xmlrpc_decode函数把这个XML编码为PHP的字符串,我们就能够随意处理了,整个Web Service交互完成。

[  结束语 ]

不 管是XML-RPC也好,SOAP也罢,只要能够让我们稳定、安全的进行远程过程的调用,完成我们的项目,那么就算整个Web Service就是成功的。另外,如果可以的话,也可以尝试使用PEAR中的XML-RPC来实现上面类似的操作,说不定会更简单,更适合你使用。

简单的使用XML-RPC进行Web Service交互就完成了,部分代码参考PHP手册,想获取详细信息建议参考手册,如果文章有不正确,请指正。