手寫一個rpc遠程調用服務demo
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質文章,第一時間送達
前言
因為公司業(yè)務需求,使用了K8S + istio進行服務部署和治理,沒有使用常規(guī)的springclould技術棧(包括注冊中心nacos和openfeign遠程服務調用)。
所以就自己開發(fā)了一個基于AOP實現的rpc遠程調用服務模塊。其實現原理實現和feign類似,都是通過遠程調用方法的代理對象發(fā)送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,就會對方法進行代理方式請求。因為這里不需要原本遠程調用方法的執(zhí)行結果,所以這里直接使用@Around環(huán)繞切面,并且不需要執(zhí)行原方法,所以直接使用JoinPoint 做參數接口(ProceedingJoinPoint繼承自JoinPoint,里面多了兩個阻塞方法proceed,用于獲取原代理方法的執(zhí)行結果)。
通過代理對象獲取到原方法的參數值,參數名,接口路由地址,接口請求方式,遠程服務和端口等等。使用okhttp工具類發(fā)送代理請求,然后返回響應結果。
/**
* @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服務中如果有遠程調用場景,就創(chuàng)建一個遠程調用類。使用注解@RpcService和@Post即可。方法中的返回值和返回類型可以自定義,比如一般項目中會有統(tǒng)一的響應結果。
/**
* @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負載均衡進行轉發(fā)請求。如果直接寫服務訪問域名就只能訪問一個機子上的服務了。

先看一下兩個服務的配置文件和demo-order的接口



服務啟動后,訪問http://localhost:18001/post,結果如下圖:


最后的結果和我們想要的結果一致。
上面的demo是很簡單的實現。如果讀者想要在自己項目中使用此類技術棧,那需要考慮服務容錯,服務發(fā)現,服務限流等等是否能兼容等。
最后
openfeign其實是可以獨立和springboot進行使用的。先引入openfeign的maven包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.0.2</version>
</dependency>
然后在使用@FeignClient注解時,對url配置接口的訪問地址,最后的執(zhí)行結果和上述的結果是一樣的。
/**
* @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);
}
————————————————
版權聲明:本文為CSDN博主「IAmZRH」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權協(xié)議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:
https://blog.csdn.net/qq_41665452/article/details/115562720
鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布
??????
??長按上方微信二維碼 2 秒
感謝點贊支持下哈 
