首页 文章 精选 留言 我的

精选列表

搜索[扩展更新],共10000篇文章
优秀的个人博客,低调大师

2020DevOps状态报告——平台模型:扩展DevOps的新方法

平台模型是我们在这个领域看到越来越多的方法,它源于负责产品或服务的端到端交付的产品团队的理念。 如果只应用于单一的产品,或者几个产品,它的效果很好。 但如果有数百种产品或服务,把一个产品团队用于这些产品,对每一个来说都是低效和昂贵的。 想象10个团队,每个团队都有自己的技术栈、工具链和流程。 会一直重复解决类似的问题、花太多的时间来评估技术、集成、维护基础设施等等。 这些时间可以更好地花在建立和改进产品团队负责的实际产品上。 缺乏标准化的技术和流程也造成其他问题: ●管理变得昂贵,几乎不存在管理 ●独立的堆栈减少了整个组织的知识共享 ●许多产品团队实际上没有能力来运行完整的基础设施和应用程序。许多开发人员将基础设施操作视为分散他们实际工作的注意力,因此他们从不真正关注它。 虽然拥有多个端到端产品团队并不能很好地跨越大型复杂环境,但由清晰目标、边界和责任定义的平台模型却能做到 一个由用户建立在心中的平台,可以大大减少单个产品团队的辛苦和开销。 广义地说,平台团队提供基础设施、环境、部署管道和其他内部服务,使内部客户(通常是应用程序开发团队)能够构建、部署和运行其应用程序。 Evan Bottcher定义的数字平台在这时可以起作用:“作为一种令人信服的内部产品的自助服务API、工具、服务、知识和支持的基础。自主交付团队可以利用该平台以更快的速度交付产品功能,同时减少协作。” 自助服务是“一个好平台的一个关键特征。具体来说,它应该允许自助服务供应、自助服务配置、自助服务管理和平台功能和资产的运营。” 平台模型通常与本地云环境相关联,也适用于从古到今的许多其他类型的体系结构。主要优势有: ●应用团队可有更具效率。他们不必是基础设施运维方面的专家,也不必对工具链中的每种工具都有深入的了解,因而他们能够专注于产品。应用程序开发人员不再需要等待集中化的团队来为他们提供测试环境或云资源,而由此产生的自治性使他们能够更快地工作。 ●改善管理。如果您的所有应用程序都运行在完全不同的基础架构堆栈上,使用不同的流程,那么您就无法有效地管理成本、遵从性和审计。一个有效的平台能带来高效的IT治理,同时授权应用程序团队快速交付。 ●结束环境切换。不断地在应用程序和基础设施操作之间切换注意力是对生产力和创造力的巨大消耗。当个体工人和团队能够专注于自己特定的环境时,他们的境况会更好。 ●持续改善基础设施。一个提供面向客户解决方案的公共平台,而不仅仅是对基础设施的原始访问,使组织具有更大的灵活性。平台的消费者不与基础设施堆栈的具体实现挂钩,因此平台团队可以迭代地替换和升级组件,并且只需要与应用程序团队进行最小程度的交互。 内部平台的使用 在对平台的讨论中,我们使用“内部平台”一词来表示由组织和为组织构建的平台。我们将这些平台与外部供应商提供的平台区分开来——例如,许多人认为AWS或其他IaaS产品是 “平台”。在调查中,我们将平台团队定义为那些负责维护其他团队用于构建和交付应用程序或服务的自助服务平台的团队。 我们提出了两个问题来衡量一个组织对内部平台的使用: • 您的开发人员使用自助服务平台的百分比是多少? • 哪些服务可供自助服务? 我们发现平台的使用在调查受访者中非常广泛。百分之六十三的人说他们至少有一个自助内部平台。 在拥有内部平台的人中,60%的人在拥有二到四个平台之间。在拥有内部平台的公司中,几乎有三分之一的公司有26%至50%的开发者使用该平台。

优秀的个人博客,低调大师

扩展AlertManager集成钉钉助力Istio on ACK可观测性监控能力

