当前位置: 首页 > 编程日记 > 正文

SpringBoot整合Grpc实现跨语言RPC通讯

什么是gRPC

gRPC谷歌开源基于go语言的一个现代的开源高性能RPC框架,可以在任何环境中运行。它可以有效地连接数据中心内和跨数据中心的服务,并提供可插拔的支持,以实现负载平衡,跟踪,健康检查和身份验证。它还适用于分布式计算的最后一英里,用于将设备,移动应用程序和浏览器连接到后端服务。

简单的服务定义:使用Protocol Buffers定义您的服务,这是一个功能强大的二进制序列化工具集和语言.

跨语言和平台工作:自动为各种语言和平台的服务生成惯用的客户端和服务器存根,当然单纯的java语言之间也是可以的。

一般主要是Java和Go,PHP,Python之间通讯。

快速启动并扩展:使用单行安装运行时和开发环境,并使用框架每秒扩展到数百万个RPC

双向流媒体和集成的身份验证:基于http/2的传输的双向流和完全集成的可插拔身份验证

官网地址:https://www.grpc.io/

这是一个可以运行的例子,本文基于此增加了一些代码:https://codenotfound.com/grpc-java-example.html

这个例子使用的jar是grpc-spring-boot-starter@io.github.lognet

这个例子也可以参考:https://github.com/yidongnan/grpc-spring-boot-starter

不过这个例子使用的是另一个jar是grpc-spring-boot-starter@net.devh

说明:Thrift也可以实现跨语言的通讯,有人对此做了对比参考:开源RPC(gRPC/Thrift)框架性能评测

服务定义

与许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法。默认情况下,gRPC使用Protocol Buffers作为接口定义语言(IDL)来描述服务接口和有效负载消息的结构。如果需要,可以使用其他替代方案。

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。

它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

数据序列化和反序列化

序列化: 将数据结构或对象转换成二进制串的过程。

反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。

这里有人翻译了官方文档:Protocol Buffers官方文档(开发指南)

也可以看IBM的文档:Google Protocol Buffer 的使用和原理

https://github.com/protocolbuffers/protobuf

https://developers.google.com/protocol-buffers/docs/javatutorial

参考官网指南:

如您所见,语法类似于C ++或Java。让我们浏览文件的每个部分,看看它的作用。

.proto文件以包声明开头,这有助于防止不同项目之间的命名冲突。在Java中,包名称用作Java包,除非您已经明确指定了a java_package,就像我们在这里一样。即使您提供了a java_package,您仍应定义一个法线package,以避免在Protocol Buffers名称空间和非Java语言中发生名称冲突。

在包声明之后,您可以看到两个特定于Java的选项: java_packagejava_outer_classname。 java_package指定生成的类应该以什么Java包名称存在。如果没有明确指定它,它只是匹配package声明给出的包名,但这些名称通常不是合适的Java包名(因为它们通常不以域名开头)。该java_outer_classname选项定义应包含此文件中所有类的类名。如果你没有java_outer_classname明确地给出,它将通过将文件名转换为camel case来生成。例如,默认情况下,“my_proto.proto”将使用“MyProto”作为外部类名。

接下来,您有消息定义。消息只是包含一组类型字段的聚合。许多标准的简单数据类型都可以作为字段类型,

包括boolint32floatdouble,和string。您还可以使用其他消息类型作为字段类型在消息中添加更多结构 - 在上面的示例中,Person消息包含PhoneNumber消息,而AddressBook消息包含Person消息。您甚至可以定义嵌套在其他消息中的消息类型 -​​ 如您所见,PhoneNumber类型在内部定义Personenum如果您希望其中一个字段具有预定义的值列表之一,您也可以定义类型 - 在此您要指定电话号码可以是其中之一MOBILEHOME或者WORK

每个元素上的“= 1”,“= 2”标记标识该字段在二进制编码中使用的唯一“标记”。标签号1-15需要少于一个字节来编码而不是更高的数字,因此作为优化,您可以决定将这些标签用于常用或重复的元素,将标签16和更高版本留给不太常用的可选元素。重复字段中的每个元素都需要重新编码标记号,因此重复字段特别适合此优化。

