go-Rrpc使用nacos作为服务发现

go-Rrpc使用nacos作为服务发现

前端vue3随便写个展示界面,API

目录结构
www.go-nacos-rpc

|---goods_service
    |---proto
    	|---goods.proto
    |---main.go
|---inventory_service
	|---proto
		|---inventory.proto
	|---main.go
|---tools
	|---core.go
	|---getip.go
	|---nacos.go
goods.proto
// goods.proto

syntax = "proto3";

option go_package = "./;goods";

service GoodService {
  rpc GetGoodsMsg (GetGoodsMsgRequest) returns (GetGoodsMsgResponse);
  rpc GetGoodsMsgList (Empty) returns (GetGoodsMsgListResponse);
}
message GetGoodsMsgRequest {
  int64  id = 1;
}

message GetGoodsMsgResponse {
  int64 id = 1;
  string name = 2;
  int64 price = 3;
  string inventory_id = 4;
  int64 inventory_count = 5;
}

message Empty{}

message GetGoodsMsgListResponse {
  repeated GetGoodsMsgResponse items = 1;
}

使用protoc生成rpc

PS D:\All_Go_test> protoc -I =. --go_out=. --go-grpc_out=. .\goods.proto
inventory.proto
// goods.proto

syntax = "proto3";

option go_package = "./;inventory";

service InventoryService {
  rpc GetInventoryMsg (GetInventoryRequest) returns (GetInventoryResponse);
  rpc GetAllInventoryMsg
}

message GetInventoryRequest {
  string  inventory_id = 1;
}

message GetInventoryResponse {
  string inventory_id = 1;
  int32 count = 2;
}

使用protoc生成rpc

PS D:\All_Go_test> protoc -I =. --go_out=. --go-grpc_out=. .\inventory.proto
tools工具

解决跨域问题core.go

package tools

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

// 解决前后端跨域

func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
		c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
		c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
		c.Header("Access-Control-Allow-Credentials", "true")
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}
		c.Next()
	}
}

动态获取服务IP地址getip.go

package tools

import (
	"net"
)

// 获取IP

func GetLocalIP() (string, error) {
	Addresses, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}
	var LocalIP string
	for _, addr := range Addresses {
		if IpNet, ok := addr.(*net.IPNet); ok && !IpNet.IP.IsLoopback() {
			if IpNet.IP.To4() != nil {
				if IpNet.IP.To4()[0] == 192 && IpNet.IP.To4()[1] == 168 {
					LocalIP = IpNet.IP.String()
				}
			}
		}
	}
	return LocalIP, nil
}

涉及nacos的操作nacos.go

package tools

import (
	"errors"
	"fmt"
	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	"github.com/nacos-group/nacos-sdk-go/vo"
)

// 获取nacos客户端

func initConfigClient() (iClient naming_client.INamingClient, err error) {
	sv := []constant.ServerConfig{
		{
			IpAddr:      "192.168.40.180",
			ContextPath: "/nacos",
			Port:        8848,
			Scheme:      "http",
		},
	}

	cc := constant.ClientConfig{
		NamespaceId:         "c0c576b7-1d60-48e9-9b2f-8d440d68c19b", // 如果需要支持多namespace,我们可以创建多个client,它们有不同的NamespaceId。当namespace是public时,此处填空字符串。
		TimeoutMs:           30000,
		NotLoadCacheAtStart: true,
		LogLevel:            "debug",
		Username:            "nacos",
		Password:            "nacos",
	}

	iClient, err = clients.NewNamingClient(
		vo.NacosClientParam{
			ClientConfig:  &cc,
			ServerConfigs: sv,
		},
	)
	if err != nil {
		return nil, err
	}
	return iClient, nil
}

// 初始化nacos并注册服务到nacos

