百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

手写一个rpc远程调用服务demo(rpc调用webservice)

off999 2025-03-20 17:38 80 浏览 0 评论

前言


  • 因为公司业务需求,使用了K8S + istio进行服务部署和治理,没有使用常规的springclould技术栈(包括注册中心nacos和openfeign远程服务调用)。
  • 所以就自己开发了一个基于AOP实现的rpc远程调用服务模块。其实现原理实现和feign类似,都是通过远程调用方法的代理对象发送HTTP请求并返回结果。
  • 废话不多说,下面直接上代码

代码

  • 下图是demo模块划分,common是公共模块,demo-order和demo-user是模拟两个服务调用。

  • 定义一个标识为远程调用类的注解 @RpcService ,有点类似于feign的@FeignClient注解。

/**
*
* @AUTHOR ZRH
* @DATE 2021/4/10
*/
@Component
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RpcService {

/**
* 远程服务名称
*/
String service();

/**
* 端口
*/
String port();
}


  • 定义两个标识远程调用接口请求方式注解 @get和@post,相当于@PostMapping和@GetMapping。

/**
*
* @AUTHOR ZRH
* @DATE 2021/4/10
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Post {

/**
* 接口路由
*
* @
return
*/
String value();
}

/**
*
* @AUTHOR ZRH
* @DATE 2021/4/10
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Get {

/**
* 接口路由
*
* @
return
*/
String value();
}

  • 然后定义一个AOP切面处理类 AopRpcHandler。只要远程调用接口方法上有注解@Post或者@Get,就会对方法进行代理方式请求。因为这里不需要原本远程调用方法的执行结果,所以这里直接使用@Around环绕切面,并且不需要执行原方法,所以直接使用JoinPoint 做参数接口(ProceedingJoinPoint继承自JoinPoint,里面多了两个阻塞方法proceed,用于获取原代理方法的执行结果)。
  • 通过代理对象获取到原方法的参数值,参数名,接口路由地址,接口请求方式,远程服务和端口等等。使用okhttp工具类发送代理请求,然后返回响应结果。

/**
* @AUTHOR ZRH
* @DATE 2021/4/10
*/
@Slf4j
@Aspect
@Component
public class AopRpcHandler {

private final static String HTTP =
"http://";

@Around(value =
"@annotation(post)")
public String aopPost(JoinPoint joinPoint, Post post) {
String result = null;
String url = null;
try {
RpcService rpcService = (RpcService) joinPoint.getSignature().getDeclaringType().getAnnotation(RpcService.class);
url = HTTP + rpcService.service() +
":" + rpcService.port() + "/" + post.value();
Object[] args = joinPoint.getArgs();
result = OkHttpUtils.post(url, JSON.toJSONString(args[0]));
} catch (Throwable throwable) {
log.error(
"服务调用异常,url = [{}]", url);
}
return result;
}

@Around(value =
"@annotation(get)")
public String aopGet(JoinPoint joinPoint, Get get) {
String result = null;
String url = null;
try {
RpcService rpcService = (RpcService) joinPoint.getSignature().getDeclaringType().getAnnotation(RpcService.class);
url = HTTP + rpcService.service() +
":" + rpcService.port() + "/" + get.value();

MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Parameter[] parameters = signature.getMethod().getParameters();
if (parameters != null && parameters.length > 0) {
Object[] args = joinPoint.getArgs();
int length = parameters.length;
url +=
"?";
for (int i = 0; i < length; i++) {
url += parameters[i] +
"=" + args[i];
if (i != length - 1) {
url +=
"&";
}
}
}
result = OkHttpUtils.get(url);
} catch (Throwable throwable) {
log.error(
"服务调用异常,url = [{}]", url);
}
return result;
}
}

  • 然后在demo-user服务中如果有远程调用场景,就创建一个远程调用类。使用注解@RpcService和@Post即可。方法中的返回值和返回类型可以自定义,比如一般项目中会有统一的响应结果。

/**
* @AUTHOR ZRH
* @DATE 2021/4/10 0010 1:06
*/
@RpcService(service =
"demo-order", port = "18002")
public class AopRpcDemo {

@Post(
"post")
public String post(String param) {
return "1";
}
}

  • 在demo-user服务中使用和正常调用接口一样。

/**
* @AUTHOR ZRH
* @DATE 2021/4/10 0010 0:42
*/
@RestController
public class DemoController {

@Autowired
private AopRpcDemo aopRpcDemo;

@PostMapping(
"post")
public String
post() {
String post = aopRpcDemo.post(
"zrh.post");
System.out.println(
"调用远程接口方法返回结= " + post);
return "ok";
}
}

  • 如果就这样把demo服务启动后,访问是访问不了的。因为在aop切面处理类中对http请求的URL没有通过域名而是通过服务名称拼接的。
  • 这里如果是基于注册中心和feign进行服务调用,那是没有问题,因为feign会通过服务名称到注册中心找到对应服务的地址进行请求远程接口。
  • 而这里因为没有使用注册中心,所以在window上需要增加hosts文件上的地址映射关系。在C:\Windows\System32\drivers\etc目录下的hosts文件增加。并在cmd控制台中使用ipconfig /flushdns刷新DNS内容。
  • @RpcService中的service写服务名而不写服务访问域名,是因为如果是多机集群部署,那么就可以使用服务名映射域名方式通过Nginx负载均衡进行转发请求。如果直接写服务访问域名就只能访问一个机子上的服务了。

  • 先看一下两个服务的配置文件和demo-order的接口



  • 服务启动后,访问http://localhost:18001/post,结果如下图:



  • 最后的结果和我们想要的结果一致。
  • 上面的demo是很简单的实现。如果读者想要在自己项目中使用此类技术栈,那需要考虑服务容错,服务发现,服务限流等等是否能兼容等。

