欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

记录php中生活难点尝试vue间接吸出

程序员文章站 2022-03-14 19:46:32
...

最古老也是最有效,并且永不过时的,TCP/UDP的二进制传输,事实上所有的通信方式归根结底都是TCP/UDP
CORBA Common Object Request Broker Architecture。古老而复杂的,支持面向对象的通信协议。
Web Service(SOA SOAP RDDI WSDL …)基于http+xml的标准化Web API
RestFul 回归简单化本源的Web API的事实标准,http+json
RMI Remote Method Invocation Java内部的分布式通信协议
JMS Java Message Service JavaEE中的消息框架标准,为很多MQ所支持
RPC Remote Procudure Call 远程过程方法调用,这只是一个统称概念,远程通信的方式,重点在于方法调用(不支持对象的概念),具体实现甚至可以用RMI RestFul等去实现,但一般不用,因为RMI不能跨语言,而RestFul效率太低。多用于服务器集群间的通信,因此常使用更加高效,短小精悍的传输模式以提高效率。
从单机到分布式->分布式通信(一台机器解决不了的问题,需要多台机器解决,多台机器内部需要进行通信)->最基本:二进制,两台机器之间通过网络通信一定是二进制数据传输TCP/IP(socket)

RPC01#
首先构造一个用于传输的类User,该类实现了序列化可以进行序列化从而通过网络传输二进制数据,他会跑在一台机器上去访问数据库,有人想拿到这个User的话我会对外提供一些服务

然后通过AUserService暴露FindById接口来进行数据查询,你给我一个ID,我给你一个User
User.java

Copy
package com.airsky.demo.rpc.Common;

import java.io.Serializable;

public class User implements Serializable {
private static final long serialVersionUID = 1L;
int id;
String name;

public User(int id, String name) {
    this.id = id;
    this.name = name;
}

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

@Override
public String toString() {
    return "User{" +
            "id=" + id +
            ", name='" + name + '\'' +
            '}';
}

}
AUserService.java

Copy
package com.airsky.demo.rpc.Common;

public interface AUserService {
User findById(int id);
}
最终在AUserServiceImpl进行了实现,对传入的id进行了模拟数据库查询,返回一个User对象。这里只进行一个模拟,具体实现的话也很简单。

Copy
package com.airsky.demo.rpc.Common;

public class AUserServiceImpl implements AUserService{

@Override
public User findById(int id) {
    //模拟数据库查询
    return new User(id,"AirSky");
}

}
如果我们想通过最原始的方式来完成上述操作该怎么做呢?这个也是很多游戏和软件类似功能最底层的实现。
首先我们这台机器对外提供服务,就需要提供一个Server打开一个Socket端口进行监听,下面的实现是在Server类中新建了一个ServerSocket对象,并监听8888端口,accept接收一个客户端连接,然后通过send方法对连接进行处理。

