腾讯云服务器实现Java客户端远程调用Linux服务器深度模型(Python)(下)

转载请注明出处:https://blog.csdn.net/qq_33427047/article/details/80454815

在上篇中已经实现了Java客户端远程调用Linux服务器深度模型并与之交互,本篇将对Java线程进行改进。


整个项目功能是:用户提问一个问题,client发送至server,返回answer。因此设计思路是:生成3个并发线程ABC:A用来启动模型;B用来和模型socket交互。与此同时,BC两个线程进行同步,依靠判断是否输入question来阻塞和唤醒。

已修改的代码如下:

/**
 * 这个是Java client和python server通信的实践,server运行的是Parlai问答模型
 * 这一版本是在服务器开启程序的情况下直接进行socket通信
 */
class Example {
    private String return_answer, answer = ""; //用来返回模型的答案
    private String guide_info = ""; //用来保存提示信息
    private String question = ""; //用户提出的问题
    private Socket socket; //client和服务器进行socket通信
    private InputStream is;
    private BufferedReader in;
    private OutputStream os;
    private PrintWriter pw;
    private int judge_answer = 0; //判断是否有答案,没有就继续等待

    public String getAnswer() {
        //线程启动后,一直监测有没有答案生成。有答案生成,标志judge_answer会被置为1
        while (true) {
            if(judge_answer != 0) {
                break;
            }
            try {
                Thread.sleep(500);
            } catch (Exception e) {}
        }
        return_answer = this.answer;
        this.answer = ""; //重置
        judge_answer = 0;
        return return_answer; //返回某问题对应的答案
    }

    //当有新问题时,赋值给this.question
    public void setQuestion(String question) {
        this.question = question;
    }

    @SuppressWarnings("finally")
    //与服务器模型进行socket连接
    public String conn2server(String ip, int port) {
        this.guide_info = "";
        System.out.println("Socket connecting...");
        try {
            this.socket = new Socket(ip, port);

            this.is = this.socket.getInputStream();
            this.in = new BufferedReader(new InputStreamReader(this.is));

            this.os = this.socket.getOutputStream();//字节输出流
            this.pw=new PrintWriter(this.os);//将输出流包装为打印流

            //连接模型如果有输出,会保存到answer中(表示模型的提示语)
            String info;
            int index = 0;
            //设置timeout,超时会抛出异常
            socket.setSoTimeout(2000);
            //如果模型没有输出,socket会一直陷入 in.read 阻塞。所以设置timeout
            while((info = in.readLine())!=null){
                if((index = info.indexOf("eof")) != -1) {
                    this.guide_info += info.substring(0, index);
                    break;
                }
                this.guide_info += info;
            }
            System.out.println("收到的Parlai: " + this.guide_info);        
        } catch (Exception e) {
            //timeout
            System.out.println("好像没有引导语 ");
            return "";
        }
        finally {
            System.out.println("Socket conncted.\n");
            return this.guide_info;
        }
    }