最后

  • openfeign其实是可以独立和springboot进行使用的。先引入openfeign的maven包


org.springframework.cloud
spring-cloud-starter-openfeign
3.0.2

  • 然后在使用@FeignClient注解时,对url配置接口的访问地址,最后的执行结果和上述的结果是一样的。

/**
* @AUTHOR ZRH
* @DATE 2021/4/10 0010 1:15
*/
@FeignClient(name =
"demo-user", url = "demo-user:18001")
public interface UserFeign {

@PostMapping(
"hello")
String hello(@RequestBody String param);
}


————————————————

相关推荐

使用 python-fire 快速构建 CLI_如何搭建python项目架构

命令行应用程序是开发人员最好的朋友。想快速完成某事?只需敲击几下键盘,您就已经拥有了想要的东西。Python是许多开发人员在需要快速组合某些东西时选择的第一语言。但是我们拼凑起来的东西在大多数时候并...

Python 闭包:从底层逻辑到实战避坑,附安全防护指南

一、闭包到底是什么?你可以把闭包理解成一个"带记忆的函数"。它诞生时会悄悄记下自己周围的变量,哪怕跑到别的地方执行,这些"记忆"也不会丢失。就像有人出门时总会带上...

使用Python实现九九乘法表的打印_用python打印一个九九乘法表

任务要求九九乘法表的结构如下:1×1=11×2=22×2=41×3=32×3=63×3=9...1×9=92×9=18...9×9=81使用Python编写程序,按照上述格式打印出完整的九...

吊打面试官(四)--Java语法基础运算符一文全掌握

简介本文介绍了Java运算符相关知识,包含运算规则,运算符使用经验,特殊运算符注意事项等,全文5400字。熟悉了这些内容,在运算符这块就可以吊打面试官了。Java运算符的规则与特性1.贪心规则(Ma...

Python三目运算基础与进阶_python三目运算符判断三个变量

#头条创作挑战赛#Python中你学会了三步运算,你将会省去很多无用的代码,我接下来由基础到进阶的方式讲解Python三目运算基础在Python中,三目运算符也称为条件表达式。它可以通过一行代码实现条...

Python 中 必须掌握的 20 个核心函数——set()详解

set()是Python中用于创建集合的核心函数,集合是一种无序、不重复元素的容器,非常适合用于成员检测、去重和数学集合运算。一、set()的基本用法1.1创建空集合#创建空集合empty_se...

15个让Python编码效率翻倍的实用技巧

在软件开发领域,代码质量往往比代码数量更重要。本文整理的15个Python编码技巧,源自开发者在真实项目中验证过的工作方法,能够帮助您用更简洁的代码实现更清晰的逻辑。这些技巧覆盖基础语法优化到高级特性...

《Python从小白到入门》自学课程目录汇总(和猫妹学Python)

小朋友们好,大朋友们好!不知不觉,这套猫妹自学Python基础课程已经结束了,猫妹体会到了水滴石穿的力量。水一直向下滴,时间长了能把石头滴穿。只要坚持不懈,细微之力也能做出很难办的事。就比如咱们的学习...

8÷2(2+2) 等于1还是16?国外网友为这道小学数学题吵疯了……

近日,国外网友因为一道小学数学题在推特上争得热火朝天。事情的起因是一个推特网友@pjmdoll发布了一条推文,让他的关注者解答一道数学题:Viralmathequationshavebeen...

Python学不会来打我(21)python表达式知识点汇总

在Python中,表达式是由变量、运算符、函数调用等组合而成的语句,用于产生值或执行特定操作。以下是对Python中常见表达式的详细讲解:1.1算术表达式涉及数学运算的表达式。例如:a=5b...

Python运算符:数学助手,轻松拿咧

Python中的运算符就像是生活中的数学助手,帮助我们快速准确地完成这些计算。比如购物时计算总价、做家务时分配任务等。这篇文章就来详细聊聊Python中的各种运算符,并通过实际代码示例帮助你更好地理解...

Python学不会来打我(17)逻辑运算符的使用方法与使用场景

在Python编程中,逻辑运算符(LogicalOperators)是用于组合多个条件表达式的关键工具。它们可以将多个布尔表达式连接起来,形成更复杂的判断逻辑,并返回一个布尔值(True或Fa...

Python编程基础:运算符的优先级_python中的运算符优先级问题

多个运算符同时出现在一个表达式中时,先执行哪个,后执行哪个,这就涉及运算符的优先级。如数学表达式,有+、-、×、÷、()等,优先级顺序是()、×、÷、+、-,如5+(5-3)×4÷2,先计算(5-3)...

Python运算符与表达式_python中运算符&的功能

一、运算符分类总览1.Python运算符全景图2.运算符优先级表表1.3.1Python运算符优先级(从高到低)优先级运算符描述结合性1**指数右→左2~+-位非/一元加减右→左3*//...

Python操作Excel:从基础到高级的深度实践

Python凭借其丰富的库生态系统,已成为自动化处理Excel数据的强大工具。本文将深入探讨五个关键领域,通过实际代码示例展示如何利用Python进行高效的Excel操作,涵盖数据处理、格式控制、可视...

取消回复欢迎 发表评论: