C++使用gRPC实例

news2024/10/2 18:18:50

什么是gRPC

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

在这里插入图片描述

本文介绍如何使用C++利用gRPC来编写client和server的代码。

rpc范例的一般编写流程

  1. 编写proto文件
  2. 根据proto文件生成对应的.cc和.h文件
  3. server程序继承service
  4. client程序使用stub
  5. 编译server和client程序

proto文件编写

syntax = "proto3";
package IM.Login; //生成.h文件后变为IM::Login namespace

//定义服务
service ImLogin {
    //定义服务函数
    rpc Regist (IMRegisterReq) returns (IMRegisterRes) {}
    rpc Login (IMLoginReq) returns (IMLoginRes) {}
}

//注册账号,根据这个生成一个C++类,并自动提供一些修改和获取成员的方法
message IMRegisterReq {
    string user_name = 1;
    string password = 2;
}

message IMRegisterRes {
    string user_name = 1;
    uint32 user_id = 2;
    uint32 result_code = 3;
}

message IMLoginReq {
    string user_name = 1;
    string password = 2;
}

message IMLoginRes {
    uint32 user_id = 1;
    uint32 result_code = 2;
}

输入如下指令生成protobuf序列化后的代码 IM.Login.pb.cc和IM.Login.pb.h

jyhlinux@ubuntu:~/grpc-v1.45.2/examples/cpp/im_login$ protoc --cpp_out=. IM.Login.proto
// $SRC_DIR: .proto 所在的源目录
// --cpp_out: 生成 c++ 代码
// $DST_DIR: 生成代码的目标目录
// xxx.proto: 要针对哪个 proto 文件生成接口代码
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/xxx.proto

生成的.cc文件和.h文件的类型是什么样呢?举个例子,比如下方proto代码

message IMLoginReq {
    string user_name = 1;
    string password = 2;
}

生成类似于如下的C++代码:IM.Login.pb.h(不完全)

class IMLoginReq final {

    // string user_name = 1;
    void clear_user_name();
    void set_user_name(ArgT0&& arg0, ArgT... args);

    // string password = 2;
    void clear_password();
	void set_user_name(ArgT0&& arg0, ArgT... args);
	
};

输入下方指令生成服务框架代码

jyhlinux@ubuntu:~/grpc-v1.45.2/examples/cpp/im_login$ protoc --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=/usr/local/bin/grpc_cpp_plugin IM.Login.proto

生成服务框架的代码,在当前目录下生成 simple.grpc.pb.h 和 simple.grpc.pb.cc 文件

grpc server端代码编写

注意要引入正确的命名空间和头文件

#include <iostream>
#include <string>
#include <memory>

//grpc头文件
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>

#include "IM.Login.grpc.pb.h"
#include "IM.Login.pb.h"

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;

using IM::Login::ImLogin;
using IM::Login::IMRegisterReq;
using IM::Login::IMRegisterRes;
using IM::Login::IMLoginReq;
using IM::Login::IMLoginRes;

class IMLoginServiceImpl : public ImLogin::Service{
    virtual Status Regist(ServerContext* context, const IMRegisterReq* request, IMRegisterRes* response) override {
        std::cout<<"Regist user_name:" << request->user_name() << std::endl;
        response->set_user_name(request->user_name()); // 这也是插件自动生成的函数 set_属性名()
        response->set_user_id(10);
        response->set_result_code(0);

        return Status::OK;
    }
    virtual Status Login(ServerContext* context, const IMLoginReq* request, IMLoginRes* response) override {
        std::cout << "Login user_name: " << request->user_name() << std::endl;
        response->set_user_id(10);
        response->set_result_code(0);

        return Status::OK;
    }
};

