首页 > 作文

PHP laravel+thrift+swoole打造微服务框架

更新时间:2023-04-07 20:13:38 阅读: 评论:0

laravel作为最受欢迎的php web框架一直广受广大互联网公司的喜爱。

笔者也参与过一些由laravel开发的项目。虽然laravel的性能广受诟病但是业界也有一些比较好的解决方案,比如堆机器,比如使用swoole进行加速。

一个项目立项到开发上线,随着时间和需求的不断激增,会越来越复杂,变成一个大项目,如果前期项目架构没设计的不好,代码会越来越臃肿,难以维护,后期的每次产品迭代上线都会牵一发而动全身。项目微服务化,松耦合模块间的关系,是一个很好的选择,随然增加了维护成本,但是还是很值得的。

那么有什么办法使一个laravel项目改造成微服务呢?

最近研究thrift的时候发现thrift对php之城非常好,那么可不可以使用使用thrift作为rpc框架,使用swoole来实现异步tcp服务,打造一个微服务框架呢。

心动不如行动我们开始尝试一下吧。首先我们创建一个laravel的项目,笔者使用的laravel官方提供的homestead的环境。

laravel new laravel-thrift-app 

安装laravel-s https://github.com/hhxsv5/laravel-s/blob/master/readme-cn.md

compor require "hhxsv5/laravel-s:~3.5.0" -vvv 

laravel-s是一个由swoole写的laravel扩展,赋予laravel更好的性能,具体使用方法参看官方文档。

在项目的根目录下新建一个thrift的目录,然后在该子目录下创建 thrift idl 文件 ur.thrift,用于定义和用户相关的服务接口。

1 namespace php app.thrift.ur  2 // 定义用户接口 3 rvice ur {     4 string getinfo(1:i32 id)5  }

这里我们定义了一个接口,接着在项目根目录下运行如下命令,根据上述 idl 文件生成相关的服务代码:

thrift -r --gen php:rver -out ./ thrift/ur.thrift 

