网络通信协议

news2024/9/28 23:34:55

WebSocket通信

  1. WebSocket是一种基于TCP的网络通信协议,提供了浏览器和服务器之间的全双工通信(full-duplex)能力。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。这使得数据可以更快地从服务器传到浏览器,而且减少了数据传输的数据量,因为头信息比较小。在WebSocket API中,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
  2. HTTP协议和WebSocket协议的主要区别如下:
    1. 连接方式:HTTP协议是无短连接的。每次请求都需要建立新的连接,请求结束后连接就断开。而WebSocket协议是长连接的,客户端和服务器建立连接后,直到其中一方主动断开,连接才会断开。
    2. 数据传输:HTTP协议只能由客户端向服务器发起请求,服务器返回响应数据。而WebSocket协议是全双工通信,服务器和客户端都可以主动发送数据。
    3. 性能开销:由于HTTP协议每次请求都需要建立新的连接,所以开销较大。而WebSocket协议建立连接后,可以进行多次数据传输,开销较小。
    4. 数据格式:HTTP协议传输的数据格式比较复杂,包含了请求行、请求头、消息体等。而WebSocket协议传输的数据通过帧来传输,数据格式比较简单。
    5. 实时性:HTTP协议的实时性不强,需要客户端定时轮询服务器获取新的数据。而WebSocket协议可以实现服务器主动推送数据,实时性较强。
  3. WebSocket主要适用于以下几种场景:
    1. 实时应用:聊天应用、多人协作应用、在线游戏、实时购物等。
    2. 实时数据推送:股票、新闻、天气、设备状态等实时信息的推送。
    3. IOT物联网:实时获取设备状态,实时控制设备等。
    4. 实时分析:实时数据分析、实时监控系统等。

WebSocket入门案例

  1. 客户端:

    1. 创建WebSocket对象:在JavaScript中,我们可以创建一个WebSocket对象,指定要连接的服务器地址。
    var ws = new WebSocket("ws://localhost:8080/websocket");
    
    1. 监听事件:WebSocket对象提供了四个事件:onopen、onmessage、onerror、onclose,我们可以通过监听这些事件来处理WebSocket的各种情况。
    ws.onopen = function(event) {
      console.log("Connection open ..."); 
    };
    
    ws.onmessage = function(event) {
      console.log("Received Message: " + event.data);
    };
    
    ws.onclose = function(event) {
      console.log("Connection closed ..."); 
    };
    
    ws.onerror = function(event) {
      console.log("Error: " + event.data);
    };
    
    1. 发送数据:WebSocket对象提供了一个send方法,我们可以通过这个方法向服务器发送数据。
    ws.send("Hello Server!");
    
    1. 关闭连接:当我们不再需要WebSocket连接时,可以调用WebSocket对象的close方法来关闭连接。
    ws.close();
    

    可以直接使用js写个小页面

    <!DOCTYPE HTML>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>WebSocket Demo</title>
    </head>
    <body>
        <input id="text" type="text" />
        <button onclick="send()">Send Message</button>
        <button onclick="closeWebSocket()">Close</button>
        <div id="message">
        </div>
    </body>
    <script type="text/javascript">
        var websocket = null;
        var clientId = Math.random().toString(36).substr(2);
    
        //判断当前浏览器是否支持WebSocket
        if('WebSocket' in window){
            //连接WebSocket节点
            websocket = new WebSocket("ws://localhost:8080/ws/"+clientId);
        }
        else{
            alert('Not support websocket')
        }
    
        //连接发生错误的回调方法
        websocket.onerror = function(){
            setMessageInnerHTML("error");
        };
    
        //连接成功建立的回调方法
        websocket.onopen = function(){
            setMessageInnerHTML("连接成功");
        }
    
        //接收到消息的回调方法
        websocket.onmessage = function(event){
            setMessageInnerHTML(event.data);
        }
    
        //连接关闭的回调方法
        websocket.onclose = function(){
            setMessageInnerHTML("close");
        }
    
        //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
        window.onbeforeunload = function(){
            websocket.close();
        }
    
        //将消息显示在网页上
        function setMessageInnerHTML(innerHTML){
            document.getElementById('message').innerHTML += innerHTML + '<br/>';
        }
    
        //发送消息
        function send(){
            var message = document.getElementById('text').value;
            websocket.send(message);
        }
    	
    	//关闭连接
        function closeWebSocket() {
            websocket.close();
        }
    </script>
    </html>
    
    
  2. 服务端:

    1. 导入WebSocket的maven坐标

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-websocket</artifactId>
      </dependency>
      
    2. 导入WebSocket服务端组件WebSocketServer,用于与客户端通信

      package com.sky.websocket;
      
      import com.sky.handler.TurnoverReportVOEncoder;
      import org.springframework.stereotype.Component;
      import javax.websocket.OnClose;
      import javax.websocket.OnMessage;
      import javax.websocket.OnOpen;
      import javax.websocket.Session;
      import javax.websocket.server.PathParam;
      import javax.websocket.server.ServerEndpoint;
      import java.util.Collection;
      import java.util.HashMap;
      import java.util.Map;
      
      /**
       * WebSocket服务
       */
      @Component
      @ServerEndpoint(value = "/ws/{sid}",encoders = {TurnoverReportVOEncoder.class}) // 为对象指定编码器(目前是转成json发送给客户端)
      public class WebSocketServer {
      
          //存放会话对象
          private static Map<String, Session> sessionMap = new HashMap();
      
          /**
           * 连接建立成功调用的方法
           */
          @OnOpen
          public void onOpen(Session session, @PathParam("sid") String sid) {
              System.out.println("客户端:" + sid + "建立连接");
              sessionMap.put(sid, session);
          }
      
          /**
           * 收到客户端消息后调用的方法
           *
           * @param message 客户端发送过来的消息
           */
          @OnMessage
          public void onMessage(String message, @PathParam("sid") String sid) {
              System.out.println("收到来自客户端:" + sid + "的信息:" + message);
          }
      
          /**
           * 连接关闭调用的方法
           *
           * @param sid
           */
          @OnClose
          public void onClose(@PathParam("sid") String sid) {
              System.out.println("连接断开:" + sid);
              sessionMap.remove(sid);
          }
      
          /**
           * 群发
           *
           * @param message
           */
          public void sendToAllClient(String message) {
              Collection<Session> sessions = sessionMap.values();
              for (Session session : sessions) {
                  try {
                      //服务器向客户端发送消息
                      session.getBasicRemote().sendText(message);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }
      
          public void sendObjToAllClient(Object object) {
              Collection<Session> sessions = sessionMap.values();
              for (Session session : sessions) {
                  try {
                      //服务器向客户端发送对象--注意第4步骤,需要为该对象指定一个编码器
                      session.getBasicRemote().sendObject(object);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }
      
      }
      
      
    3. 导入配置类WebSocketConfiguration,注册WebSocket的服务端组件ServerEndpointExporter

      package com.sky.config;
      
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.socket.server.standard.ServerEndpointExporter;
      
      /**
       * WebSocket配置类,用于注册WebSocket的Bean
       */
      @Configuration
      public class WebSocketConfiguration {
          @Bean
          public ServerEndpointExporter serverEndpointExporter() {
              return new ServerEndpointExporter();
          }
      }
      
    4. 如果想向客户端推送封装好的对象,在WebSocket中,需要提供一个编码器来将这个对象转换为可以通过网络传输的格式,通常是字符串或者二进制数据。

      package com.sky.handler;
      
      import com.fasterxml.jackson.databind.ObjectMapper;
      import com.sky.vo.TurnoverReportVO;
      
      import javax.websocket.EncodeException;
      import javax.websocket.Encoder;
      import javax.websocket.EndpointConfig;
      
      /**
       * @projectName: sky-take-out
       * @package: com.sky.handler
       * @className: TurnoverReportVOEncoder
       * @author: fangjiayueyuan
       * @description: TODO
       * @date: 2023/12/24 16:16
       * @version: 1.0
       */
      public class TurnoverReportVOEncoder implements Encoder.Text<TurnoverReportVO>{
          private static ObjectMapper objectMapper = new ObjectMapper();
      
          @Override
          public String encode(TurnoverReportVO turnoverReportVO) throws EncodeException {
              try {
                  // 使用Jackson库将对象转换为JSON字符串
                  return objectMapper.writeValueAsString(turnoverReportVO);
              } catch (Exception e) {
                  throw new EncodeException(turnoverReportVO, "对象转换为JSON字符串时发生错误", e);
              }
          }
      
          @Override
          public void init(EndpointConfig endpointConfig) {
              // 这里可以进行编码器的初始化操作,但在这个例子中我们不需要进行任何操作
          }
      
          @Override
          public void destroy() {
              // 这里可以进行编码器的清理操作,但在这个例子中我们不需要进行任何操作
          }
      }
      
      
    5. 导入定时任务类WebSocketTask,定时向客户端推送数据

      package com.sky.task;
      
      import com.sky.service.ReportService;
      import com.sky.vo.TurnoverReportVO;
      import com.sky.websocket.WebSocketServer;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.scheduling.annotation.Scheduled;
      import org.springframework.stereotype.Component;
      
      import java.time.LocalDate;
      import java.time.LocalDateTime;
      import java.time.format.DateTimeFormatter;
      
      @Component
      public class WebSocketTask {
          @Autowired
          private WebSocketServer webSocketServer;
      
          @Autowired
          private ReportService reportService;
      
          /**
           * 通过WebSocket每隔5秒向客户端发送消息
           */
          @Scheduled(cron = "0/5 * * * * ?")
          public void sendMessageToClient() {
              webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));
          }
      
          /**
           * 通过WebSocket每隔5秒向客户端发送消息
           */
          @Scheduled(cron = "0/5 * * * * ?")
          public void sendObjMessageToClient() {
              TurnoverReportVO turnoverStatistics = reportService.getTurnoverStatistics(LocalDate.parse("2023-01-01"), LocalDate.now());
              webSocketServer.sendToAllClient("传个对象过去");
              webSocketServer.sendObjToAllClient(turnoverStatistics);
          }
      }
      
      

RPC通信

**RPC(Remote Procedure Call)**是一种通信协议,它允许运行在一台计算机上的程序调用另一台计算机上的程序中的函数或方法,就像调用本地函数一样,无需程序员显式处理底层的网络细节。

RPC的主要特征包括:

  1. 透明性:对于调用者来说,远程过程调用和本地过程调用是透明的,调用者无需关心过程调用的是本地过程还是远程过程。

  2. 语言无关性:RPC通常支持多种编程语言,只要两个通信的程序遵循同一RPC协议,它们就可以进行通信,无论它们是用什么编程语言编写的。

  3. 同步性:RPC通常是同步的,也就是说,当一个RPC调用发出后,调用者会停止执行,直到得到结果。然而,也有一些RPC系统支持异步调用。

为什么使用RPC

  1. 简化分布式系统的开发:RPC隐藏了底层的网络通信和数据传输的复杂性,使得开发分布式应用更加简单。

  2. 提高代码的可重用性:通过RPC,可以将一些通用的功能实现为服务,然后在多个应用中重用这些服务。

  3. 提高系统的可扩展性:通过RPC,可以将一个大的系统分解为多个可以独立开发和部署的小的服务。

RPC的替代方案

  1. RESTful API:RESTful API是一种基于HTTP协议的通信方式,它使用HTTP的方法(如GET、POST、PUT、DELETE等)来操作资源。RESTful API比RPC更简单,更易于使用,但它不如RPC灵活,因为它只能使用HTTP协议,而RPC可以使用任何传输协议。
  2. 消息队列:消息队列是一种异步的通信方式,它允许程序通过发送和接收消息来进行通信。消息队列可以解耦发送者和接收者,使得它们可以独立地扩展和失败。然而,消息队列的使用比RPC更复杂,因为它需要处理消息的发送、接收、存储和确认。

RPC入门案例

以Thrift为例:

  1. 定义数据类型和服务接口:使用Thrift的IDL(接口定义语言)定义数据类型和服务接口,然后通过Thrift的编译器生成对应语言的代码。

    namespace java com.sankuai.mdp.thrift
    
    struct User{
        1:i32 id
        2:string name
        3:i32 age=0
    }
    
    service UserService{
        User getById(1:i32 id)
        bool isExist(1:string name)
    }
    
  2. 通过Thrift编译器生成Java代码:会生成两个对象:User、UserService

    thrift --gen java HelloWorld.thrift
    
  3. 服务端代码,实现UserService.Iface接口;启动服务端.

    package com.sankuai.mdp.thriftserversnapshot.service.impl;
    
    import com.sankuai.mdp.thriftapisnapshot.entity.User;
    import com.sankuai.mdp.thriftapisnapshot.entity.UserService;
    import org.apache.thrift.TException;
    
    /**
     * @projectName: thrift-api-snapshot
     * @package: com.sankuai.mdp.thriftserversnapshot.service.impl
     * @className: UserServiceImpl
     * @author: fangjiayueyuan
     * @description: TODO
     * @date: 2023/12/17 21:33
     * @version: 1.0
     */
    public class UserServiceImpl implements UserService.Iface{
        @Override
        public User getById(int id) throws TException {
            System.out.println("-----调用getById-----");
            User user = new User();
            user.setId(id);
            user.setName("dog");
            user.setAge(18);
            return user;
        }
    
        @Override
        public boolean isExist(String name) throws TException {
            return false;
        }
    }
    
    
    package com.sankuai.mdp.thriftserversnapshot.service.impl;
    
    import com.sankuai.mdp.thriftapisnapshot.entity.UserService;
    import org.apache.thrift.protocol.TBinaryProtocol;
    import org.apache.thrift.server.TServer;
    import org.apache.thrift.server.TSimpleServer;
    import org.apache.thrift.transport.TServerSocket;
    import org.apache.thrift.transport.TServerTransport;
    import org.apache.thrift.transport.TTransportException;
    
    /**
     * @projectName: thrift-api-snapshot
     * @package: com.sankuai.mdp.thriftserversnapshot.service.impl
     * @className: SimpleService
     * @author: fangjiayueyuan
     * @description: TODO
     * @date: 2023/12/17 21:59
     * @version: 1.0
     */
    public class SimpleService {
        public static void main(String[] args) {
            try{
                TServerTransport serverTransport = new TServerSocket(9090);
                UserService.Processor processor = new UserService.Processor(new UserServiceImpl());
                TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();
                TSimpleServer.Args targs = new TSimpleServer.Args(serverTransport);
                targs.processor(processor);
                targs.protocolFactory(protocolFactory);
                TServer server = new TSimpleServer(targs);
                server.serve();
            } catch (TTransportException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
  4. 客户端代码,调用服务端的方法,就像调用本地方法一样

    package com.sankuai.mdp.thriftclientsnapshot.service.impl;
    
    import com.sankuai.mdp.thriftapisnapshot.entity.User;
    import com.sankuai.mdp.thriftapisnapshot.entity.UserService;
    import org.apache.thrift.TException;
    import org.apache.thrift.protocol.TBinaryProtocol;
    import org.apache.thrift.transport.TSocket;
    import org.apache.thrift.transport.TTransport;
    import org.apache.thrift.transport.TTransportException;
    
    /**
     * @projectName: thrift-api-snapshot
     * @package: com.sankuai.mdp.thriftclientsnapshot.service.impl
     * @className: SimpleClient
     * @author: fangjiayueyuan
     * @description: TODO
     * @date: 2023/12/17 21:58
     * @version: 1.0
     */
    public class SimpleClient {
        public static void main(String[] args) {
            TTransport transport = null;
            try {
                transport = new TSocket("localhost", 9090);
                TBinaryProtocol protocol = new TBinaryProtocol(transport);
                UserService.Client client = new UserService.Client(protocol);
                transport.open();
                User result = client.getById(1);
                System.out.println("Result:" + result);
            } catch (TTransportException e) {
                e.printStackTrace();
            } catch (TException e) {
                throw new RuntimeException(e);
            } finally {
                if (transport != null) {
                    transport.close();
                }
            }
        }
    }
    
    
  5. 先后启动运行服务端SimpleService.java、客户端代码SimpleClient.java
    在这里插入图片描述
    在这里插入图片描述
    Git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1333819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法通关村-番外篇排序算法

大家好我是苏麟 , 今天带来番外篇 . 冒泡排序 BubbleSort 最基本的排序算法&#xff0c;最常用的排序算法 . 我们以关键字序列{26,53,48,11,13,48,32,15}看一下排序过程: 动画演示 : 代码如下 : (基础版) class Solution {public int[] sortArray(int[] nums) {for(int i …

ArkUI - 状态管理

目录 一、State装饰器 二、自定义组件 三、Prop和Link、Provide和Consume 四、Observed和ObjectLink 五、页面路由 跳转模式 实例模式 使用步骤 一、State装饰器 这里涉及到两个概念 状态 和 视图 状态&#xff08;State&#xff09;&#xff1a;指驱动视图更新的数…

【附三菱 MX OPC Server 6.04的安装包】MX-OPC下载以及用GX Works2和组态王进行仿真连接

使用MX-OPC来完成三菱和组态王的仿真连接。 文章目录 目录 文章目录 软件下载 1.OPC设置 2.GX Works 2 设置 3.GX Works 2 和OPC 连接测试 4.和组态王进行仿真连接 5.安装OPC后&#xff0c;GX Works2 无法打开 提示堆栈不足 6.收尾&#xff08;组态王变量的删除&#xff0…

如何使用java来实现windows系统关机

可以使用Java代码来调用操作系统的命令行来实现Windows关机操作。具体步骤如下&#xff1a; import java.io.IOException;public class ShutdownWindows {public static void main(String[] args) {try {// 调用命令行执行关机命令Process process Runtime.getRuntime().exec…

Python - 深夜数据结构与算法之 Graph

目录 一.引言 二.图的简介 1.Graph 图 2.Undirected graph 无向图 3.Directed Graph 有向图 4.DFS / BFS 遍历 三.经典算法实战 1.Num-Islands [200] 2.Land-Perimeter [463] 3.Largest-Island [827] 四.总结 一.引言 Graph 无论是应用还是算法题目在日常生活中比较…

股市中的Santa Claus Rally (圣诞节行情)

圣诞节行情 Santa Claus Rally Santa Claus Rally 是指 12 月 25 日圣诞节前后股市的持续上涨这样一个现象。《股票交易员年鉴》的创始人 Yale Hirsch 于 1972 年创造了这个定义&#xff0c;他将当年最后五个交易日和次年前两个交易日的时间范围定义为反弹日期。 根据 CFRA Re…

OpenAI科学家Hyung Won Chung演讲精华版

文章目录 第一个观点&#xff1a;涌现第二个观点&#xff1a;如何扩大规模1、标记化2、嵌入3、计算4、评估&#xff08;损失函数&#xff09;5、反向传播 最近从Google跳槽到OpenAI的AI科学家 Hyung Won Chung 比较拗口&#xff0c;我就简称尚哥了 他最近做了一个技术演讲 …

GPU性能实时监测的实用工具

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

ElasticSearch入门介绍和实战

目录 1.ElasticSearch简介 1.1 ElasticSearch&#xff08;简称ES&#xff09; 1.2 ElasticSearch与Lucene的关系 1.3 哪些公司在使用Elasticsearch 1.4 ES vs Solr比较 1.4.1 ES vs Solr 检索速度 2. Lucene全文检索框架 2.1 什么是全文检索 2.2 分词原理之倒排索引…

Unity-Shader-渲染队列

Unity-Shader-渲染队列 渲染简介Unity中的几种渲染队列Background (1000)最早被渲染的物体的队列。Geometry (2000) 不透明物体的渲染队列。大多数物体都应该使用该队列进行渲染&#xff0c;也就是Unity Shader中默认的渲染队列。AlphaTest (2450) 有透明通道&#xff0c;需要进…

Python画皮卡丘

代码&#xff1a; import turtledef getPosition(x, y):turtle.setx(x)turtle.sety(y)print(x, y)class Pikachu:def __init__(self):self.t turtle.Turtle()t self.tt.pensize(3)t.speed(9)t.ondrag(getPosition)def noTrace_goto(self, x, y):self.t.penup()self.t.goto(…

Python - 深夜数据结构与算法之 Heap Binary Heap

目录 一.引言 二.堆与二叉堆介绍 1.Heap 堆 2.Binary Heap 二叉堆 3.HeapifyUp 添加节点 4.HeapifyDown 删除节点 5.Heap 时间复杂度 6.Insert & Delete 代码实现 三.经典算法实战 1.Smallest-K [M14] 2.Sliding-Window-Max [239] 3.Ugly-Number [264] 4.Top-…

机器学习或深度学习的数据读取工作(大数据处理)

机器学习或深度学习的数据读取工作&#xff08;大数据处理&#xff09;主要是.split和re.findall和glob.glob运用。 读取文件的路径&#xff08;为了获得文件内容&#xff09;和提取文件路径中感兴趣的东西(标签) 1&#xff0c;“glob.glob”用于读取文件路径 2&#xff0c;“.…

靠谱免费的MAC苹果电脑杀毒软件CleanMyMac X2024

您是否曾经为Mac电脑的性能下降、存储空间不足而烦恼&#xff1f;是否希望有一个简单而高效的解决方案来优化您的Mac系统&#xff1f;那么&#xff0c;我向您介绍一款非常出色的工具&#xff1a;CleanMyMac X。它能够轻松处理这些问题&#xff0c;并让您的Mac恢复到最佳状态。 …

新版IDEA中Git的使用(三)

说明&#xff1a;前面介绍了在新版IDEA中Git的基本操作、分支操作&#xff0c;本文介绍一下在新版IDEA中&#xff0c;如何回滚代码&#xff1b; 分以下三个阶段来介绍&#xff1a; 未Commit的文件&#xff1b; 已经Commit&#xff0c;但未Push的文件&#xff1b; 已经Push的…

常见的Ubuntu命令30条(二)

Ubuntu命令是指在Ubuntu操作系统中用于执行各种任务和操作的命令行指令。这些命令可以用于管理系统、配置网络、安装软件、浏览文件等。Ubuntu命令通常在终端&#xff08;Terminal&#xff09;应用程序中输入并执行。 history&#xff1a;显示命令行历史记录。grep&#xff1a…

Spark编程语言选择:Scala、Java和Python

在大数据处理和分析领域&#xff0c;Apache Spark已经成为一种非常流行的工具。它提供了丰富的API和强大的性能&#xff0c;同时支持多种编程语言&#xff0c;包括Scala、Java和Python。选择合适的编程语言可以直接影响Spark应用程序的性能、可维护性和开发效率。在本文中&…

jvm_下篇_补充:浅堆深堆与内存泄露

笔记来源&#xff1a;尚硅谷 JVM 全套教程&#xff0c;百万播放&#xff0c;全网巅峰&#xff08;宋红康详解 java 虚拟机&#xff09; 同步更新&#xff1a;https://gitee.com/vectorx/NOTE_JVM https://codechina.csdn.net/qq_35925558/NOTE_JVM https://github.com/uxiahnan…

shell 如何调用多个脚本

简介 这篇文章主要描述如何通过主脚本去调用其他脚本中的方法&#xff0c;调用的过程中可能出现哪些坑&#xff0c;如何避免。 目录 1. 主脚本调用其他脚本的方法 1.1. bash方法 1.2. source方法 2. 避坑技巧 2.1. 路径配置无效 2.2. source变量冲突 3. 总结 1. 主脚本调…

工具系列:TensorFlow决策森林_(5)使用文本和神经网络特征

文章目录 设置使用原始文本作为特征使用预训练的文本嵌入同时训练决策树和神经网络构建模型训练和评估模型 欢迎来到 TensorFlow决策森林&#xff08; TF-DF&#xff09;的 中级教程。 在本文中&#xff0c;您将学习有关 TF-DF的一些更高级的功能&#xff0c;包括如何处理自…