void RunServer() {
    std::string server_addr("0.0.0.0:50001");

    //服务类实例
    IMLoginServiceImpl service;

    ServerBuilder builder;

    //第二参数: The credentials associated with the server.
    builder.AddListeningPort(server_addr, grpc::InsecureServerCredentials());
    builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIME_MS, 5000);
    builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 10000);
    //是否允许在没有任何未完成流的情况下发送保持连接的ping包
    builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1);
    builder.RegisterService(&service);

    //创建启动
    std::unique_ptr<Server> server(builder.BuildAndStart());
    std::cout << "Server listening on " << server_addr << std::endl;
 
    server->Wait();
}

int main(int argc, const char** argv) {

    RunServer();
    return 0;
}

grpc client端

#include <iostream>
#include <string>
#include <memory>

//grpc头文件
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>

#include "IM.Login.grpc.pb.h"
#include "IM.Login.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;

using IM::Login::ImLogin;
using IM::Login::IMRegisterReq;
using IM::Login::IMRegisterRes;
using IM::Login::IMLoginReq;
using IM::Login::IMLoginRes;

class ImLoginClient{
public:
    ImLoginClient(std::shared_ptr<Channel> channel)
        :stub_(ImLogin::NewStub(channel)){}

    void Regist(const std::string &user_name, const std::string &password) {
        IMRegisterReq request;
        request.set_user_name(user_name);
        request.set_password(password);

        IMRegisterRes response;
        ClientContext context;
        std::cout << "-> Regist req" << std::endl; 
        Status status = stub_->Regist(&context, request, &response);

        if(status.ok()) {
            std::cout << "user_name: " << response.user_name() << ", user_id: " << response.user_id() << std::endl;
        } else {
            std::cout << "user_name: " << response.user_name() << ",Regist failed: " << response.result_code() << std::endl;
        }
    }

    void Login(const std::string &user_name, const std::string &password) {
        IMLoginReq request;
        request.set_user_name(user_name);
        request.set_password(password);

        IMLoginRes response;
        ClientContext context;
        std::cout << "-> Login req" << std::endl; 
        Status status = stub_->Login(&context, request, &response);

        if(status.ok()) {
            std::cout << "user_id: " << response.user_id() << ", login ok" << std::endl;
        } else {
            std::cout << "user_name: " << request.user_name() << ",Login failed: " << response.result_code() << std::endl;
        }
    }

private:
    std::unique_ptr<ImLogin::Stub> stub_;
};

int main() {

    std::string server_addr = "localhost:50001";
    ImLoginClient im_login_client(
        grpc::CreateChannel(server_addr, grpc::InsecureChannelCredentials())
    );
    std::string user_name = "jyh";
    std::string password = "123456";

    im_login_client.Regist(user_name, password);
    im_login_client.Login(user_name, password);
    return 0;
}

异步gRPC server端代码编写

#include <iostream>
#include <string>
#include <thread>
#include <memory>

#include "IM.Login.grpc.pb.h"
#include "IM.Login.pb.h"
#include <grpc/support/log.h>
#include <grpcpp/grpcpp.h>

using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerCompletionQueue; // 
using grpc::ServerContext;
using grpc::Status;

using IM::Login::ImLogin;
using IM::Login::IMRegisterReq;
using IM::Login::IMRegisterRes;
using IM::Login::IMLoginReq;
using IM::Login::IMLoginRes;

class ServerImpl final {
public:
    ~ServerImpl() {
        server_->Shutdown();
        cq_->Shutdown();
    }
    
    void Run() {
        std::string server_address("0.0.0.0:50001");

        //成员初始化
        ServerBuilder builder;
        builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
        builder.RegisterService(&service_); // 初始化service_
        cq_ = builder.AddCompletionQueue();
        server_ = builder.BuildAndStart(); // 
        std::cout << "Server listening on " << server_address << std::endl;

        //Proceed to the server's main loop
        HandleRpcs();
    }

private:

    class CallData{
    public:
        CallData(ImLogin::AsyncService* service, ServerCompletionQueue* cq)
            :service_(service), cq_(cq), status_(CREATE){
            std::cout << "CallData constructing, this:" << this << std::endl;
            Proceed();
        }
        virtual ~CallData(){}
        virtual void Proceed() {
            return;
        }
        //与异步服务器的 gRPC 运行时进行通信的方式。
        ImLogin::AsyncService* service_;

