创建一个maven工程,这个是服务端,命名为 rpc-order,maven创建的时候选择快速启动就好。创建服务端项目,名字为rpc-order,里面有两个module,分别命名为 order -api 这是接口层;还有实现层 order-provider,实现提供方法给客户端调用。
接口层创建接口方法:
package com.lp;
/**
* @auther lp
* @date 2020/6/20 0020 13:35
*/
public interface IOrderService {
String queryOrderList();
String queryById(String id);
}
然后定义一个rpc连接请求的参数类:
package com.lp;
import java.io.Serializable;
/**
* @auther lp
* @date 2020/6/20 0020 14:29
*/
public class RpcRequest implements Serializable {
private String className;
private String methodName;
private Object[] args;
private Class[] types;
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
public Class[] getTypes() {
return types;
}
public void setTypes(Class[] types) {
this.types = types;
}
}
这样api端就已经完成了,install一下。然后开始写实现的module和socket连接,实现方法,上面的注解可以先不看。
package com.lp;
/**
* @auther lp
* @date 2020/6/20 0020 13:38
*/
@LpRemoteService
public class OrderServiceImpl implements IOrderService {
@Override
public String queryOrderList() {
return "查询订单列表";
}
@Override
public String queryById(String id) {
return "通过Id查询订单";
}
}
定义一个bean里面是这个累的实例和方法
package com.lp;
import java.lang.reflect.Method;
/**
* @auther lp
* @date 2020/6/20 0020 16:01
*/
public class BeanMethod {
private Object bean;
private Method method;
public Object getBean() {
return bean;
}
public void setBean(Object bean) {
this.bean = bean;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
再定义一个类来存储发布过的实例
package com.lp;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @auther lp
* @date 2020/6/20 0020 16:00
*/
public class Mediator {
//用来存储发布服务的实例,map相当于服务调用的路由
public static Map map = new ConcurrentHashMap<>();
private volatile static Mediator instance;
private Mediator(){};
public static Mediator getInstance(){
if(instance==null){
synchronized (Mediator.class){
if(instance==null){
instance=new Mediator();
}
}
}
return instance;
}
public Object processor(RpcRequest request){
String key=request.getClassName()+"."+request.getMethodName(); //key
BeanMethod beanMethod=map.get(key);
if(beanMethod==null){
return null;
}
Object bean=beanMethod.getBean();
Method method=beanMethod.getMethod();
try {
return method.invoke(bean,request.getArgs());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
代理类: 里面的ProcessHadlerZ类是我直接升级的为注解的版本,初始化版本源码里面也有
package com.lp;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @auther lp
* @date 2020/6/20 0020 14:02
*/
public class RpcProxyServer {
private final ExecutorService executorService = Executors.newCachedThreadPool();
public void publish(Object service,int port){
ServerSocket serverSocket =null;
try {
serverSocket = new ServerSocket(port);
while (true){
Socket socket = serverSocket.accept(); //监听客户端请求
//避免IO阻塞,但是受限制与线程的资源
executorService.execute(new ProcessHadlerZ(socket));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.lp;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
/**
* @auther lp
* @date 2020/6/20 0020 16:30
*/
public class ProcessHadlerZ implements Runnable {
private Socket socket;
public ProcessHadlerZ(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
ObjectInputStream inputStream=null;
ObjectOutputStream outputStream=null;
try {
inputStream=new ObjectInputStream(socket.getInputStream());
RpcRequest request=(RpcRequest)inputStream.readObject(); //反序列化
//路由
Mediator mediator=Mediator.getInstance();
Object rs=mediator.processor(request);
System.out.println("服务端的执行结果:"+rs);
outputStream=new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(rs);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.lp;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @auther lp
* @date 2020/6/20 0020 16:04
*/
@Component
public class IntialMediator implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean.getClass().isAnnotationPresent(LpRemoteService.class)){
//只对加了服务标记的类进行处理
Method[] methods = bean.getClass().getDeclaredMethods();
for (Method method:methods) {
String key = bean.getClass().getInterfaces()[0].getName()+"."+method.getName();
BeanMethod beanMethod = new BeanMethod();
beanMethod.setBean(bean);
beanMethod.setMethod(method);
Mediator.map.put(key,beanMethod);
}
}
return bean;
}
}
最后就是socketserver的相关操作
package com.lp;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @auther lp
* @date 2020/6/20 0020 16:22
* spring容器启用完成之后会发布一个ContextRefreshedEvent
*/
@Component
public class SocketServerIntial implements ApplicationListener {
private final ExecutorService executorService= Executors.newCachedThreadPool();
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
//启动服务
ServerSocket serverSocket=null;
try {
serverSocket=new ServerSocket(8888);
while(true){
Socket socket=serverSocket.accept(); //监听客户端请求
executorService.execute(new ProcessHadlerZ(socket));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(serverSocket!=null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
自定义注解
package com.lp;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @auther lp
* @date 2020/6/20 0020 15:51
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface LpRemoteService {
}
第一个版本的启动类:
package com.lp;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
IOrderService iOrderService = new OrderServiceImpl();
RpcProxyServer rpcProxyServer = new RpcProxyServer();
rpcProxyServer.publish(iOrderService,8080);
}
}
使用注解的启动:
package com.lp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @auther lp
* @date 2020/6/20 0020 16:43
*/
@Configuration
@ComponentScan("com.lp")
public class BootStrap {
public static void main(String [] args){
ApplicationContext applicationContext=
new AnnotationConfigApplicationContext(BootStrap.class);
}
}
自此,服务端的接口实现方法只要打了自定义注解,就能被加载和识别,客户端只需要引入相应的自定义注解就可以调用,来看一下调用端,要引入这边的maven依赖
com.lp
order-api
1.0-SNAPSHOT
然后因为使用了spring-boot,所以也需要引入相应的依赖,这里需要注意的是版本号我是在parent里面定义的。
org.springframework.boot
spring-boot-starter-web
我们想跟使用spring的注解一样,将接口注入,这时候自定义注解
package com.lp;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface LpReference {
}
对加了注解的bean判断处理
package com.lp;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
/**
* @auther lp
* @date 2020/6/21 0021 12:52
*/
@Component
public class ReferenceProxy implements BeanPostProcessor {
@Autowired
RemoteHandler remoteHandler;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Field [] fields = bean.getClass().getDeclaredFields();
for (Field field:fields) {
//针对加了这个注解的,设置为一个代理的值
if(field.isAnnotationPresent(LpReference.class)){
field.setAccessible(true);
Object proxy = Proxy.newProxyInstance(field.getType().getClassLoader(),new Class>[]{field.getType()},remoteHandler);
try {
//对加了注解的加了一个代理,代理实现了remoteHandler
field.set(bean,proxy);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return bean;
}
}
定义处理类,这里的端口号和ip是在配置文件里面通过spring,因为要使用springboot启动项目内置tomcat端口号8080,所以端口号有改动,这里为8888
package com.lp;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @auther lp
* @date 2020/6/20 0020 14:14
*/
@Component
public class RemoteHandler implements InvocationHandler {
@Value("${lp.host}")
private String host;
@Value("${lp.port}")
private int port;
public RemoteHandler() {
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//建立远程连接
RpcNetTransport rpcNetTransport = new RpcNetTransport(host,port);
RpcRequest request = new RpcRequest();
request.setArgs(args);
request.setClassName(method.getDeclaringClass().getName());
request.setTypes(method.getParameterTypes()); //参数的类型
request.setMethodName(method.getName());
return rpcNetTransport.send(request);
}
}
配置文件:
lp.host=localhost
lp.port=8888
spring.application.name=rpc-service
server.port=8080
客户端代理
package com.lp;
import java.lang.reflect.Proxy;
/**
* @auther lp
* @date 2020/6/20 0020 14:08
*/
public class RpcProxyClient {
public T client(final Class interfaceCls,final String host,int port){
return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class>[]{interfaceCls}, new RemoteHandler());
}
}
最后就是在controller,注入调用相对应的接口方法测试
package com.lp;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @auther lp
* @date 2020/6/21 0021 12:48
*/
@RestController
public class ServiceController {
@LpReference
private IOrderService iOrderService;
@LpReference
private TestService testService;
@GetMapping("/ser")
public String ser(){
return iOrderService.queryOrderList();
}
@GetMapping("/test")
public String test(){
return testService.say();
}
}
全部完毕之后,先打开服务端,再打开客户端,注意这里使用注解端口号已经改为8888 ,如果要测试不带注解的版本,需要把端口号和handler里面的参数做相应的修改。打开浏览器输入 :localhost:8080/ser ,就可以调用其他服务的接口方法。这个源码里面是有没用注解和用了注解两个版本。
源码地址
服务端:https://github.com/chonghuidt/rpc-order.git
客户端:https://github.com/chonghuidt/rpc-service.git
本文纯属个人学习研究,如果转载请标明出处,希望能你有所帮助。