必须使用以下修饰符之一注释每个字段:

  • required:必须提供该字段的值,否则该消息将被视为“未初始化”。尝试构建一个未初始化的消息将抛出一个RuntimeException。解析未初始化的消息将抛出一个IOException。除此之外,必填字段的行为与可选字段完全相同。
  • optional:该字段可能已设置,也可能未设置。如果未设置可选字段值,则使用默认值。对于简单类型,您可以指定自己的默认值,就像我们type在示例中为电话号码所做的那样。否则,使用系统默认值:数字类型为0,字符串为空字符串,bools为false。对于嵌入式消息,默认值始终是消息的“默认实例”或“原型”,其中没有设置其字段。调用访问器以获取尚未显式设置的可选(或必需)字段的值始终返回该字段的默认值。
  • repeated:该字段可以重复任意次数(包括零)。重复值的顺序将保留在协议缓冲区中。将重复字段视为动态大小的数组。

永远是必需的 你应该非常小心地将字段标记为required。如果您希望在某个时刻停止写入或发送必填字段,则将字段更改为可选字段会有问题 - 旧读者会认为没有此字段的邮件不完整,可能会无意中拒绝或丢弃它们。您应该考虑为缓冲区编写特定于应用程序的自定义验证例程。谷歌的一些工程师得出的结论是,使用required弊大于利; 他们更喜欢只使用optionalrepeated。但是,这种观点并不普遍。

.proto可以在Protocol Buffer Language Guide中找到编写文件的完整指南- 包括所有可能的字段类型。不要去寻找类继承类似的工具,但协议缓冲区不会这样做。

如果你还不是很理解,就只要参考下面这个例子就行了。

我们将使用以下工具/框架:

  • gRPC 1.16
  • Spring Boot 2.1
  • Maven 3.5

我们的项目具有以下目录结构:

使用Protocol Buffers定义服务

先看proto文件

syntax = "proto3";option java_multiple_files = true;
package com.codenotfound.grpc.helloworld;message Person {string first_name = 1;string last_name = 2;
}message Greeting {string message = 1;
}message A1 {int32  a = 1;int32  b = 2;
}message A2 {int32  message = 1;
}service HelloWorldService {rpc sayHello (Person) returns (Greeting);rpc addOperation (A1) returns (A2);
}

注意:A1,A2是我定义的,其实就是定义入参出参,1和2那是表示是第几个参数而已,数据类型有string和int32。

Maven设置

我们使用Maven构建并运行我们的示例。

下面显示的是POM文件中Maven项目的XML表示。它包含编译和运行示例所需的依赖项。

为了配置和公开Hello World gRPC服务端点,我们将使用Spring Boot项目。

为了便于管理不同的Spring依赖项,使用了Spring Boot Starters。这些是一组方便的依赖项描述符,您可以在应用程序中包含这些描述符。

我们包含spring-boot-starter-web依赖项,该依赖项自动设置将托管我们的gRPC服务端点的嵌入式Apache Tomcat。

spring-boot-starter-test包括用于包括测试启动的应用程序的依赖关系的JUnit,Hamcrest和的Mockito。

用于gRPC框架的Spring启动启动程序自动配置并运行嵌入式gRPC服务器,@GRpcService启用Beans作为Spring Boot应用程序的一部分。启动器支持Spring Boot版本1.5.X和2.XX我们通过包含grpc-spring-boot-starter依赖项来启用它。

协议缓冲区支持许多编程语言中生成的代码。本教程重点介绍Java。

有多种方法可以生成基于protobuf的代码,在本例中,我们将使用grobc-java GitHub页面上记录的protobuf-maven-plugin。

我们还包括os-maven-plugin扩展,它可以生成各种有用的平台相关项目属性。由于协议缓冲区编译器是本机代码,因此需要此信息。换句话说,protobuf-maven-plugin需要为正在运行的平台获取正确的编译器。

最后,插件部分包括spring-boot-maven-plugin。这允许我们构建一个可运行的超级jar。这是执行和传输代码的便捷方式。此外,该插件允许我们通过Maven命令启动示例。

