青训营笔记|大数据方向--分布式文件存储系统开发(5)

80 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的的第5天

 上回我们做完了grpc小项目的准备工作,这次我们把这个通信过程完全的实现,首先,grpc协议通信时是要区分客户端与服务端的,因此整个项目大致可以分为客户端代码与服务端代码。

 这个项目的文件结构如下

image.png\

服务端

NameNodeRpcServer.java,文件内容如下

package com.li.server;

import java.io.IOException;

import io.grpc.Server;
import io.grpc.ServerBuilder;

public class NameNodeRpcServer {
    private Server server = null;
    private int port = 8888;

    public void start() throws IOException{
        server = ServerBuilder
                .forPort(this.port)
                .addService(new NameNodeRpcServerImpl())
                .build()
                .start();
        System.out.println("服务已启动,端口号为:" + this.port);
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                // TODO Auto-generated method stub
                shutdown();
            }
        });
    }

    private void shutdown(){
        if(this.server != null)
            this.server.shutdown();
    }

    public void blockUntilShutdown() throws InterruptedException{
        if(this.server != null){
            this.server.awaitTermination();
        }
    }
}

在这里主要实现了启动服务,监听端口的,添加服务以及优雅关闭的功能。用Server与ServerBuilder构造服务端实体,forPort()设置端口,addService()添加服务以及addShutdownHook()用于在线程结束时执行关闭的操作,接下来我们实现一下NameNodeRpcServerImpl这个服务类,NameNodeRpcServerImpl.java这个文件的内容如下

package com.li.server;

import com.li.rpc.domain.DataRequest;
import com.li.rpc.domain.DataResponse;
import com.li.rpc.rpcservice.GrpcServiceApisGrpc.GrpcServiceApisImplBase;

import io.grpc.stub.StreamObserver;;

public class NameNodeRpcServerImpl extends GrpcServiceApisImplBase{
    @Override
    public void getName(DataRequest request, StreamObserver<DataResponse> responseObserver) {
        // TODO Auto-generated method stub
        String name = request.getName();
        String id = request.getInstanceId();
        String message = request.getData();
        System.out.println("接收到DataRequest请求:");
        System.out.println("name = " + name);
        System.out.println("InstanceId = " + id);
        System.out.println("message = " + message);

        DataResponse response = DataResponse.newBuilder()
                                            .setInstanceId("1111122222")
                                            .setMessage("Good morning!" + name)
                                            .build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

可以看到,这里需要继承我们在上次protobuf使用过程中生成的文件,即GrpcServiceApisGrpc.java,我们需要继承这个类里面的Base类,重写getName()方法,然后构造DataResponse并用responseObserver.onNext(response);返回构造的response就成功了,最后再来一个main方法来测试一下NameNodeServer。

 即创建Server.java文件,文件内容如下

package com.li.server;

import java.io.IOException;

public class Server {
    private NameNodeRpcServer server = null;
    public static void main(String[] args) {
        Server nameNode = new Server();
        nameNode.start();
    }

    private void start(){
        this.server = new NameNodeRpcServer();
        try {
            this.server.start();
            this.server.blockUntilShutdown();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

客户端

客户端的结构与服务端类似,先建立连接,DataClient.java文件内容如下

package com.li.client;

import com.li.rpc.domain.DataRequest;
import com.li.rpc.domain.DataResponse;
import com.li.rpc.rpcservice.GrpcServiceApisGrpc.GrpcServiceApisBlockingStub;
import static com.li.rpc.rpcservice.GrpcServiceApisGrpc.newBlockingStub;

import java.util.concurrent.TimeUnit;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

public class DataClient {
    private GrpcServiceApisBlockingStub blockingStub = null;
    private ManagedChannel channel = null;
    public DataClient(String ip, int port){
        this.channel = ManagedChannelBuilder.forTarget(ip + ":" + port).usePlaintext().build();
        this.blockingStub = newBlockingStub(this.channel);
        System.out.println("connected to server at " + ip + ":" + port);
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                try {
                    channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
    }

    public DataResponse getName(DataRequest request){
        return this.blockingStub.getName(request);
    }

    public void shutdown() throws InterruptedException{
        this.channel.shutdown().awaitTermination(5L, TimeUnit.SECONDS);
    }
}

可以看到我们呢使用ManagedChannelBuilder与套接字构建了一个与Server通信的Channel,然后通过protoc.exe生成的GrpcServiceApisGrpc.java中的newBlockingStub实例化服务实体,通过它就可以调用getName()这个方法,接下来我们构造一个DataRequest来请求服务,DataClientServer.java文件的内容如下

package com.li.client;

import com.li.rpc.domain.DataRequest;
import com.li.rpc.domain.DataResponse;

public class DataClientServer {
    private String ip;
    private int port;
    private DataClient dataClient = null;

    public DataClientServer(String ip, int port){
        this.ip = ip;
        this.port = port;
        this.dataClient = new DataClient(this.ip, this.port);
    }
    public void sendMessage(String id, String name, String data){
        DataRequest request = DataRequest.newBuilder()
                            .setData(data)
                            .setInstanceId(id)
                            .setName(name)
                            .build();
        DataResponse response = dataClient.getName(request);
        System.out.println("成功取得消息, Name为:" + response.getMessage());
    }
    
    public void shutdown() throws InterruptedException{
        this.dataClient.shutdown();
    }
}

请求的构造方法非常简单,最后我们再测试一下整个项目。创建Client.java,文件内容如下

package com.li.client;

public class Client {
    private String ip = "localhost";
    private int port = 8888;
    DataClientServer clientServer = null;
    
    public void start(){
        this.clientServer = new DataClientServer(ip, port);
    }

    public void shutdown(){
        if(this.clientServer != null)
            try {
                clientServer.shutdown();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }

    public void sendMessage(String id, String name, String data){
        this.clientServer.sendMessage(id, name, data);
    }

    public static void main(String[] args) {
        String id = "helloworld-";
        String name = "client-01";
        String data = "Good evening";
        Client client = new Client();
        client.start();
        int count = 5;
        for(int i = 0; i < count; i++)
        client.sendMessage(id + i, name, data);
    }
}

我们先启动Server.java,然后启动Client.java就可以看到两者的终端输出如下的内容
Server端的输出

image.png

Client端的输出

image.png

至此我们就会使用grpc的基本功能了,对它的工作流程有了一个大致的认识