func InitNacosSVC(ServiceName, Ip, ClusterName, GroupName string, Port int, Weight float64, Metadata map[string]string) error {

	iClient, err := initConfigClient()
	if err != nil {
		fmt.Println("初始化Nacos客户端失败了")
		return err
	}
	if iClient == nil {
		fmt.Println("iClient是空的")
		panic("iClient空指针")
	}
	// 注册服务到nacos

	success, err := iClient.RegisterInstance(vo.RegisterInstanceParam{
		Ip:          Ip,
		Port:        uint64(Port),
		ServiceName: ServiceName,
		Weight:      Weight,
		Enable:      true,
		Healthy:     true,
		Ephemeral:   true,
		Metadata:    Metadata,
		ClusterName: ClusterName, // 默认值DEFAULT
		GroupName:   GroupName,   // 默认值DEFAULT_GROUP
	})

	if err != nil {
		fmt.Println(err)
	}
	if success {
		fmt.Println("register nacos success")
		return nil
	}
	return errors.New("注册inventory服务到nacos失败")
}

// 获取服务信息

func GetNacosSVC(svcName string) (addr string, err error) {
	iClient, err := initConfigClient()
	if err != nil {
		return "", err
	}
	if iClient == nil {
		fmt.Println("iClient是空的")
		panic("iClient空指针")
	}
	// SelectInstances 只返回满足这些条件的实例列表:healthy=${HealthyOnly},enable=true 和weight>0
	instances, err := iClient.SelectInstances(vo.SelectInstancesParam{
		ServiceName: svcName,
		GroupName:   "myGroup",             // 默认值DEFAULT_GROUP
		Clusters:    []string{"myCluster"}, // 默认值DEFAULT
		HealthyOnly: true,
	})
	if err != nil {
		fmt.Println("获取实例失败:", err)
	}
	var Addr string
	// 解析实例列表
	for _, instance := range instances {
		Addr = fmt.Sprintf("%s:%d", instance.Ip, instance.Port)
	}
	return Addr, nil
}
RPC服务端 inventory_service/main.go
package main

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"net"
	pb "testgo.ww.www/inventory_service/proto"
	"testgo.ww.www/tools"
)

var db *sql.DB

// inventory server
type server struct {
	pb.UnimplementedInventoryServiceServer
}

func initDB() error {
	var err error
	dsn := "dbuser1:NSD2021@tedu.cn@tcp(192.168.40.199:13306)/inventory"
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return err
	}
	return db.Ping()
}
func (s *server) GetInventoryMsg(ctx context.Context, req *pb.GetInventoryRequest) (*pb.GetInventoryResponse, error) {
	// 查询数据库
	var inventoryMsg int32
	err := db.QueryRow("SELECT count FROM `inventory` WHERE id = ?", req.GetInventoryId()).Scan(&inventoryMsg)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, status.Errorf(codes.NotFound, "Inventory with ID %v not found", req.GetInventoryId())
		}
		return nil, status.Errorf(codes.Internal, "Failed to query inventory: %v", err)
	}

	return &pb.GetInventoryResponse{
		InventoryId: req.InventoryId,
		Count:       inventoryMsg,
	}, nil
}

var (
	inventoryPort        = 9090
	inventorySVCName     = "inventory"
	inventoryClusterName = "myCluster"
	inventoryGroupName   = "myGroup"
	inventoryWeight      = 10
	inventoryMetadata    = map[string]string{"app": "inventory"}
)