项目的pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.codenotfound</groupId><artifactId>grpc-java-hello-world</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>grpc-java-hello-world</name><description>gRPC Java Example</description><url>https://codenotfound.com/grpc-java-example.html</url><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.0.RELEASE</version><relativePath /> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><grpc-spring-boot-starter.version>3.0.0</grpc-spring-boot-starter.version><os-maven-plugin.version>1.6.1</os-maven-plugin.version><protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>io.github.lognet</groupId><artifactId>grpc-spring-boot-starter</artifactId><version>${grpc-spring-boot-starter.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><extensions><extension><groupId>kr.motd.maven</groupId><artifactId>os-maven-plugin</artifactId><version>${os-maven-plugin.version}</version></extension></extensions><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>${protobuf-maven-plugin.version}</version><configuration><protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact><pluginId>grpc-java</pluginId><pluginArtifact>io.grpc:protoc-gen-grpc-java:1.16.1:exe:${os.detected.classifier}</pluginArtifact></configuration><executions><execution><goals><goal>compile</goal><goal>compile-custom</goal></goals></execution></executions></plugin></plugins></build></project>

注意:必须使用这个编译compile工具生成特定语言(比如我们这里是java)的执行代码。

手动执行以下Maven命令,应在target / generated-sources / protobuf /下生成不同的消息和服务类。

mvn compile

也可以使用IDE编译。

编译执行:

会生成编译后的文件:

注意上面的文件是自动编译生成的,不是你自己写的!

Spring Boot安装程序

创建一个SpringGrpcApplication包含一个main()方法,该方法使用Spring Boot的SpringApplication.run()方法来引导应用程序。

package com.codenotfound;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SpringGrpcApplication {public static void main(String[] args) {SpringApplication.run(SpringGrpcApplication.class, args);}
}

创建服务器

定义Service,我增加了一个加法运算方法,需要注意的是输入,输出参数都写在方法定义里。

服务实现在HelloWorldServiceImplPOJO中定义,该POJO实现HelloWorldServiceImplBase从HelloWorld.proto文件生成的类。

我们覆盖该sayHello()方法并Greeting根据Person请求中传递的名字和姓氏生成响应。

请注意,响应是一个StreamObserver对象。换句话说,该服务默认是异步的。在接收响应时是否要阻止是客户的决定,我们将在下面进一步了解。

我们使用响应观察者的onNext()方法返回Greeting,然后调用响应观察者的onCompleted()方法告诉gRPC我们已经完成了写响应。

HelloWorldServiceImplPOJO标注有@GRpcService其自动配置到端口露出指定GRPC服务默认端口6565。

request:入参

responseObserver:出参

格式按照标准

package com.codenotfound.grpc;import com.codenotfound.grpc.helloworld.A1;
import com.codenotfound.grpc.helloworld.A2;
import org.lognet.springboot.grpc.GRpcService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codenotfound.grpc.helloworld.Greeting;
import com.codenotfound.grpc.helloworld.HelloWorldServiceGrpc;
import com.codenotfound.grpc.helloworld.Person;
import io.grpc.stub.StreamObserver;@GRpcService
public class HelloWorldServiceImplextends HelloWorldServiceGrpc.HelloWorldServiceImplBase {private static final Logger LOGGER= LoggerFactory.getLogger(HelloWorldServiceImpl.class);@Overridepublic void sayHello(Person request,StreamObserver<Greeting> responseObserver) {LOGGER.info("server received {}", request);String message = "Hello " + request.getFirstName() + " "+ request.getLastName() + "!";Greeting greeting= Greeting.newBuilder().setMessage(message).build();LOGGER.info("server responded {}", greeting);System.out.println("message>>>" + message);responseObserver.onNext(greeting);responseObserver.onCompleted();}@Overridepublic void addOperation(A1 request,StreamObserver<A2> responseObserver) {LOGGER.info("server received {}", request);int message = request.getA() + request.getB();A2 a2 = A2.newBuilder().setMessage(message).build();LOGGER.info("server responded {}", a2);System.out.println("message>>>" + message);responseObserver.onNext(a2);responseObserver.onCompleted();}
}

