携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
前言
前面一篇文章 使用consul注册中心,Hyperf框架之间进行RPC互相调用 展示了hyperf框架之间互相调用的例子,属于同一语言框架下的RPC互相调用,今天来看看跨语言的调用,今天来加上golang的Gin框架
原理
这里整体的代码不做讲解,只挑一些重点讲解,如果需要整个Gin框架代码讲解,可以看看我之前做的一个关于Gin与ffmpeg的视频生成器的一个文章: 使用gin和ffmpeg制作一个短视频生成器(1),详细讲解了一个Gin项目整体框架及其代码的作用。
不会Golang也没有关系,可以看看我之前总结的一个Golang万字总结:Golang万字小册总结
文章最后会把这个Gin框架所有的代码放到GitHub分享出来
服务注册
Gin框架中最核心的部分是consul注册,服务方法注册,在Gin的入口文件中在启动整个框架之前使用go
关键字启动一个RPC服务注册的功能。
可以看看项目根目录下的rpc/consul.go
的内容:
package rpc
import (
"fmt"
"gin-api/config"
"log"
"net"
"net/http"
_ "net/http/pprof"
"strings"
"github.com/google/uuid"
consulapi "github.com/hashicorp/consul/api"
)
// var count int64
// consul 服务端会自己发送请求,来进行健康检查
func consulCheck(w http.ResponseWriter, r *http.Request) {
// s := "consulCheck" + fmt.Sprint(count) + "remote:" + r.RemoteAddr + " " + r.URL.String()
// fmt.Println(s)
// fmt.Fprintln(w, s)
// count++
}
func ConsulRegister() {
env, err := config.Get()
consuConfig := consulapi.DefaultConfig()
// fmt.Println(env.CONSUL_URL)
// config.Address = "172.18.167.66:8500"
consuConfig.Address = env.CONSUL_URL
client, err := consulapi.NewClient(consuConfig)
if err != nil {
log.Fatal("consul client error : ", err)
}
serviceName := "GolangApiService"
instanceId := serviceName + "-" + strings.Replace(uuid.New().String(), "-", "", -1)
registration := new(consulapi.AgentServiceRegistration)
registration.ID = instanceId // 服务节点的名称
registration.Name = serviceName // 服务名称
registration.Port = 8006 // 服务端口
// registration.Tags = []string{"v1000"} // tag,可以为空
registration.Address = localIP() // 服务 IP
checkPort := 8080
registration.Check = &consulapi.AgentServiceCheck{ // 健康检查
HTTP: fmt.Sprintf("http://%s:%d%s", registration.Address, checkPort, "/check"),
Timeout: "3s",
Interval: "5s", // 健康检查间隔
DeregisterCriticalServiceAfter: "30s", //check失败后30秒删除本服务,注销时间,相当于过期时间
// GRPC: fmt.Sprintf("%v:%v/%v", IP, r.Port, r.Service),// grpc 支持,执行健康检查的地址,service 会传到 Health.Check 函数中
}
err = client.Agent().ServiceRegister(registration)
if err != nil {
log.Fatal("register server error : ", err)
}
http.HandleFunc("/check", consulCheck)
http.ListenAndServe(fmt.Sprintf(":%d", checkPort), nil)
}
func localIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
return ""
}
复制代码
引用了github.com/hashicorp/consul/api
远程第三方的包,提供了一些方法与consul进行注册,监听,健康检查等功能,与Hyperf框架中相似,只是在Gin这个框架中的文档没有Hyperf那么详细,需要自己尝试收集资料。
入口文件
项目入口文件启动服务注册。main.go
:
package main
import (
"gin-api/router"
"gin-api/rpc"
"gin-api/config"
"github.com/gin-gonic/gin"
)
func main() {
gin.SetMode(gin.ReleaseMode)
go rpc.RegisterApiService()
go rpc.ConsulRegister()
env, _ := config.Get()
//引入路由
r := router.Router()
//run
r.Run(":" + env.APP_PORT)
}
复制代码
依次进行RPC远程方法暴露注册,consul注册,项目路由注册,项目启动。
RPC方法注册
rpc/golangApiService.go
:
package rpc
import (
"gin-api/service"
go_jsonrpc "github.com/sunquakes/go-jsonrpc"
)
type GolangApi struct{}
type MsgParams struct {
Msg string `json:"msg"`
}
type MsgResult = string
//测试获取消息
func (*GolangApi) GetMsg(params *MsgParams, result *MsgResult) error {
res := service.GetMsg(params.Msg)
*result = interface{}(res).(MsgResult)
return nil
}
func RegisterApiService() {
s, _ := go_jsonrpc.NewServer("http", "0.0.0.0", "8006")
s.Register(new(GolangApi))
s.Start()
}
复制代码
修改消费者端
Hyperf作为消费者,Gin作为生产者
因为Gin使用的jsonrpc使用的底层协议是是http不是tcp,需要设置一下: admin/config/autoload/services.php
:
<?php
return [
'consumers' => value(function () {
$consumers = [];
$services = [
'AppAdminService' => App\JsonRpc\AppServiceInterface::class,
'GolangApiService' => App\JsonRpc\GolangServiceInterface::class,
];
foreach ($services as $name => $interface) {
$protocol = 'jsonrpc';
if ($name == 'GolangApiService') {
$protocol = 'jsonrpc-http';
}
$consumers[] = [
...
复制代码
消费者端方法映射
App\JsonRpc\GolangServiceInterface::class
:
<?php
namespace App\JsonRpc;
interface GolangServiceInterface
{
/**
* @param string $msg 测试消息
*/
public function getMsg(string $msg);
}
复制代码
Admin消费者实际消费方法
admin/app/Controller/IndexController.php
:
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Controller;
use Hyperf\Di\Annotation\Inject;
use App\JsonRpc\AppServiceInterface;
use App\JsonRpc\GolangServiceInterface;
use App\JsonRpc\PythonServiceInterface;
class IndexController extends AbstractController
{
...
/**
* golang服务
* @Inject
* @var GolangServiceInterface
*/
protected $golang_service_interface;
...
public function golang()
{
$user = $this->request->input('msg', 'Hyperf');
$method = $this->request->getMethod();
$msg = $this->golang_service_interface->getMsg($user);
return [
'method' => $method,
'message' => "Hello {$msg}.",
];
}
}
复制代码
后面需要在消费者端新建一个接口,调用方法映射的接口来测试,还需要增加路由这些就不详细说了。
最后看看效果:
总结
GitHub地址:github.com/koala9527/h…
这里只复现了Hyperf调用Gin的例子,没有互相调用,因为Gin只做了一个基础的功能,一般做一些比较简单的的工具接口,没有作为一个主要服务去做,不需要连接数据库,没必要互相调用增加服务之间的复杂度,减少服务之间的互相依赖。
接下来的Python Flask框架的RPC调用暂时搁浅一段时间,下篇预告是在hyperf框架中使用中间件对接口进行混淆加密,防止重放攻击,敬请期待。