一、服务端
1.修改服务提供方
package com.MyRpc.Server;
import com.MyRpc.Common.CloseUtils;
import com.MyRpc.Common.RpcServiceRunnable;
import org.I0Itec.zkclient.ZkClient;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class RpcServer extends Thread{
private Integer port;
private String Address;
public void startServerSocket(){
ServerSocket serverSocket=null;
ThreadPoolExecutor executor=null;
try{
serverSocket = new ServerSocket(port);
register();
executor=new ThreadPoolExecutor(5,10,60, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(10));
while (true){
Socket accept = serverSocket.accept();
RpcServiceRunnable server = new RpcServiceRunnable(accept);
executor.execute(server);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
CloseUtils.close(serverSocket,executor,null,null, null,null);
}
}
private void register(){
ZkClient client=new ZkClient(Address,30000,1000);
String s = "/dubbo";
if (!client.exists(s))
client.createPersistent(s);
InetAddress localHost=null;
try {
localHost = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
throw new RuntimeException("获取本机ip地址失败");
}
String ip = localHost.getHostAddress();
String service = ip + ":" + this.port;
System.out.println("service = " + service);
String path = s + "/" + service;
if (client.exists(path))
client.delete(path);
client.createEphemeral(path,service);
}
public Integer getPort() {
return port;
}
public RpcServer(Integer port, String address) {
this.port = port;
Address = address;
}
public void setPort(Integer port) {
this.port = port;
}
public void run() {
startServerSocket();
}
}
2.具体执行的业务逻辑
package com.MyRpc.Common;
import com.MyRpc.Common.bean.RpcRequest;
import com.MyRpc.Common.bean.RpcResponse;
import com.MyRpc.Server.RpcServerFactory;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.Socket;
public class RpcServiceRunnable implements Runnable{
private Socket client;
public RpcServiceRunnable(Socket client) {
this.client=client;
}
public void run() {
InputStream in=null;
OutputStream out=null;
HessianInput hessianInput=null;
HessianOutput hessianOutput=null;
RpcResponse response=new RpcResponse();
try {
in=client.getInputStream();
hessianInput=new HessianInput(in);
out=client.getOutputStream();
hessianOutput=new HessianOutput(out);
Object object = hessianInput.readObject();
if (!(object instanceof RpcRequest)){
setError(response,hessianOutput,"非法参数");
return;
}
RpcRequest request= (RpcRequest) object;
String className = request.getClassName();
Class<?> aClass = Class.forName(className);
Object impl = RpcServerFactory.getBean(aClass);
if (impl==null){
setError(response,hessianOutput,"没有对应的实体类");
return;
}
Object target = AopTargetUtils.getTarget(impl);
if (target!=null){
impl=target;
}
Class<?> implClass = impl.getClass();
if (!implClass.isAnnotationPresent(Rpc.class)) {
throw new RuntimeException("该没有服务提供");
}
Method method = implClass.getMethod(request.getMethodName(), request.getParamsType());
if (method==null){
setError(response,hessianOutput,"没有找到对应的方法");
return;
}
Object result = method.invoke(impl, request.getParams());
response.setResult(result);
hessianOutput.writeObject(response);
} catch (Exception e) {
e.printStackTrace();
}finally {
CloseUtils.close(null,null,in,out,hessianInput,hessianOutput);
}
}
private void setError(RpcResponse response,HessianOutput out,String error){
response.setError(new Exception(error));
try {
out.writeObject(response);
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.实现ApplicationContextAware接口
package com.MyRpc.Server;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.List;
public class RpcServerFactory implements ApplicationContextAware {
private Integer port;
private String address;
private List<String> classNames;
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public static <T> T getBean(Class<T> clazz){
return applicationContext.getBean(clazz);
}
public void init(){
RpcServer server=new RpcServer(port,address);
server.start();
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public List<String> getClassNames() {
return classNames;
}
public void setClassNames(List<String> classNames) {
this.classNames = classNames;
}
}
4.判断是否是代理对象工具类
package com.MyRpc.Common;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
import java.lang.reflect.Field;
public class AopTargetUtils {
public static Object getTarget(Object proxy) throws Exception {
if(!AopUtils.isAopProxy(proxy)) {
return proxy;
}
if(AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy);
} else {
return getCglibProxyTargetObject(proxy);
}
}
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(proxy);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
return target;
}
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
AopProxy aopProxy = (AopProxy) h.get(proxy);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();
return target;
}
}
二、客户端
1.客户端业务逻辑
package com.MyRpc.client;
import com.MyRpc.Common.CloseUtils;
import com.MyRpc.Common.bean.RpcRequest;
import com.MyRpc.Common.bean.RpcResponse;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class RpcClient {
public Object start(RpcRequest request,String host,int port) throws Exception {
InputStream in=null;
OutputStream out=null;
HessianInput hessianInput=null;
HessianOutput hessianOutput=null;
try{
Socket socket = new Socket(host, port);
out=socket.getOutputStream();
hessianOutput=new HessianOutput(out);
hessianOutput.writeObject(request);
in=socket.getInputStream();
hessianInput=new HessianInput(in);
System.out.println("获取响应信息");
Object object = hessianInput.readObject();
if (!(object instanceof RpcResponse)){
throw new RuntimeException("服务器参数格式不正确");
}
RpcResponse response= (RpcResponse) object;
if (response.getError()!=null)
throw new RuntimeException("服务器繁忙");
return response.getResult();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("客户端发生异常");
}finally {
CloseUtils.close(null,null,in,out,hessianInput,hessianOutput);
}
}
}
2.生成客户端代理对象
package com.MyRpc.Common;
import com.MyRpc.Common.bean.RpcRequest;
import com.MyRpc.client.RpcClient;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
public class RpcClientProxy implements InvocationHandler {
private List<String> list = new LinkedList<String>();
private String address;
public RpcClientProxy() {
}
public <T> T getProxyObject(Class<T> clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this);
}
private void initServer() {
final ZkClient client = new ZkClient(address, 30000, 1000);
final String path = "/dubbo";
if (!client.exists(path))
throw new RuntimeException("没有对应的服务器提供服务");
list.clear();
final List<String> children = client.getChildren(path);
for (String child : children) {
list.add(client.readData(path + "/" + child).toString());
}
client.subscribeChildChanges(path, new IZkChildListener() {
public void handleChildChange(String s, List<String> ChildrenList) throws Exception {
list.clear();
for (String child : ChildrenList) {
list.add(client.readData(path + "/" + child).toString());
}
System.out.println("-----------------server change---------------" + list);
}
});
}
private int count = 0;
public String getServer() {
initServer();
System.out.println(list.size());
int index = count % list.size();
count++;
System.out.println("index = " + index);
return list.get(index);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RpcRequest request = new RpcRequest();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParamsType(method.getParameterTypes());
request.setParams(args);
String server = getServer();
String[] split = server.split(":");
RpcClient rpc = new RpcClient();
return rpc.start(request, split[0], Integer.valueOf(split[1]));
}
public RpcClientProxy(String address) {
this.address = address;
}
}
3.实现FactoryBean接口
package com.MyRpc.client;
import com.MyRpc.Common.RpcClientProxy;
import org.springframework.beans.factory.FactoryBean;
public class RpcClientFactory<T> implements FactoryBean<T> {
private String address;
private Class<T> interfaceClass;
public T getObject() throws Exception {
return new RpcClientProxy(address).getProxyObject(interfaceClass);
}
public Class<?> getObjectType() {
return interfaceClass;
}
public boolean isSingleton() {
return true;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Class<T> getInterfaceClass() {
return interfaceClass;
}
public void setInterfaceClass(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
}
}
4.实现BeanDefinitionRegistryPostProcessor, ApplicationContextAware接口
package com.MyRpc.client;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.List;
public class RpcClientManager implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
private ApplicationContext applicationContext;
private List<String> classNames;
private String address;
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
for (String className : classNames) {
Class clazz=null;
try {
clazz= Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
GenericBeanDefinition definition = (GenericBeanDefinition) beanDefinitionBuilder.getRawBeanDefinition();
definition.getPropertyValues().add("interfaceClass",definition.getBeanClassName());
definition.getPropertyValues().add("address",address);
definition.setBeanClass(RpcClientFactory.class);
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
beanDefinitionRegistry.registerBeanDefinition(clazz.getSimpleName(),definition);
}
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public List<String> getClassNames() {
return classNames;
}
public void setClassNames(List<String> classNames) {
this.classNames = classNames;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
三、测试
1.使用mvn install安装框架到maven仓库
2.引入依赖
<dependency>
<groupId>com.myRpc</groupId>
<artifactId>RPC</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
3.配置服务端applicationContext-rpc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean class="com.MyRpc.Server.RpcServerFactory" init-method="init">
<property name="address" value="127.0.0.1:2181"/>
<property name="port" value="10001"/>
</bean>
<context:component-scan base-package="com.travel.background.test"/>
</beans>
4.测试类接口
package com.travel.background.test;
public interface IHello {
void sayhello();
}
5.接口实现类
package com.travel.background.test;
@Rpc
@Service
public class HelloService implements IHello {
@Override
public void sayhello() {
System.out.println("执行结束");
}
}
6.配置客户端applicationContext-rpc.xml
<bean class="com.MyRpc.client.RpcClientManager">
<property name="address" value="127.0.0.1:2181"/>
<property name="classNames">
<list>
<value>com.travel.background.test.IHello</value>
</list>
</property>
</bean>
7.测试controller
package com.travel.background.controller;
import com.travel.background.test.IHello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("hello")
public class HelloController {
@Autowired
IHello iHello;
@RequestMapping("hello")
@ResponseBody
public String hello(){
iHello.sayhello();
return "hello";
}
}
手写RPC1
手写RPC2集成Zookeeper