        ServerCompletionQueue *cq_;

        ServerContext ctx_;

        enum CallStatus{CREATE, PROCESS, FINISH};

        CallStatus status_; //current serving state
    };

    class RegistCallData : public CallData {
    public:
        RegistCallData(ImLogin::AsyncService* service, ServerCompletionQueue* cq)
            :CallData(service, cq), responder_(&ctx_){
            Proceed();
        }

        ~RegistCallData(){}

        void Proceed() override {
            std::cout << "this: " << this 
                    <<" RegistCallData Proceed(), status : " << status_
                    << std::endl;
            //状态机
            if(status_ == CREATE) {
                std::cout << "this: " << this 
                    <<" RegistCallData Proceed(), status : " << "CREATE"
                    << std::endl;

                status_ = PROCESS; // 

                //this唯一标识这个request(故不同实例可并发的服务不同的requests)
                service_->RequestRegist(&ctx_, &request_, &responder_, cq_, cq_,this);
            } else if(status_ == PROCESS) {
                std::cout << "this: " << this <<" RegistCallData Proceed(), status : " << "PROCESS"<< std::endl;

                new RegistCallData(service_, cq_); //1. 创建处理逻辑

                reply_.set_user_name(request_.user_name());
                reply_.set_user_id(10);
                reply_.set_result_code(0);

                status_ = FINISH;
                responder_.Finish(reply_, Status::OK, this);
            } else  {
                std::cout << "this: " << this <<" RegistCallData Proceed(), status : " << "FINISH"<< std::endl;
                GPR_ASSERT(status_ == FINISH);
                delete this;
            }
        }

    private:
        IMRegisterReq request_;
        IMRegisterRes reply_;
        ServerAsyncResponseWriter<IMRegisterRes> responder_;
    };

    class LoginCallData : public CallData {
    public:
        LoginCallData(ImLogin::AsyncService* service, ServerCompletionQueue* cq)
            :CallData(service, cq), responder_(&ctx_){
            Proceed();
        }

        ~LoginCallData(){}

        void Proceed() override {
            std::cout << "this: " << this 
                    <<" LoginCallData Proceed(), status : " << status_
                    << std::endl;
            //状态机
            if(status_ == CREATE) {
                std::cout << "this: " << this 
                    <<" LoginCallData Proceed(), status : " << "CREATE"
                    << std::endl;

                status_ = PROCESS; // 

                //this唯一标识这个request(故不同实例可并发的服务不同的requests)
                service_->RequestLogin(&ctx_, &request_, &responder_, cq_, cq_,this);
            } else if(status_ == PROCESS) {
                std::cout << "this: " << this <<" LoginCallData Proceed(), status : " << "PROCESS"<< std::endl;

                new LoginCallData(service_, cq_); //1. 创建处理逻辑

                reply_.set_user_id(10);
                reply_.set_result_code(0);

                status_ = FINISH;
                responder_.Finish(reply_, Status::OK, this);
            } else  {
                std::cout << "this: " << this <<" LoginCallData Proceed(), status : " << "FINISH"<< std::endl;
                GPR_ASSERT(status_ == FINISH);
                delete this;
            }
        }

    private:
        IMLoginReq request_;
        IMLoginRes reply_;
        ServerAsyncResponseWriter<IMLoginRes> responder_;
    };

    void HandleRpcs() { // 可运行在多线程
        new RegistCallData(&service_, cq_.get()); //
        new LoginCallData(&service_, cq_.get());

        void* tag;
        bool ok;

        while(true) {
            std::cout << "before cq_->Next "<< std::endl;
            // Read from the queue, blocking until an event is available or the queue is
            // shutting down.
            GPR_ASSERT(cq_->Next(&tag, &ok));
            std::cout << "after cq_->Next " << std::endl;          
            GPR_ASSERT(ok);

            std::cout << "before static_cast" << std::endl;
            static_cast<CallData*>(tag)->Proceed();
            std::cout << "after static_cast" << std::endl;

        }
    }