func main() {
	// 初始化数据库
	err := initDB()
	if err != nil {
		panic(fmt.Sprintf("Failed to initialize database: %v", err))
	}
	inventorySVCIP, err := tools.GetLocalIP()
	if err != nil {
		panic(fmt.Sprintf("获取IP地址失败: %v", err))
	}
	// 创建nacos服务实例
	err = tools.InitNacosSVC(inventorySVCName, inventorySVCIP, inventoryClusterName, inventoryGroupName, inventoryPort, float64(inventoryWeight), inventoryMetadata)
	if err != nil {
		panic(err)
	}
	// 开启端口			fmt.Sprintf(":%d", inventoryPort)
	listen, err := net.Listen("tcp", fmt.Sprintf(":%d", inventoryPort))
	if err != nil {
		panic(fmt.Sprintf("Failed to listen on port %d: %v", inventoryPort, err))
	}
	// 创建grpc服务
	grpcServer := grpc.NewServer()
	// 注册服务
	pb.RegisterInventoryServiceServer(grpcServer, &server{})
	// 启动服务
	fmt.Println("Grpc service is start running... ", inventoryPort)
	err = grpcServer.Serve(listen)
	if err != nil {
		fmt.Println(err)
		return
	}
}
RPC客户端 goods_service/main.go
package main

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/status"
	"net/http"
	"strconv"
	pb "testgo.ww.www/goods_service/proto"
	pb2 "testgo.ww.www/inventory_service/proto"
	"testgo.ww.www/tools"
)

var (
	db               *sql.DB
	goodsPort        = 9091
	goodsSVCName     = "goods"
	goodsClusterName = "myCluster"
	goodsGroupName   = "myGroup"
	goodsWeight      = 10
	goodsMetadata    = map[string]string{"app": "goods"}
)

type Server struct {
	pb.UnimplementedGoodServiceServer
}

// 初始化数据库连接

func initDB() error {
	var err error
	dsn := "dbuser1:NSD2021@tedu.cn@tcp(192.168.40.199:13306)/goods"
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return err
	}
	return db.Ping()
}

// 创建 gRPC 客户端

func CreateGRPCClient(serviceName string) (pb2.InventoryServiceClient, *grpc.ClientConn, error) {
	clientAddr, err := tools.GetNacosSVC(serviceName)
	if err != nil || clientAddr == "" {
		return nil, nil, fmt.Errorf("获取nacos的服务失败: %v", err)
	}
	fmt.Println("获取的nacos服务为:", clientAddr)

	conn, err := grpc.NewClient(clientAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		return nil, nil, fmt.Errorf("创建gRPC连接失败: %v", err)
	}

	return pb2.NewInventoryServiceClient(conn), conn, nil
}

// 获取单个商品信息

func (s *Server) GetGoodsMsg(ctx context.Context, req *pb.GetGoodsMsgRequest) (res *pb.GetGoodsMsgResponse, err error) {
	var (
		id          int
		name        string
		price       int
		inventoryId string
	)
	// 查询数据库并保存结果
	err = db.QueryRow("SELECT id,name,price,inventory_id FROM `goods` WHERE id = ?", req.GetId()).Scan(&id, &name, &price, &inventoryId)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, status.Errorf(codes.NotFound, "Inventory with ID %d not found", req.GetId())
		}
		return nil, status.Errorf(codes.Internal, "Failed to query inventory: %v", err)
	}
	res = &pb.GetGoodsMsgResponse{
		Id:          int64(id),
		Name:        name,
		Price:       int64(price),
		InventoryId: inventoryId,
	}
	return res, nil
}

// 获取所有商品信息

func (s *Server) GetGoodsMsgList(context.Context, *pb.Empty) (res *pb.GetGoodsMsgListResponse, err error) {
	rows, err := db.Query("SELECT id, name, price, inventory_id FROM `goods`")
	if err != nil {
		return nil, status.Errorf(codes.Internal, "Failed to query goods: %v", err)
	}
	defer func(rows *sql.Rows) {
		err := rows.Close()
		if err != nil {
			panic(err)
		}
	}(rows)
	goodsList := &pb.GetGoodsMsgListResponse{
		Items: []*pb.GetGoodsMsgResponse{},
	}
	for rows.Next() {
		var item pb.GetGoodsMsgResponse
		if err := rows.Scan(&item.Id, &item.Name, &item.Price, &item.InventoryId); err != nil {
			return nil, status.Errorf(codes.Internal, "Failed to scan goods row: %v", err)
		}
		goodsList.Items = append(goodsList.Items, &item)
	}
	return goodsList, nil
}

// 调用inventory服务获取单个商品的库存数量方法

