gRPC三种流和消息格式

消息格式

RPC流

服务端实现protocol buffer定义的方法,客户端保留一个存根,提供服务端方法的抽象,客户端只需要调用存根中的方法,就可以远程调用服务端方法。

  • 调用存根方法
  • 存根创建HTTP POST请求(gRPC中所有请求都是 POST),设置content-type为application/grpc
  • 到达服务端,会先检查请求头是不是gRPC请求,否则返回415

长度前缀的消息分帧

在写入消息前,先写入长度消息表明每条消息的大小。

每条消息都有额外的4字节来设置大小,也就是说消息的大小不能超过4GB

帧首中还有单字节无符号整数,用来表明数据是否进行了压缩
为1表示使用 message-encoding中的编码机制进行了压缩

在这里插入图片描述

请求消息

客户端发送,包含3个部分:请求头信息、长度前缀的消息、流结束标记

1、对于gRPC 都是POST

2、协议:Http/Https

3、/服务名/方法名

4、目标URI的主机名

5、对不兼容代理的检测,gRPC下这个值必须为 trailers

6、超时时间

7、媒体类型

8、压缩类型

在这里插入图片描述

在这里插入图片描述

当因为没有要发送的数据而需要关闭请求流时,必须发送一个带标记的空数据帧

在这里插入图片描述

响应信息

服务端发送,包含3个部分:响应头信息、长度前缀的信息、trailers

END_STREAM 标记不会随数据帧一起发送,而是作为单独的头信息来发送,名为 trailer

在这里插入图片描述

在这里插入图片描述

三种流

一元RPC

通信时始终只有一个请求和一个响应

在这里插入图片描述

protocol buffer

syntax = "proto3";
package hello;
// 第一个分割参数,输出路径;第二个设置生成类的包路径

option go_package = "./proto/hello";



// 设置服务名称
service Greeter {
  // 设置方法
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// 请求信息用户名.
message HelloRequest {
  string name = 1;
}

// 响应信息
message HelloReply {
  string message = 1;
}

服务端

package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net"

	"google.golang.org/grpc"
	pb "mygrpc/proto/hello"
)

var (
	port = flag.Int("port", 50051, "The server port")
)

