办公设备维修网
资讯中心 您所在的位置:网站首页 资讯中心 使用gRPC代替SpringCloud微服务项目中的RPC框架OpenFeign

使用gRPC代替SpringCloud微服务项目中的RPC框架OpenFeign

2024-05-29 06:01:09| 来源: 网络整理

这是目录哦 一. 前言二. 代码仓库三. 关于gRPC和OpenFeign四. 使用gRPC替代OpenFeign1. 原OpenFeign客户端2. proto接口定义3. gRPC服务端4. gRPC客户端5. 服务测试 五. 总结 一. 前言

前段时间一直在忙着另一门课程的SpringCloud微服务项目,其中各个微服务之间使用的是OpenFeign进行服务之间的接口调用。这时候刚好在网络程序设计的课程中学习到了gRPC这个RPC框架,便想着在项目中使用gRPC替代部分的OpenFeign客户端,于是便有了这篇文章。

二. 代码仓库

github地址:CampusForum项目

三. 关于gRPC和OpenFeign

在介绍两者之前先来看看RPC。

RPC(Remote Procedure Call)是远程过程调用协议,是一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。它使得开发者可以像调用本地方法一样调用远程的过程,从而简化了分布式系统的开发过程。

gRPC和OpenFeign都是RPC框架,它们实现RPC的具体工具或者库,它提供了一种透明调用机制,让使用者不必显式的区分本地调用和远程调用。然而,它们在实现细节和使用场景上有所不同:

gRPC:

gRPC是Google开发的一个高性能、开源的RPC框架,其协议设计在HTTP/2上实现,可以提供更好的性能。gRPC支持多种语言,包括Java、C++、Python等,这使得gRPC可以在多种语言环境中使用。gRPC使用Protocol Buffers作为接口定义语言,这不仅可以用于定义服务接口,还可以用于定义服务接口的消息格式。

OpenFeign:

OpenFeign是Spring Cloud中的一个子项目,用于简化HTTP API的开发。OpenFeign主要用于微服务架构中,它可以使HTTP请求像本地方法调用一样简单。OpenFeign的一个重要特性是其声明式的编程风格,开发者只需要定义一个接口并在接口上添加注解,就可以完成一个HTTP API的开发。

接下来介绍在SpringCloud微服务项目使用gRPC替代OpenFeign的具体过程。

四. 使用gRPC替代OpenFeign 1. 原OpenFeign客户端

在项目目录的resource-service esource-clientsrcmainjavacomustc esourceclient下,具体代码如下,其中的uploadAvatarImage和getUniversityName这两个接口将会由user-service中的user-server微服务进行调用。后面将会由gRPC来实现这两个接口的远程调用。

@FeignClient(value = "resource-server", fallback = ResourceClientFuse.class, contextId = "ResourceClient")public interface ResourceClient { /** * 上传图片接口 * * @param file 图片 * @return 图片链接 */ @PostMapping(value = "/private/resource/image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) RestResult uploadBlogImage(@RequestPart("file") MultipartFile file); /** * 上传头像接口 * * @param file 头像图片文件 * @param name 图片命名 * @return 访问链接 */ @PostMapping(value = "/private/resource/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) RestResult uploadAvatarImage(@RequestPart("file") MultipartFile file, @RequestParam("name") String name); /** * 通过院校代码获取名称 * * @param schoolCode 院校代码 * @return 高校名称 */ @GetMapping("/resource/university/name") RestResult getUniversityName(@RequestParam("schoolCode") Integer schoolCode);} 2. proto接口定义

对于gRPC,首先需要进行proto接口的定义。在本项目中,我把接口放在了commoncommon-grpc模块,该模块需要在pom文件中添加如下的依赖。

8 8 UTF-8 3.21.7 0.6.1 1.52.1 io.grpc grpc-stub ${grpc.version} io.grpc grpc-protobuf ${grpc.version} jakarta.annotation jakarta.annotation-api 1.3.5 true

然后再添加如下的插件,gRPC是使用Protocol Buffers来定义接口的,所以我们只要编写对应的proto文件,即可通过下面的插件将编写的proto文件自动转化为对应的java类。

kr.motd.maven os-maven-plugin 1.7.0 org.xolstice.maven.plugins protobuf-maven-plugin ${protobuf-plugin.version} com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} compile compile-custom

接着就是proto文件的编写,proto必须放在模块的src/main/proto文件夹下,因为插件会默认转换该文件夹下的proto文件,否则的话还要在配置插件时指定proto文件的位置。

创建一个resource.proto文件,接口定义如下:

syntax = "proto3"; //protocol buffers 的版本option java_multiple_files = true; //设置为 true,表示每一个 message 文件都会有一个单独的 class 文件option java_package = "com.ustc.common.grpc"; //用于标识生成的 java 文件的 packagevoption java_outer_classname = "ResourceProto"; //用于指定 proto 文件生成的 java 类的 outerclass 类名。package resource; //用来定义 message 的包名,包名是用来唯一标识 message 的//根据Feign客户端定义的服务接口service ResourceActions { rpc getUniversityName (SchoolCode) returns (University); rpc uploadAvatarImage (AvatarImage) returns (ImageUrl);}message SchoolCode { int32 code = 1;}message University { string name = 1;}//由于protobuf中不支持java的MultipartFile类型,因此使用字节数组来传输MultipartFile//这样在传输过程中MultipartFile的文件类型ContentType也会丢失,因此需要添加contentType参数message AvatarImage{ bytes image = 1; string name = 2; string contentType = 3;}message ImageUrl { string url = 1;}

