Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Socket技术详解
目前java动态代理的实现分为两种
1.基于JDK的动态代理
2.基于CGILB的动态代理
在业务中使用动态代理,一般是为了给需要实现的方法添加预处理或者添加后续操作,但是不干预实现类的正常业务,把一些基本业务和主要的业务逻辑分离。我们一般所熟知的Spring的AOP原理就是基于动态代理实现的。
【Java知识点详解 2】动态代理
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
【Java知识点详解 8】反射
RPC是远程过程调用的简称,广泛应用在大规模分布式应用中,作用是有助于系统的垂直拆分,使系统更易拓展。Java中的RPC框架比较多,各有特色,广泛使用的有RMI、Hessian、Dubbo等。RPC还有一个特点就是能够跨语言。
RPC服务和HTTP服务对比
package com.guor.rpc.server;
public interface HelloService {
String sayHello(String name);
}
package com.guor.rpc.server;
public class HelloServiceImpl implements HelloService{
@Override
public String sayHello(String name) {
System.out.println("hello " + name);
return "hello " + name;
}
}
package com.guor.rpc.server;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 大致思路:
* 1、角色1:客户端、角色2:发布服务的接口(服务端)、角色3:服务的注册中心;
* 2、服务端通过register方法将接口注册到注册中心,key为关键字,value为接口的具体实现;
* 3、客户端与服务端通过socket进行通信,通过反射技术,客户端发送一个字符串到注册中心,
获取注册中心中map对应的值,即服务端接口的一切信息;
* 4、客户端通过动态代理对象接收不同的接口类型
*/
public class ServerCenterImpl implements ServerCenter{
//服务端的所有可供客户端访问的接口都注册到map中
private static Map serverRegister = new HashMap();
private static int port;
public ServerCenterImpl(int port){
this.port = port;
}
//连接池,一个对象处理一个客户请求(连接池中大小与CPU有关)
private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private static boolean isRunning = false;
//开启服务端服务
@Override
public void start() {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(port));
isRunning = true;
while(true){
System.out.println("服务端 start...");
//接收客户端请求,处理请求,并返回结果
//客户端没发送一次请求,则服务端从连接池中获取一个线程
Socket socket = serverSocket.accept();//等待客户端连接
executor.execute(new ServiceTask(socket));
}
}catch (Exception e){
System.out.println("start exception." + e);
}
}
@Override
public void close() {
isRunning = false;
executor.shutdown();
}
//服务端通过register方法将接口注册到注册中心,key为关键字,value为接口的具体实现;
@Override
public void register(Class servie, Class serviceImpl) {
serverRegister.put(servie.getName(), serviceImpl);
}
private static class ServiceTask implements Runnable{
private Socket socket;
public ServiceTask(){
}
public ServiceTask(Socket socket){
this.socket = socket;
}
@Override
public void run() {
ObjectInputStream input = null;
ObjectOutputStream output = null;
try{
//因为ObjectOutputStream对发送数据的顺序严格要求,因此需要参照发送的顺序逐个接收
input = new ObjectInputStream(socket.getInputStream());
//接口名
String serviceName = input.readUTF();
//方法名
String methodName = input.readUTF();
//方法参数类型
Class[] parameterTypes = (Class[])input.readObject();
//方法参数
Object[] arguments = (Object[])input.readObject();
//根据客户端请求,到map中找到与之对应的具体接口
Class serviceClass = serverRegister.get(serviceName);
Method method = serviceClass.getMethod(methodName, parameterTypes);
Object result = method.invoke(serviceClass.newInstance(), arguments);// = person.say("hello");
//将方法执行完毕的返回值传给客户端
output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(result);
}catch (Exception e){
System.out.println("ServerCenterImpl" + e);
}finally {
if(output!=null){
try{
output.close();
}catch (Exception e){
}
}
if(input!=null){
try{
input.close();
}catch (Exception e){
}
}
}
}
}
}
package com.guor.rpc.client;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;
//通过socket + 动态代理 + 反射远程调用接口中的方法, 连接池, 并发
public class Client {
//获取代表服务端接口的动态代理对象(HelloService)
//serviceInterface:请求的接口名
//add:待请求服务端的ip:port
public static T getRemoteProxyObj(Class serviceInterface, InetSocketAddress addr){
System.out.println("Client start...");
/*
newProxyInstance(a, b, c);
a:类加载器:需要代理哪个类,就要获取哪个类的类加载器
b:需要代理的对象,具备哪些方法 --> 接口
java中单继承、多实现
*/
return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class>[]{serviceInterface}, new InvocationHandler() {
/**
*
* @param proxy 代理的对象
* @param method 哪个方法 sayHello()
* @param args 参数列表
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
ObjectOutputStream output = null;
ObjectInputStream input = null;
try {
//客户端向服务端发送请求:请求某一个具体的接口
Socket socket = new Socket();
socket.connect(new InetSocketAddress(9999));
output = new ObjectOutputStream(socket.getOutputStream());
//接口名
output.writeUTF(serviceInterface.getName());
//方法名
output.writeUTF(method.getName());
//方法参数类型
output.writeObject(method.getParameterTypes());
//方法参数
output.writeObject(args);
//接收服务端处理后的返回值
input = new ObjectInputStream(socket.getInputStream());
System.out.println("接收服务端处理后的返回值"+input.readObject());
return input.read();
}catch (Exception e) {
System.out.println("invoke exception"+e);
return null;
}finally {
if(input!=null){
try{
input.close();
}catch (Exception e){
}
}
if(output!=null) {
try {
output.close();
} catch (Exception e) {
}
}
}
}
});
}
}
package com.guor.rpc.test;
import com.guor.rpc.server.HelloService;
import com.guor.rpc.server.HelloServiceImpl;
import com.guor.rpc.server.ServerCenter;
import com.guor.rpc.server.ServerCenterImpl;
public class RPCServerTest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
//服务中心
ServerCenter serverCenter = new ServerCenterImpl(9999);
//将Hello接口和实现类注册到服务中心
serverCenter.register(HelloService.class, HelloServiceImpl.class);
serverCenter.start();
}
}).start();
}
}
package com.guor.rpc.test;
import com.guor.rpc.client.Client;
import com.guor.rpc.server.HelloService;
import java.net.InetSocketAddress;
/**
* 1、客户端通过socket请求服务端,并且通过字符串形式,将需要的接口发送给服务端(通过动态代理发送接口名、方法名)
* 2、服务端将可以提供的接口注册到服务中心(通过map保存,key为接口的名字,value为接口的实现类)
* 3、服务端接收到客户端的请求后,通过请求的接口名在服务中心的map中寻找对应的接口实现类
* 找到之后,解析刚才客户端发过来的接口名、方法名,解析完毕后,通过反射技术将该方法执行,执行完毕后,再讲该方法的返回值返回给客户端
*/
public class RPCClientTest {
public static void main(String[] args) throws Exception {
HelloService service = Client.getRemoteProxyObj(Class.forName("com.guor.rpc.server.HelloService"), new InetSocketAddress("127.0.0.1", 9999));
service.sayHello("素小暖");
}
}
往期精彩内容:
Java知识体系总结(2021版)
Java多线程基础知识总结(绝对经典)
超详细的springBoot学习笔记
常见数据结构与算法整理总结
Java设计模式:23种设计模式全面解析(超级详细)
Java面试题总结(附答案)