初探grpc-web(服务端go)_grpcwebproxy-程序员宅基地

技术标签: grpc-web  node.js  javascript  

从 https://github.com/improbable-eng/grpc-web/releases/tag/v0.13.0 按操作系统选择下载,如:grpcwebproxy-v0.13.0-win64.exe.zip 。

下载完成后把 grpcwebproxy.exe 放到自建的文件夹,并添加到环境变量的path中。

使用grpcwebproxy代理服务,命令为:
示例:grpcwebproxy --allow_all_origins --backend_addr=localhost:50051 --run_tls_server=false --server_http_debug_port=5005
本地实际为:
grpcwebproxy --allow_all_origins --backend_addr=192.168.1.103:8090 --run_tls_server=false --server_http_debug_port=5005

其中用到的参数说明:
在这里插入图片描述
使用命令把当前文件夹下面所有的.proto文件转成XXX_pb.js和XXX_grpc_web_pb.js,命令为:
protoc --js_out=import_style=commonjs:. --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. ./*.proto

hello.proto 文件内容为:

syntax = "proto3";

package hello;

option go_package=".;hello";

// 请求参数
message HelloRequest {
    
    string name = 1;
}

// 响应消息
message HelloInfo {
    
    string hello_data = 1;
}

//服务定义
service HelloService {
    
    rpc Hello (HelloRequest) returns (HelloInfo);
}

转成 XXX_pb.js和XXX_grpc_web_pb.js:

  1. hello_grpc_web_pb.js:
/**
 * @fileoverview gRPC-Web generated client stub for hello
 * @enhanceable
 * @public
 */

// GENERATED CODE -- DO NOT EDIT!


/* eslint-disable */
// @ts-nocheck



const grpc = {
    };
grpc.web = require('grpc-web');

const proto = {
    };
proto.hello = require('./hello_pb.js');

/**
 * @param {string} hostname
 * @param {?Object} credentials
 * @param {?Object} options
 * @constructor
 * @struct
 * @final
 */
proto.hello.HelloServiceClient =
    function(hostname, credentials, options) {
    
  if (!options) options = {
    };
  options['format'] = 'text';

  /**
   * @private @const {!grpc.web.GrpcWebClientBase} The client
   */
  this.client_ = new grpc.web.GrpcWebClientBase(options);

  /**
   * @private @const {string} The hostname
   */
  this.hostname_ = hostname;

};


/**
 * @param {string} hostname
 * @param {?Object} credentials
 * @param {?Object} options
 * @constructor
 * @struct
 * @final
 */
proto.hello.HelloServicePromiseClient =
    function(hostname, credentials, options) {
    
  if (!options) options = {
    };
  options['format'] = 'text';

  /**
   * @private @const {!grpc.web.GrpcWebClientBase} The client
   */
  this.client_ = new grpc.web.GrpcWebClientBase(options);

  /**
   * @private @const {string} The hostname
   */
  this.hostname_ = hostname;

};


/**
 * @const
 * @type {!grpc.web.MethodDescriptor<
 *   !proto.hello.HelloRequest,
 *   !proto.hello.HelloInfo>}
 */
const methodDescriptor_HelloService_Hello = new grpc.web.MethodDescriptor(
  '/hello.HelloService/Hello',
  grpc.web.MethodType.UNARY,
  proto.hello.HelloRequest,
  proto.hello.HelloInfo,
  /**
   * @param {!proto.hello.HelloRequest} request
   * @return {!Uint8Array}
   */
  function(request) {
    
    return request.serializeBinary();
  },
  proto.hello.HelloInfo.deserializeBinary
);


/**
 * @const
 * @type {!grpc.web.AbstractClientBase.MethodInfo<
 *   !proto.hello.HelloRequest,
 *   !proto.hello.HelloInfo>}
 */
const methodInfo_HelloService_Hello = new grpc.web.AbstractClientBase.MethodInfo(
  proto.hello.HelloInfo,
  /**
   * @param {!proto.hello.HelloRequest} request
   * @return {!Uint8Array}
   */
  function(request) {
    
    return request.serializeBinary();
  },
  proto.hello.HelloInfo.deserializeBinary
);