    /**
     * 方法说明:这个方法和socket2py()方法进行线程协作 (要注意应在同一个对象当中使用方法)
     * 具体过程:该方法一直循环,直到用户提出一个问题,即 this.question != "" 时,
     * 将阻塞的socket2py方法唤醒,然后令自身阻塞,等待socket2py方法执行完毕
     */
    public synchronized void waitQuestion() {
        while (true) {
            try {
                Thread.sleep(3000); 
                if(this.question != "") {
                    notifyAll();
                    this.wait();
                }
            } catch (Exception e) {}
        }
    }
    /**
     * 方法说明:这个方法和waitQuestion()方法进行线程协作 (要注意应在同一个对象当中使用方法)
     * 具体过程:该方法会一直与模型保持socket通信
     * 1、首先模型启动后,this.question == "",该线程会阻塞
     * 2、由于waitQuestion方法会一直监测question的输入,当输入后该线程会被唤醒
     * 3、唤醒后执行一次socket 发送、接收操作
     * 4、操作结束,得到模型的回答结果并保存,将question置空
     * 5、唤醒waitQuestion()线程,执行下一次while循环时阻塞自身,等待下一个问题的输入
     */
    public synchronized void socket2py() {
        // 返回的结果是字符串类型,强制转换res为String类型
        //client.execute("run_parlai", params);
        int index;
        this.answer = "";
        String info = "";
// wait
        while (true) {
            //同步
            while(this.question == "") {
                try {
                    this.wait();
                } catch (Exception e) {}
            }
            this.pw.write(this.question);
            this.pw.flush();

            try {
                while((info=in.readLine())!=null){     
                    if((index = info.indexOf("eof")) != -1) {
                        this.answer += info.substring(0, index);
                        break;
                    }
                    this.answer += info;
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            System.out.println(this.answer);
            this.question = "";
            judge_answer = 1;
            notify();
        }
    }

    public void closeSocket() {
        try {
            this.socket.shutdownOutput();
            this.socket.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }//关闭输出流
    }

    /**
     * 这个是Java client和python server通信的实践,server运行的是Parlai问答模型
     * 这一版本是从Java client远程开启server的python深度学习模型的程序,然后进行socket通信 
     */
    public void run_py_socket() throws MalformedURLException, InterruptedException {
        // TODO Auto-generated method stub
        XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
        //config.setServerURL(new URL("http://192.168.77.89:8888/RPC2"));
        //URL是python服务端的SimpleXMLRPCServer(("192.168.77.89", 8888)),注意http和/RPC2
        config.setServerURL(new URL("http://server ip:port/RPC2"));
        XmlRpcClient client = new XmlRpcClient();
        client.setConfig(config);
        Object[] params = null;
        try {
            // 返回的结果是字符串类型,强制转换res为String类型
            System.out.println("Starting program...");
            client.execute("run_parlai", params);
            System.out.println("Program completed.");
        } catch (XmlRpcException e) {
            System.out.println("Connection Error.");
//          e.printStackTrace();
        }
    }
}

/**
 * 这个是线程类,用于实现RPC、socket、线程协作的3个线程
 */
class RunnableSocket extends Thread {
    private int lable; // 用于判断3个线程中,每个线程分别执行什么方法
    private String question = ""; // 用户提出的问题
    private String guide_answer; // 模型引导语
    private Example ex;

    public String getGuide_answer() {
        return guide_answer;
    }
    public Example getEx() {
        return ex;
    }
    public void setEx(Example ex) {
        this.ex = ex;
    }
    public String getQuestion() {
        return question;
    }
    public void setQuestion(String question) {
        this.question = question;
        ex.setQuestion(this.question);
    }
    public String getAnswer() {
        return ex.getAnswer();
    }

    // 类的构造方法,传入new_ex是因为3个线程应该执行同一个对象的不同方法
    public RunnableSocket(int l, Example new_ex) {
        this.lable = l;
        this.question = "";
        this.guide_answer = "";
        this.ex = new_ex;
    }
    public void run() { 
        if(lable == 0) {
            try {
                // RPC 启动模型
                ex.run_py_socket();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        else if (lable == 1){
            ex.setQuestion(this.question);
            // 建立socket连接,得到引导语
            this.guide_answer = ex.conn2server("server ip", port);
            // socket通信
            ex.socket2py();
            this.question = "";
        }
        else {
            ex.setQuestion(this.question);
            // 根据question,和socket2py进行线程协作
            ex.waitQuestion();
            this.question = "";
        }
    }
}

/**
 * 运行线程类
 */
class RunModel {
    private RunnableSocket r0; //用来启动模型
    private RunnableSocket r1; //用来socket通信
    private RunnableSocket r2; //用来和r1线程进行临界资源同步
    private Example ex; //3个线程调用的应该是同一个对象的方法

    public RunModel() {
        this.ex = new Example();
    }

    public void setQuestion(String question) {
        // 因为3个线程是调用Example类的同一个对象,所以执行一次即可
        this.r0.setQuestion(question);
//      this.r1.setQuestion(question);
//      this.r2.setQuestion(question);
    }

    public String getAnswer() {
        return this.r1.getEx().getAnswer();
    }
    public String getGuideAnswer() {
        //得到引导语,如果立即执行则很可能在引导语生成之前(socket建立连接)就返回""。所以需要等待片刻
        try {Thread.sleep(1000);} catch (Exception e) {}
        return this.r1.getGuide_answer();
    }

    public void run_model() {
        try {
            this.r0 = new RunnableSocket(0, ex);
            this.r0.start();

            // 启动模型后,睡眠一段时间。否则可能会r1线程先执行,socket连接失败
            Thread.sleep(1000);

            this.r1 = new RunnableSocket(1, ex);
            this.r1.start();

            this.r2 = new RunnableSocket(2, ex);
            this.r2.start();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

调用run_model() 方法启动3个线程;
setQuestion(***) 方法来 set问题;
getAnswer() 方法获得模型回答。

你可能感兴趣的:(腾讯云服务器实现Java客户端远程调用Linux服务器深度模型(Python)(下))