继续使用consul注册中心,Hyperf框架与Gin之间进行RPC调用

携手创作,共同成长!这是我参与「掘金日新计划 · 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}.",
        ];
    }

}
复制代码

后面需要在消费者端新建一个接口,调用方法映射的接口来测试,还需要增加路由这些就不详细说了。

最后看看效果:

image.png

总结

GitHub地址:github.com/koala9527/h…

这里只复现了Hyperf调用Gin的例子,没有互相调用,因为Gin只做了一个基础的功能,一般做一些比较简单的的工具接口,没有作为一个主要服务去做,不需要连接数据库,没必要互相调用增加服务之间的复杂度,减少服务之间的互相依赖。
接下来的Python Flask框架的RPC调用暂时搁浅一段时间,下篇预告是在hyperf框架中使用中间件对接口进行混淆加密,防止重放攻击,敬请期待。

猜你喜欢

转载自juejin.im/post/7125647647221743653
今日推荐