百度搜出来的都是什么辣鸡方案,嵌入个页面你TM叫集成?好意思么!好在努力的人都比较幸运,在一个python大神的帮助下实现了集成方案。废话不多少,直接上方案。本文不会贴太多代码,只描述关键点,有问题可以私信交流,别找我要源码,思路提供了,自力更生才能进步。

既然是集成jupyter,那么最简单的办法就是自己请求jupyter sever接口。jupyter本身就是一个前后端项目,其实集成jupyter核心逻辑就是用自己的前端页面请求jupyter server。jupyter server api 可以从The REST API — Jupyter Server documentation 获取,也可以自己启动一个jupyter项目,通过浏览器查看页面请求获取。这个方案是可行,只是获取token 比较麻烦,而且会话还要基于文档创建,感觉比较费事,有兴趣的自行研究吧。我采用的是另外一种方案,就是jupyter kernelgateway ,这个也有文档,不过需要翻墙。其实这个感觉就是启动一个纯净的jupyter server 服务,没有乱七八糟的权限控制,建议用这个。启动命令很简单:

jupyter kernelgateway --JupyterWebsocketPersonality.list_kernels=True

那么如何交互进行命令发送和结果获取呢,继续往下看。因为项目原因, 我采用的是三方通信模式,就是前端命令先发送到java服务端,然后java服务端再转发给jupyter server。

编辑器我用的是微软的monaco编辑器,其它编辑器也行,如果后面要做python动态代码提示的话建议用微软的monaco编辑器,毕竟lsp 语言服务协议都是人家定,monaco有天生优势。

编辑器初始化也很简单,我简单贴下我的代码,网上一大堆编辑器初始化方案。

var el =document.getElementById("scriptTt"+id) ;// create Monaco editorvar scriptCodeMirror= monaco.editor.create(document.getElementById("scriptTt"+id), {model: monaco.editor.createModel(nowScript, "python"),glyphMargin: true,lightbulb: {enabled: true},scrollBeyondLastLine: false,automaticLayout: true,autoIndent:true,//自动布局minimap: { // 关闭代码缩略图enabled: true // 是否启用预览图},fontSize: 10,wordWrap: "bounded",wrappingIndent: 'indent'});

然后就是和java服务端建立websocket 连接。我用的是xterm.js +webssh.js ,代码贴出来参考一下,用什么都行,只要能建立websocket就行。

//初始化客户端
client = new WSSHClient('${applicationScope["webSshServer"]}');//建立websocket 连接openJupyterTerminal({operate : 'connect',jupyterKerneUrl:$("#kernelUrl").val()});function openJupyterTerminal(options) {//执行连接操作client.connect({onError : function(error) {//连接失败回调term.write('Error: ' + error + '\r\n');},onConnect : function() {//连接成功回调client.sendInitData(options);},onClose : function() {//连接关闭回调$.messager.alert('信息提示', '连接已关闭!', 'info');},onData : function(data) { console.log("curtRunner-----------------");console.log(curtRunner);console.log(data);if(curtRunner=="python"){data=$.parseJSON( data ); if(data.status==0){runflag=true;$("#jupyterConnect").css("display","none");$("#jupyterOffConnect").css("display","inline");$.messager.alert("提示", "交互式环境加载完成!", "info");};if(data.status==1){msgData=$.parseJSON( data.msgData ); console.log(msgData);if(msgData.msg_type=="execute_result"){if(selectLogCodeMirror != null){selectLogCodeMirror.setValue(selectLogCodeMirror.getValue()+msgData.content.data["text/plain"]);     }};if(msgData.msg_type=="display_data"){if(msgData.content.data["image/png"]){$("#figureImg").attr("src", "data:image/jpg;base64,"+msgData.content.data["image/png"]);$('#wz').window('open');}    }if(msgData.msg_type=="stream"){if(selectLogCodeMirror != null){selectLogCodeMirror.setValue(selectLogCodeMirror.getValue()+msgData.content.text);}};if(msgData.msg_type=="status"){if(msgData.content.execution_state=="idle"){pythonRuning=false;// $($(runCellId).find(".runCell")).css("display","inline"); // $($(runCellId).find(".stopCell")).css("display","none");$($(runCellId).find(".outSpan")).html("执行结果");}else{// $($(runCellId).find(".runCell")).css("display","none"); // $($(runCellId).find(".stopCell")).css("display","inline");pythonRuning=true;};};if(msgData.msg_type=="error"){$.messager.alert("提示", "代码执行异常。<br/>"+msgData.content.evalue , "error");};if(msgData.msg_type=="shutdown_reply"){if(msgData.content.status="ok"){$("#jupyterConnect").css("display","inline");$("#jupyterOffConnect").css("display","none");$.messager.alert("提示", "jupyter server 连接已断开。<br/>", "error");}}};if(data.status==2||data.status==3){runflag=false;$("#jupyterConnect").css("display","inline");$("#jupyterOffConnect").css("display","none");msgData=data.msgData; $.messager.alert("提示", "连接异常,请检查jupyer服务。<br/>"+msgData,"error" );}}}});}

