1.コンセプトコーミング
1.ソケットとは何ですか?
Socketは、アプリケーション層とTCP / IPプロトコルスイート間の通信用のミドルウェア抽象化層です。これは一連のインターフェイスです。デザインモードでは、Socketは実際にはファサードモードであり、複雑なTCP / IPプロトコルファミリをSocketインターフェイスの背後に隠します。ユーザーにとっては、一連の単純なインターフェイスがすべてであり、Socketは指定されたプロトコルに準拠するようにデータを整理できます。
2.動的エージェントとは何ですか?
現在、Java動的プロキシの実装は2つのタイプに分けられます
1.JDKベースの動的プロキシ
2.CGILBに基づく動的エージェント
ビジネスでの動的エージェントの使用は、通常、実装する必要のあるメソッドに前処理または後続の操作を追加することですが、実装クラスの通常のビジネスに干渉することはなく、一部の基本的なビジネスをメインのビジネスロジックから分離します。私たちが一般的に知っているSpringAOPの原則は、動的プロキシの実装に基づいています。
3.リフレクションとは何ですか?
Javaリフレクションメカニズムは実行状態にあり、どのクラスでも、このクラスのすべてのプロパティとメソッドを知ることができます。どのオブジェクトでも、そのメソッドとプロパティのいずれかを呼び出すことができます。この種の動的な情報の取得と動的な呼び出しオブジェクトメソッドのこの関数は、Java言語のリフレクションメカニズムと呼ばれます。
4. RPCとは何ですか?
RPCは、リモートプロシージャコールの略で、大規模な分散アプリケーションで広く使用されています。その機能は、システムの垂直分割を支援し、システムを拡張しやすくすることです。Javaには多くのRPCフレームワークがあり、それぞれに独自の特性があり、広く使用されているものはRMI、Hessian、Dubboなどです。RPCのもう1つの機能は、言語をまたがる機能です。
2つ目は、RPCリモートメソッド呼び出しをシミュレートすることです。
1.アイデア
- クライアントはソケットを介してサーバーを要求し、必要なインターフェイスを文字列の形式でサーバーに送信します(インターフェイス名とメソッド名は動的プロキシを介して送信されます)。
- サーバーは、使用可能なインターフェースをサービス・センターに登録します(マップによって保管され、キーはインターフェースの名前であり、値はインターフェースの実装クラスです)。
- サーバーは、クライアントの要求を受信すると、サービスセンターのマップで、要求されたインターフェイス名を使用して対応するインターフェイス実装クラスを検索します。対応するインターフェイス実装クラスを見つけた後、クライアントから送信されたインターフェイス名とメソッド名を解析します。メソッドが実行され、実行が完了すると、メソッドの戻り値がクライアントに返されます。
2.スケッチを分析します
3、コードの実装
1.インターフェース
package com.guor.rpc.server;
public interface HelloService {
String sayHello(String name);
}
2.インターフェース実装クラス
package com.guor.rpc.server;
public class HelloServiceImpl implements HelloService{
@Override
public String sayHello(String name) {
System.out.println("hello " + name);
return "hello " + name;
}
}
3.ソケットサーバー(マルチスレッド、リフレクション)
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<String, Class> serverRegister = new HashMap<String, Class>();
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){
}
}
}
}
}
}
4.ソケットクライアント(動的プロキシ)
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> 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) {
}
}
}
}
});
}
}
5.テスト
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("素小暖");
}
}
6.コンソール出力
過去のハイライト: