NodeJs 实践之他说

NodeJs 实践之他说

作为前端,我们知道 node 在构建方面是成功的,我们也听说过全栈,那么 node 是否能应用在企业级的后端?一起来看一下腾讯视频的 NodeJs 改造

Tip: 故事大概是 2018 年,主角杨浩,来源于:

背景

腾讯视频是一个内容型的网页。

在 2014 年以前使用的是 C++ 动态生成页面。有两个问题:

  • 前端不太会维护 C++ 的那套东西
  • C++ 定时生成网页。有多少个视频,它就会生成多少个网页,然后推送到对应的服务器中。如果更改了某个视频的信息,得等到下次生成网页才会更新

于是打算使用 NodeJs 来对其进行改造。

第一只怪 - 打通 NodeJs

由于腾讯视频是内容型的网页,当时有 30% 的流量来自搜索引擎,所以需要更好的 SEO,于是选用 SSR(服务器渲染)。

Tip: Vue_SSR中也提到服务端渲染的优势:更快的首屏加载、更好的 SEO

NodeJs 扮演的角色如下:

请求经过 cdn,经过 nginx 通过负载均衡访问 NodeJs 服务,NodeJs 从各个后台服务拉取数据,渲染好了在返回给前端。

Tip:相当于以前用 c++ 生成页面,现在由 NodeJs 生成页面。

打通 RPC 调用

rpc(作用类似 http 协议) 就是远端资源调用,因为 node 需要从各个后台服务拉取数据。这里涉及4个方面的事情:

  • 负载均衡。node 和后台服务之间有一层负载均衡,用的是一种类DNS负载均衡,所以得和负载均衡服务交互,拿到每次需要访问服务器的ip
  • Mongo/mysql/redis(redis - 基于键值对的内存数据库) 存储的打通。比较简单,就是对应 npm 包的使用
  • 后台私有协议。例如二进制的协议某场景下比http协议好一些
  • 监控系统/日志系统

Tip: DNS除了能解析域名之外还具有负载均衡的功能

高并发下进程管理

node 是单线程,使用 cluster 模块创建多个 Nodejs 进程,实现高并发和高可用性。但 cluster 还有点缺陷,做了以下几点优化:

  • 心跳 - master 定时给 cluster 发信息,如果有回复说明它还活着,否则就是僵死,就 kill 它
  • 内存检测 - 监控 cluster 内存,如果内存过高,可能就是内存泄漏,也杀死它
  • 重启 - cluster kill 后,有的应用可能不能用,就需要将其重启

Tip:在 Node.js 中,cluster 模块提供了一种简单的方式来创建多个 Node.js 进程,以实现高并发和高可用性。通过集群模块,开发者可以使用现有的单线程程序代码,并将其自动拆分到多个子进程中执行,从而充分利用 CPU 和内存资源,提高应用的效率和稳定性。

第二只怪 - 维护 NodeJs

终于把 Node 打通了,现在可以用 node 写点东西了。

要用 node 写一个稳定的服务,也不是那么简单。node 很容易挂掉,比如一点语法问题。

Node 人员不足

懂前端的人很多,但懂 node 的就相对要少。写后端需要懂后端那套东西,要会服务器调优,还要懂运维。

为了解决 Node 人员不足,决定使用框架来平滑 node 曲线。

之前要用 node 写项目难度大,是因为需要经历这4步:业务逻辑 -> 会写 NodeJs -> 熟悉 rpc 调用 -> 熟悉运维(性能调优)

现在用框架,只需要写业务逻辑就能开干。

这里框架主要使用配置化,屏蔽底层复杂的实现,对外暴露友好的配置。就像 webpack,让前端构建生态非常繁荣。

要做配置化,就得分析 ssr 本质:从各个后台领取数据,简单处理后进行渲染。

ssr抽象表示:请求参数 -> 后端数据 + 模板 -> 页面文本

ssr 公式:内容=f(数据源,模板)

只要将数据源模板配置化,就可以通过一个函数解决 ssr 的问题。

模板引擎的选型

研究了如下几种模板:

  1. art-template 国内有名的开源模板引擎
  2. es6 template string + vm.runInNewContext(编译和运行代码,作用类似 new Function('console.log("1")'))
  3. vue ssr、react ssr

