随着用户的增长和业务的增多,单节点服务已经满足不了需求,用hyperf对主业务进行了重构。
hyperf是一个后现代的php框架,基于php+swoole,支持协程,解决了php让人诟病的性能问题和多线程支持不够的问题。官方也提供了各种组件,比如 配置中心、定时任务、消息队列和微服务,对于日常业务需求,基本能做到开箱即用,有点php界的springcloud的意思。
用过hyperf的微服务后,眼前一亮。hyperf采用了新起一个Server,在Service层接收和返回数据,本地也可以复用这些Service,通信协议用的是jsonrpc2.0,既支持http协议,又支持tcp协议,用http协议调试时可以直接用postman,方便直观,服务安全方面,线上服务器只要对微服务端口进行隔离,不对外部服务器开放即可。
之前在与JAVA组进行业务对接用的是restful,现在hyperf又提供了一种新的思路,重新设计架构。

如上图所示,在后台服务与服务之间,采用jsonrpc进行交互,根据业务分库分表,不同业务之前不直接访问数据库。上层独立出子项目,每个子项目包含一个前端和一个业务中台,业务中台采用php,不直接访问数据库,从根据不同的业务从不同的业务后台取数据,进行数据组装。
下面以加法运算为例,展示一下springboot、hyperf、go之间相互请求。
<?php
declare(strict_types=1);
namespace App\JsonRpc;
use Hyperf\RpcServer\Annotation\RpcService;
#[RpcService(name: "PhpHttpService", protocol: "jsonrpc-http", server: "jsonrpc-http")]
class PhpHttpService implements PhpServiceInterface
{
public function add(array $args): array
{
$result = [];
$result["c"] = $args["a"] + $args["b"];
return $result;
}
}
<?php
declare(strict_types=1);
namespace App\JsonRpc;
use Hyperf\RpcClient\AbstractServiceClient;
class JavaHttpServiceConsumer extends AbstractServiceClient implements JavaServiceInterface
{
protected string $serviceName = 'JavaHttpService';
protected string $protocol = 'jsonrpc-http';
public function add(array $args): array
{
return $this->__request(__FUNCTION__, compact('args'));
}
}
<?php
namespace App\Task;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Crontab\Annotation\Crontab;
use Hyperf\Di\Annotation\Inject;
use App\JsonRpc\JavaHttpServiceConsumer;
#[Crontab(name: "ClientTask", rule: "*/5 * * * * *", callback: "execute", memo: "客户端定时任务")]
class ClientTask
{
#[Inject]
private StdoutLoggerInterface $logger;
#[Inject]
private JavaHttpServiceConsumer $javaHttpServiceConsumer;
public function execute()
{
$seed = time();
srand($seed);
try {
$a = rand(0, 100);
$b = rand(0, 100);
$result = $this->javaHttpServiceConsumer->add(["a" => $a, "b" => $b]);
$this->logger->info(sprintf("[http] PHP asked:\"%d+%d=?\"; Java answered:\"%d\"", $a, $b, $result["c"]));
} catch (Exception $e) {
$this->logger->info($e->getMessage());
}
}
}
package io.moonquakes.javahttp.service;
import io.moonquakes.javahttp.dto.ArgsDto;
import io.moonquakes.javahttp.dto.ResultDto;
public class JavaHttpService implements IJavaHttpService {
@Override
public ResultDto add(ArgsDto args) {
ResultDto resultDto = new ResultDto();
resultDto.setC(args.getA() + args.getB());
return resultDto;
}
}
package io.moonquakes.javahttp.client;
import com.sunquakes.jsonrpc4j.JsonRpcClient;
import com.sunquakes.jsonrpc4j.JsonRpcProtocol;
import io.moonquakes.javahttp.dto.ArgsDto;
import io.moonquakes.javahttp.dto.ResultDto;
@JsonRpcClient(value = "GoHttp", protocol = JsonRpcProtocol.http, url = "localhost:3602")
public interface IGoHttpClient {
ResultDto Add(ArgsDto args);
}
package io.moonquakes.javahttp.task;
import io.moonquakes.javahttp.client.IGoHttpClient;
import io.moonquakes.javahttp.dto.ArgsDto;
import io.moonquakes.javahttp.dto.ResultDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Slf4j
@EnableScheduling
@Component
public class ClientTask {
@Autowired(required = false)
private IGoHttpClient goHttpClient;
@Scheduled(fixedDelay = 10000)
public void run() {
ArgsDto args = new ArgsDto();
try {
int a = (int) (Math.random() * 100);
int b = (int) (Math.random() * 100);
args.setA(a);
args.setB(b);
ResultDto resultDto = goHttpClient.Add(args);
log.info(String.format("[http] Java asked:\"%d+%d=?\"; Go answered:\"%d\"", a, b, resultDto.getC()));
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
go func() {
s, _ := jsonrpc4go.NewServer("http", "127.0.0.1", "3602")
s.Register(new(GoHttp))
s.Start()
}()
phpHttpClient, _ := jsonrpc4go.NewClient("http", "127.0.0.1", "9504")
a = rand.Intn(100)
b = rand.Intn(100)
_ = phpHttpClient.Call("php_http/add", Params{Args{a, b}}, result, false)
moonquakes是一个演示项目。它展示了如何在一些web框架中使用jsonrpc协议进行通信,这些web框架是由java、php或golang编写的。
在moonquakes中,java框架用的是springboot,它使用 jsonrpc4j 与go和php框架通信;php框架是 Hyperf ,它有自己的 jsonrpc组件 来与go和java框架通信;go框架使用 jsonrpc4go 与java和php框架通信。

尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我正在使用puppet为ruby程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search
我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
从MB升级到新的MBP后,Apple的迁移助手没有移动我的gem。我这次是通过macports安装rubygems,希望在下次升级时避免这种情况。有什么我应该注意的陷阱吗? 最佳答案 如果你想把你的gems安装在你的主目录中(在传输过程中应该复制过来,作为一个附带的好处,会让你以你自己的身份运行geminstall,而不是root),将gemhome:键设置为您在~/.gemrc中的主目录中的路径. 关于通过MacPorts的RubyGems是个好主意吗?,我们在StackOverf
当我执行>rvminstall1.9.2时一切顺利。然后我做>rvmuse1.9.2也很顺利。但是当涉及到ruby-v时..sam@sjones:~$rvminstall1.9.2/home/sam/.rvm/rubies/ruby-1.9.2-p136,thismaytakeawhiledependingonyourcpu(s)...ruby-1.9.2-p136-#fetchingruby-1.9.2-p136-#downloadingruby-1.9.2-p136,thismaytakeawhiledependingonyourconnection...%Total%Rece
当谈到运行时自省(introspection)和动态代码生成时,我认为ruby没有任何竞争对手,可能除了一些lisp方言。前几天,我正在做一些代码练习来探索ruby的动态功能,我开始想知道如何向现有对象添加方法。以下是我能想到的3种方法:obj=Object.new#addamethoddirectlydefobj.new_method...end#addamethodindirectlywiththesingletonclassclass这只是冰山一角,因为我还没有探索instance_eval、module_eval和define_method的各种组合。是否有在线/离线资