查看文件这时候我们会发现在app\thrift\ur`目录下生成对应的服务代码。

自动化专业就业

通过 compor 安装 thrift php 依赖包:

compor require apache/thrift 

编写服务代码,在 app目录下新建一个 rvices/rver 子目录,然后在该目录下创建服务接口类 urrvice,该类实现自 `app\thrift\ur\urif` 接口:

 1 <?php 2 namespace app\rvices\rver; 3  4  5 u app\thrift\ur\urif; 6  7 class urrvice implements urif 8 { 9     public function getinfo($id)10     {11         return "chensi".$id;12     }13 }

在 app 目录下新建一个 sockets目录用于存放 swoole 相关代码,首先我们创建一个 城市病rvertransport.php用来存放服务端代理类,并编写代码如下:

 1 <?php 2 namespace app\sockets; 3  4  5 u thrift\rver\trvertransport; 6  7 class rvertransport extends trvertransport 8 { 9     /**10      * @var array 服务器选项11      */12     public $options = [13         'dispatch_mode'         => 1, //1: 轮循, 3: 争抢14         'open_length_check'     => true, //打开包长检测15         'package_max_length'    => 8192000, //最大的请求包长度,8m16         'package_length_type'   => 'n', //长度的类型,参见php的pack函数17         'package_length_offt' => 0,   //第n个字节是包长度的值18         'package_body_offt'   => 4,   //从第几个字节计算长度19     ];20 21     /**22      * @var swoolerver23      */24     public $rver;25     protected $host;26     protected $port;27     protected $socktype;28 29 30     public function __construct($swoole, $host, $port = 9999, $socktype = swoole_sock_tcp, $options = [])31     {32         $this->rver = $swoole;33         $this->host   = $host;34         $this->port   = $port;35         $this->socktype = $socktype;36         $this->options = array_merge($this->options,$options);37 38     }39 40 41     public function listen()42     {43         $this->rver =$this->rver->addlistener($this->host,$this->port,$this->socktype);44         $this->rver->t($this->options);45         return null;46     }47 48 49     public function clo()50     {51         //$this->rver->shutdown();52         return null;53     }54 55 56     protected function acceptimpl()57     {58         return null;59     }60 }

我们在代理类的构造函数中初始化 swoole tcp 服务器参数,由于我们使用的是laravel-s然后在该类中定义 listen 方法启动这个swoole增加监听的端口并监听客户端请求。

我们在 app/sockets目录下创建 transport.php文件用于存放基于 swoole 的传输层实现代码:

  1 <?php  2 /**  3  * created by phpstorm.  4  * ur: 74100  5  * date: 2019/10/21  6  * time: 2:22  7  */  8 namespace app\sockets;  9  10 u swoole\rver as swoolerver; 11 u thrift\exception\ttransportexception; 12 u thrift\transport\ttransport; 13  14 class transport extends ttransport 15 { 16     /** 17      * @var swoole服务器实例 18      */ 19     protected $rver; 20     /** 21      * @var int 客户端连接描述符 22      */ 23     protected $fd = -1; 24     /** 25      * @var string 数据 26      */ 27     protected $data = ''; 28     三山夹两盆/** 29      * @var int 数据读取指针 30      */ 31     protected $offt = 0; 32  33     /** 34      * swooletransport constructor. 35      * @param swoolerver $rver 36      * @param int $fd 37      * @param string $data 38      */ 39     public function __construct(swoolerver $rver, $fd, $data) 40     { 41         $this->rver = $rver; 42         $this->fd = $fd; 43         $this->data = $data; 44     } 45  46     /** 47      * whether this transport is open. 48      * 49      * @return boolean true if open 50      */ 51     public function isopen() 52     { 53         return $this->fd > -1; 54     } 55  56     /** 57      * open the transport for reading/writing 58      * 59      * @throws ttransportexception if cannot open 60      */ 61     public function open() 62     { 63         if ($this->isopen()) { 64             throw new ttransportexception('swoole transport already connected.', ttransportexception::already_open); 65         } 66     } 67  68     /** 69      * clo the transport. 70      * @throws ttransportexception 71      */ 72     public function clo() 73     { 74         if (!$this->isopen()) { 75             throw new ttransportexception('swoole transport not open.', ttransportexception::not_open); 76         } 77         $this->rver->clo($this->fd, true); 78         $this->fd = -1; 79     } 80  81     /** 82      * read some data into the array. 83      * 84      * @param int $len how much to read 85      * @return string the data that has been read 86      * @throws ttransportexception if cannot read any more data 87      */ 88     public function read($len) 89     { 90         if (strlen($this->data) - $this->offt < $len) { 91             throw new ttransportexception('swoole transport[' . strlen($this->data) . '] read ' . $len . ' bytes failed.'); 92         } 93         $data = substr($this->data, $this->offt, $len); 94         $this->offt += $len; 95         return $data; 96     } 97  98     /** 99      * writes the given data out.100      *101      * @param string $buf the data to write102      * @throws ttransportexception if writing fails103      */104     public function write($buf)105     {106         if (!$this->isopen()) {107             throw new ttransportexception('swoole transport not open.', ttransportexception::not_open);108         }109         $this->rver->nd($this->fd, $buf);110     }111 }

transport类主要用于从传输层写入或读取数据,最后我们创建 rver.php 文件,用于存放基于 swoole 的 rpc 服务器类:

 1 <?php 2 /** 3  * created by phpstorm. 4  * ur: 74100 5  * date: 2019/10/21 6  * time: 2:24 7  */ 8 namespace app\sockets; 9 10 u swoole\rver as swoolerver;11 u thrift\rver\trver;12 13 class rver extends trver14 {15     public function rve()16     {17 18         $this->transport_->rver->on('receive', [$this, 'handlereceive']);19         $this->transport_->listen();20 21     }22 23     public function stop()24     {25         $this->transport_->clo();26     }27 28     /**29      * 处理rpc请求30      * @param rver $rver31      * @param int $fd32      * @param int $fromid33      * @param string $data34      */35     public function handlereceive(swoolerver $rver, $fd, $fromid, $data)36     {37         $transport = new transport($rver, $fd, $data);38         $inputtransport = $this->inputtransportfactory_->gettransport($transport);39         $outputtransport = $this->outputtransportfactory_->gettransport($transport);40         $inputprotocol = $this->inputprotocolfactory_->getprotocol($inputtransport);41         $outputprotocol = $this->outputprotocolfactory_->getprotocol($outputtransport);42         $this->processor_->process($inputprotocol, $outputprotocol);43     }44 }

该类继承自 thrift\rver\trver,在子类中需要实现 rve` 和 `stop`方法,分别定义服务器启动和关闭逻辑,这里我们在 rve方法中定义了 swoole tcp 服务器收到请求时的回调处理函数,其中 $this->transport 指向 app\swoole\rvertransport 实例,回调函数 handlereceive中我们会将请求数据传入传输层处理类 transport进行初始化,然后再通过一系列转化通过处理器对请求进行处理,该方法中 `$this` 指针指向的属性都是在外部启动 rpc 服务器时传入的,后面我们会看到。定义好请求回调后,即可通过 `$this->transport_->listen()` 启动服务器并监听请求。

最后我们使用laravel-s的事件回调。

在laravel-s的配置文件新增master进程启动时的事件。

'event_handlers'           => [     'rverstart' => \app\events\rverstartevent::class, ], 

