1. Penteado de conceito
1. O que é Socket?
Socket é a camada de abstração de middleware para comunicação entre a camada de aplicação e o pacote de protocolos TCP / IP, é um conjunto de interfaces. No modo de design, Socket é na verdade um modo de fachada, que oculta a família de protocolos TCP / IP complexos por trás da interface Socket.Para os usuários, um conjunto de interfaces simples é tudo, permitindo que Socket organize os dados de acordo com o protocolo especificado.
Tecnologia de soquete detalhada
2. O que é um agente dinâmico?
Atualmente, a implementação do proxy dinâmico java é dividida em dois tipos
1. Proxy dinâmico baseado em JDK
2. Agente dinâmico baseado em CGILB
O uso de agentes dinâmicos em negócios é geralmente para adicionar pré-processamento ou operações subsequentes aos métodos que precisam ser implementados, mas não interfere com o negócio normal da classe de implementação e separa alguns negócios básicos da lógica de negócios principal. O princípio Spring AOP que geralmente conhecemos é baseado na implementação de proxy dinâmico.
[Pontos de conhecimento Java detalhados 2] agente dinâmico
3. O que é reflexão?
O mecanismo de reflexão Java está no estado de execução, para qualquer classe, você pode conhecer todas as propriedades e métodos desta classe; para qualquer objeto, você pode chamar qualquer um de seus métodos e propriedades; este tipo de aquisição dinâmica de informações e invocação dinâmica de métodos de objeto A função é chamada de mecanismo de reflexão da linguagem java.
[Pontos de conhecimento Java detalhados 8] reflexão
4. O que é RPC?
RPC é a abreviatura de remote procedure call, muito utilizada em aplicações distribuídas em grande escala e tem por função auxiliar na divisão vertical do sistema e facilitar a expansão do sistema. Existem muitos frameworks RPC em Java, cada um com suas próprias características, e os amplamente utilizados são RMI, Hessian, Dubbo, etc. Outra característica do RPC é sua capacidade de cruzar idiomas.
Comparação de serviço RPC e serviço HTTP
Dois, simular chamadas de método remoto RPC
1. Idéias
- O cliente solicita o servidor por meio do soquete e envia a interface necessária para o servidor na forma de uma string (o nome da interface e o nome do método são enviados por meio do proxy dinâmico).
- O servidor registra as interfaces disponíveis para o centro de serviço (armazenadas por mapa, a chave é o nome da interface e o valor é a classe de implementação da interface).
- Depois que o servidor recebe a solicitação do cliente, ele procura a classe de implementação da interface correspondente no mapa do centro de serviço através do nome da interface solicitada.Depois de encontrar a classe de implementação da interface correspondente, ele analisa o nome da interface e o nome do método enviado pelo cliente. O método é executado e, após a conclusão da execução, o valor de retorno do método é retornado ao cliente.
2. Analise o esboço
Três, implementação de código
1. Interface
package com.guor.rpc.server;
public interface HelloService {
String sayHello(String name);
}
2. Classe de implementação de interface
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. Servidor de soquete (multithreading, reflexão)
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. Cliente de soquete (proxy dinâmico)
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. Teste
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. Saída do console
Destaques do passado:
Resumo do sistema de conhecimento Java (versão 2021)
Resumo do conhecimento básico de multithreading Java (absolutamente clássico)
Notas de estudo super detalhadas do springBoot
Resumo de algoritmos e estruturas de dados comuns
Padrões de design Java: uma análise abrangente de 23 padrões de design (superdetalhado)