type server struct {
	pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: %v", in.GetName())
	return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func main() {
	flag.Parse()
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	// 开启rpc
	s := grpc.NewServer()
	// 注册服务
	pb.RegisterGreeterServer(s, &server{})
	log.Printf("server listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

客户端

package main

import (
	"context"
	"flag"
	"log"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	pb "mygrpc/proto/hello" // 引入编译生成的包
)

const (
	defaultName = "world"
)

var (
	addr = flag.String("addr", "localhost:50051", "the address to connect to")
	name = flag.String("name", defaultName, "Name to greet")
)

func main() {
	flag.Parse()
	// 与服务建立连接.
	conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	// 创建指定服务的客户端
	c := pb.NewGreeterClient(conn)

	// 连接服务器并打印出其响应。
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	// 调用指定方法
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.GetMessage())
}

服务流RPC

通信时可以是一个请求,服务端多次响应,比如查询业务,服务端模糊匹配找到一次就返回客户端一次响应这样的多次响应。
在这里插入图片描述

在protobuf中的 service添加以下代码

rpc searchOrders(google.protobuf.StringValue) returns (stream Order);

服务端代码

package main

import (
	"context"
	"flag"
	"fmt"
	"google.golang.org/grpc"
	"io"
	"log"
	pb "mygrpc/proto/hello"
	"net"
)

var (
	port = flag.Int("port", 50051, "The service port")
)

type server struct {
	pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: %v", in.GetName())
	return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func (s *server) SearchOrders(req *pb.HelloRequest, stream pb.Greeter_SearchOrdersServer) error {
	log.Printf("Recved %v", req.GetName())
	// 具体返回多少个response根据业务逻辑调整
	for i := 0; i < 10; i++ {
		// 通过 send 方法不断推送数据
		err := stream.Send(&pb.HelloReply{})
		if err != nil {
			log.Fatalf("Send error:%v", err)
			return err
		}
	}
	return nil
}
func (s *server) UpdateOrders(stream pb.Greeter_UpdateOrdersServer) error {

	for {
		log.Println("开始接受客户端的流")
		// Recv 对客户端发来的请求接收
		order, err := stream.Recv()
		if err == io.EOF {
			// 流结束,关闭并发送响应给客户端
			return stream.Send(&pb.HelloReply{Message: "接受客户流结束"})
		}
		if err != nil {
			return err
		}
		// 更新数据
		log.Printf("Order ID : %s - %s", order.GetName(), "Updated")
	}
}
func main() {
	flag.Parse()
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	// 开启rpc
	s := grpc.NewServer()
	// 注册服务
	pb.RegisterGreeterServer(s, &server{})
	log.Printf("service listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}


客户端代码

package main

import (
	"context"
	"flag"
	"io"
	"log"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	pb "mygrpc/proto/hello" // 引入编译生成的包
)

const (
	defaultName = "world"
)

var (
	addr = flag.String("addr", "localhost:50051", "the address to connect to")
	name = flag.String("name", defaultName, "Name to greet")
)

func main() {
	flag.Parse()
	// 与服务建立连接.
	conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer func(conn *grpc.ClientConn) {
		err := conn.Close()
		if err != nil {

		}
	}(conn)
	// 创建指定服务的客户端
	c := pb.NewGreeterClient(conn)

	// 连接服务器并打印出其响应。
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	// 调用指定方法
	searchStream, _ := c.SearchOrders(ctx, &pb.HelloRequest{Name: "开始服务端rpc流测试"})
	for {
		// 客户端 Recv 方法接收服务端发送的流
		searchOrder, err := searchStream.Recv()
		if err == io.EOF {
			log.Print("EOF")
			break
		}
		if err == nil {
			log.Print("Search Result : ", searchOrder)
		}
	}
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.GetMessage())
}

客户流RPC

客户端多个请求发给服务端,服务端发送一个响应给客户端,比如更新业务,客户端的读个请求发过来,服务端更新完返回一个成功的结果

在这里插入图片描述

在protobuf中的 service添加以下代码

rpc updateOrders(stream HelloRequest) returns (stream HelloReply);

服务端代码

func (s *server) UpdateOrders(stream pb.OrderManagement_UpdateOrdersServer) error {
  
	ordersStr := "Updated Order IDs : "
	for {
    // Recv 对客户端发来的请求接收
		order, err := stream.Recv()
		if err == io.EOF {
			// 流结束,关闭并发送响应给客户端
			return stream.SendAndClose(&wrapper.StringValue{Value: "Orders processed " + ordersStr})
		}
		if err != nil {
			return err
		}
		// 更新数据
		orderMap[order.Id] = *order
		log.Printf("Order ID : %s - %s", order.Id, "Updated")
		ordersStr += order.Id + ", "
	}
}

客户端代码

package main

import (
	"context"
	"flag"
	"log"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	pb "mygrpc/proto/hello" // 引入编译生成的包
)

const (
	defaultName = "world"
)

var (
	addr = flag.String("addr", "localhost:50051", "the address to connect to")
	name = flag.String("name", defaultName, "Name to greet")
)

func main() {
	flag.Parse()
	// 与服务建立连接.
	conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer func(conn *grpc.ClientConn) {
		err := conn.Close()
		if err != nil {

		}
	}(conn)
	// 创建指定服务的客户端
	c := pb.NewGreeterClient(conn)

	// 连接服务器并打印出其响应。
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	// 调用指定方法
	updateStream, err := c.UpdateOrders(ctx)
	if err != nil {
		log.Fatalf("%v.UpdateOrders(_) = _, %v", c, err)
	}
	// Updating order 1
	if err := updateStream.Send(&pb.HelloRequest{Name: "1"}); err != nil {
		log.Fatalf("%v.Send(%v) = %v", updateStream, &pb.HelloRequest{Name: "1"}, err)
	}
	// Updating order 2
	if err := updateStream.Send(&pb.HelloRequest{Name: "2"}); err != nil {
		log.Fatalf("%v.Send(%v) = %v", updateStream, &pb.HelloRequest{Name: "2"}, err)
	}
	// 发送关闭信号并接收服务端响应
	err = updateStream.CloseSend()
	if err != nil {
		log.Fatalf("%v.CloseAndRecv() got error %v, want %v", updateStream, err, nil)
	}
	log.Printf("客户端流传输结束")
}

双工流RPC

对应的业务就比如实时的消息流

在这里插入图片描述

protobuf

  // 设置双工rpc
  rpc processOrders(stream HelloRequest) returns (stream HelloReply);

服务端

package main

import (
	"flag"
	"fmt"
	"google.golang.org/grpc"
	"io"
	"log"
	pb "mygrpc/proto/hello"
	"net"
	"sync"
)

var (
	port = flag.Int("port", 50051, "The service port")
)

type server struct {
	pb.UnimplementedGreeterServer
}

func (s *server) ProcessOrders(stream pb.Greeter_ProcessOrdersServer) error {
	var (
		waitGroup sync.WaitGroup // 一组 goroutine 的结束
		// 设置通道
		msgCh = make(chan *pb.HelloReply)
	)
	// 计数器加1
	waitGroup.Add(1)
	// 消费队列中的内容
	go func() {
		// 计数器减一
		defer waitGroup.Done()
		for {
			v := <-msgCh
			fmt.Println(v)
			err := stream.Send(v)
			if err != nil {
				fmt.Println("Send error:", err)
				break
			}
		}
	}()
	waitGroup.Add(1)
	// 向队列中添加内容
	go func() {
		defer waitGroup.Done()
		for {
			req, err := stream.Recv()
			if err == io.EOF {
				break
			}
			if err != nil {
				log.Fatalf("recv error:%v", err)
			}
			fmt.Printf("Recved :%v \n", req.GetName())
			msgCh <- &pb.HelloReply{Message: "服务端传输数据"}
		}
		close(msgCh)
	}()
	// 等待 计数器问0 推出
	waitGroup.Wait()

	// 返回nil表示已经完成响应
	return nil
}
func main() {
	flag.Parse()
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	// 开启rpc
	s := grpc.NewServer()
	// 注册服务
	pb.RegisterGreeterServer(s, &server{})
	log.Printf("service listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

客户端

package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"sync"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	pb "mygrpc/proto/hello" // 引入编译生成的包
)

const (
	defaultName = "world"
)

var (
	addr = flag.String("addr", "localhost:50051", "the address to connect to")
	name = flag.String("name", defaultName, "Name to greet")
)

func main() {
	flag.Parse()
	// 与服务建立连接.
	conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer func(conn *grpc.ClientConn) {
		err := conn.Close()
		if err != nil {
		}
	}(conn)
	// 创建指定服务的客户端
	c := pb.NewGreeterClient(conn)

	// 连接服务器并打印出其响应。
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	// 设置
	var wg sync.WaitGroup
	// 调用指定方法
	stream, _ := c.ProcessOrders(ctx)
	if err != nil {
		panic(err)
	}
	// 3.开两个goroutine 分别用于Recv()和Send()
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			req, err := stream.Recv()
			if err == io.EOF {
				fmt.Println("Server Closed")
				break
			}
			if err != nil {
				continue
			}
			fmt.Printf("Recv Data:%v \n", req.GetMessage())
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()

		for i := 0; i < 2; i++ {
			err := stream.Send(&pb.HelloRequest{Name: "hello world"})
			if err != nil {
				log.Printf("send error:%v\n", err)
			}
		}
		// 4. 发送完毕关闭stream
		err := stream.CloseSend()
		if err != nil {
			log.Printf("Send error:%v\n", err)
			return
		}
	}()
	wg.Wait()
	log.Printf("服务端流传输结束")
}

代码仓库

https://github.com/onenewcode/mygrpc.git

也可以直接下载绑定的资源。

深入浅出gRPC:原理、HTTP/2协议与四种通信模式详解 想象你是一个快递站的分拣员,现在要处理一批发往同一栋楼的快递包裹。:想象你是一个快递员,要给同一栋楼的10户人家送快递。:每个包裹被拆成统一规格的小箱子(二进制帧),每个小箱子贴有数字标签(Stream ID)。1️⃣ 机器自动扫描小箱子的数字标签(解析 Stream ID)。2️⃣ 不同包裹的小箱子混合传送(多路复用),传送带永远不会空闲。:即使某个小箱子损坏(传输错误),其他小箱子不受影响。:机器处理二进制小箱子的速度远超人工阅读文本。 阅读详情

相关推荐

RPC和GRPC

createdtime 20211122 updatedtime 20211124 author venki.chen 一、是什么 1. 定义,是做什么用的? rpc是什么? ①在分布式计算,远程过程调用(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。RPC是一种服务器-客户端(..

陈文小超的博客 2万+

gRPC协议详解

gRPC是一个高性能、开源和通用的RPC(远程过程调用)框架,由Google发起并开发,于2015年对外发布。它基于HTTP/2协议和Protocol Buffers设计,支持多种编程语言(如C++、Java、Python、Go、Ruby、C#、Node.js等)。gRPC的核心思想是基于接口定义语言(IDL)来定义服务,在编译期间自动生成具体的服务端和客户端代码。这种设计使得服务端和客户端可以完全专注于业务,而无需关心通信协议的细节。

技术人集结地 5908

C++下的gRPC与protobuf使用和介绍

gRPC 和协议缓冲区简介。本页介绍 gRPC 和协议缓冲区。gRPC 可以使用 协议缓冲区作为其接口定义语言 (IDL) 和基础消息 交换格式。如果你不熟悉 gRPC 和/或协议缓冲区,请阅读此内容!如果只想深入了解并首先查看 gRPC 的运行情况,请选择一种语言并尝试其快速入门。在 gRPC 中,客户端应用程序可以直接调用服务器应用程序上的方法 在不同的计算机上,就好像它是本地对象一样,使您可以更轻松地 创建分布式应用程序和服务。

yiyu20180729的博客 2659

吃透GRPC协议

GRPC协议讲解

wuyouke的专栏 1万+

深入解析grpc源码1-grpc介绍及使用

1grpc 介绍 在grpc 官网上,grpc 是这样介绍的: 开源高性能远程调用(RPC)框架,可以在任何环境中运行。它支持可插拔的负载均衡、链路跟踪、健康检查和身份验证等等商业和安全功能 grpc 是一项进程间通信技术,可以用来连接、调用、操作和调试构建分布式程序,调用过程如同调用函数一样,整个过程操作起来很简单,就像调用本地方法一样。与许多rpc 系统一样,grpc 是定义服务的思想,服务器需要实现此接口并运行grpc 来处理客户端调用。 gRPC客户端和服务器可以在各种环境中运行并相互通

白日梦程序员的博客 2715

gRPC底层揭秘:HTTP/2流控与消息分帧机制详解

在分布式架构与微服务实践中,RPC(远程过程调用)的传输效率是性能瓶颈的关键。HTTP/2作为现代网络协议,通过多路复用与二进制分帧大幅提升了连接利用率。而gRPC选择构建于HTTP/2之上,其流控机制(flow control)与消息分帧规则,决定了高并发场景下的吞吐与内存安全。理解连接级与流级窗口的协同、WINDOW_UPDATE帧的背压作用,以及gRPC独有的5字节消息头与DATA帧的映射关系,能帮助开发者精准定位线上超时与内存堆积问题。本文从传输原理到Java工程调优,结合面试高频问题,拆解流控参数

weixin_31766003的博客 271

gRPC基础讲解

RPC 即远程过程调用协议(Remote Procedure Call Protocol),可以让我们像调用本地函数一样发起远程调用。RPC 凭借其强大的治理功能,成为解决分布式系统通信问题的一大利器。gRPC是一个现代的、高性能、开源的和的通用 RPC 框架(g就是谷歌的意思),基于 HTTP2 协议设计,序列化使用 PB(Protocol Buffer),PB 是一种语言无关的高性能序列化框架,基于 HTTP2+PB 保证了的高性能。上面的图就是一个客户端远程连接一个服务器,调用它的函数。

记录一位C++爱好者的笔记 5245

基于grpc和protobuf的简易计算器

Protocol Buffers(Protobuf)是由Google开发的一种二进制数据序列化格式和通信协议。它通过定义结构化的消息格式,使得数据的存储、传输和交换更为高效。Protobuf通常用于跨语言的数据交换,支持多种编程语言,并广泛应用于网络通信、分布式系统、持久化存储等领域。gRPC(gRPC Remote Procedure Calls)是一个由Google开源的高性能远程过程调用(RPC)框架。

2301_80188590的博客 1054

gRPC详解

gRPC是rpc框架中的一种,是rpc中的大哥是一个高性能,开源和通用的RPC框架,基于Protobuf序列化协议开发,且支持众多开发语言。面向服务端和协议端,基于http/2设计,带来诸如双向流,流控,头部压缩,单TCP连接上的多路复用请求等特性。这些特性使得其在移动设备上表现的更好,更省电和节省空间。在gPRC里客户端可以向调用本地对象一样直接调用另一台不同机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多RPC系统类似,gRPC也是基于以下理念:定义一个服务,指定其能够被远程调用的

IAMZTDSF的博客 4万+

grpc介绍与通信协议详解

grpc是什么grpc有什么有点grpc基于什么。

SHELLCODE_8BIT的博客 5257

告别Keil/IAR:在VSCode中用CMake+Clangd打造丝滑的STM32开发环境(附完整配置)

本文详细介绍了如何在VSCode中使用CMake和Clangd搭建高效的STM32开发环境,替代传统的Keil/IAR工具。通过完整的配置指南和快速开发技巧,帮助开发者实现轻量级、高度可定制的现代化开发流程,显著提升STM32项目的开发效率。

weixin_26843605的博客 164

Python使用gRPC入门,定义proto文件和收发消息

先来看一个非常简单的例子。假设你想定义一个“搜索请求”的消息格式,每一个请求含有一个查询字符串、你感兴趣的查询结果所在的页数,以及每一页多少条查询结果。// 声明使用 proto3 语法// 每个字段都要指定数据类型// 这里的数字2 是标识符,最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]// 这里是注释,使用 //文章的第一行指定了你正在使用 proto3 语法:如果不指定,编译器会使用 proto2。

2230

SAR ADC和sigma delta ADC的区别

0、ADC基本概念 ADC,即analog to digital converter,模拟到数字的转换器,简称模数转换器。 为什么需要ADC?因为现实的世界中,电信号是连续的,例如心电信号,或者声音信号,这些连续的信号被称为“模拟信号”。 图1:心电信号 图2:声音信号 然而这种连续的信号是无法被计算机处理的,计算机只认识0和1组成的“数字信号“。ADC就充当了这样一个桥梁,将“模拟信号”转换为计算机能识别的“数字信号”。举个例子,当前的心电电压为1.2V(实际没有这么高),ADC可能会得

长河柱天的博客 2万+

gRPC-02 gRPC底层原理简单解析

gRPC底层原理简单解析

wanmei002的博客 2099

R 分析裂区试验设计

裂区试验有主区部分和副区部分, 因而有主区部分误差(误差 a,简记为Ea )和副区部分误差(误差b,简记为Eb) 分别用于测验主区处理以及副区处理和主、副互作的显著性。对同一个二因素试验资料作自由度和平方和的分解,则可发现随机区组的误差项自由度和平方和分别为DFe、SSe,而裂区设计有两个误差项,其自由度和平方和分别为DFEa,DFEb, SSEa, SSEb。而区组,处理效应等各个变异项目的自由

samhuairen的专栏 8933

高通camera open代码流程(hal到kernel)

内容:简单介绍Camera open代码调用流程,从HAl到kernel。(api1+hal1) 前言 Android camera api和hal对应关系。安卓在版本更新过程中,camera相关代码也经历了几个版本的更新,表现为camera HAL版本更新(HAL1->HAL2->HAL3),camera API版本更新(camera API1->camera API2),接下...

oxoo123的专栏 4276

gRPC(四)基础:gRPC流

这章的内容需要安装好插件和protoc,建议阅读我的上一篇grpc使用篇点击跳转点击跳转我们先把基础的模板和空定义写出来在进行完善,不太懂的看我上一篇文章。

林钟一的博客 2397

游戏数据训练世界模型:AI物理规律学习与仿真应用实践

世界模型作为理解现实世界动态的AI工具,通过多模态数据学习物理和空间属性来预测未来状态。其核心原理在于从环境交互中提取物理规律,技术价值体现在能够安全、低成本地训练AI系统应对复杂场景。在应用层面,游戏数据因其结构化环境和内置物理引擎,成为世界模型训练的优质素材。游戏世界中的物体交互、运动轨迹等数据天然包含现实物理规律,特别适合训练物理AI。本文以General Intuition公司获得3.2亿美元融资为切入点,深入探讨游戏数据在世界模型训练中的技术实现路径,涵盖数据提取管道构建、多模态数据融合方法,以及

weixin_29056865的博客 366

gRPC基于HTTP/2的消息分帧与流控机制及Java实践

HTTP/2的二进制分帧与多路复用是gRPC实现高性能RPC的基石。在TCP字节流之上,HTTP/2通过9字节帧头定义消息边界,gRPC再以5字节消息头封装业务数据,而流控机制(如WINDOW_UPDATE)则通过连接级与流级窗口协调消费速度,形成背压保护。理解这些底层原理,能帮助Java开发者定位大消息卡顿、流式调用hang住等线上问题。在实际工程中,通过Netty ChannelHandler观测帧序列、调整flowControlWindow参数,可有效优化传输吞吐与稳定性。本文从协议设计讲到Java侧

weixin_32175665的博客 296

带你一分钟了解电感与磁珠的区别

在电路设计中会经常使用到电感和磁珠,虽然在电路中的作用有所不同,但它们都属于磁性电子元器件,本文简单介绍下电感和磁珠的区别电感和磁珠电感和磁珠的结构主要由磁性材料金属线圈和外壳组成,具体结构如下图所示:电感磁珠结构图电感其实是金属线圈缠绕在磁芯上,而磁珠是在内部折叠后外围包裹着铁氧体磁性材料。电感器(Inductor)又称扼流器、电抗器、动态电抗器是储能器件,能够把电能转化为磁能而存储起来的元件。磁珠则是能量转换(消耗)器件,将流过他的高频信号以热能的形式消耗掉。

攻城狮鹏哥 1408

在Allegro设计界面中如何修改封装焊盘

在高密度板的情况下,SUIPLA(支持内层没有的焊盘摘掉)打勾,在高密度板的情况下,内层没有的焊盘摘掉可以增加走线的空间。3、修改完焊盘后,选择File→Update to Design(更新到设计)。然后关闭焊盘修改对话框即可。,将会定期不定期更新技术文章,互相学习交流。或者点击Options选项卡下要修改的焊盘,然后点击Edit修改。在Allegro PCB设计界面中修改封装焊盘的方法。2、然后点击封装上要修改的焊盘,右击选择Edit。修改完成后如下图所示。

bhxc809751502的博客 7806

多 Agent 通信协议选型:gRPC、消息队列还是共享存储

不存在万能协议。关键是根据延迟敏感度 × 耦合容忍度做决策。我的建议:主链路用 gRPC 保证低延迟,异步分发用 NATS 解耦,状态同步走 Redis。三个通道用统一Message格式串联——这样换通道时不需要改消息结构。

2609_95049439的博客 8326

2026工控高薪:C#上位机面试15题(含答案)

基础岗(10-15K):考察串口/Modbus/PLC通信基础;资深岗(20-30K):考察工业级优化(抗干扰/断线重连)、物联网(MQTT)、跨协议整合;架构岗(30K+):考察上位机架构设计、边缘计算、项目落地避坑。我整理了2026年近20场高薪岗位(月薪20K+)的面试高频题,筛选出15道核心题,每题标注“考察点+实战答案”——答案拒绝纯理论,全部结合车间现场、PLC通信、物联网落地等真实场景,帮你避开“背答案但不落地”的面试坑。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1273

gRPC为何选HTTP/2?流控与消息分帧机制深度剖析

在微服务架构中,服务间通信效率直接影响系统整体性能与稳定性。HTTP/2作为现代传输协议,凭借多路复用、二进制分帧和流控制等核心能力,成为gRPC这类高性能RPC框架的理想基石。其连接、流、消息与帧的层级关系,以及连接级与流级双层窗口设计,共同保障了高并发场景下可靠、有序的数据传输。理解这些底层原理,有助于开发者精准定位网络瓶颈,合理配置Netty通道参数,并有效规避大消息导致的背压问题。从协议机制到Java工程实践,深入掌握HTTP/2的流控与分帧细节,是构建高效、健壮微服务系统的关键技能。

weixin_34221654的博客 236

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

gRPC-Java实现

gRPC 简介 gRPC是一个高性能、开源和通用的 RPC 框架,面向HTTP/2设计目前 提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持。 gRPC 基于 HTTP/2 标准设计,带来诸如双...

Kb的博客 1万+

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

grpc框架分析之sprinboot整合grpc

gRPC简介: gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持。 ...

qq_38697245的博客 3877

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

上一篇: RPC简介和grpc的使用
下一篇: go语言 grpc 拦截器
过去日记
过去日记 新星创作者: 后端开发技术领域 新星创作者: 后端开发技术领域
博客等级 码龄3年 5342粉丝 225原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

过去日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值