/**
 * @param {!proto.hello.HelloRequest} request The
 *     request proto
 * @param {?Object<string, string>} metadata User defined
 *     call metadata
 * @param {function(?grpc.web.Error, ?proto.hello.HelloInfo)}
 *     callback The callback function(error, response)
 * @return {!grpc.web.ClientReadableStream<!proto.hello.HelloInfo>|undefined}
 *     The XHR Node Readable Stream
 */
proto.hello.HelloServiceClient.prototype.hello =
    function(request, metadata, callback) {
    
  return this.client_.rpcCall(this.hostname_ +
      '/hello.HelloService/Hello',
      request,
      metadata || {
    },
      methodDescriptor_HelloService_Hello,
      callback);
};


/**
 * @param {!proto.hello.HelloRequest} request The
 *     request proto
 * @param {?Object<string, string>} metadata User defined
 *     call metadata
 * @return {!Promise<!proto.hello.HelloInfo>}
 *     Promise that resolves to the response
 */
proto.hello.HelloServicePromiseClient.prototype.hello =
    function(request, metadata) {
    
  return this.client_.unaryCall(this.hostname_ +
      '/hello.HelloService/Hello',
      request,
      metadata || {
    },
      methodDescriptor_HelloService_Hello);
};


module.exports = proto.hello;


  1. hello_pb.js:
// source: hello.proto
/**
 * @fileoverview
 * @enhanceable
 * @suppress {missingRequire} reports error on implicit type usages.
 * @suppress {messageConventions} JS Compiler reports an error if a variable or
 *     field starts with 'MSG_' and isn't a translatable message.
 * @public
 */
// GENERATED CODE -- DO NOT EDIT!
/* eslint-disable */
// @ts-nocheck

var jspb = require('google-protobuf');
var goog = jspb;
var global = Function('return this')();

goog.exportSymbol('proto.hello.HelloInfo', null, global);
goog.exportSymbol('proto.hello.HelloRequest', null, global);
/**
 * Generated by JsPbCodeGenerator.
 * @param {Array=} opt_data Optional initial data array, typically from a
 * server response, or constructed directly in Javascript. The array is used
 * in place and becomes part of the constructed object. It is not cloned.
 * If no data is provided, the constructed object will be empty, but still
 * valid.
 * @extends {jspb.Message}
 * @constructor
 */
proto.hello.HelloRequest = function(opt_data) {
    
  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.hello.HelloRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
    
  /**
   * @public
   * @override
   */
  proto.hello.HelloRequest.displayName = 'proto.hello.HelloRequest';
}
/**
 * Generated by JsPbCodeGenerator.
 * @param {Array=} opt_data Optional initial data array, typically from a
 * server response, or constructed directly in Javascript. The array is used
 * in place and becomes part of the constructed object. It is not cloned.
 * If no data is provided, the constructed object will be empty, but still
 * valid.
 * @extends {jspb.Message}
 * @constructor
 */
proto.hello.HelloInfo = function(opt_data) {
    
  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.hello.HelloInfo, jspb.Message);
if (goog.DEBUG && !COMPILED) {
    
  /**
   * @public
   * @override
   */
  proto.hello.HelloInfo.displayName = 'proto.hello.HelloInfo';
}