art-template 中的 forEach 可以使用预编译语法来实现,由于交互较少,所以无需使用 vue和react。而且 es6 模板速度测试比 vue-server-render 快很多。

所以最终选取第二种方案:es6 template

数据源

数据源的配置用如下一个 json 表示:

module.exports = {
    video: {
        url: "http://...."
    },
    vidviewcount: {
        dependencies: ['video'],
        url: "protobuf://union.video.qq.com/...."
    },
    rank: {
        url: "redis://admin:admin@135246:65535/get?key=haha"
    }
}

这个 json 表示 ssr 过程中数据获取逻辑,其中 vidviewcount 通过 dependencies 字段指明依赖 video。

这里用 http、protobuf、redis三种协议(方式)获取数据。一个协议对应一个请求器,不在框架中的协议可以注册即可。就像这样:

factory.registerRequestor('http', requestor);
function requestor(){
    ...
}

为了增加配置的灵活性,这里增加了几个 hook:

{
    ...
    fixBefore: function(param){
        // 检测参数合法性
        return param
    },
    fixAfter: function(data){
        // 检测返回数据合法性
        if(!data.vid){
            throw Error('xxx')
        }
        return data
    },
    onError: function(e){
        return err;
    }
}

写配置就是写 SSR 逻辑

只要学会写配置就能搞定 ssr 逻辑。

公式:内容=f(数据源,模板)(参数)

ssr 外部用 koa(nodejs 的web框架) 封装一下就是一个服务:

let app = koa()
let ssr = pigfarm(data, template)
app.use(async ctx => {
    ctx.body = await ssr(ctx.query)
})

第三只怪 - 抢后端饭碗的问题

后台有后台擅长的地方(逻辑、计算密集),前端有前端擅长的地方(前端网页优化)。

寻找一个合作共赢的方式。这里做了如下几个有特色的前端服务:

热更新

每次业务逻辑的改动需要经历长时间的发布重启

前面已经将数据源模板做到了配置化,现在修改逻辑,只需要更改数据库中的数据源和模板即可,做到热更新。

首页调优

v.qq.com 首页包含27个模块

  • 富含个性化内容,无法缓存
  • 页面庞大,速度慢
  • 全网页超过40个rpc
  • 个性化接口调用慢

利用 transfer-encoding:chunked 快速返回首屏数据,后面再加载2、3、4...屏的数据

Tip:BigPipe 是一个前端性能优化技术,采用分块渲染的方式。transfer-encoding:chunked 是一种 HTTP协议中定义的传输编码方式之一。运行服务器在不知道响应体大小的情况下,将响应分成若干个固定大小的快进行传输。

容灾

前端容灾是指在前端应用中,为了保障可靠性和稳定性而采用的一系列技术和策略,以确保即使在系统出现部分异常或错误的情况下,仍然可以正常提供服务。比如网络问题、服务器故障等

这里可以做整页备份

js 中用高阶函数非常容易实现缓存。请看示例:

function memoize(func) {
  // 用于缓存
  const cache = {};
  return function(...args) {
    const key = JSON.stringify(args);
    // 如果缓存中有值,直接返回
    if (cache[key]) {
      return cache[key];
    }
    
    const result = func.apply(this, args);
    cache[key] = result;
    return result;
  };
}
作者:彭加李
出处:http://www.cnblogs.com/pengjiali/p/17409232.html
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
本文转载于网络 如有侵权请联系删除