    std::unique_ptr<ServerCompletionQueue> cq_;
    ImLogin::AsyncService service_; //与异步服务器的 gRPC 运行时进行通信的方式
    std::unique_ptr<Server> server_;
};

int main() {
    ServerImpl server;
    server.Run();    
    return 0;
}

CMakeLists文件编写

这个CMakeLists.txt是从/grpc-v1.45.2/examples/cpp/helloworld中拷贝出来进行修改的,

这个CMakeLists.txt文件放在稍后会创建的build目录下(examples/cpp/im_login/build/

#修改的地方用changed标记

cmake_minimum_required(VERSION 3.5.1)

project(IMLogin C CXX)  #changed

include(../cmake/common.cmake)

# Proto file
get_filename_component(im_proto "./IM.Login.proto" ABSOLUTE) #changed
get_filename_component(im_proto_path "${im_proto}" PATH) #changed

# Generated sources
set(im_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/IM.Login.pb.cc") #changed
set(im_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/IM.Login.pb.h") #changed
set(im_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/IM.Login.grpc.pb.cc") #changed
set(im_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/IM.Login.grpc.pb.h") #changed
add_custom_command( #changed
      OUTPUT "${im_proto_srcs}" "${im_proto_hdrs}" "${im_grpc_srcs}" "${im_grpc_hdrs}"
      COMMAND ${_PROTOBUF_PROTOC}
      ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
        --cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
        -I "${im_proto_path}"
        --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
        "${im_proto}"
      DEPENDS "${im_proto}")

# Include generated *.pb.h files
include_directories("${CMAKE_CURRENT_BINARY_DIR}")

# im_grpc_proto
#changed
add_library(im_grpc_proto
  ${im_grpc_srcs}
  ${im_grpc_hdrs}
  ${im_proto_srcs}
  ${im_proto_hdrs})
target_link_libraries(im_grpc_proto
  ${_REFLECTION}
  ${_GRPC_GRPCPP}
  ${_PROTOBUF_LIBPROTOBUF})

# Targets greeter_[async_](client|server)
foreach(_target
  client server async_server #生成的可执行程序名
  )
  add_executable(${_target} "${_target}.cc")
  target_link_libraries(${_target}
    im_grpc_proto
    ${_REFLECTION}
    ${_GRPC_GRPCPP}
    ${_PROTOBUF_LIBPROTOBUF})
endforeach()

编译测试

cd ~/grpc-v1.45.2/examples/cpp/im_login/
mkdir build 
cmake ..
make

进行测试1

#终端1执行
./server
#终端2执行
./client

在这里插入图片描述

在这里插入图片描述

进行测试2

#终端1执行
./async_server
#终端2执行
./client

测试结果:

服务端

jyhlinux@ubuntu:~/grpc-v1.45.2/examples/cpp/im_login/build$ ./async_server 
Server listening on 0.0.0.0:50001
CallData constructing, this:0x55a0d9d11c50
this: 0x55a0d9d11c50 RegistCallData Proceed(), status : 0
this: 0x55a0d9d11c50 RegistCallData Proceed(), status : CREATE
CallData constructing, this:0x55a0d9d12420
this: 0x55a0d9d12420 LoginCallData Proceed(), status : 0
this: 0x55a0d9d12420 LoginCallData Proceed(), status : CREATE
before cq_->Next 
after cq_->Next 
before static_cast
this: 0x55a0d9d11c50 RegistCallData Proceed(), status : 1
this: 0x55a0d9d11c50 RegistCallData Proceed(), status : PROCESS
CallData constructing, this:0x55a0d9d28620
this: 0x55a0d9d28620 RegistCallData Proceed(), status : 0
this: 0x55a0d9d28620 RegistCallData Proceed(), status : CREATE
after static_cast
before cq_->Next 
after cq_->Next 
before static_cast
this: 0x55a0d9d11c50 RegistCallData Proceed(), status : 2
this: 0x55a0d9d11c50 RegistCallData Proceed(), status : FINISH
after static_cast
before cq_->Next 
after cq_->Next 
before static_cast
this: 0x55a0d9d12420 LoginCallData Proceed(), status : 1
this: 0x55a0d9d12420 LoginCallData Proceed(), status : PROCESS
CallData constructing, this:0x55a0d9d11c50
this: 0x55a0d9d11c50 LoginCallData Proceed(), status : 0
this: 0x55a0d9d11c50 LoginCallData Proceed(), status : CREATE
after static_cast
before cq_->Next 
after cq_->Next 
before static_cast
this: 0x55a0d9d12420 LoginCallData Proceed(), status : 2
this: 0x55a0d9d12420 LoginCallData Proceed(), status : FINISH
after static_cast
before cq_->Next 

客户端

jyhlinux@ubuntu:~/grpc-v1.45.2/examples/cpp/im_login/build$ ./client 
-> Regist req
user_name: jyh, user_id: 10
-> Login req
user_id: 10, login ok

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/54327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python数据分析实战-实现一维列表(数组)和多维列表(数组)的相互转化(附源码和实现效果)

前面我介绍了可视化的一些方法以及机器学习在预测方面的应用&#xff0c;分为分类问题&#xff08;预测值是离散型&#xff09;和回归问题&#xff08;预测值是连续型&#xff09;&#xff08;具体见之前的文章&#xff09;。 从本期开始&#xff0c;我将做一个数据分析类实战…

[附源码]计算机毕业设计springboot校园疫情管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

文件的上传与下载

文章目录一、 实验目的&#xff1a;二、实验要求&#xff1a;三、实验内容&#xff1a;1、单文件上传2、多文件上传3、上传文件的大小限定4、实现文件的下载功能&#xff0c;并解决下载乱码问题一、 实验目的&#xff1a; 掌握Servlet的HttpServletRequest对文件上传的支持&am…

内存管理---分页机制

目录 物理内存管理带来的问题 直接映射 一级页表 二级页表 参考&#xff1a; &#xff08;C语言内存七&#xff09;分页机制究竟是如何实现的&#xff1f; - Smah - 博客园 物理内存管理带来的问题 比如4GB的flash, 如果应用程序可直接访问物理内存&#xff0c;那么可能一个…

云小课|基于华为云WAF的日志运维分析,构筑设备安全的城墙

阅识风云是华为云信息大咖&#xff0c;擅长将复杂信息多元化呈现&#xff0c;其出品的一张图(云图说)、深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云。更多精彩内容请单击此处。 摘要&#xff1a;云日志服务用于收集来自主机和云服务的日志数据&#x…

windows安装docker版青龙面板

1.下载docker Docker Desktop 官方下载地址&#xff1a; windows docker 2 管理员运行PowerShell&#xff0c;执行下面的命令(Hyper-V 和容器特性) Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All3.运行第一步下载好的exe 双击下载的 Docker fo…

springboot基于vue.js的掌上博客系统的设计与实现毕业设计源码063131

Springboot掌上博客系统的设计与实现 摘 要 掌上博客系统是当今网络的热点&#xff0c;博客技术的出现使得每个人可以零成本、零维护地创建自己的网络媒体&#xff0c;Blog站点所形成的网状结构促成了不同于以往社区的Blog文化&#xff0c;Blog技术缔造了“博客”文化。 本文课…

恶补了 Python 装饰器的八种写法,你随便问~

对于每一个学习 Python 的同学&#xff0c;想必对 符号一定不陌生了&#xff0c;正如你所知&#xff0c; 符号是装饰器的语法糖&#xff0c;符号后面的函数就是我们本文的主角&#xff1a;装饰器。 装饰器放在一个函数开始定义的地方&#xff0c;它就像一顶帽子一样戴在这个…

Java---File详解

目录 一、File的概述 二、File的创建 三、File的常见成员方法 1&#xff1a;判断和获取 2&#xff1a;创建和删除 &#xff08;1&#xff09;createNewFile() &#xff08;2&#xff09;mkdir() &#xff08;3&#xff09;delete&#xff08;&#xff09; 3&#xff1a…

高新技术企业认定条件

主要是评估企业的以下几个条件&#xff1a; 一是企业成立满一年以上&#xff0c;并且符合高企八大领域&#xff1b; 二是拥有核心自主知识产权&#xff0c;并且近一年高新产品的收入啊&#xff0c;需要占总收入的60%以上&#xff1b; 三是科技人员占当年职工总数的比例不低于…

mybatis学习:四、关联查询、缓存

7. 关联查询 7.1 准备工作: 数据库表: #订单表&#xff1a; create table tb_order (id INT AUTO_INCREMENT PRIMARY KEY,userid INT,createtime DATETIME,state VARCHAR(20) ) ENGINEInnoDB AUTO_INCREMENT1;#订单详情表 CREATE TABLE tb_orderdetail(…

[附源码]Python计算机毕业设计SSM酒店式公寓服务系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[LeetCode 1769]移动所有球到每个盒子所需的最小操作数

题目描述 题目链接&#xff1a;[LeetCode 1769]移动所有球到每个盒子所需的最小操作数 有 n 个盒子。给你一个长度为 n 的二进制字符串 boxes &#xff0c;其中 boxes[i] 的值为 ‘0’ 表示第 i 个盒子是 空 的&#xff0c;而 boxes[i] 的值为 ‘1’ 表示盒子里有 一个 小球。…

Gradle简单配置

写在前面&#xff1a;一开始配置gradle的时候&#xff0c;最好不要把他maven仓库配置在一起&#xff0c;前段时间配置公司项目身心俱疲。大部分公司里面用gradle都是配置的自己的仓库地址&#xff0c;所以有的东西就不要和网上的配置保持一致了&#xff0c;比如说这个仓库地址。…

网站都变成灰色了,它是怎么实现的?

大家好&#xff0c;我是二哥呀。 想必大家都感受到了&#xff0c;很多网站、APP 在昨天都变灰了。 先来感受一下变灰后的效果。 这种灰色的效果怎么实现的呢&#xff1f;如何做到图片、文字、按钮都变灰的效果呢&#xff1f; 方案 1&#xff0c;换一套灰色的 UI&#xff0c;…

Github限时开源24小时,Alibaba架构师内部最新发布SpringCloud开发手册

前言 微服务是SOA之后越来越流行的体系结构模式之一&#xff01; 如果您关注行业趋势&#xff0c;就会发现,如今商业机构不再像几年前那样&#xff0c;开发大型应用程序&#xff0c;来管理端到端之间的业务功能&#xff0c;而是选择快速灵活的微服务。 ​ 通过微服务&#x…

Unity 之 后处理实现界面灰度效果(PostProcessing实现 | Shader实现)

Unity 之 后处理实现界面灰度效果前言一&#xff0c;Post Processing1.1 设置组件1.2 代码控制二&#xff0c;Shader材质实现2.1 原理API2.2 编写Shader2.3 编写代码2.4 实现效果效果展示前言 在Unity中实现后处理效果有两种方式&#xff1a;一种是通过使用Unity官方提供的Pos…

H3C AC三层组网架构,AP自动上线自动固化

核心交换机: [HX]dis cu [HX]dis current-configuration version 7.1.075, Alpha 7571 sysname HX irf mac-address persistent timer irf auto-update enable undo irf link-delay irf member 1 priority 1 dhcp enable lldp global enable system-working-mode standard xbar…

[附源码]计算机毕业设计仓库管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

C#连接MySql数据库

C#连接MySql数据库 具体步骤如下 第一步&#xff1a;提前将mysql服务打开&#xff0c;用xampp的需要将Mysql服务开启&#xff1b; 第二步&#xff1a;新建一个C#项目 第三步&#xff1a;在项目中下载&#xff1a;MySql.Data的NuGet&#xff0c;作者为Oracle&#xff1b; 第四布…