func GetGoodsCount(inventoryId string) int {
	inventoryClient, conn, err := CreateGRPCClient("inventory")
	if err != nil {
		fmt.Println(err)
	}
	// 关闭连接
	defer func(conn *grpc.ClientConn) {
		err := conn.Close()
		if err != nil {
			fmt.Println(err)
		}
	}(conn)

	res, err := inventoryClient.GetInventoryMsg(context.Background(), &pb2.GetInventoryRequest{
		InventoryId: inventoryId,
	})
	if err != nil {
		panic(err)
	}
	return int(res.Count)
}

// 获取单个商品信息和库存数量的处理函数

func GetGoodsAndInventory(c *gin.Context) {
	goodsIdString := c.Query("goods_id")
	if goodsIdString == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "goods_id is required"})
		return
	}
	goodsID, err := strconv.ParseInt(goodsIdString, 10, 64)
	// 获得商品信息
	req := &pb.GetGoodsMsgRequest{Id: goodsID}
	resp, err := (&Server{}).GetGoodsMsg(context.Background(), req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	// 获取库存数量
	resp.InventoryCount = int64(GetGoodsCount(resp.InventoryId))

	c.JSON(http.StatusOK, gin.H{
		"goods_id":        resp.Id,
		"name":            resp.Name,
		"price":           resp.Price,
		"inventory_id":    resp.InventoryId,
		"inventory_count": resp.InventoryCount,
	})
}

// 获取所有商品信息和库存数量的处理函数

func GetAllGoodsAndInventory(c *gin.Context) {
	// 获得所有商品信息
	req := &pb.Empty{}
	resp, err := (&Server{}).GetGoodsMsgList(context.Background(), req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// 为每个商品获取库存数量
	for _, item := range resp.Items {
		inventoryCount := GetGoodsCount(item.InventoryId)
		item.InventoryCount = int64(inventoryCount)
	}

	c.JSON(http.StatusOK, resp)
}

func main() {
	// 创建数据库连接
	err := initDB()
	if err != nil {
		panic(fmt.Sprintf("连接数据库失败%v", err))
	}
	// 获取本地IP
	goodsSVCIP, err := tools.GetLocalIP()
	if err != nil {
		panic(fmt.Sprintf("获取IP失败%v", err))
	}
	// 注册goods服务到nacos
	err = tools.InitNacosSVC(goodsSVCName, goodsSVCIP, goodsClusterName, goodsGroupName, goodsPort, float64(goodsWeight), goodsMetadata)
	if err != nil {
		panic(fmt.Sprintf("注册NACOS失败%v", err))
	}
	// 创建路由
	r := gin.Default()
	r.Use(tools.Cors())
	r.GET("/goods", GetGoodsAndInventory)
	r.GET("/goods_list", GetAllGoodsAndInventory)
	if err := r.Run(":8088"); err != nil {
		panic(fmt.Sprintf("启动服务失败: %v", err))
	}
}
前端vue3
<template>
  <div class="fix">
  <el-table :data="tableData" :border="true" style="width: 100%">
    <el-table-column prop="id" label="序号" width="180" />
    <el-table-column prop="name" label="名称" width="180" />
    <el-table-column prop="price" label="价格" />
    <el-table-column prop="inventory_id" label="库存序号" />
    <el-table-column prop="inventory_count" label="库存" />
  </el-table>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import axios from 'axios';
const tableData = ref([])
const getMsg = async () => {  
     try {
        const res = await axios({
            method: 'get',
            url: 'http://127.0.0.1:8088/goods_list',
        });
        tableData.value = res.data.items
    } catch (error) {
        console.error('Request failed:', error);
    }
}
onMounted(()=>{
    getMsg()
})
</script>


<style lang='scss' scoped>
.fix {
    position: relative;
    padding-bottom: 60px; /* 预留按钮的高度 */
}
.el-button {
    position: fixed;
    bottom: 50px;
    right: 473px;
}

</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值