相关文章

  • nextline函数_Java 中nextLine()方法没有执行直接跳过解决办法[通俗易懂]

    大家好,又见面了,我是你们的朋友全栈君。使用Java的Scanner类nextLne()方法从显示器输入数据时,nextInt()后面的nextLine()直接跳过没有执行;截图:第三个输入直接跳过通过上网的查找我终于发现了问题出在哪里:原来nextLine()函数获取的是一整行的内容其中也包括了(\n)也就是换行符而nextInt()函数获取的仅仅是一个值不包含(\n),那么nextInt()后面的nextLine()读取一行,就把(\n)读进去了,意思就是nextLine()的值是(\n),所以不会进行数据获取。解决办法:可以选择多添加一个nextLine()去获取nextInt()后面的换行符(\n)改了之后程序正常输入:[03]java中的方法以及控制语句00Java中的语句块语句块(有时叫做复合语句),是用花括号扩起的任意数量的简单Java语句.块确定了局部变量的作用域.块中的程序代码,作为一个整体,是要被一起执行的.块可以被嵌套在另一个块中,但…Java中的方法应用一.如何定义java中的方法所谓方法,就是用来解决一类问题的代码的有序

  • Python基本特殊方法之__format__

    大家好,又见面了,我是全栈君__format__()方法  __format__()传参方法:someobject.__format__(specification)  specification为指定格式,当应用程序中出现”{0:specification}”.format(someobject)或format(someobject,specification)时,会默认以这种方式调用  当specification为”“时,一种合理的返回值是returnstr(self),这为各种对象的字符串表示形式提供了明确的一致性  注意,”{0!s}”.format()和”{0!r}”.format()并不会调用__format__()方法,他们会直接调用__str__()或者__repr__()例:自定义我们自己的__format__()格式#coding=utf-8 classformatest: def__init__(self,name,age): self.name,self.age=name,age def__format__(self,specification): ifs

  • postman请求数据库方法(Omysql)

    一、github地址:https://github.com/liyinchigithub/Omysql二、效果三、使用方式如果你电脑已经安装配置Git、node环境,可以直接按下面步骤进行操作:1、从github拉取工程gitclonegit@github.com:liyinchigithub/express-mysql-restfulAPI.git复制2、切换到目录下cdOmysql复制3、安装依赖包npminstall复制4、启动omysql服务nodeserver.js复制5、如果你的电脑还没有node环境,具体配置方法可以参考下面两个链接:windowshttps://jingyan.baidu.com/article/1876c8529c79e2890b1376dd.htmlmachttps://jingyan.baidu.com/article/6b1823098bdd9fba59e1597a.html在postman上面构建以下请求,即可实现postman访问数据库!API接口文档1、功能创建数据库请求方法:POST 请求地址: http://127.0.0.1:8004

  • Python模拟登陆 —— 征服验证码 1 豆瓣

    captcha是CompletelyAutomatedPublicTuringTesttoTellComputersandHumansApart,全自动区分计算机和人类的图灵测试)的简称。登陆失败若干次之后,豆瓣登录页面才会出现验证码。所以为了确保py文件运行正确,要先故意输错几次,出现验证码框之后,再运行。:)登录界面使用Python3.6。fromurllib.requestimporturlretrieve importrequests frombs4importBeautifulSoup fromosimportremove try: importcookielib except: importhttp.cookiejarascookielib try: fromPILimportImage except: pass url='https://accounts.douban.com/login' datas={'source':'index_nav', 'remember':'on&#

  • vue + vant 笔记

    1、a、安装vant:npmivant-S   b、按需引入组件:安装webpack插件:babel-plugin-import //在当前项目下安装:npminstallbabel-plugin-import-D//.babelrc中配置 //注意:webpack1无需设置libraryDirectory { "plugins":[ ["import",{ "libraryName":"vant", "libraryDirectory":"es", "style":true }] ] } //对于使用babel7的用户,可以在babel.config.js中配置 module.exports={ plugins:[ ['import',{ libraryName:'vant', libraryDirectory:'es', style:true },'vant'] ] };//在mian.js或者需要的组件中引入所需的组件 import{Button,Cell}from'vant';复制   2、实现下拉刷新上拉加载:<van-pull-refresh

  • 机器翻译及相关技术;注意力机制与Seq2seq模型;Transformer

    seq2seq的用途有很多,比如机器翻译,写诗,作曲,看图写文字等等用途很广泛!该模型最早在2014年被Cho和Sutskever先后提出,前者将该模型命名为“Encoder-DecoderModel”也就是编码-解码模型,后者将其命名为“SequencetoSequenceModel”也就是序列到序列模型,两者有一些细节上的差异,但总体大致思想基本相同。   seq2seq根据字面意思来看就是序列到序列,再具体点就是输入一个序列(可以是一句话,一个图片等)输出另一个序列。这里以RNN为基础的机器翻译为例,介绍seq2seq和attention注意力机制。(seq2seq实现的方法有很多,比如MLP,CNN,RNN等) 这是机器翻译的部分数据(法语->英语):     我们先了解下机器翻译的大致流程:     大致意思就是根据输入的文本,神经网络开始学习和记忆,这个就是所谓的Encoder编码过程;然后根据自己的记忆,把文本一一翻译出来,这个就是所谓的Decoder解码过程。 现在再让我们更进一步了解seq2seq的具体流程:

  • Redis分布式缓存(五)

    1SpringBoot整合Redis 1导入依赖 创建SpringBoot项目时不要乱选默认开发工具,有一个坑 <!--若创建时已选择,则不用导入--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 复制 2编写配置文件 #配置redis #Redis服务器地址 spring.redis.host=192.168.0.108 #Redis服务器连接端口 spring.redis.port=6379 #使用数据库的索引编号,一个示例有16个数据库0到15 spring.redis.database=0 #Redis服务器连接密码(默认为空) spring.redis.password= 复制 3定制RedisTemplate的模板 com/yu/config/RedisConfig @Configur

  • iTOP-iMX6UL开发板【全能版】-动态调频技术简介

    本文档以iMX6UL为例,简单介绍cpufreq的5种模式。 在imx6ul的menuconfig中,进入 CPUPowerManagement---> CPUFrequencyscaling---> DefaultCPUFreqgovernor(ondemand)---> 中,如下图所示,可以看到有5个选项。这5个选项,可以将cpu的频率设置为不同的 管理模式,默认设置的是ondemand模式。   cpufreq是一个动态调整cpu频率的模块,系统启动时会生成 “/sys/devices/system/cpu/cpu0/cpufreq/”,如下图所示,是imx6ul文件上的 cpufreq文件夹。   以上参数可以通过cat命令,例如: “cat/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor”可以获取当前 CPU频率的模式。作者机器当前模式是“ondemand”,也就是动态调频模式。   其它参数: cpuinfo_cur_freq为当前频率;

  • Linux常用命令之shutdown

    shutdown执行过程:     shutdown命令是用shell编写的程序,必须由超级用户才能执⾏。 shutdown命令执行后,会以广播的形式通知正在系统中工作的所有用户,系统将在指定的时间内关闭。请保存文件,停止作业,注销用户。此时login指令被冻结,新的用户不能登录。当所有用户从系统中注销或者指定时间已到时,shutdown就发送信号给init程序,要求init程序改变系统运行级别。接着,init程序根据shutdown指令传递过来的参数,相应地改变运行级别。例如,如果shutdown指定的参数是关机命令,init程序就执行nit0进行关机;如果shutdown指定的参数要重启系统,init程序就执行init6进行系统重启。 shutdown命令的详细语法如下: shutdown[-fFhknrc(参数名称)][-t秒数]时间[警告信息]各参数含义如下。 各参数含义如下 -f:重新启动时不执行fsck。 -F:重新启动时执行fsck。 -h:将系统关机,在某种程度上功能与halt命令相当。 -k:只是发送信息给所有⽤户,但并不会真正

  • 《结对编项目作业名称-设计文档》

    项目:关灯游戏,所用软件,pygame成员:祁昊,刘孝东 关灯游戏设计文档: pygame作为一种游戏编程语言,以其简单性、可移植性等优点,得到了广泛地应用,特别是py使用比c,c++等语言简便,使其成为网络编程首选编程语言。,Pygame是跨平台Python模块,专为电子游戏设计。基于这样一个设想,所有需要的游戏功能和理念都(主要是图像方面)都完全简化为游戏逻辑本身,所有的资源结构都可以由高级语言提供,如Python。工具tile编辑器和一个关卡编辑器。得到广大程序员的接受和认可。 “关灯游戏”是大众化的一个小程序,人们手机中是一个很常见的一个小游戏,人们 对它并不陌生,在紧张的现在生活中给人们带来了不少的乐趣,我们写的这 个关灯小游戏可以让人们业余时间的适当放松,再繁忙的生活节奏中适当的放松下来。在这个程序中我用了python语言来编写,运用了python中的一些常用的功能,python语言是在国内外广泛使用的一种语言。python语言功能丰富、表达能力强、使用灵活方便、应用面广、目标程序效率高、可移植性好,纯粹的面向对象的编程,特别适合用于编写应用软件。代码简单易懂,在游戏的同时

  • 解决Element-u的 el-form 使用 v-if校验失灵问题

    解决Element-u的el-form使用v-if校验失灵问题 在element-ui的校验过程中,鉴于使用自带的校验方式,繁琐且麻烦,因而使用了element-ui-verify的插件。但是现在碰到一个新的问题,不知道具体是哪块问题,看了两个的源码,想使用element-ui自带的addFields时,无法生效,因而是内部方法,外部无法拿到【此处先记一笔,看看后期这块是否是解决的要点】。 主要出现的问题是,使用了v-if时,导致无法有效被element-ui知晓,而报[ElementWarn]pleasepasscorrectprops!,意思就是说使用v-if的字段无法及时被收录。而这个是概率问题,有时候可以有时候不行。目前还无法有效判断出来。 <templatev-if="editMobile"> <el-rowtype="flex":gutter="2"v-if="editMobile"> <el-col:span="20"> <el-form-item ref="captcha" label="验证码:" prop="captcha"

  • 32: Binary Tree Level Order Traversal

       /************************************************************************/      /*      32:     BinaryTreeLevelOrderTraversal                                         */    

  • 电脑连接手机热点 | 如何手动设置固定ip | 手机访问电脑资源

    手机开启热点,电脑连接后的ip一般是DHCP分配好了的,但是如果我们想要自己手动设置ip呢? 打开wifi属性 这里动态分配的ip是192.168.10.69 那我们想设置成192.168.10.1呢 如下设置,只改ip,其它的默认抄DHCP的 网关和ip不能重复~ 然后就OK了 把WiFi设置专用专用网络,然后把专用网络的防火墙关了,手机就能访问电脑了 子网前缀长度怎么设置 箴言:因为这些东西是非常简单的。不要抱怨自己学不会,那是因为你没有足够用心。

  • CCF 202109-2 非零段划分

    暴力70分能拿到,100分不能 知识点: 了解到差分的思想,现在认为如果用到了一个数组来记录区间变化量,则可称为差分。 unique函数:【整理】C++中的unique函数-nimphy-博客园(cnblogs.com)   参考别人的文章后自己写,第一次没过。 原因是 for(inti=1;i<n-1;i++){ if(a[i-1]<a[i]&&a[i]>a[i+1]){ cnt[a[i]]++; } else{ cnt[a[i]]--; } }复制 else判断条件错误,应该是 for(inti=1;i<n-1;i++){ if(a[i-1]<a[i]&&a[i]>a[i+1]){ cnt[a[i]]++; } elseif(a[i-1]>a[i]&&a[i]<a[i+1]){ cnt[a[i]]--; } }复制 什么是“山峰”“山谷”当时没搞清楚 AC代码: #include<iostream> #include<algorithm> #i

  • [svc]sed&amp;awk过滤行及sed常用例子

    -sed过滤行 sed'2p' sed'2,5p' sed'2p;3p;4p' -awk过滤行 awk'NR==2' awk'NR>=2&&NR<=3' awk'NR==2||NR==3||NR==4' -awk条件过滤 awk'{if($3>10)print$0}' 复制 过滤某1行 sed过滤第2行 [root@n1~]#sed-n'2p'/etc/passwd bin:x:1:1:bin:/bin:/sbin/nologin 复制 awk过滤第2行 [root@n1~]#awk'NR==2'/etc/passwd bin:x:1:1:bin:/bin:/sbin/nologin 复制 过滤2<i<4行 sed过滤2<i<4行 [root@n1~]#sed-n'2,4p'/etc/passwd bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin 复制 aw

  • mysql AB复制

    主: 修改配置文件: [mysqld] log-bin=mysql-bin server-id=1 账户授权 mysql>GRANTREPLICATIONSLAVEON*.*    TO'rep'@'%'IDENTIFIEDBY'Passw0rd'; 锁表,保持一致性 mysql>FLUSHTABLESWITHREADLOCK; 备份数据库 shell>mysqldump-uroot-pPassw0rd--all-databases>toslave.sql 读取日志名和偏移量 mysql>SHOWMASTERSTATUS; 重新启用写活动: mysql>UNLOCKTABLES; 拷贝toslave.sql至从服务器 scptoslave.sqlroot@10.210.74.49 修改从服务配置文件 [mysqld] server-id=2 启动从服务器 用--skip-slave-start选项启动从服务器,以便它不立即尝试连接主服务器。 /bin/mysqld_safe--

  • OpenGL ES EGL eglCreateWindowSurface

    一.EGL前言 二.EGL绘制流程简介 三.eglCreateWindowSurface函数简介 1.eglCreateWindowSurface函数 2.EGLSurface分类 四.eglCreateWindowSurface函数使用 五.猜你喜欢 零基础OpenGLES学习路线推荐:OpenGLES学习目录>>OpenGLES基础 零基础OpenGLES学习路线推荐:OpenGLES学习目录>>OpenGLES特效 零基础OpenGLES学习路线推荐:OpenGLES学习目录>>OpenGLES转场 零基础OpenGLES学习路线推荐:OpenGLES学习目录>>OpenGLES函数 零基础OpenGLES学习路线推荐:OpenGLES学习目录>>OpenGLESGPUImage使用 零基础OpenGLES学习路线推荐:OpenGLES学习目录>>OpenGLESGLSL编程 一.EGL前言 EGLNativeDisplayType–系统显示类型,标识你所开发设备的物理屏幕,DX/OPenGL

  • [Quote] How To Change, Customize &amp; Create Android Boot Animation [Guide]

    Fromhttp://www.addictivetips.com/mobile/how-to-change-customize-create-android-boot-animation-guide/   HowToChange,Customize&CreateAndroidBootAnimation[Guide] byHaroonQRajaonMay19,2011   55   ThebootanimationisthefirstthingthatyouseewhenpoweringonyourAndroidphoneortablet,aftertheoperatorormanufacturerlogo.Althoughitdoesnotserveapurposefunctionally,aneye-catchingbootanimationcancertainlymakeyourdevicestandoutwhilebooting.InthisthirdinstallmentofourAndroidcustomizationseries,w

  • mysql 使用binary logs 恢复数据

    确认binarylogs是否开启 showbinarylogs   如果没有启动注意在my.cnf的mysqld节中开启 log-bin=mysql-bin   /mysql/bin/mysqlbinlogmysql-bin.000014  检查要恢复数据的起始时间(14:35:50)和截止时间(14:36:13) DELIMITER/*!*/; #at4 #20102914:35:50serverid1end_log_pos120CRC320x33a2b7cfStart:binlogv4,serverv5.6.49-logcreated20102914:35:50atstartup #Warning:thisbinlogiseitherinuseorwasnotclosedproperly. ROLLBACK/*!*/; BINLOG' xmKaXw8BAAAAdAAAAHgAAAABAAQANS42LjQ5LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAADGYppfEzg

  • 使用 GitHub Pages+Hexo 搭建个人博客(mac)

    一、环境配置 1.brew下载||输入命令: /usr/bin/ruby-e"$(curl-fsSLhttps://raw.githubusercontent.com/Homebrew/install/master/install)" 复制 2.git下载||输入命令: brewinstallgit 复制 3.nodejs下载||输入命令: brewinstallnode 复制 4.检查git、nodejs版本 git--version node-v 复制 二、hexo 5.安装hexo sudonpminstall-ghexo-cli 复制 6.初始化hexo hexoinithexo 复制 7.进入到hexo文件夹下 cdhexo npminstall 复制 8.开启hexo服务器。 hexos 复制 浏览器中输入:localhost:4000,页面如图所示,hexo本地配置成功。 三、关联github 9.创建仓库。登录到自己到github账号,新建仓库,仓库名为:用户名.http://github.io,如图所示。10.进到hexo文件夹下

  • SVN服务器搭建和使用(一)

    原文地址:SVN服务器搭建和使用(一)-xjbest-博客园http://www.cnblogs.com/xiaobaihome/archive/2012/03/20/2407610.html Subversion是优秀的版本控制工具,其具体的的优点和详细介绍,这里就不再多说.首先来下载和搭建SVN服务器.现在Subversion已经迁移到apache网站上了,下载地址:http://subversion.apache.org/packages.html这是二进制文件包的下载地址,你可在左侧的导航栏找到源代码,文档的下载地址.windows操作系统下面的二进制文件包一共有5种,如图:个人认为最好用VisualSVNserver服务端和TortoiseSVN客户端搭配使用.点开上面的VisualSVN连接,下载VisualSVNserver,如图:然后下载TortoiseSVN客户端,官网下载地址:http://tortoisesvn.net/downloads.html注意下载跟你电脑匹配的安装包,在页面的下面你还可以找到语言包,如图:下载完成后,应该有这些安装包,如图:To

相关推荐

推荐阅读