编写rverstartevent类。

 1 <?php 2 namespace app\events; 3 u app\sockets\rvertransport; 4 u hhxsv5\laravels\swoole\events\rverstartinterface; 5 u app\rvices\rver\urrvice; 6 u app\sockets\tframedtransportfactory; 7 u app\thrift\ur\urprocessor; 8 u thrift\factory\tbinaryprotocolfactory; 9 u swoole\http\rver;10 u app\sockets\rver as trver;11 12 13 class rverstartevent implements rverstartinterface14 {15     public function __construct()16     {17     }18     public function handle(rver $rver)19     {20         // 初始化thrift21         $processor = new urprocessor(new urrvice());22         $tfactory = new tframedtransportfactory();23         $pfactory = new tbinaryprotocolfactory();24         // 监听本地 9999 端口,等待客户端连接请求25         $transport = new rvertransport($rver,'127.0.0.1', 9999);26         $rver = new trver($processor, $transport, $tfactory, $tfactory, $pfactory, $pfactory);27         $rver->欠款纠纷案由rve();28     }29 }

这时候我们服务端的代码已经写完。开始写客户端的代码。

接下来,我们来修改客户端请求服务端远程接口的代码,在此之前在 app/sockets目录下新建一个 clienttransport.php 来存放客户端与服务端通信的传输层实现代码:

  1 <?php  2 namespace app\sockets;  3     u swoole\client;  4     u thrift\exception\ttransportexception;  5     u thrift\transport\ttransport;  6   7     class clienttransport extends ttransport  8     {  9         /** 10          * @var string 连接地址 11          */ 12         protected $host; 13         /** 14          * @var int 连接端口 15          */ 16         protected $port; 17         /** 18          * @var client 19          */ 20         protected $client; 21  22         /** 23          * clienttransport constructor. 24          * @param string $host 25          * @param int $port 26          */ 27         public function __construct($host, $port) 28         { 29             $this->host = $host; 30             $this->port = $port; 31             $this->client = new client(swoole_sock_tcp); 32         } 33  34         /** 35          * whether this transport is open. 36          * 37          * @return boolean true if open 38          */ 39         public function isopen() 40         { 41             return $this->client->sock > 0; 42         } 43  44         /** 45          * open the transport for reading/writing 46          * 47          * @throws ttransportexception if cannot open 48          */ 49         public function open() 50         { 51             if ($this->isopen()) { 52                 throw new ttransportexception('clienttransport already open.', ttransportexception::already_open); 53             } 54             if (!$this->client->connect($this->host, $this->port)) { 55                 throw new ttransportexception( 56                     'clienttransport could not open:' . $this->client->errcode, 57                     ttransportexception::unknown 58                 ); 59             } 60         } 61  62         /** 63          * clo the transport. 64          * @throws ttransportexception 65          */ 66         public function clo() 67         { 68             if (!$this->isopen()) { 69                 throw new ttransportexception('clienttransport not open.', ttransportexception::not_open); 70             } 71             $this->client->clo(); 72         } 73  74         /** 75          * read some data into the array. 76          * 77          * @param int $len how much to read 78          * @return string the data that has been read 79          * @throws ttransportexception if cannot read any more data 80          */ 81         public function read($len) 82         { 83             if (!$this->isopen()) { 84                 throw new ttransportexception('clienttransport not open.', ttransportexception::not_open); 85             } 86             return $this->client->recv($len, true); 87         } 88  89         /** 90          * writes the given data out. 91          * 92          * @param string $buf the data to write 93          * @throws ttransportexception if writing fails 94          */ 95         public function write($buf) 96         { 97             if (!$this->isopen()) { 98                 throw new ttransportexception('clienttransport not open.', ttransportexception::not_open); 99             }100             $this->client->nd($buf);101         }102     }

我们在 app/rvices/client 目录下创建 urrvice.php,用于存放 rpc 客户端连接与请求服务接口方法:

 1 <?php 2     namespace app\rvices\client; 3  4     u app\sockets\clienttransport; 5     u app\thrift\ur\urclient; 6     u thrift\exception\texception; 7     u thrift\protocol\tbinaryprotocol; 8     u thrift\protocol\tmultiplexedprotocol; 9     u thrift\transport\tbufferedtransport;10     u thrift\transport\tframedtransport;11     u thrift\transport\tsocket;12 13     class urrvice14     {15         public function geturinfoviaswoole(int $id)16         {17             try {18                 // 建立与 swoolerver 的连接19                 $socket = new clienttransport("127.0.0.1", 9999);20 21                 $transport = new tframedtransport($socket);22                 $protocol = new tbinaryprotocol($transport);23                 $client = new urclient($protocol);24                 $transport->open();25 26                 $单招都考什么result = $client->getinfo($id);27 28                 $transport->clo();29                 return $result;30             } catch (texception $texception) {31                 dd($texception);32             }33         }34     }

测试,新增一个路由。

1 route::get('/ur/{id}', function($id) {2     $urrvice = new urrvice();3     $ur = $urrvice->geturinfoviaswoole($id);4     return $ur;5 });

启动laravel-s。

php bin/laravels start 

在浏览器中输入 http://192.168.10.100:5200/ur/2 (192.168.10.100为我homestead设置的地址,5200为laravel-s设置的端口号)

这时候我们就会发现浏览器上面出现chensi2几个大字。一个由larave+thrift+swoole搭建的微服务框架就这样完成了。端口号固定9999也可以使用consul做服务发现。

当然了有兴趣的可以写一个package自己去实现而不用laravels这个扩展。

本文发布于:2023-04-07 20:13:37,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/4896023023c331a6f501ef305e5834ca.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:PHP laravel+thrift+swoole打造微服务框架.doc

本文 PDF 下载地址:PHP laravel+thrift+swoole打造微服务框架.pdf

标签:代码   服务器   客户端   项目
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图