Copy
public class Server {
//加上条件变量,不然s.close()不可达
private static boolean running = true;
public static void main(String[] args) throws Exception {
ServerSocket s=new ServerSocket(8888);
while (running){
System.out.println(“接收连接中…”);
Socket client=s.accept();
System.out.println(“连接主机:” + client.getInetAddress());
send(client);
client.close();
}
s.close();
}
下面来实现send方法对连接进行进一步处理。大概就是你给我一个id,我给你一个id和id对应的名字,就是这么简单。send方法传入了一个socket对象,我们可以通过socket对象的getInputStream方法来打开一个输入流,getOutputStream方法打开一个输出流。使用DataOutputStream的readInt对输入流中的数据进行指定方式读取,使用该包装流来简化读写操作,如果客户端以正确的方式传送了ID的即可读取到,然后调用AUserService的findById方法来查询id对应的User对象,最后通过DataOutputStream的writeInt和writeUTF以指定的方式分别向输出流中写入获取到的id和name。

Copy
private static void send(Socket client) throws Exception {
//获取Socket的输入流,用Data流包装读取二进制数据
DataInputStream dis = new DataInputStream(client.getInputStream());
//获取Socket的输出流,用Data流包装写出二进制数据
DataOutputStream dos = new DataOutputStream(client.getOutputStream());

    //读取Id
    int id = dis.readInt();
    System.out.println("接收到ID为:"+id);
    AUserService service= new AUserServiceImpl();
    User user = service.findById(id);
    //写出数据
    System.out.println("返回的对象为:"+user);
    dos.writeInt(user.getId());
    dos.writeUTF(user.getName());
    dos.flush();
    dis.close();
    dos.close();
}

服务端实现了我们再来实现以下客户端。服务端监听了8888端口我们是不是就需要新建一个Socket来连接8888端口,同样使用getOutputStream来获取一个输出流,再用DataOutputStream向流中写指定格式数据(二进制),最后来使用getInputStream获取输入流得到数据n。得到数据之后new一个新对象拿来用。

Copy
public class Client {
public static void main(String[] args) throws Exception {
Socket s = new Socket(“127.0.0.1”,8888);
//使用DataOutputStream包装以二进制写入
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeInt(8888);
DataInputStream dis = new DataInputStream(s.getInputStream());
int id = dis.readInt();
String name = dis.readUTF();
User user = new User(id,name);
System.out.println(user);
}
}
现在我们启动服务端,然后启动客户端后,服务端和客户端就会收到以下数据

以上这种是最简单最原始的方式,这种方式非常的不灵活,这种方式只能传输单一的对象,对于传输的对象必须了解才能传的过去拿的过来,如果在代码量大的情况下需要对某个或者多个对象加属性,那么我们的就需要大量整 改,尤其是传输过程和业务逻辑代码全部混合在一起的时候,所以我们就需要一种写起来更加爽的方式。RPC02应运而生。

RPC02#
假如我现在只是个业务开发,对网络这块儿不太熟,现在我想告诉自己,能不能给个简单的方法,让我直接访问接口编写逻辑代码就行了。所以这里我们就需要对网络这块儿进行一个简单的封装。这就是RPC的演进过程。
最简单的方法是将网络操作封装为代理类Stub,Stub屏蔽了关于网络的细节

Copy
package com.airsky.demo.rpc.rpc02;

import com.airsky.demo.rpc.Common.User;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

public class Stub {
public User findUserById(Integer id) throws Exception {
Socket s = new Socket(“127.0.0.1”,8888);
//使用DataOutputStream包装以二进制写入
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeInt(id);
DataInputStream dis = new DataInputStream(s.getInputStream());
int id1 = dis.readInt();
String name = dis.readUTF();
User user = new User(id1,name);
return user;
}
}
现在只要在new一个Stub出来,用Stub的findUserById方法就能拿到对象
Client中调用代理类

Copy
package com.airsky.demo.rpc.rpc02;

public class Client {
public static void main(String[] args) throws Exception {
Stub stub = new Stub();
System.out.println(stub.findUserById(33));
}
}
这就是第一步的演进,开发是一个不断螺旋递增的迭代过程,瀑布式的模型在很多场景下,尤其是互联网下早就被敏捷开发所替代了,敏捷一定要迭代。我们很多时候了解到的都是结果,直接接触到了最终的版本,所以我们根本理解不了中间的演进过程,也理解不了前人们为什么做出来这么多的改进,这就是我们不能理解RPC的原因。从历史演进学习技术,会理解得更透彻,知道前因后果,怎么问题的步骤与思路。

RPC03#
到了这里大家可能会说,作为一个Stub,这非常的不完善,你只能代理一个方法,返回一个类,这也太弱了。那我们就一点点来演进。
如果说Stub能给我提供这样的一个接口,我们想使用Service的findUserById,而Stub的接口提供给我这个方法。通过Stub代理过的findUserById方法我们就能远程访问了。当我们调用findUserById的时候,他帮我们加进去了一些代码,这些代码就是网络服务,所以Stub这里实现了代理模式里面的动态代理,这里最难的。
Stub

Copy
package com.airsky.demo.rpc.rpc03;

import com.airsky.demo.rpc.Common.IUserService;
import com.airsky.demo.rpc.Common.User;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

public class Stub {
public static IUserService getStub(){
InvocationHandler h=new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket s = new Socket(“127.0.0.1”,8888);
//使用ByteArrayOutputStream在内存中声明一个数组流
// ByteArrayOutputStream bos = new ByteArrayOutputStream();
//使用DataOutputStream包装以二进制写入
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeInt(456);//这里写死了需要改进
// s.getOutputStream().write(bos.toByteArray());
//
// System.out.println(s.getInputStream().read());
DataInputStream dis = new DataInputStream(s.getInputStream());
int id1 = dis.readInt();
String name = dis.readUTF();
User user = new User(id1,name);

            dos.close();
            dis.close();
            s.close();
            return user;
        }
    };
    Object o= Proxy.newProxyInstance(IUserService.class.getClassLoader(),new Class[]{IUserService.class},h);
    System.out.println(o.getClass().getName());
    System.out.println(o.getClass().getInterfaces()[0]);
    return (IUserService)o;

}

}

相关标签: 拓扑学