阿里云容器服务Kubernetes(简称ACK)支持一键部署Istio,可以参考文档在ACK上部署使用Isito。Istio on ACK提供了丰富的监控能力,为网格中的服务收集遥测数据,其中Mixer是负责提供策略控制和遥测收集的Istio组件。使用Prometheus进行监控是Istio提供的监控能力之一。 告警能力在Prometheus的架构中被划分成两个独立的部分:Prometheus负责产生告警,而Alertmanager负责告警产生后的后续处理。如下所示,通过在Prometheus中定义告警规则,Prometheus会周期性的对告警规则进行计算,如果满足告警触发条件就会向Alertmanager发送告警信息。 Alertmanager作为一个独立的组件,负责接收并处理来自Prometheus Server(也可以是其它的客

优秀的个人博客,低调大师

mica 中验证码扩展 mica-captcha 验证码组件

验证码效果 保证了验证码的安全性的同时尽量做到了美观,欢迎试用,更多使用方式可以查看 mica 示例项目。 依赖坐标 Maven <dependency> <groupId>net.dreamlu</groupId> <artifactId>mica-captcha</artifactId> </dependency> Gradle implementation "net.dreamlu:mica-captcha" 配置项 配置项 默认值 说明 mica.captcha.cache-name micaCaptchaCache 验证码cache名,默认:micaCaptchaCache mica.captcha.cookie-name mica-captcha 验证码cookie名,默认:mica-captcha mica.captcha.enabled true 是否开启验证码,默认:true. 注意:结合 mica-pro 的 mica-cache-redis 组件实现cache name 使用 # 号自动配置超时时间。 使用 首先需要配置 Spring cache,如 ehcache、redis等,然后给micaCaptchaCache 配置一个超时时间。 Servlet 生成验证码 @Autowired private MicaCaptchaServlet micaCaptcha; /** * 图形验证码 */ @GetMapping(value = "/captcha.jpg", produces = MediaType.IMAGE_JPEG_VALUE) @ResponseBody public ResponseEntity<Resource> captcha(HttpServletResponse response) { return micaCaptcha.generate(response); } 校验验证码 boolean validated = micaCaptcha.validate(response, userInputCaptcha); WebFlux 生成验证码 @Autowired private MicaCaptchaReactive micaCaptcha; /** * 图形验证码 */ @GetMapping(value = "/captcha.jpg", produces = MediaType.IMAGE_JPEG_VALUE) @ResponseBody public Mono<ResponseEntity<Resource>> captcha(ServerWebExchange exchange) { return Mono.just(micaCaptcha.generate(exchange)); } 校验验证码 boolean validated = micaCaptcha.validate(exchange, userInputCaptcha); 生成其他类型验证码 micaCaptcha.generateBase64 生成 base64 图片验证码(主要为 App 或者前后端分离设计)。 micaCaptcha.validateBase64 校验 base64 图片验证码。 开源推荐 Spring boot 微服务高效开发mica工具集:https://gitee.com/596392912/mica Avue一款基于vue可配置化的神奇框架:https://gitee.com/smallweigit/avue pig宇宙最强微服务(架构师必备):https://gitee.com/log4j/pig SpringBlade完整的线上解决方案(企业开发必备):https://gitee.com/smallc/SpringBlade IJPay 支付SDK让支付触手可及:https://gitee.com/javen205/IJPay

优秀的个人博客,低调大师

Unity C#基础之 多线程的前世今生(下) 扩展篇

在前面两篇Unity C#基础之 多线程的前世今生(上) 科普篇和Unity C#基础之 多线程的前世今生(中) 进阶篇中,相信大家对多线程有了一定的了解,这篇再详细的聊一聊在使用多线程中需要注意的地方~ 示例工程下载Unity 2017.3.0 P4 .NET版本4.6 本篇知识点 异常处理 线程取消 CancellationTokenSource 多线程临时变量 线程安全 lock 语法糖 await async 异常处理 首先我们先执行下面一段代码 循环20次用Task线程执行以下Code,当执行循环 i=11 或 i=12时抛出异常 打印信息中没有 11、12的打印信息,也没有抛出异常的信息,这是因为主线程的Trycatch已经跳过 然后我们在try块中添加 Task.WaitAll(taskList.ToArray()); 打印信息如下 出现抛出异常信息 然后我们去掉try块中的 Task.WaitAll(taskList.ToArray()); 在每个线程中添加Try Catch 打印结果可以捕捉到异常 所以要想捕捉到异常且不卡界面,捕捉异常最好在每个自己的线程中捕捉 相应的全部Code private void TryCatchOnClick() { System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); watch.Start(); Debug.Log($"TryCatchOnClick Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); try { TaskFactory taskFactory = new TaskFactory(); List<Task> taskList = new List<Task>(); #region 异常处理 //多线程里的异常是会被吞掉,除非waitall // 建议 多线程里面,是不允许异常的,也就是内部try catch,自己处理好 for (int i = 0; i < 20; i++) { string name = string.Format($"TryCatchOnClick{i}"); Action<object> act = t => { try { Thread.Sleep(2000); if (t.ToString().Equals("TryCatchOnClick11")) { throw new Exception(string.Format($"{t} 执行失败")); } if (t.ToString().Equals("TryCatchOnClick12")) { throw new Exception(string.Format($"{t} 执行失败")); } Debug.Log($"{t} 执行成功"); } catch (Exception ex) { Debug.Log(ex.Message); } }; taskList.Add(taskFactory.StartNew(act, name)); } //Task.WaitAll(taskList.ToArray()); #endregion } catch (AggregateException aex) { foreach (var item in aex.InnerExceptions) { Debug.Log(item.Message); } } catch (Exception ex) { Debug.Log(ex.Message); } watch.Stop(); Debug.Log($"TryCatchOnClick End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); } 线程取消 某个线程达到预期效果后需要取消其他的线程,我们可以用 CancellationTokenSource,当然可以用一个共享的bool变量,但是CancellationTokenSource的好处是可以让没来的及启动的线程直接取消,从根本上取消启动 private void TaskCancel() { System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); watch.Start(); Debug.Log($"TaskCancel Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}TaskCancel"); try { TaskFactory taskFactory = new TaskFactory(); List<Task> taskList = new List<Task>(); //线程取消不是操作线程,而是操作信号量(共享变量,多个线程都能访问到的东西,变量/数据库的数据/硬盘数据) //每个线程在执行的过程中,经常去查看下这个信号量,然后自己结束自己 //线程不能别人终止,只能自己干掉自己,延迟是少不了的 //CancellationTokenSource可以在cancel后,取消没有启动的任务 CancellationTokenSource cts = new CancellationTokenSource();//bool值 for (int i = 0; i < 200; i++) { string name = string.Format("btnThreadCore_Click{0}", i); Action<object> act = t => { try { Thread.Sleep(2000); if (t.ToString().Equals("btnThreadCore_Click11")) { throw new Exception(string.Format("{0} 执行失败", t)); } if (t.ToString().Equals("btnThreadCore_Click12")) { throw new Exception(string.Format("{0} 执行失败", t)); } if (cts.IsCancellationRequested)//检查信号量 { Debug.Log($"{t} 放弃执行"); return; } else { Debug.Log($"{t} 执行成功"); } } catch (Exception ex) { cts.Cancel(); Debug.Log(ex.Message); } }; taskList.Add(taskFactory.StartNew(act, name, cts.Token)); } Task.WaitAll(taskList.ToArray()); } catch (AggregateException aex) { foreach (var item in aex.InnerExceptions) { Debug.Log(item.Message); } } catch (Exception ex) { Debug.Log(ex.Message); } watch.Stop(); Debug.Log($"TaskCancel End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); } 打印结果如下 多线程临时变量 这个就比较简单了,直接上Code private void TaskTempVariable() { System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); watch.Start(); Debug.Log($"TaskTempVariable Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); try { TaskFactory taskFactory = new TaskFactory(); List<Task> taskList = new List<Task>(); ////i 只有一个,真实实行的时候,已经是5了, ////k 多个k,每次是独立的k,跟i没关系 ////int k; for (int i = 0; i < 5; i++) { int k = i; new Action(() => { Thread.Sleep(1000); Debug.Log($"对应的数值K:{k}"); Debug.Log($"对应的数值I:{i}"); }).BeginInvoke(null, null); } Task.WaitAll(taskList.ToArray()); } catch (AggregateException aex) { foreach (var item in aex.InnerExceptions) { Debug.Log(item.Message); } } catch (Exception ex) { Debug.Log(ex.Message); } watch.Stop(); Debug.Log($"TaskTempVariable End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); } 打印结果 线程安全 lock ConcurrentDictionary多线程版字典 多线程1000次操作一个int和List private static readonly object StaticAsyncLock = new object(); private int TotalCount = 0;// private List<int> IntList = new List<int>(20000); private void TaskSafe() { System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); watch.Start(); Debug.Log($"TaskSafe Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); try { TaskFactory taskFactory = new TaskFactory(); List<Task> taskList = new List<Task>(); //共有变量:都能访问局部变量/全局变量/数据库的一个值/硬盘文件 //线程内部不共享的是安全 //解决多线程冲突第一个办法:lock ,lock的方法块儿里面是单线程的,所以将整个方法Lock多线程将变得毫无意义;lock里面的代码要尽量的少 //解决多线程冲突第二个办法:没有冲突,从数据上隔离开 for (int i = 0; i < 10000; i++) { int TempI = i; taskList.Add(taskFactory.StartNew(() => { lock (StaticAsyncLock)//lock后的方法块,任意时刻只有一个线程可以进入语法糖 lock(StaticAsyncLock) 编译后等于 Monitor.Enter(StaticAsyncLock) { //这里就是单线程 this.TotalCount += 1;//多个线程同时操作,有时候操作被覆盖了 IntList.Add(TempI); } })); //语法糖 lock(StaticAsyncLock) 编译后等于 Monitor.Enter(StaticAsyncLock) Monitor.Exit(StaticAsyncLock) //检查下这个变量(引用) 有没有被lock 有就等着,没有就占用,然后进去执行,执行完了释放 //lock(this) 锁定当前实例,别的地方如果要使用这个实力里面的其他变量,则都被锁定了无法使用(不推荐这么写) //如果每个实例想要单独的锁定 private object //string a="123456" lock(a) string b="123456" 享元模式的内存分配,字符串值是唯一的,会锁定别的变量b //private static readonly object StaticAsyncLock = new object(); } Task.WaitAll(taskList.ToArray()); Debug.Log(this.TotalCount); Debug.Log(IntList.Count); } catch (AggregateException aex) { foreach (var item in aex.InnerExceptions) { Debug.Log(item.Message); } } catch (Exception ex) { Debug.Log(ex.Message); } watch.Stop(); Debug.Log($"TaskSafe End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); } 添加Lock锁前后打印结果 语法糖 await async (C#5.0 .NET 4.5 CLR 4.0) 如果用一句话简单概括await async,那就是:多线程版的协程 先来一个简单的示例,做一个大象装冰箱,最后咱们再详细的分析 打印结果 是不是很有趣?这种书写逻辑基本上和原来的协程一样,而且是真正的多线程,但是依据不能运行UnityEngine中的组件(例如:GameObject),下面我们详细的说一说 await async 第一个示例 在基础的方法上添加关键字 async 根据提示只有aysnc没有await 会有一个警告,跟普通方法没有区别(不得不说VS2017还是很不错的,提示、自动修补都很友好) 打印结果 第二个是示例 在第一个示例的基础上添加await关键字,这时当主线程到达await task时就返回了,继续执行方法外部的函数,可以理解为unity协程中的yeild reture,当task的线程块执行完毕后再回调 awati task后面的函数部分,这个回调的线程是不确定的:可能是主线程 可能是子线程 也可能是其他线程 /// <summary> /// async/await /// 不能单独await /// await 只能放在task前面 /// 不推荐void返回值,使用Task来代替 /// Task和Task<T>能够使用await, Task.WhenAny, Task.WhenAll等方式组合使用。Async Void 不行 /// </summary> private async void NoReturn() { //主线程执行 Debug.Log($"NoReturn Sleep before await,线程ID:{Thread.CurrentThread.ManagedThreadId}"); TaskFactory taskFactory = new TaskFactory(); Task task = taskFactory.StartNew(() => { Debug.Log($"多线程 Sleep before,线程ID:{Thread.CurrentThread.ManagedThreadId}"); Thread.Sleep(3000); Debug.Log($"多线程 Sleep after,线程ID:{Thread.CurrentThread.ManagedThreadId}"); }); await task;//主线程到这里就返回了,执行主线程任务 //子线程执行 其实是封装成委托,在task之后成为回调(编译器功能 状态机实现) //task.ContinueWith() //这个回调的线程是不确定的:可能是主线程 可能是子线程 也可能是其他线程 Debug.Log($"NoReturn Sleep after await,线程ID:{Thread.CurrentThread.ManagedThreadId}"); } 打印结果 第三个示例,如果需要获取这个返回的线程怎么办呢?直接把void 换成Task /// <summary> /// 无返回值 async Task == async void /// Task和Task<T>能够使用await, Task.WhenAny, Task.WhenAll等方式组合使用。Async Void 不行 /// </summary> /// <returns></returns> private async Task NoReturnTask() { //这里还是主线程的id Debug.Log($"NoReturnTask Sleep before await,线程ID:{Thread.CurrentThread.ManagedThreadId}"); await Task.Run(() => { Debug.Log($"多线程 Sleep before,线程ID:{Thread.CurrentThread.ManagedThreadId}"); Thread.Sleep(9000); Debug.Log($"多线程 Sleep after,线程ID:{Thread.CurrentThread.ManagedThreadId}"); }); Debug.Log($"NoReturnTask Sleep after await,线程ID:{Thread.CurrentThread.ManagedThreadId}"); //return new TaskFactory().StartNew(() => { }); //不能return 没有async才行 } 然后执行 打印结果 第四个示例,也是最终版的示例 返回线程+返回数值 ,获取一个Task<T> ,其中T就是返回值的类型 /// <summary> /// 带返回值的Task /// 要使用返回值就一定要等子线程计算完毕 卡线程 /// </summary> /// <returns>async 就只返回long</returns> private async Task<long> FinallyAsync() { Debug.Log($"SumAsync start 线程ID:{Thread.CurrentThread.ManagedThreadId}"); long result = 0; await Task.Run(() => { Debug.Log($"SumAsync await Task.Run 线程ID:{Thread.CurrentThread.ManagedThreadId}"); Thread.Sleep(1000); for (long i = 0; i < 999999999; i++) { result += i; } }); return result; } 然后执行 打印结果 非await版返回值 这种主线程获取result不会卡死 /// <summary> /// 真的返回Task 不是async /// /// 要使用返回值就一定要等子线程计算完毕 /// </summary> /// <returns>没有async Task</returns> private Task<int> TaskReturn() { Debug.Log($"TaskReturn start 线程ID:{Thread.CurrentThread.ManagedThreadId}"); TaskFactory taskFactory = new TaskFactory(); Task<int> iResult = taskFactory.StartNew<int>(() => { Thread.Sleep(3000); Debug.Log($"TaskReturn Task.Run 线程ID:{Thread.CurrentThread.ManagedThreadId}"); return 123; }); Debug.Log($"TaskReturn end 线程ID:{Thread.CurrentThread.ManagedThreadId}"); return iResult; } 执行函数 打印结果 以上就是await async 的相关内容,有需要补充的东西欢迎留言

优秀的个人博客,低调大师

解决Centos7 下安装PHP7 phpredis扩展报错办法

heckingwhether-lcshouldbeexplicitlylinkedin...no checkingdynamiclinkercharacteristics...GNU/Linuxld.so checkinghowtohardcodelibrarypathsintoprograms...immediate checkingwhetherstrippinglibrariesispossible...yes checkingiflibtoolsupportssharedlibraries...yes checkingwhethertobuildsharedlibraries...yes checkingwhethertobuildstaticlibraries...no creatinglibtool appendingconfigurationtag"CXX"tolibtool configure:creating./config.status config.status:creatingconfig.h [root@iZ236oj5mp5Zphpredis-master]#make&&makeinstall /bin/sh/usr/local/src/redis-stable/phpredis-master/libtool--mode=compilecc-I.-I/usr/local/src/redis-stable/phpredis-master-DPHP_ATOM_INC-I/usr/local/src/redis-stable/phpredis-master/include-I/usr/local/src/redis-stable/phpredis-master/main-I/usr/local/src/redis-stable/phpredis-master-I/usr/local/php/include/php-I/usr/local/php/include/php/main-I/usr/local/php/include/php/TSRM-I/usr/local/php/include/php/Zend-I/usr/local/php/include/php/ext-I/usr/local/php/include/php/ext/date/lib-DHAVE_CONFIG_H-g-O2-c/usr/local/src/redis-stable/phpredis-master/redis.c-oredis.lo mkdir.libs cc-I.-I/usr/local/src/redis-stable/phpredis-master-DPHP_ATOM_INC-I/usr/local/src/redis-stable/phpredis-master/include-I/usr/local/src/redis-stable/phpredis-master/main-I/usr/local/src/redis-stable/phpredis-master-I/usr/local/php/include/php-I/usr/local/php/include/php/main-I/usr/local/php/include/php/TSRM-I/usr/local/php/include/php/Zend-I/usr/local/php/include/php/ext-I/usr/local/php/include/php/ext/date/lib-DHAVE_CONFIG_H-g-O2-c/usr/local/src/redis-stable/phpredis-master/redis.c-fPIC-DPIC-o.libs/redis.o Infileincludedfrom/usr/local/src/redis-stable/phpredis-master/redis.c:27:0: /usr/local/src/redis-stable/phpredis-master/common.h:3:40:致命错误:ext/standard/php_smart_str.h:没有那个文件或目录 #include<ext/standard/php_smart_str.h> ^ 编译中断。 make:***[redis.lo]错误1 解决办法,因为最新的 phpredis 分了几大分支,针对最新的PHP稳定发行版 php7 有专门为php7的分支,所以我们从github拉下phpredis 源码 需要切换到 PHP7的分支 首先git clone phpredis : git clone https://github.com/nicolasff/phpredis git checkout php7 /usr/local/php7/bin/phpize ./configure --with-php-config=/usr/local/php/bin/php-config make && make install 然后看到了等字样,就说明安装成功了

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册