服务端使用了@GRpcService注解.

也可以使用这个jar注解@GrpcService就可以,代码和前面一种一致的

<dependency><groupId>net.devh</groupId><artifactId>grpc-spring-boot-starter</artifactId><version>2.5.1.RELEASE</version>
</dependency>

https://github.com/yidongnan/grpc-spring-boot-starter/blob/master/README-zh.md

源码在这里:

 你可以自定义端口:

grpc:port: 9090

创建客户端

下面是客户端代码,这里为了简单服务端和客户端写一个项目,实际开发肯定是分开,不然也就没必要用Grpc这么麻烦了。

客户端代码在HelloWorldClient类中指定。

@Component如果启用了自动组件扫描,我们将使用Spring 注释客户端,这将导致Spring自动创建并将下面的bean导入容器(将@SpringBootApplication注释添加到主SpringWsApplication类等同于使用@ComponentScan)。

要调用gRPC服务方法,我们首先需要创建一个stub

有两种类型的stub可用:

  • 一个阻塞/同步stub,将等待服务器响应
  • 一个非阻塞/异步stub使非阻塞调用到服务器,其中,所述响应是异步返回。

在此示例中,我们将实现阻塞stub

为了传输消息,gRPC使用http/2和其间的一些抽象层。这种复杂性隐藏在MessageChannel处理连接的背后。

一般建议是为每个应用程序使用一个通道并在服务stub之间共享它。

我们使用一个init()带注释的方法@PostConstruct,以便MessageChannel在bean初始化之后构建一个新的权限。然后使用该通道创建helloWorldServiceBlockingStub

gRPC默认使用安全连接机制,如TLS。因为这是一个简单的开发测试将使用usePlaintext(),以避免必须配置不同的安全工件,如密钥/信任存储。

sayHello()方法使用Builder模式创建Person对象,我们在其上设置'firstname'和'lastname'输入参数。

helloWorldServiceBlockingStub则用来发送走向世界您好GRPC服务的请求。结果是一个Greeting对象,我们从中返回包含消息。

package com.codenotfound.grpc;import com.codenotfound.grpc.helloworld.A1;
import com.codenotfound.grpc.helloworld.A2;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.codenotfound.grpc.helloworld.Greeting;
import com.codenotfound.grpc.helloworld.HelloWorldServiceGrpc;
import com.codenotfound.grpc.helloworld.Person;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;@Component
public class HelloWorldClient {private static final Logger LOGGER= LoggerFactory.getLogger(HelloWorldClient.class);private HelloWorldServiceGrpc.HelloWorldServiceBlockingStub helloWorldServiceBlockingStub;@PostConstructprivate void init() {ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9090).usePlaintext().build();helloWorldServiceBlockingStub = HelloWorldServiceGrpc.newBlockingStub(managedChannel);}public String sayHello(String firstName, String lastName) {Person person = Person.newBuilder().setFirstName(firstName).setLastName(lastName).build();LOGGER.info("client sending {}", person);Greeting greeting = helloWorldServiceBlockingStub.sayHello(person);LOGGER.info("client received {}", greeting);return greeting.getMessage();}public int addOperation(int a, int b) {A1 a1 = A1.newBuilder().setA(a).setB(b).build();A2 a2 = helloWorldServiceBlockingStub.addOperation(a1);return a2.getMessage();}
}

注意如果使用自定义端口需要修改这个,默认是6565,保持和你修改的配置文件一致,或者你不配置用默认的就行:

gRPC测试用例

package com.codenotfound;import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.codenotfound.grpc.HelloWorldClient;@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringGrpcApplicationTests {@Autowiredprivate HelloWorldClient helloWorldClient;@Testpublic void testSayHello() {assertThat(helloWorldClient.sayHello("Grpc", "Java")).isEqualTo("Hello Grpc Java!");assertThat(helloWorldClient.addOperation(1, 2)).isEqualTo(3);}
}

2个断言:

