本博客文章(学习笔记)导航 (点击这里访问)
RPC:远程过程调用,是分布式系统常见的一种通信方法,从跨进程到跨物理机已经有十几年的历史
优点:可以将远程调用变成像调用本地方法一样简单
系统的交互方式有两种:直接交互 和 间接交互(中间件交互),下面介绍一下这两种交互方式
step1 server把自己的服务注册到registery
step2 client定于redistry,获取自己想知道的服务信息
step3 如果server信息发生了改变,registory会通知订阅者信息发生了改变
step4 client要发起调用,就可以根据从registory中获取的信息直接调用即可
step1 client调用接口方法(stub中的接口方法)
step2 将调用信息序列号,以便于在网络上传输
step3 client和server之间建立网络连接
step4 server反序列化传输对象
step5 server的stub查找要调用的方法以及参数
step6 server找到实际实现类的对象,通过反射获取执行结果,再次发送到stub上
step7 stub序列化传输对象
step8 server和client建立网络连接
step9 client反序列化传输对象
step10 client获取调用结果
一共5大模块
step1 新建项目
step2 在项目下,新建6个模块,删除src文件
client:客户端模块
server:服务端模块
codec:编码解码模块
common:通用模块
propto:协议模块
transport:网络通信模块
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.5version>
dependency>
<dependency>
<groupId>org.eclipse.jettygroupId>
<artifactId>jetty-servletartifactId>
<version>9.4.19.v20190610version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.44version>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.8version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.26version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.3version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
<properties>
<java.version>1.8java.version>
<common.version>2.5common.version>
<jetty.version>9.4.19.v20190610jetty.version>
<fastjson.version>1.2.44fastjson.version>
<lombok.version>1.18.8lombok.version>
<slf4j.version>1.7.26slf4j.version>
<logback.version>1.2.3logback.version>
<junit.version>4.12junit.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>${common.version}version>
dependency>
<dependency>
<groupId>org.eclipse.jettygroupId>
<artifactId>jetty-servletartifactId>
<version>${jetty.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>${fastjson.version}version>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>${logback.version}version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.3version>
<configuration>
<source>${java.version}source>
<target>${java.version}target>
configuration>
plugin>
plugins>
build>
协议模块主要包括 网络结点、请求对象、响应对象、服务类
package com.smgeek.gkrpc;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 表示网络传输的一个端点
*/
@Data
@AllArgsConstructor
public class Peer {
private String host;
private int port;
}
package com.smgeek.gkrpc;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 表示服务
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ServiceDescriptor {
private String clazz;
private String method;
private String returnType;
private String[] parameterTypes;
}
package com.smgeek.gkrpc;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 表示请求的类
*/
@Data
public class Request {
private ServiceDescriptor service;
private Object[] parameters;
}
package com.smgeek.gkrpc;
import lombok.Data;
/**
* 表示响应的类
*/
@Data
public class Response {
/**
* 服务返回编码
* 0 成功
* 非0 失败
*/
private int code=0;
/**
* 具体错误的响应信息
*/
private String message="ok";
/**
* 返回数据
*/
private Object data;
}
通用模块只有一个动态代理
动态代理
package com.smgeek.gkrpc.common.utils;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
/**
* 反射工具类
*/
public class ReflectionUtils {
/**
* 根据class创建对象
* @param clazz 带创建的对象类
* @param 对象类型
* @return 创建好的对象
*/
public static <T>T newInstance(Class<T> clazz){
try{
return clazz.newInstance();
}catch (Exception e){
throw new IllegalStateException(e);
}
}
/**
* 获取某个clazz的共有方法
* @param clazz 任意类
* @return 当前类的所有方法
*/
public static Method[] getPublicMethods(Class clazz){
//获取这个对象的所有方法
Method[] methods=clazz.getDeclaredMethods();
List<Method> pmethods=new ArrayList<>();
for(Method m:methods){
if(Modifier.isPublic(m.getModifiers())){
pmethods.add(m);
}
}
return pmethods.toArray(new Method[0]);
}
/**
* 调用指定对象的指定方法
*
* @param obj 被调用方法的对象
* @param method 被调用的方法
* @param args 方法参数
* @return 返回代理生成的对象
*/
public static Object invoke(Object obj,Method method,Object... args){
try{
return method.invoke(obj,args);
}catch (Exception e){
throw new IllegalStateException(e);
}
}
}
package com.smgeek.gkrpc.common.utils;
import org.junit.Test;
public class TestClass {
private String a(){
return "a";
}
public String b(){
return "b";
}
protected String c(){
return "c";
}
}
package com.smgeek.gkrpc.common.utils;
import org.junit.Test;
import java.lang.reflect.Method;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class ReflectionUtilsTest {
@Test
public void newInstance(){
TestClass t=ReflectionUtils.newInstance(TestClass.class);
assertNotNull(t);
}
@Test
public void getPublicMethods(){
Method[] methods=ReflectionUtils.getPublicMethods(TestClass.class);
assertEquals(1,methods.length);
String mname=methods[0].getName();
assertEquals("b",mname);
}
@Test
public void invoke(){
Method[] methods=ReflectionUtils.getPublicMethods(TestClass.class);
Method b=methods[0];
TestClass t=new TestClass();
Object r = ReflectionUtils.invoke(t, b);
assertEquals("b",r);
}
}
便于对象在网络上传输,需要序列化和反序列化
package com.smgeek.gkrpc.codec;
/**
* 序列化 对象转化为byte数组
*/
public interface Encoder {
byte[] encode(Object obj);
}
package com.smgeek.gkrpc.codec;
/**
* 反序列化 二进制数组转对象
*/
public interface Decoder {
<T>T decode(byte[] bytes,Class<T> clazz);
}
<dependencies>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
dependency>
dependencies>
package com.smgeek.gkrpc.codec;
import com.alibaba.fastjson.JSON;
/**
* 基于json的序列化实现
*/
public class JSONEncoder implements Encoder {
@Override
public byte[] encode(Object obj) {
return JSON.toJSONBytes(obj);
}
}
package com.smgeek.gkrpc.codec;
import com.alibaba.fastjson.JSON;
/**
* 基于JSON 的反序列化
*/
public class JSONDecoder implements Decoder{
@Override
public <T> T decode(byte[] bytes, Class<T> clazz) {
return JSON.parseObject(bytes,clazz);
}
}
package com.smgeek.gkrpc.codec;
import lombok.Data;
@Data
public class TestBean {
private String name;
private int age;
}
package com.smgeek.gkrpc.codec;
import org.junit.Test;
import java.lang.reflect.Method;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class JSONEncoderTest {
@Test
public void encode(){
Encoder encoder=new JSONEncoder();
TestBean bean=new TestBean();
bean.setName("smgeek");
bean.setAge(18);
byte[] bytes=encoder.encode(bean);
assertNotNull(bytes);
}
}
package com.smgeek.gkrpc.codec;
import java.lang.reflect.Method;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
public class JSONDecoderTest {
@Test
public void decode() {
Encoder encoder=new JSONEncoder();
TestBean bean=new TestBean();
bean.setName("smgeek");
bean.setAge(18);
byte[] bytes=encoder.encode(bean);
Decoder decoder=new JSONDecoder();
TestBean bean2 = decoder.decode(bytes, TestBean.class);
assertEquals(bean.getName(),bean2.getName());
assertEquals(bean.getAge(),bean2.getAge());
}
}
<dependencies>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
dependency>
<dependency>
<groupId>org.eclipse.jettygroupId>
<artifactId>jetty-servletartifactId>
dependency>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-protoartifactId>
<version>${project.version}version>
dependency>
dependencies>
客户端主要作用
package com.smgeek.gkrpc.transport;
import com.smgeek.gkrpc.Peer;
import java.io.InputStream;
/**
* 1 创建连接
* 2 发送数据 并且等待响应
* 3 关闭连接
*/
public interface TransportClient {
void connect(Peer peer);
InputStream write(InputStream data);
void close();
}
服务端主要作用
package com.smgeek.gkrpc.transport;
/**
* 1 启动 监听端口
* 2 接受请求
* 3 关闭监听
*/
public interface TransportServer {
void init(int port,RequestHandler handler);
void start();
void stop();
}
package com.smgeek.gkrpc.transport;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 处理网络请求的handler
*/
public interface RequestHandler {
void onRequest(InputStream recive, OutputStream toResp);
}
package com.smgeek.gkrpc.transport;
import com.smgeek.gkrpc.Peer;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* HTTP 客户端
*/
public class HTTPTransportClient implements TransportClient {
private String url;
@Override
public void connect(Peer peer) {
this.url="http://"+peer.getHost()+":"+peer.getPort();
}
@Override
public InputStream write(InputStream data) {
try {
HttpURLConnection httpConn =(HttpURLConnection)new URL(url).openConnection();
httpConn.setDoOutput(true);
httpConn.setDoInput(true);
httpConn.setRequestMethod("POST");
httpConn.connect();
IOUtils.copy(data,httpConn.getOutputStream());
int resultCode=httpConn.getResponseCode();
if(resultCode==HttpURLConnection.HTTP_OK){
return httpConn.getInputStream();
}else{
return httpConn.getErrorStream();
}
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
@Override
public void close() {
}
}
package com.smgeek.gkrpc.transport;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* HTTP 服务端
*/
@Slf4j
public class HTTPTransportServer implements TransportServer{
private RequestHandler handler;
private Server server;
@Override
public void init(int port, RequestHandler handler) {
this.handler=handler;
this.server=new Server(port);
//servlet 接收请求
ServletContextHandler ctx=new ServletContextHandler();
server.setHandler(ctx);
ServletHolder holder=new ServletHolder(new ResquestServlet());
ctx.addServlet(holder,"/*");
}
@Override
public void start() {
try {
server.start();
server.join();
} catch (Exception e) {
log.error(e.getMessage(),e);
}
}
@Override
public void stop() {
try {
server.stop();
} catch (Exception e) {
log.error(e.getMessage(),e);
}
}
class ResquestServlet extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
log.info("client connect");
InputStream in=req.getInputStream();
OutputStream out=resp.getOutputStream();
if(handler!=null){
handler.onRequest(in,out);
}
out.flush();
}
}
}
<dependencies>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-protoartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-codecartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-transportartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-commonartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
dependency>
dependencies>
package com.smgeek.gkrpc;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
/**
* 表示服务
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ServiceDescriptor {
private String clazz;
private String method;
private String returnType;
private String[] parameterTypes;
public static ServiceDescriptor from(Class clazz, Method method){
ServiceDescriptor sdp=new ServiceDescriptor();
sdp.setClazz(clazz.getName());
sdp.setMethod(method.getName());
sdp.setReturnType(method.getReturnType().getName());
Class[] parameterClasses=method.getParameterTypes();
String[] parameterTypes=new String[parameterClasses.length];
for(int i=0;i<parameterClasses.length;i++){
parameterTypes[i]=parameterClasses[i].getName();
}
sdp.setParameterTypes(parameterTypes);
return sdp;
}
@Override
public String toString() {
return "ServiceDescriptor{" +
"clazz='" + clazz + '\'' +
", method='" + method + '\'' +
", returnType='" + returnType + '\'' +
", parameterTypes=" + Arrays.toString(parameterTypes) +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServiceDescriptor that = (ServiceDescriptor) o;
return Objects.equals(clazz, that.clazz) &&
Objects.equals(method, that.method) &&
Objects.equals(returnType, that.returnType) &&
Arrays.equals(parameterTypes, that.parameterTypes);
}
@Override
public int hashCode() {
return toString().hashCode();
}
}
package com.smgeek.gkrpc.server;
import com.smgeek.gkrpc.codec.Decoder;
import com.smgeek.gkrpc.codec.Encoder;
import com.smgeek.gkrpc.codec.JSONEncoder;
import com.smgeek.gkrpc.transport.HTTPTransportServer;
import com.smgeek.gkrpc.transport.TransportServer;
import lombok.Data;
/**
* 配置
*/
@Data
public class RpcServerConfig {
private Class<? extends TransportServer> transportClass = HTTPTransportServer.class;
private Class<? extends Encoder> encoderClass= JSONEncoder.class;
private Class<? extends Decoder> decoderClass=Decoder.class;
private int port=3000;
}
package com.smgeek.gkrpc.server;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.lang.reflect.Method;
/**
* 表示一个具体服务
*/
@Data
@AllArgsConstructor
public class ServiceInstance {
private Object target;
private Method method;
}
package com.smgeek.gkrpc.server;
import com.smgeek.gkrpc.Request;
import com.smgeek.gkrpc.ServiceDescriptor;
import com.smgeek.gkrpc.common.utils.ReflectionUtils;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 管理rpc暴漏的服务
*/
@Slf4j
public class ServiceManager {
private Map<ServiceDescriptor ,ServiceInstance> sercices;
public ServiceManager(){
this.sercices=new ConcurrentHashMap<>();
}
public <T> void register(Class<T> interfaceClass,T bean ){
Method[] methods = ReflectionUtils.getPublicMethods(interfaceClass);
for(Method method:methods){
ServiceInstance sis=new ServiceInstance(bean,method);
ServiceDescriptor sdp=ServiceDescriptor.from(interfaceClass,method);
sercices.put(sdp,sis);
log.info("register service: {} {}",sdp.getClazz(),sdp.getMethod());
}
}
public ServiceInstance lookup(Request request){
ServiceDescriptor sdp=request.getService();
return sercices.get(sdp);
}
}
package com.smgeek.gkrpc.server;
public interface TestInterface {
void hello();
}
package com.smgeek.gkrpc.server;
public class TestClass implements TestInterface {
@Override
public void hello() {
}
}
package com.smgeek.gkrpc.server;
import com.smgeek.gkrpc.Request;
import com.smgeek.gkrpc.ServiceDescriptor;
import com.smgeek.gkrpc.common.utils.ReflectionUtils;
import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.Method;
import static org.junit.Assert.assertNotNull;
public class ServiceManagerTest {
ServiceManager sm;
@Before
public void init(){
sm=new ServiceManager();
TestInterface bean=new TestClass();
sm.register(TestInterface.class,bean);
}
@Test
public void register(){
TestInterface bean=new TestClass();
sm.register(TestInterface.class,bean);
}
@Test
public void lookup(){
Method method = ReflectionUtils.getPublicMethods(TestInterface.class)[0];
ServiceDescriptor sdp=ServiceDescriptor.from(TestInterface.class,method);
Request request=new Request();
request.setService(sdp);
ServiceInstance sis = sm.lookup(request);
assertNotNull(sis);
}
}
package com.smgeek.gkrpc.server;
import com.smgeek.gkrpc.Request;
import com.smgeek.gkrpc.Response;
import com.smgeek.gkrpc.ServiceDescriptor;
import com.smgeek.gkrpc.codec.Decoder;
import com.smgeek.gkrpc.codec.Encoder;
import com.smgeek.gkrpc.common.utils.ReflectionUtils;
import com.smgeek.gkrpc.transport.RequestHandler;
import com.smgeek.gkrpc.transport.TransportServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
/**
*
* @author CandyDingDing
* @version 1.1.0
* @date 2022/4/3
*/
@Slf4j
public class RpcServer {
private RpcServerConfig config;
private TransportServer net;
private Encoder encoder;
private Decoder decoder;
private ServiceManager serviceManager;
private ServiceInvoker serviceInvoker;
public RpcServer(RpcServerConfig config) {
this.config = config;
this.net = ReflectionUtils.newInstance(config.getTransportClass());
this.net.init(config.getPort(),handler);
this.encoder = ReflectionUtils.newInstance(config.getEncoderClass());;
this.decoder = ReflectionUtils.newInstance(config.getDecoderClass());
this.serviceManager = new ServiceManager();
this.serviceInvoker = new ServiceInvoker();
}
public <T> void register(Class<T> interfaceClass,T bean ){
serviceManager.register(interfaceClass,bean);
}
public void start(){
this.net.start();;
}
public void stop(){
this.net.stop();
}
private RequestHandler handler= new RequestHandler() {
@Override
public void onRequest(InputStream recive, OutputStream toResp) {
Response resp=new Response();
try {
byte[] inBytes= IOUtils.readFully(recive,recive.available());
Request request=decoder.decode(inBytes,Request.class);
log.info("get request:{}",request);
ServiceInstance sis=serviceManager.lookup(request);
Object ret=serviceInvoker.invoke(sis,request);
resp.setData(ret);
} catch (Exception e) {
log.warn(e.getMessage(),e);
resp.setCode(1);
resp.setMessage("RescServer got error:"+e.getClass().getName()+":"+e.getMessage());
}finally {
try {
byte[] outBytes=encoder.encode(resp);
toResp.write(outBytes);
} catch (IOException e) {
log.warn(e.getMessage(),e);
}
}
}
};
}
<dependencies>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-protoartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-codecartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-transportartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-commonartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
dependency>
dependencies>
package com.smgeek.gkrpc.client;
import com.smgeek.gkrpc.Peer;
import com.smgeek.gkrpc.transport.TransportClient;
import java.util.List;
/**
* @Describe 选择哪个Server来服务
* @Author CandyDingDing
* @Version 1.0
* @Motto 且视他人之疑目如盏盏鬼火,大胆地去走吾之夜路
* @Date 2022/4/3
*/
public interface TransportSelector {
/**
* 初始化selector
* @param peers 可以链接的server端点信息
* @param count client和server的建立多少连接
* @param clazz client实现的class
*/
void init(List<Peer> peers, int count,Class<? extends TransportClient> clazz);
/**
* 选择一个transport 与 server交互
*
* @return 网络client
*/
TransportClient select();
/**
* 释放用完的client
* @param client TransportClient
*/
void release(TransportClient client);
void close();
}
package com.smgeek.gkrpc.client;
import com.smgeek.gkrpc.Peer;
import com.smgeek.gkrpc.common.utils.ReflectionUtils;
import com.smgeek.gkrpc.transport.TransportClient;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@Slf4j
public class RandomTransportSelector implements TransportSelector {
/**
* 存储连介绍的客户端
*/
private List<TransportClient> clients;
public RandomTransportSelector() {
clients=new ArrayList<>();
}
@Override
public synchronized void init(List<Peer> peers, int count, Class<? extends TransportClient> clazz) {
count=Math.max(count,1);
for(Peer peer:peers){
for(int i=0;i<count;i++){
TransportClient client= ReflectionUtils.newInstance(clazz);
client.connect(peer);
clients.add(client);
log.info("connect server:{} ",peer);
}
}
}
@Override
public synchronized TransportClient select() {
int i=new Random().nextInt(clients.size());
return clients.remove(i);
}
@Override
public synchronized void release(TransportClient client) {
clients.add(client);
}
@Override
public synchronized void close() {
for(TransportClient client:clients){
client.close();
}
clients.clear();
}
}
package com.smgeek.gkrpc.client;
import com.smgeek.gkrpc.Peer;
import com.smgeek.gkrpc.codec.Decoder;
import com.smgeek.gkrpc.codec.Encoder;
import com.smgeek.gkrpc.codec.JSONDecoder;
import com.smgeek.gkrpc.codec.JSONEncoder;
import com.smgeek.gkrpc.transport.HTTPTransportClient;
import com.smgeek.gkrpc.transport.TransportClient;
import lombok.Data;
import java.util.Arrays;
import java.util.List;
/**
* @Author CandyDingDing
* @Version 1.0
* @Motto 且视他人之疑目如盏盏鬼火,大胆地去走吾之夜路
* @Describe
* @Date 2022/4/4
*/
@Data
public class RpcClientConfig {
private Class<? extends TransportClient> transportClass= HTTPTransportClient.class;
private Class<? extends Encoder> encoderClass =JSONEncoder.class;
private Class<? extends Decoder> decoderClass = JSONDecoder.class;
private Class<? extends TransportSelector> selectorClass=RandomTransportSelector.class;
private int connectCount=1;
private List<Peer> severs=Arrays.asList(new Peer("127.0.0.1",3000));
}
package com.smgeek.gkrpc.client;
import com.smgeek.gkrpc.codec.Decoder;
import com.smgeek.gkrpc.codec.Encoder;
import com.smgeek.gkrpc.common.utils.ReflectionUtils;
import java.lang.reflect.Proxy;
/**
* @Author CandyDingDing
* @Version 1.0
* @Motto 且视他人之疑目如盏盏鬼火,大胆地去走吾之夜路
* @Describe
* @Date 2022/4/4
*/
public class RpcClient {
private RpcClientConfig config;
private Encoder encoder;
private Decoder decoder;
private TransportSelector selector;
public RpcClient() {
}
public RpcClient(RpcClientConfig config) {
this.config = config;
this.encoder= ReflectionUtils.newInstance(this.config.getEncoderClass());
this.decoder= ReflectionUtils.newInstance(this.config.getDecoderClass());
this.selector= ReflectionUtils.newInstance(this.config.getSelectorClass());
this.selector.init(this.config.getSevers(),this.config.getConnectCount(),this.config.getTransportClass());
}
public <T> T getProxy(Class<T> clazz){
return (T) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[]{clazz},
new RemoteInvoker(clazz,encoder,decoder,selector)
);
}
}
package com.smgeek.gkrpc.client;
import com.smgeek.gkrpc.Request;
import com.smgeek.gkrpc.Response;
import com.smgeek.gkrpc.ServiceDescriptor;
import com.smgeek.gkrpc.codec.Decoder;
import com.smgeek.gkrpc.codec.Encoder;
import com.smgeek.gkrpc.transport.TransportClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @Author CandyDingDing
* @Version 1.0
* @Motto 且视他人之疑目如盏盏鬼火,大胆地去走吾之夜路
* @Describe 调用远程服务的代理类
* @Date 2022/4/4
*/
@Slf4j
public class RemoteInvoker implements InvocationHandler {
private Class clazz;
private Encoder encoder;
private Decoder decoder;
private TransportSelector selector;
public RemoteInvoker(Class clazz, Encoder encoder, Decoder decoder,TransportSelector selector){
this.decoder=decoder;
this.encoder=encoder;
this.selector=selector;
this.clazz=clazz;
};
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Request request=new Request();
request.setService(ServiceDescriptor.from(clazz,method));
request.setParameters(args);
Response resp = invokeRemote(request);
if(resp==null || resp.getCode()!=0){
throw new IllegalStateException("fail to invoke remotr"+resp);
}
return resp.getData();
}
private Response invokeRemote(Request request) {
TransportClient client=null;
Response resp=null;
try{
client=selector.select();
byte[] outBytes=encoder.encode(request);
InputStream revice = client.write(new ByteArrayInputStream(outBytes));
byte[] inBytes=IOUtils.readFully(revice,revice.available());
resp=decoder.decode(inBytes,Response.class);
}catch (Exception e){
log.warn(e.getMessage(),e);
resp=new Response();
resp.setCode(1);
resp.setMessage("RpcClient got error :"+e.getClass()+":"+e.getMessage());
} finally {
if(client!=null){
selector.release(client);
}
}
return resp;
}
}
<dependencies>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-clientartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>hand.candygroupId>
<artifactId>gk-rpc-serverartifactId>
<version>${project.version}version>
dependency>
dependencies>
package single.rpc.example;
import single.rpc.client.RpcClient;
/**
* @Author CandyDingDing
* @Version 1.0
* @Motto 且视他人之疑目如盏盏鬼火,大胆地去走吾之夜路
* @Describe
* @Date 2022/4/4
*/
public class Client {
public static void main(String[] args) {
RpcClient client = new RpcClient();
CalcService service = client.getProxy(CalcService.class);
int add = service.add(1, 2);
int minus = service.minus(1, 2);
System.out.println(add);
System.out.println(minus);
}
}
package single.rpc.example;
import single.rpc.server.RpcServer;
import single.rpc.server.RpcServerConfig;
/**
* @Author CandyDingDing
* @Version 1.0
* @Motto 且视他人之疑目如盏盏鬼火,大胆地去走吾之夜路
* @Describe
* @Date 2022/4/4
*/
public class Server {
public static void main(String[] args) {
RpcServer server = new RpcServer(new RpcServerConfig());
server.register(CalcService.class, new CalcServiceImpl());
server.start();
}
}
package single.rpc.example;
/**
* @Author CandyDingDing
* @Version 1.0
* @Motto 且视他人之疑目如盏盏鬼火,大胆地去走吾之夜路
* @Describe
* @Date 2022/4/4
*/
public interface CalcService {
int add(int a, int b);
int minus(int a, int b);
}
package single.rpc.example;
/**
* @Author CandyDingDing
* @Version 1.0
* @Motto 且视他人之疑目如盏盏鬼火,大胆地去走吾之夜路
* @Describe git镜像:https://hub.fastgit.xyz/
* @Date 2022/4/4
*/
public class CalcServiceImpl implements CalcService {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public int minus(int a, int b) {
return a - b;
}
}
视频课里面的代码有问题:
修正后的代码可以参考https://https://gitee.com/candydingding/rpc
先启动server 再启动client
server结果
视频连接:https://www.imooc.com/video/20219
文档笔记:https://www.yuque.com/lililil-9bxsv/kb/tg9xha
代码地址:https://gitee.com/candydingding/rpc