最后使用compile和compile-custom插件来生成对应的java对象。由于compile用来编译消息对象,compile-custom则依赖消息对象,生成接口服务。因此要先使用compile插件,再使用compile-custom插件。

3. gRPC服务端

在服务的调用方resource-service esource-server中编写gRPC服务端代码。

首先还是需要在pom文件中导入如下的服务端依赖。第一个依赖是前面的common-grpc模块,需要大家导入自己对应的proto接口的模块。

com.ustc common-grpc # 导入自己的proto接口的模块 1.0-SNAPSHOT net.devh grpc-server-spring-boot-starter 2.14.0.RELEASE

然后在服务端提供gRPC方法的实现。这里需要使用@GrpcService注解去标注服务。

@GrpcServicepublic class ResourceActionsImpl extends ResourceActionsGrpc.ResourceActionsImplBase { @Resource private UniversityService universityService; @Resource private ImageService imageService; // 获得大学名称 @Override public void getUniversityName(SchoolCode request, StreamObserver responseObserver){ String universityName = universityService.getUniversityName(request.getCode()); responseObserver.onNext(University.newBuilder().setName(universityName).build()); responseObserver.onCompleted(); } // 上传头像 @Override public void uploadAvatarImage(AvatarImage request, StreamObserver responseObserver){ // 将request中的字节数组转化为MultipartFile文件 InputStream inputStream = new ByteArrayInputStream(request.getImage().toByteArray()); MultipartFile image = null; try { image = new MockMultipartFile(request.getName(), request.getName(), request.getContentType(), inputStream); } catch (IOException e) { throw new RuntimeException(e); } // 调用图片服务进行上传 String url = imageService.uploadAvatar(image, request.getName()); // 传回URL参数 responseObserver.onNext(ImageUrl.newBuilder().setUrl(url).build()); responseObserver.onCompleted(); }}

最后,需要在application.yaml 中进行配置,将当前服务注册到 nacos 配置中心里。

grpc: server: port: 40001spring: application: name: @artifactId@ # artifactId为resource-server cloud: nacos: server-addr: xxx.xxx.xxx.xxx:端口号 # nacos地址 4. gRPC客户端

在服务的调用方user-serviceuser-server中编写gRPC客户端代码。

首先需要在pom文件中导入如下的客户端依赖。第一个也是前面的common-grpc模块,注意这里的第二个依赖和服务端导入的第二个依赖是不同的。一个是grpc-server-spring-boot-starter,另一个是grpc-client-spring-boot-starter。

com.ustc common-grpc 1.0-SNAPSHOT net.devh grpc-client-spring-boot-starter 2.14.0.RELEASE

然后就是客户端代码的编写,客户端在调用远程服务时,需要使用注解@GrpcClient("对应的服务端名称")才能将stub注入。

@Servicepublic class GrpcClientService { @GrpcClient("resource-server") ResourceActionsGrpc.ResourceActionsBlockingStub resourceActionsBlockingStub; // 获得大学名称 public String getUniversityName(int schoolCode){ University university = resourceActionsBlockingStub.getUniversityName( SchoolCode.newBuilder().setCode(schoolCode).build() ); return university.getName(); } // 上传头像 public String uploadAvatarImage(MultipartFile file, String name){ // 将MultipartFile转化为需要传输的ImageUrl类型后,调用远程服务 ImageUrl imageUrl = null; try { imageUrl = resourceActionsBlockingStub.uploadAvatarImage( AvatarImage.newBuilder() .setImage(ByteString.copyFrom(file.getBytes())) .setName(name) .setContentType(file.getContentType()) .build() ); } catch (IOException e) { throw new RuntimeException(e); } return imageUrl.getUrl(); }}

最后也需要将客户端在nacos上注册服务。

spring: application: name: @artifactId@ # artifactId为user-server cloud: nacos: server-addr: xxx.xxx.xxx.xxx:端口号 # nacos地址grpc: client: resource-server: # 这个需要是服务端的名称 negotiation-type: plaintext # 表示这个服务的通信不使用TLS加密 5. 服务测试

最后,使用postman对gRPC替换的两个服务进行测试,user-server分别在登录服务和头像上传服务中使用到了gRPC的远程调用。通过图片可以看到,服务均能正常运行。说明确实可以使用gRPC在微服务项目中进行远程调用。

五. 总结

通过此次学习,成功将网络程序设计这门课程中学习到的gRPC的知识运用到了具体的项目中,使用了SpringBoot+Nacos+gRPC实现了微服务之间的远程调用。



【本文地址】 转载请注明 

最新文章

推荐文章

CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备16040606号-1