这个代码无非就是建立连接,然后解析接收到的数据,没什么好说的。

然后来说java服务端。java服务端的职责就是接收前端命令,然后转发给jupyter。初次接收命令会进行初始化操作,初始化的时候会连接jupyter server。关键代码如下:

private void connectToJupyerServer(JupyterClientInfo jupyterClientInfo, JupyterClientData jupyterClientData, WebSocketSession webSocketSession) throws Exception {logger.info("connectToJupyerServer");String  baseUrl="";String[]  baseUrlArr=jupyterClientData.getJupyterKerneUrl().split("://");if(baseUrlArr.length==2){baseUrl=baseUrlArr[1];}else{baseUrl= baseUrlArr[0];};jupyterClientInfo.setJupyerUrl(baseUrl);String result=HttpUtils.httpRequestToString("http://"+baseUrl+"/api/kernels","GET",null);System.out.println("-----kernels------------");System.out.println(result);JSONArray kernelsJson= JSONArray.parseArray(result);JSONObject kernelobj=new JSONObject();if(kernelsJson.size()>0){kernelobj=(JSONObject) kernelsJson.get(0);}else{//获取 kernelresult = HttpUtils.httpRequestToString("http://"+baseUrl+"/api/kernelspecs","GET",null);System.out.println("-----kernelspecs------------");System.out.println(result);JSONObject kernelspecs= JSONObject.parseObject(result);String kernelsNmae=kernelspecs.getString("default");JSONObject newkernelsJson=new JSONObject();newkernelsJson.put("name", kernelsNmae);newkernelsJson.put("path", "/opendata");//创建内核result=HttpUtils.httpRequestToString("http://"+baseUrl+"/api/kernels","POST", JSONObject.toJSONString(newkernelsJson) );kernelobj=JSONObject.parseObject(result);};if (kernelobj!=null){System.out.println("server url---------");System.out.println("ws://"+baseUrl+String.format("/api/kernels/%s/channels",kernelobj.get("id")));jupyterClientInfo.setKernelId(kernelobj.get("id").toString());WebSocketClient webSocketClient = new WebSocketClient(new URI("ws://"+baseUrl+String.format("/api/kernels/%s/channels",kernelobj.get("id"))),new Draft_6455()) {@SneakyThrows@Overridepublic void onOpen(ServerHandshake serverHandshake) {logger.info("[连接 jupyter server] 连接成功");SocketMsgData socketMsgData=new SocketMsgData();socketMsgData.setStatus(SocketMsgData.StatusEnum.ONOPEN.ordinal());socketMsgData.setMsgData("[连接 jupyter server] 连接成功");if(webSocketSession.isOpen()){sendMessage(webSocketSession, JSONObject.toJSONString(socketMsgData).getBytes(StandardCharsets.UTF_8));}}@SneakyThrows@Overridepublic void onMessage(String message) {logger.info("[jupyter client] 收到消息={}",message);SocketMsgData socketMsgData=new SocketMsgData();socketMsgData.setStatus(SocketMsgData.StatusEnum.ONMESSAGE.ordinal());socketMsgData.setMsgData(message);if(webSocketSession.isOpen()){sendMessage(webSocketSession, JSONObject.toJSONString(socketMsgData).getBytes(StandardCharsets.UTF_8));}}@SneakyThrows@Overridepublic void onClose(int code, String reason, boolean remote) {logger.info("[jupyter client] 退出连接");SocketMsgData socketMsgData=new SocketMsgData();socketMsgData.setStatus(SocketMsgData.StatusEnum.ONCLOSE.ordinal());socketMsgData.setMsgData("[jupyter client] 退出连接");if(webSocketSession.isOpen()){sendMessage(webSocketSession, JSONObject.toJSONString(socketMsgData).getBytes(StandardCharsets.UTF_8));}}@SneakyThrows@Overridepublic void onError(Exception ex) {logger.info("[jupyter client] 连接错误={}",ex.getMessage());SocketMsgData socketMsgData=new SocketMsgData();socketMsgData.setStatus(SocketMsgData.StatusEnum.ONCLOSE.ordinal());socketMsgData.setMsgData("[jupyter client] 连接错误={}"+ex.getMessage());if(webSocketSession.isOpen()){sendMessage(webSocketSession, JSONObject.toJSONString(socketMsgData).getBytes(StandardCharsets.UTF_8));}}};webSocketClient.connect();jupyterClientInfo.setWebSocketClient(webSocketClient);}else{throw new Exception("找不到kernel信息,请确认jupyter kernelgateway服务是否启动");}}

说一下java服务端大概逻辑,由于服务端是用来做转发的,所以它是websocket服务端也是websocket的客户端。初始化的时候,会把服务端session 和客户端实例保存到全局map对象里面,用的时候就从全局对象取出来就行。

package cn.objectspace.jupyter.form;import com.alibaba.fastjson.JSONObject;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import lombok.Data;
import org.java_websocket.client.WebSocketClient;
import org.springframework.web.socket.WebSocketSession;import java.util.UUID;/**
* @Description:  jupyter 连接信息
* @Author: zxf
* @Date: 2021/3/23
*/
@Data
public class JupyterClientInfo {public JupyterClientInfo(){//组装ws 信息JSONObject contentJson=new JSONObject();contentJson.put("code", "1==1");contentJson.put("silent", false);contentJson.put("store_history", false);contentJson.put("user_expressions", new JSONObject());contentJson.put("allow_stdin", false);JSONObject hdrJson=new JSONObject();hdrJson.put("msg_id", UUID.randomUUID().toString().replaceAll("-",""));hdrJson.put("username", "dataOpen");hdrJson.put("session", UUID.randomUUID().toString().replaceAll("-",""));hdrJson.put("msg_type", "execute_request");hdrJson.put("version","5.0");JSONObject msgJson=new JSONObject();msgJson.put("header", hdrJson);msgJson.put("metadata", new JSONObject());msgJson.put("parent_header", new JSONObject());msgJson.put("buffers", new JSONObject());msgJson.put("content", contentJson);msgJson.put("channel","shell");this.jupyerConent=contentJson;this.jupyerMsg=msgJson;}//客户端连接private WebSocketSession webSocketSession;// 转发jupytersever 的客户端private WebSocketClient  webSocketClient;//jupyer 消息体private JSONObject jupyerMsg;//jupyer 消息内容private JSONObject jupyerConent;//jupyter urlprivate String jupyerUrl;//jupyter urlprivate String kernelId;
}

转发给jupyter的时候,需要消息组装。

   private void transToJupyerServer(JupyterClientInfo jupyterClientInfo, String command) throws IOException {WebSocketClient webSocketClient=jupyterClientInfo.getWebSocketClient();if (webSocketClient != null) {JSONObject msg=jupyterClientInfo.getJupyerMsg();JSONObject  content=jupyterClientInfo.getJupyerConent();content.put("code",command);msg.put("content",content);JSONObject  header=msg.getJSONObject("header");header.put("msg_id",UUID.randomUUID().toString().replaceAll("-",""));header.put("date",getISO8601Timestamp(new Date()));msg.put("header",header);logger.info("[消息发送] "+msg);webSocketClient.send(JSONObject.toJSONString(msg));}}

再说一下注意事项,jupyter创建的内核断开连接的时候要注意主动回收,不回收的话python进程会一直存在。如果是多用户访问,那么每个用户都应该去单独创建维护内核 。这也是为什么采用三方通信的初衷,js去维护实在不靠谱!

整体方案大概就这样,如果有什么不明白的,留言吧。

最后展示一下效果。

java 编辑器 集成jupyter notebook相关推荐

  1. 集成Jupyter notebook的工具或平台

    公司的机器学习平台期望通过集成Jupyter notebook来提供探索式分析功能,在设计过程中首先分析了几款常见的集成jupyter的工具或软件平台,如下所示: Neptune https://ap ...

  2. Sublime Text 安装 Helium 插件搭出Jupyter Notebook的效果

    Sublime Text 安装 Helium 插件搭出Jupyter Notebook的效果 自从用Python编程以来,使用过几个编辑器:Jupyter Notebook.Spyder.PyChar ...

  3. jupyter notebook高级教程系列——slide幻灯片制作

    声明:前面已经详细讲解了ipython.jupyter notebook的使用.配置等内容,本文作为补充篇继续讲解jupyternotebook.本文主要由两个部分组成,jupyter noteboo ...

  4. stata安装_Stata自带的代码编辑器太丑了,那为何不使用Jupyter Notebook?

    每次打开Stata自带的dofile编辑器,我都会想吐槽一下它的界面,由于学术界"以丑为美"的观念根深蒂固,深深毒害了一批人,所以很多人可能都觉得没什么,我这个人对美感有着极致追求 ...

  5. Jupyter notebook与Spyder,以及Jupyter notebook与Spyder集成插件

    1.自己总结的Jupyter notebook与Spyder的区别 Spyder是anaconda自带IDE,有"变量资源管理器",可以很方便的检查变量,不便于管理多个项目的py文 ...

  6. 桌面版应用_类Jupyter notebook编辑器桌面版应用nteract

    Jupyter notebook是一款学习和科研的神奇,相信很多小伙伴已经利用它在学术科研.研究开发的道路上收获了便捷易用.可视直观的优点.我们使用的Jupyter notebook都是网页形式的,在 ...

  7. 用Jupyter Notebook集成Symphony大数据平台实践金融分析

    简介 Jupyter Notebook是一种开源的基于Web的交互式笔记本,便于管理分享程序文档,支持实时代码,可视化,以及Markdown语法,目前支持40多种编程语言,是数据科学生态圈的开发利器, ...

  8. Jupyter notebook与Spyder集成

    Spyder notebook plugin Spyder插件在Spyder中使用Jupyter notebook.目前,它支持基本功能,例如创建新笔记本,在文件系统中打开任何笔记本以及在任何位置保存 ...

  9. Python安装Jupyter Notebook配置使用教程

    原文见:https://blog.csdn.net/qq_27825451/article/details/84427269 一.什么是jupyter 1.简介: jupyter notebook是一 ...

最新文章

  1. ARM 的几个重要特点
  2. 【k-means clustering】【一】基础算法
  3. POJ 2190 模拟
  4. vex机器人比赛 一队人数_VEX世锦赛中国区总决赛在渝闭幕 重庆晚报记者带你了解VEX机器人的那些事...
  5. Silverlight2 边学边练 之五 视频
  6. Spring Cloud Config服务端配置细节(一)
  7. 高通芯片曾被发现一重大漏洞 影响骁龙845等30多款芯片
  8. Phase2 Day11 Set Map
  9. python项目之神奇时钟
  10. #学习笔记 使用c语言来制作一个计算器
  11. mcc、mbuild和mex命令详解
  12. 单片机中code、data、idata、xdata等关键字意思
  13. 不重装系统解决win10更新错误0x800f0922
  14. PowerBI中的函数日期表
  15. xtuoj 模拟 1176 First Blood
  16. kali linux 渗透测试学习笔记——被动信息收集
  17. 意群是记忆的最小单位
  18. 2021年中国减速机市场供需及进出口贸易情况:印度是我国行星齿轮减速器主要出口地[图]
  19. iPhone和MacOS同步复制粘贴功能导致iPhone死机问题
  20. 基于MATLAB和python输出曼德勃罗集

热门文章

  1. OCR文字识别项目(原理)
  2. Python海龟绘图,绘出最靓丽的景色
  3. w7双击桌面上的计算机,打不开盘符了,如何解决win7中双击桌面图标打不开怎么办...
  4. 高性能家用计算机能干嘛,买台服务器当家用电脑是种什么体验?那酸爽用过才会懂...
  5. matlab使用日记(三)——人工神经网络
  6. 使用mapbox地图的一丢丢小心得
  7. 手机html图片自动滚动,html5手机触屏滑动图片下一张查看特效
  8. exynos 4412 时钟配置
  9. itop4412的wifi驱动配置
  10. 如何增加swap空间大小