首先,根据rpc的调用逻辑,需要实现 " 像调用本地方法一样,调用远程方法“,那么我们需要考虑以下几点:
举个例子,老王在外面出差,身份证忘带了,打电话让老婆把身份证拍个照片发给他。那么这个行为就是rpc远程调用。本来如果老王在家,就可以自己拍照。但是现在人在外面,打电话让老婆拍身份证照片,就是之前说的rpc主旨 ” 像调用本地方法一样,调用远程方法“。
a、 rpc-consumer(消费者模块):
项目角色中的消费者,主要进行远程调用方。例如上面那个例子中出差的老王,他要做的一些行为都是消费者行为。
b、 rpc-core(自定义rpc核心模块):
整个框架的核心骨架部分,大脑核心。
c、 rpc-provider(生产者模块):
项目角色中的生产者,主要是被远程调用方。例如上面那个例子中的老婆,在案例中为老王提供服务。
d、 rpc-demo-api(api的模块):
该模块主要包括接口层和实体类,为项目提供可远程调用的功能方法。
① .pom依赖
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.4version>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>rpc-consumerartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>rpc-consumername>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>com.examplegroupId>
<artifactId>rpc-coreartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.examplegroupId>
<artifactId>rpc-demo-apiartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
exclude>
excludes>
configuration>
plugin>
plugins>
build>
project>
②. application.properties配置端口号
server.port=8000
③. RpcConsumerApplication在启动类模拟消费情况
package com.example.rpcconsumer;
import com.example.rpc.client.Rpcfx;
import com.example.rpc.demo.api.Order;
import com.example.rpc.demo.api.OrderService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RpcConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(RpcConsumerApplication.class, args);
//调用
OrderService orderService = Rpcfx.create(OrderService.class, "http://localhost:8080/");
Order orderById = orderService.findOrderById(1);
System.out.println("实际调用结果:");
System.out.println(orderById);
}
}
我们底层采用:
① .pom依赖
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>rpc-coreartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.3.RELEASEversion>
<relativePath/>
parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.6.0version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.70version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.16version>
dependency>
<dependency>
<groupId>com.squareup.okhttp3groupId>
<artifactId>okhttpartifactId>
<version>3.12.2version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-clientartifactId>
<version>5.1.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>5.1.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
project>
②. api 包自定义的一些类RpcRequest、RpcResponse、RpcResolver
package com.example.rpc.api;
import lombok.Data;
/**
* 请求实体
*/
@Data
public class RpcRequest {
/**
* 哪个接口类
*/
private String serviceClass;
/**
* 哪个方法
*/
private String method;
/**
* 参数
*/
private Object [] params;
}
package com.example.rpc.api;
import lombok.Data;
/**
* 响应实体
*/
@Data
public class RpcResponse {
/**
* 响应状态
*/
private String status;
/**
* 返回值
*/
private Object result;
/**
* xinxi
*/
private String message;
private Exception exception;
}
package com.example.rpc.api;
/**
* 用与真实对象获取
*/
public interface RpcResolver {
/**
* 获取bean
* @param serviceClass
* @return
*/
Object resolve(String serviceClass);
/**
* 获取bean+泛型
* @param aClass
* @param
* @return
*/
<T>T resolveT(Class<T> aClass);
}
③. client包自定义主要客户端用的请求,代理请求调用生产者并处理Rpcfx
package com.example.rpc.client;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;
import com.example.rpc.api.RpcRequest;
import com.example.rpc.api.RpcResponse;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* RPC核心板块
*/
public class Rpcfx {
public static <T>T create(final Class<T> tClass,final String url) {
//用动态代理替换
return (T)Proxy.newProxyInstance(Rpcfx.class.getClassLoader(),new Class[]{tClass},new RpcHandler(tClass, url));
}
/**
* 自定义处理类,动态代理实际运行的类
*/
public static class RpcHandler implements InvocationHandler{
// public final MediaType JSONTYPE = MediaType.get("application/json; charset=utf-8");
public static MediaType JSONTYPE = MediaType.parse("application/json;charset=UTF-8");
private final Class<?> serviceClass;
private final String url;
public RpcHandler(Class<?> serviceClass, String url) {
this.serviceClass = serviceClass;
this.url = url;
}
//实际调用的方法
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
//建立一个请求体
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setMethod(method.getName());
rpcRequest.setParams(params);
rpcRequest.setServiceClass(this.serviceClass.getName());
//请求
RpcResponse rpcResponse = post(rpcRequest,url);
if(!"200".equals(rpcResponse.getStatus())){
// 这里判断response.status,处理异常
//failed
throw rpcResponse.getException();
}
// 考虑封装一个全局的RpcException
//返回具体响应内容
String s = rpcResponse.getResult().toString();
//开启autoType
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
return JSON.parse(s);
}
/**
* 采用OKHttp远程调用
* @param rpcRequest
* @param url
* @return
*/
private RpcResponse post(RpcRequest rpcRequest, String url) throws IOException {
String reqJson = JSON.toJSONString(rpcRequest);
System.out.println("远程请求参数: "+reqJson);
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(JSONTYPE,reqJson))
.build();
String respJson = okHttpClient.newCall(request).execute().body().string();
System.out.println("远程调用返回结果:"+respJson);
return JSON.parseObject(respJson,RpcResponse.class);
}
}
}
④. server包自定义配合服务端,解析rpc远程调用的底层实现类。RpcInvoker
package com.example.rpc.server;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.example.rpc.api.RpcRequest;
import com.example.rpc.api.RpcResolver;
import com.example.rpc.api.RpcResponse;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 生产者端进行反射调用
*/
public class RpcInvoker {
private RpcResolver resolver;
public RpcInvoker(RpcResolver resolver){
this.resolver = resolver;
}
public RpcResponse invoke(RpcRequest request) {
RpcResponse response = new RpcResponse();
String serviceClass = request.getServiceClass();
try {
// 作业1:改成泛型和反射
//直接用反射
// Object result = invokeOne(serviceClass,request);
Object result = invokeTwo(serviceClass,request);
// 两次json序列化能否合并成一个
response.setResult(JSON.toJSONString(result, SerializerFeature.WriteClassName));
// response.setResult(JSON.toJSONString(result));
response.setStatus("200");
return response;
} catch ( Exception e) {
// 3.Xstream
// 2.封装一个统一的RpcfxException
// 客户端也需要判断异常
e.printStackTrace();
response.setException(e);
response.setStatus("710");
return response;
}
}
/**
* 反射加泛型
* @param serviceClass
* @param request
* @return
* @throws ClassNotFoundException
*/
private Object invokeTwo(String serviceClass, RpcRequest request) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException {
Class<?> aClass = Class.forName(serviceClass);
Object obj = resolver.resolveT(aClass);
Method method = resolveMethodFromClass(aClass, request.getMethod());
return method.invoke(obj,request.getParams());
}
private Object invokeOne(String serviceClass, RpcRequest request) throws InvocationTargetException, IllegalAccessException {
Object service = resolver.resolve(serviceClass);//this.applicationContext.getBean(serviceClass);
Method method = resolveMethodFromClass(service.getClass(), request.getMethod());
return method.invoke(service, request.getParams()); // dubbo, fastjson,
}
private Method resolveMethodFromClass(Class<?> klass, String methodName) {
return Arrays.stream(klass.getMethods()).filter(m -> methodName.equals(m.getName())).findFirst().get();
}
}
①. pom依赖
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.4version>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>rpc-providerartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>rpc-providername>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>com.examplegroupId>
<artifactId>rpc-coreartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.examplegroupId>
<artifactId>rpc-demo-apiartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
exclude>
excludes>
configuration>
plugin>
plugins>
build>
project>
②. application.properties
server.port= 8080
③. DemoResolver 实现RpcResolver,ApplicationContextAware接口,目的就是获取目标bean
package com.example.rpcprovider;
import com.example.rpc.api.RpcResolver;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
/**
* 获取生产者的bean
* Created by IntelliJ IDEA.
* @author NJ
* @create 2022/3/10 17:26
*/
@Service
public class DemoResolver implements RpcResolver, ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public Object resolve(String serviceClass) {
return this.applicationContext.getBean(serviceClass);
}
@Override
public <T> T resolveT(Class<T> aClass) {
return this.applicationContext.getBean(aClass);
}
}
④. OrderServiceImpl 生产者的本地service实现方法,具体执行者
package com.example.rpcprovider;
import com.example.rpc.demo.api.Order;
import com.example.rpc.demo.api.OrderService;
import org.springframework.stereotype.Service;
@Service
public class OrderServiceImpl implements OrderService {
@Override
public Order findOrderById(int id) {
return new Order(id, "hello rpc" + System.currentTimeMillis(), 9.9f);
}
}
④. RpcProviderApplication 生产者的给消费者提供访问的入口
package com.example.rpcprovider;
import com.example.rpc.api.RpcRequest;
import com.example.rpc.server.RpcInvoker;
import com.example.rpc.api.RpcResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class RpcProviderApplication {
public static void main(String[] args) {
SpringApplication.run(RpcProviderApplication.class, args);
}
@Autowired
RpcInvoker invoker;
@PostMapping("/")
private Object ppp(@RequestBody RpcRequest request){
return invoker.invoke(request);
}
@Bean
public RpcInvoker creatBean(@Autowired RpcResolver resolver){
return new RpcInvoker(resolver);
}
}
①. pom依赖
<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.0modelVersion>
<parent>
<groupId>com.examplegroupId>
<artifactId>rpc-coreartifactId>
<version>1.0-SNAPSHOTversion>
parent>
<groupId>com.examplegroupId>
<artifactId>rpc-demo-apiartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>rpc-demo-apiname>
<properties>
<java.version>1.8java.version>
properties>
project>
②. Order业务实体
package com.example.rpc.demo.api;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
private int id;
private String name;
private float amount;
}
③. OrderService业务接口
package com.example.rpc.demo.api;
/**
* 接口类
*/
public interface OrderService {
Order findOrderById(int id);
}
先启动生产者服务,再启动消费者,即可看到消费者打印的日志,如下:
2022-03-11 11:08:39.191 INFO 23984 --- [ main] c.e.rpcconsumer.RpcConsumerApplication : Starting RpcConsumerApplication using Java 1.8.0_121 on BY with PID 23984 (E:\nj\geek_learn\src\main\java\com\nj\learn\rpc\rpc-consumer\target\classes started by rvwrb in E:\nj\geek_learn)
2022-03-11 11:08:39.196 INFO 23984 --- [ main] c.e.rpcconsumer.RpcConsumerApplication : No active profile set, falling back to default profiles: default
2022-03-11 11:08:40.138 INFO 23984 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8000 (http)
2022-03-11 11:08:40.148 INFO 23984 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-03-11 11:08:40.148 INFO 23984 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.52]
2022-03-11 11:08:40.270 INFO 23984 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-03-11 11:08:40.270 INFO 23984 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1004 ms
2022-03-11 11:08:40.665 INFO 23984 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8000 (http) with context path ''
2022-03-11 11:08:40.677 INFO 23984 --- [ main] c.e.rpcconsumer.RpcConsumerApplication : Started RpcConsumerApplication in 2.272 seconds (JVM running for 3.884)
远程请求参数: {"method":"findOrderById","params":[1],"serviceClass":"com.example.rpc.demo.api.OrderService"}
远程调用返回结果:{"status":"200","result":"{\"@type\":\"com.example.rpc.demo.api.Order\",\"amount\":9.9,\"id\":1,\"name\":\"hello rpc1646968121630\"}","message":null,"exception":null}
实际调用结果:
Order(id=1, name=hello rpc1646968121630, amount=9.9)
作者自己也是通过网上课程学习的rpc底层框架技术,整个文档起到笔记作用,其中带一些自己的理解,文中可能会有一些不专业,还望大家阅读之后能够指出。谢谢大家,一起进步。