一个是原始的就是字符串输出,第二个是我增加的做个简单的加法运算1+2=3就对了。

运行测试用例:

故意修改为和4比较结果,报错就对了。

总结:建议先跑完整的例子,不要陷入grpc太深。

定义好.proto,再生成对应编译文件,再写实现类,定义服务端,使用客户端调用。

相关文章:

python 第六章 函数

1.函数的定义 def 名称(形参): 函数体 2.函数的调用 名称(实参) 单独文件&#xff1a;模块 调用方式——模块.名称 3.函数的参数类型 1.位置参数&#xff1a; def add(a,b):add(2,3) #顺序&#xff0c;个数&#xff0c;数据类型都要相同&#xff01;&#xff01;&#xf…

C++简单使用Jsoncpp来读取写入json文件

一、源码编译 C操作json字符串最好的库应该就是jsoncpp了&#xff0c;开源并且跨平台。它可以从这里下载。 下载后将其解压到任意目录&#xff0c;它默认提供VS2003和VS2010的工程文件&#xff0c;使用VS2010可以直接打开makefiles\msvc2010目录下的sln文件。 工程文件提供Json…

BZOJ 3420: Poi2013 Triumphal arch

二分答案 第二个人不会走回头路 那么F[i]表示在i的子树内(不包括i)所需要的额外步数 F[1]0表示mid可行 k可能为0 #include<cstdio> #include<algorithm> using namespace std; int cnt,n,mid,F[300005],last[300005]; struct node{int to,next; }e[600005]; void a…

Java泛型使用需要小心

这是源自实际开发的一个坑&#xff0c;只是被我简化了。 Set<Integer> gs null;Set gss new HashSet();gs gss;gss.add("19");System.out.println(gs);for (int i : gs) {if (i19) {System.out.println("1");}} 代码经过一些转换你如果不注意以…

证明实对称正定矩阵A的Gauss-Seidel法必定收敛(完整过程)

Solution: ​ \quad将nnn阶实对称矩阵AAA设为D−L−LTD-L-L^TD−L−LT,其中DDD是AAA的所有主对角元素构成对角矩阵&#xff0c;−L-L−L是AAA的所有主对角线以下的元素构成的严格下三角矩阵。 ​ \quad此时Gauss−SeidelGauss-SeidelGauss−Seidel法的迭代矩阵为(D−L)−1LT(…

5月中旬的一些总结

考完英语口语了&#xff0c;最大的帮助就是找到了练习的方法和思路。 周三晚上有谷歌的全球IO大会。 ******** 写吴斌老师的课程作业&#xff0c;这才发现winedt过期了。用了rept之后本来是解决问题了&#xff0c;可是一联网就又不行了。总要关上再打开。用防火墙阻断却找不到选…

项目总结10:通过反射解决springboot环境下从redis取缓存进行转换时出现ClassCastException异常问题...

通过反射解决springboot环境下从redis取缓存进行转换时出现ClassCastException异常问题 关键字 springboot热部署 ClassCastException异常 反射 redis 前言 最近项目出现一个很有意思的问题&#xff0c;用户信息(token)储存在redis中&#xff1b;在获取token&#xff0c;反序列…

Rouche Theorem(Stein复分析)

Rouche Theorem&#xff1a; \quadIffandgareholomorphicfunctionsinaregionΩcontainingacircleCanditsinterior,and∣f(z)∣≥∣g(z)∣forz∈C,fandfghavethesamenumbersofzerosinsidethecircleC.If\quad f\quad and\quad g\quad are\quad holomorphic\quad functions\quad i…

Java线上程序频繁JVM FGC问题排障与启示

线上Java程序的JVM频繁FGC&#xff0c;现象如图所示&#xff1a; 一直持续FGC 5次左右&#xff0c;每次耗时1秒多不等。 FGC的原因实际上是内存不够用&#xff0c;但是运维反映堆内存是2G&#xff0c;从运维提供的参数看也是。 内存实际上一直只用到1G以内。 这时候可以自己写…

python常用数据结构的常用操作

