赞
踩
首先,根据rpc的调用逻辑,需要实现 " 像调用本地方法一样,调用远程方法“,那么我们需要考虑以下几点:
举个例子,老王在外面出差,身份证忘带了,打电话让老婆把身份证拍个照片发给他。那么这个行为就是rpc远程调用。本来如果老王在家,就可以自己拍照。但是现在人在外面,打电话让老婆拍身份证照片,就是之前说的rpc主旨 ” 像调用本地方法一样,调用远程方法“。
a、 rpc-consumer(消费者模块):
项目角色中的消费者,主要进行远程调用方。例如上面那个例子中出差的老王,他要做的一些行为都是消费者行为。
b、 rpc-core(自定义rpc核心模块):
整个框架的核心骨架部分,大脑核心。
c、 rpc-provider(生产者模块):
项目角色中的生产者,主要是被远程调用方。例如上面那个例子中的老婆,在案例中为老王提供服务。
d、 rpc-demo-api(api的模块):
该模块主要包括接口层和实体类,为项目提供可远程调用的功能方法。
① .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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>rpc-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>rpc-consumer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.example</groupId> <artifactId>rpc-core</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.example</groupId> <artifactId>rpc-demo-api</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </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依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>rpc-core</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.3.RELEASE</version> <relativePath/> </parent> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.70</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.12.2</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-client</artifactId> <version>5.1.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>5.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </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 <T> * @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依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>rpc-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>rpc-provider</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.example</groupId> <artifactId>rpc-core</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.example</groupId> <artifactId>rpc-demo-api</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </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依赖
<?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> <parent> <groupId>com.example</groupId> <artifactId>rpc-core</artifactId> <version>1.0-SNAPSHOT</version> <!-- <relativePath/> <!– lookup parent from repository –>--> </parent> <groupId>com.example</groupId> <artifactId>rpc-demo-api</artifactId> <version>0.0.1-SNAPSHOT</version> <name>rpc-demo-api</name> <properties> <java.version>1.8</java.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底层框架技术,整个文档起到笔记作用,其中带一些自己的理解,文中可能会有一些不专业,还望大家阅读之后能够指出。谢谢大家,一起进步。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。