if (jspb.Message.GENERATE_TO_OBJECT) {
    
/**
 * Creates an object representation of this proto.
 * Field names that are reserved in JavaScript and will be renamed to pb_name.
 * Optional fields that are not set will be set to undefined.
 * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
 * For the list of reserved names please see:
 *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
 * @param {boolean=} opt_includeInstance Deprecated. whether to include the
 *     JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @return {!Object}
 */
proto.hello.HelloRequest.prototype.toObject = function(opt_includeInstance) {
    
  return proto.hello.HelloRequest.toObject(opt_includeInstance, this);
};


/**
 * Static version of the {@see toObject} method.
 * @param {boolean|undefined} includeInstance Deprecated. Whether to include
 *     the JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @param {!proto.hello.HelloRequest} msg The msg instance to transform.
 * @return {!Object}
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.hello.HelloRequest.toObject = function(includeInstance, msg) {
    
  var f, obj = {
    
    name: jspb.Message.getFieldWithDefault(msg, 1, "")
  };

  if (includeInstance) {
    
    obj.$jspbMessageInstance = msg;
  }
  return obj;
};
}


/**
 * Deserializes binary data (in protobuf wire format).
 * @param {jspb.ByteSource} bytes The bytes to deserialize.
 * @return {!proto.hello.HelloRequest}
 */
proto.hello.HelloRequest.deserializeBinary = function(bytes) {
    
  var reader = new jspb.BinaryReader(bytes);
  var msg = new proto.hello.HelloRequest;
  return proto.hello.HelloRequest.deserializeBinaryFromReader(msg, reader);
};


/**
 * Deserializes binary data (in protobuf wire format) from the
 * given reader into the given message object.
 * @param {!proto.hello.HelloRequest} msg The message object to deserialize into.
 * @param {!jspb.BinaryReader} reader The BinaryReader to use.
 * @return {!proto.hello.HelloRequest}
 */
proto.hello.HelloRequest.deserializeBinaryFromReader = function(msg, reader) {
    
  while (reader.nextField()) {
    
    if (reader.isEndGroup()) {
    
      break;
    }
    var field = reader.getFieldNumber();
    switch (field) {
    
    case 1:
      var value = /** @type {string} */ (reader.readString());
      msg.setName(value);
      break;
    default:
      reader.skipField();
      break;
    }
  }
  return msg;
};


/**
 * Serializes the message to binary data (in protobuf wire format).
 * @return {!Uint8Array}
 */
proto.hello.HelloRequest.prototype.serializeBinary = function() {
    
  var writer = new jspb.BinaryWriter();
  proto.hello.HelloRequest.serializeBinaryToWriter(this, writer);
  return writer.getResultBuffer();
};


/**
 * Serializes the given message to binary data (in protobuf wire
 * format), writing to the given BinaryWriter.
 * @param {!proto.hello.HelloRequest} message
 * @param {!jspb.BinaryWriter} writer
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.hello.HelloRequest.serializeBinaryToWriter = function(message, writer) {
    
  var f = undefined;
  f = message.getName();
  if (f.length > 0) {
    
    writer.writeString(
      1,
      f
    );
  }
};


/**
 * optional string name = 1;
 * @return {string}
 */
proto.hello.HelloRequest.prototype.getName = function() {
    
  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};


/**
 * @param {string} value
 * @return {!proto.hello.HelloRequest} returns this
 */
proto.hello.HelloRequest.prototype.setName = function(value) {
    
  return jspb.Message.setProto3StringField(this, 1, value);
};





if (jspb.Message.GENERATE_TO_OBJECT) {
    
/**
 * Creates an object representation of this proto.
 * Field names that are reserved in JavaScript and will be renamed to pb_name.
 * Optional fields that are not set will be set to undefined.
 * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
 * For the list of reserved names please see:
 *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
 * @param {boolean=} opt_includeInstance Deprecated. whether to include the
 *     JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @return {!Object}
 */
proto.hello.HelloInfo.prototype.toObject = function(opt_includeInstance) {
    
  return proto.hello.HelloInfo.toObject(opt_includeInstance, this);
};


/**
 * Static version of the {@see toObject} method.
 * @param {boolean|undefined} includeInstance Deprecated. Whether to include
 *     the JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @param {!proto.hello.HelloInfo} msg The msg instance to transform.
 * @return {!Object}
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.hello.HelloInfo.toObject = function(includeInstance, msg) {
    
  var f, obj = {
    
    helloData: jspb.Message.getFieldWithDefault(msg, 1, "")
  };

  if (includeInstance) {
    
    obj.$jspbMessageInstance = msg;
  }
  return obj;
};
}


/**
 * Deserializes binary data (in protobuf wire format).
 * @param {jspb.ByteSource} bytes The bytes to deserialize.
 * @return {!proto.hello.HelloInfo}
 */
proto.hello.HelloInfo.deserializeBinary = function(bytes) {
    
  var reader = new jspb.BinaryReader(bytes);
  var msg = new proto.hello.HelloInfo;
  return proto.hello.HelloInfo.deserializeBinaryFromReader(msg, reader);
};


/**
 * Deserializes binary data (in protobuf wire format) from the
 * given reader into the given message object.
 * @param {!proto.hello.HelloInfo} msg The message object to deserialize into.
 * @param {!jspb.BinaryReader} reader The BinaryReader to use.
 * @return {!proto.hello.HelloInfo}
 */
proto.hello.HelloInfo.deserializeBinaryFromReader = function(msg, reader) {
    
  while (reader.nextField()) {
    
    if (reader.isEndGroup()) {
    
      break;
    }
    var field = reader.getFieldNumber();
    switch (field) {
    
    case 1:
      var value = /** @type {string} */ (reader.readString());
      msg.setHelloData(value);
      break;
    default:
      reader.skipField();
      break;
    }
  }
  return msg;
};


/**
 * Serializes the message to binary data (in protobuf wire format).
 * @return {!Uint8Array}
 */
proto.hello.HelloInfo.prototype.serializeBinary = function() {
    
  var writer = new jspb.BinaryWriter();
  proto.hello.HelloInfo.serializeBinaryToWriter(this, writer);
  return writer.getResultBuffer();
};


/**
 * Serializes the given message to binary data (in protobuf wire
 * format), writing to the given BinaryWriter.
 * @param {!proto.hello.HelloInfo} message
 * @param {!jspb.BinaryWriter} writer
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.hello.HelloInfo.serializeBinaryToWriter = function(message, writer) {
    
  var f = undefined;
  f = message.getHelloData();
  if (f.length > 0) {
    
    writer.writeString(
      1,
      f
    );
  }
};


/**
 * optional string hello_data = 1;
 * @return {string}
 */
proto.hello.HelloInfo.prototype.getHelloData = function() {
    
  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};


/**
 * @param {string} value
 * @return {!proto.hello.HelloInfo} returns this
 */
proto.hello.HelloInfo.prototype.setHelloData = function(value) {
    
  return jspb.Message.setProto3StringField(this, 1, value);
};


goog.object.extend(exports, proto.hello);

编写client.js(只先执行一个hello方法):

var {
    
  HelloServiceClient
} =require('./hello_grpc_web_pb');

var {
     HelloRequest } =require('./hello_pb');

let client = new HelloServiceClient('http://localhost:5005');

let helloRequest = new HelloRequest();
helloRequest.setName('tom');

client.hello(helloRequest, {
    }, (err, response) => {
    
  console.log(err, response);
});

打包client.js放到dist/main.js并在index.html头部引入:
npx webpack client.js

运行index.html:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
go写的服务端:

package main

import (
	"context"
	"fmt"
	"golang-grpc-demo/hello"
	"net"

	"google.golang.org/grpc"
)

// HelloServerImpl 定义hello接口实现
type HelloServerImpl struct {
    
	hello.UnimplementedHelloServiceServer
}

// Hello 定义hello的rpc方法
func (h *HelloServerImpl) Hello(ctx context.Context, request *hello.HelloRequest) (*hello.HelloInfo, error) {
    
	name := request.GetName()
	fmt.Println(name)
	return &hello.HelloInfo{
    HelloData: "hello " + name}, nil
}

func main() {
    
	lis, err := net.Listen("tcp", ":8090")
	if err != nil {
    
		fmt.Println(err)
	}
	server := grpc.NewServer()
	hello.RegisterHelloServiceServer(server, &HelloServerImpl{
    })

	if err := server.Serve(lis); err != nil {
    
		fmt.Println(err)
	}
}

使用这个:
在这里插入图片描述

也可以监听:
在这里插入图片描述
在这里插入图片描述

再写一个clientold.js(去执行sayRepeatHello方法):

var {
    
  GreeterClient
} =require('./helloworld_grpc_web_pb');

var {
     HelloRequest,RepeatHelloRequest } =require('./helloworld_pb');

let client = new GreeterClient('http://localhost:5005');

let helloRequest = new HelloRequest();
helloRequest.setName('kitty');
// helloRequest.setCity('合肥');

client.sayHello(helloRequest, {
    }, (err, response) => {
    
  console.log(err, 22,response,response.array);
});

// client.sayRepeatHello(repeatHelloRequest, {});
// server streaming call
var streamRequest = new RepeatHelloRequest();
streamRequest.setName('World');
streamRequest.setCount(7);

var stream = client.sayRepeatHello(streamRequest, {
    });
stream.on('data', (response) => {
    
  console.log(response.getMessage());
});
stream.on('error', (err) => {
    
  console.log(`Unexpected stream error: code = ${
    err.code}` +
              `, message = "${err.message}"`);
});

在这里插入图片描述
服务端go代码:

package main

import (
	"context"
	"fmt"
	"golang-grpc-demo/hello"
	"net"
	"strconv"

	"google.golang.org/grpc"
)

// HelloServerImpl 定义hello接口实现
type HelloServerImpl struct {
    
	hello.UnimplementedGreeterServer
}

// SayHello 定义hello的rpc方法
func (h *HelloServerImpl) SayHello(ctx context.Context, request *hello.HelloRequest) (*hello.HelloReply, error) {
    
	name := request.GetName()
	fmt.Println("SayHello方法接收到请求参数:", name)
	return &hello.HelloReply{
    Message: "SayHello: " + name}, nil
}

// SayRepeatHello 定义hello的rpc方法
func (h *HelloServerImpl) SayRepeatHello(request *hello.RepeatHelloRequest, stream hello.Greeter_SayRepeatHelloServer) error {
    
	name := request.GetName()
	count := request.GetCount()
	fmt.Println("SayRepeatHello方法接收到请求参数:", name, count)
	for i := 0; i < int(count); i++ {
    
		stream.Send(&hello.HelloReply{
    Message: "Count:" + strconv.Itoa(i) + " SayHello: " + name + "\n"})
	}
	return nil
}

func main() {
    
	lis, err := net.Listen("tcp", ":8090")
	if err != nil {
    
		fmt.Println(err)
	}
	server := grpc.NewServer()
	hello.RegisterGreeterServer(server, new(HelloServerImpl))

	if err := server.Serve(lis); err != nil {
    
		fmt.Println(err)
	}
}

要求的类型:
在这里插入图片描述

测试一下,故意写一个字符串类型的次数:
在这里插入图片描述
结果可见:控制台报错了,请求并没有发到服务端:

在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_43437334/article/details/114502010

智能推荐

JavaWeb(14) 页面静态化之使用freemarker模板生成一个html静态页面_java实现静态模板-程序员宅基地

文章浏览阅读6.2k次。题外话: 页面静态化(展示数据从JSP页面变成HTML页面)实现方式-->模板技术 从本质上来讲,模板技术是一个占位符动态替换技术。一个完整的模板技术需要四个元素:①模板语言(使用的语法) ②包含模板语言的模板文件(.ftl结尾) ③模板引擎(jar包)④拥有动态数据的数据对象 FreeMarker是一款模板引擎:即一种基于模板和要改变的数据,并用来..._java实现静态模板

CRC16-CCITT校验算法实现(C#版)_c# static byte[] datacrc16_ccitt-程序员宅基地

文章浏览阅读6.4k次。public ushort[] CRC16Table = { 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,_c# static byte[] datacrc16_ccitt

android 图片压缩避免内存溢出的解决办法-程序员宅基地

文章浏览阅读462次。在android中的很多应用中都需要拍照上传图片,随着手机的像素越来越高,拍摄的图片也越来越大。在拍摄后显示的时候,使用universalimageloader.这个开源项目可以避免内存溢出。但是在上传的时候,一般需要压缩,但是压缩的时候很容易导致内存溢出。解决的办法就是,压缩后的二进制流,不用导出Bitmap,而是直接存储为本地文件,上传的时候直接通过本地文件上传。代码如下:1.图片压缩获..._android压缩图片会造成内存溢出

Subnet简介-程序员宅基地

文章浏览阅读2.9w次,点赞2次,收藏13次。Subnet(子网)在一般的概念中,有两个基本含义:1 这个子网的网段(CIDR)和IP版本;2 这个子网的路由(含默认路由)。事实上,Subnet模型也确实有这两个字段cidr和ip_version,分别表示一个子网的网段和IP版本。另外Subnet模型还有两字段gateway_ip和host_routes,表示一个子网的路由信息。gateway_ip是这个子网的默认网关IP。host_rout..._subnet

HBase的列族必须提前定义-程序员宅基地

文章浏览阅读282次,点赞4次,收藏7次。在HBase中,列族(Column Family)必须在创建表时提前定义,而列(Column)是动态添加的,无需提前定义。一旦表创建完成并列族定义好之后,可以动态地向表中的列族中添加新的列,而无需修改表的结构。2. **列是动态添加的:** 列是在列族内动态添加的,无需提前定义。而列是动态添加的,可以根据需要随时向列族中添加新的列。1. **列族必须提前定义:** 在创建HBase表时,需要指定表的列族,每个列族都需要提前定义。列族的定义是静态的,一旦表创建完成并列族定义好之后,列族的结构就不能再改变了。

人工智能伦理框架:如何建立AI技术的道德规范-程序员宅基地

文章浏览阅读365次,点赞6次,收藏9次。1.背景介绍人工智能(AI)技术的发展已经进入了关键时期,它正在改变我们的生活、经济和社会结构。然而,随着AI技术的不断发展,也引发了一系列道德、伦理和法律问题。为了确保AI技术的可持续发展和社会接受,我们需要建立一个人工智能伦理框架,以指导AI技术的研发和应用。在过去的几年里,许多学者、企业家、政府机构和非政府组织都开始关注AI伦理问题,并提出了许多关于AI伦理的建议和规范。然而,这些建..._人工智能道德框架

随便推点

opencv-python常用函数解析及参数介绍(八)——轮廓与轮廓特征_python opencv提取圆轮廓-程序员宅基地

文章浏览阅读986次,点赞2次,收藏9次。在前面的文章中我们已经学会了使用膨胀与腐蚀、使用梯度、使用边缘检测的方式获得图像的轮廓,那么在获得轮廓后我们可以对图像进行什么样的操作呢?本文将介绍轮廓的绘制与轮廓特征的使用。_python opencv提取圆轮廓

linux redis自动关闭问题_linux redis启动 linux redis启动一会后自动关闭-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏5次。linux 自动关闭的问题问题:redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured_linux redis启动 linux redis启动一会后自动关闭

【SpringCloud-Alibaba系列教程】14.一文教你入门RocketMQ_《芋道 spring cloud alibaba 消息队列 rocketmq 入门-程序员宅基地

文章浏览阅读560次。<本文已参与 RocketMQ Summit 优秀案例征文活动,点此了解详情>MQ简介MQ(Message Queue)是一种跨进程的通信机制,用于消息传递。通俗点说,就是一个先进先出的数据结构。MQ应用场景异步解耦很多场景不使用MQ会产生各个应用见紧密耦合在在一起,其实我们要遵循的原则就是高内聚低耦合,通过上图我们就可以看到,消息生产者,不管消息消费者状态如何,生产好的消息就直接投递到MQ中,消息消费者也是同样,不管消息生产者如何,只取MQ中的消息进行处理。这是解耦.._《芋道 spring cloud alibaba 消息队列 rocketmq 入门

基于微信小程序的校园导航小程序设计与实现_简单的校园导航微信小程序怎么弄-程序员宅基地

文章浏览阅读1k次,点赞23次,收藏41次。今天带来的是基于SpringBoot的校园导航微信小程序设计与实现,智能化的管理方式可以大幅降低学校的运营人员成本,实现了校园导航的标准化、制度化、程序化的管理,有效地防止了校园导航的随意管理,提高了信息的处理速度和精确度,能够及时、准确地查询和修正建筑速看等信息。课题主要采用微信小程序、SpringBoot架构技术,前端以小程序页面呈现给学生,结合后台java语言使页面更加完善,后台使用MySQL数据库进行数据存储。微信小程序主要包括学生信息、校园简介、建筑速看、系统信息等功能。_简单的校园导航微信小程序怎么弄

The server time zone value ‘�й���׼ʱ��‘ is unrecognized or represents more than one time zone.-程序员宅基地

文章浏览阅读33次。java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone.

利用base64对图片进行编码及解码_平台接收base64图片编码-程序员宅基地

文章浏览阅读5.9k次。试图将图片通过json进行传输,这个时候就想到利用base64编码的方法来图片。主要步骤有1、对图片文件进行编码,转换为base64编码的格式,及一长串字符;2、可将字符通过json进行传送;3、目的方接收json数组,取出编码字符串,并进行解码,显示图片该方法难点主要还是在于对图片的编解码处理,以下是用PHP实现的编解码处理代码<?php $image_f_平台接收base64图片编码</div>