作为基础练习吧。列表LIST&#xff0c;元组TUPLE,集合SET&#xff0c;字符串STRING等等&#xff0c;显示&#xff0c;增删&#xff0c;合并。。。 #List shoplist [apple,mango,carrot,banana] print I have ,len(shoplist), items to purchase. print These items are: for …

h5 和native 交互那些事儿

前端菜菜一枚&#xff0c;写下关于h5 和native 交互那些事情。偏前端&#xff0c;各种理论知识&#xff0c;不在赘述。之前有各位大牛已经写过。我只写代码&#xff0c;有问题&#xff0c;下面留言/* 关于h5 和native 之间的交互 JSBridge 解决问题&#xff0c;偏向前端* 使用U…

手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫

系列教程 手把手教你写电商爬虫-第一课 找个软柿子捏捏 如果没有看过第一课的朋友&#xff0c;请先移步第一课&#xff0c;第一课讲了一些基础性的东西&#xff0c;通过软柿子"切糕王子"这个电商网站好好的练了一次手&#xff0c;相信大家都应该对写爬虫的流程有了一…

Python程序设计 第六章 函数(续

复习 1. 10进制 ⇒\Rightarrow⇒ 2进制 除2取余&#xff0c;从低位到高位存储到字符串中&#xff0c;从高位到低位def d2b(n):if n>1:d2b(n//2)print(n%2,end)d2b(4)出口&#xff1a; 条件&#xff0c;值确定 &#xff08;一&#xff09;return (二&#xff09;函数体执行结…

K8S的横向自动扩容的功能Horizontal Pod Autoscaling

K8S 作为一个集群式的管理软件&#xff0c;自动化、智能化是免不了的功能。Google 在 K8S v1.1 版本中就加入了这个 Pod 横向自动扩容的功能&#xff08;Horizontal Pod Autoscaling&#xff0c;简称 HPA&#xff09;。 HPA 与之前的 Deployment、Service 一样&#xff0c;也属…

第八周例行报告

此作业要求参见&#xff1a;https://edu.cnblogs.com/campus/nenu/2018fall/homework/2326 1、本周PSP 类型 任务 开始时间 结束时间 中断时间 Delta时间 会议 事后诸葛亮会议 11.3 14&#xff1a;12 11.3 15&#xff1a;08 0min 56min 博客 编写博客《事后诸葛…

HTTP头部信息解释分析(详细整理)

这篇文章为大家介绍了HTTP头部信息&#xff0c;中英文对比分析&#xff0c;还是比较全面的&#xff0c;若大家在使用过程中遇到不了解的&#xff0c;可以适当参考下 HTTP 头部解释 1. Accept&#xff1a;告诉WEB服务器自己接受什么介质类型&#xff0c;*/* 表示任何类型&#…

深圳杯---深圳市生活垃圾处理社会总成本分析

2017年3月18日&#xff0c;国务院向全国发布了《生活垃圾分类制度实施方案》&#xff0c;这标志着中国垃圾分类制度建设开始了一个全新阶段&#xff0c;垃圾分类已成为推进社会经济绿色发展、提升城市管理和服务水平、优化人居环境的重要举措。为了保证这一目标能够顺利实现&am…

你真的掌握了并发编程volatile synchronized么?

先看代码&#xff1a; import java.util.concurrent.atomic.AtomicInteger;/**** author xialuomantian*/ public class NewTest {static volatile int a 1;static volatile int b 1;//static int a 1;//static int b 1;public static AtomicInteger aa new AtomicInteg…

SQLSERVER存储过程基本语法使用

一、定义变量 --简单赋值 declare a int set a5 print a --使用select语句赋值 declare user1 nvarchar(50) select user1张三 print user1 declare user2 nvarchar(50) select user2 Name from ST_User where ID1 print user2 --使用update语句赋值 declare user3 nv…

线上java JVM问题排查

作者&#xff1a;霞落满天 第一部分 是我以前公司的一则正式案例&#xff1a; 第二部分 是我另一个博客上写的主要是最近发现大家问的比较多就写了此文 第一部分 线上真实故障案例 下面是一个老系统&#xff0c;代码写的有点问题导致出现这样一个JVM占比过高的问题&#xff…

走向云时代的大型机

大型机&#xff0c;又称大型主机&#xff0c;英文名mainframe&#xff0c;是指使用专用的处理器指令集、操作系统和应用软件的有机整体。大型机最早诞生于上个世纪六十年代&#xff0c;经过四十多年的不断发展&#xff0c;其在可靠性、安全性、可用性和灵活性方面首屈一指。近年…

区分 欧几里得距离 曼哈坦距离 明考斯基距离

欧几里德距离(Euclidean Distance)&#xff0c;欧氏距离。一种通常采用的表示相似度的距离定义&#xff0c;是表示在m维空间中两个点之间的真实距离。 对于n维空间中的两个点之间的欧几里得距离d(i,j)表示为&#xff1a; d(i,j) (|xi1-xj1|2|xi2-xj2|2……|xip-xjp|2)1/2 当n2…

传统行业转型微服务的挖坑与填坑

原文:传统行业转型微服务的挖坑与填坑一、微服务落地是一个复杂问题&#xff0c;牵扯到IT架构&#xff0c;应用架构&#xff0c;组织架构多个方面 在多家传统行业的企业走访和落地了微服务之后&#xff0c;发现落地微服务是一个非常复杂的问题&#xff0c;甚至都不完全是技术问…

Windows下安装Mongodb SpringBoot集成MongoDB和Redis多数据源

全文内容&#xff1a; Mongodb安装 说明&#xff1a;Mongodb和redis是开发中常用的中间件&#xff0c;Redis的安装使用比较简单就不写了&#xff0c;只说本地也就是Windows安装Mongodb。 SpringBoot集成MongoDB和Redis 文中还有一个彩蛋Hutool 1.下载最新稳定版 https://w…

使用CSDN-markdown编辑器

欢迎使用Markdown编辑器写博客 本Markdown编辑器使用StackEdit修改而来&#xff0c;用它写博客&#xff0c;将会带来全新的体验哦&#xff1a; Markdown和扩展Markdown简洁的语法代码块高亮图片链接和图片上传LaTex数学公式UML序列图和流程图离线写博客导入导出Markdown文件丰…

HTTP缓存相关头

本文说的是HTTP中控制客户端缓存的头有哪些。网上这方面的文章很多了&#xff0c;这里就说下个人的理解。 在请求一个静态文件的时候&#xff08;图片&#xff0c;css&#xff0c;js&#xff09;等&#xff0c;这些文件的特点是文件不经常变化&#xff0c;将这些不经常变化的文…

Thrift RPC 系列教程(4)——源码目录结构组织

Thrift 代码就是编程代码。是代码&#xff0c;就应该有良好的工程组织&#xff0c;并且&#xff0c;单独git仓库、版本管理&#xff0c;都是必不可少的。 前面我们简单总结了一些 Thrift 的一些基础知识点&#xff0c;但无非是一些细节层面的东西&#xff0c;所谓『细枝末节』也…

Spring Bean四种注入方式(Springboot环境)

阅读此文建议参考本人写的Spring常用注解&#xff1a;https://blog.csdn.net/21aspnet/article/details/104042826 给容器中注册组件的四种方法&#xff1a; 1.ComponentScan包扫描组件标注注解Component(ControllerServiceRepository) 使用场景&#xff1a;自己写的代码&…

chrome dev debug network 的timeline说明

在使用chrome的时候F12的开发者工具中有个network&#xff0c;其中对每个请求有个timeline的说明&#xff0c;当鼠标放上去会有下面的显示&#xff1a; 这里面的几个指标在说明在chrome使用文档有说明&#xff1a; 下面我用人类的语言理解下&#xff1a; Proxy 与代理服务器的连…

【MATLAB】函数句柄

在MATLAB平台中&#xff0c;对函数的调用方法分为直接调用法和间接调用法。 1、直接调用函数&#xff0c;被调用的函数通常称为子函数。一个文件中只能有一个主函数。 2、函数句柄——提供一种间接调用函数的方法。创建函数句柄需要用到操作符。 创建函数句柄的一般句法格式…