首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

微服务架构学习与思考(02):微服务实施的前提条件?有哪些问题需要思考?

一、前言 地址:https://www.cnblogs.com/jiujuan/p/13284412.html 前一篇文章简单分析了微服务的好处,以及会带来的问题。 遇到问题并不可怕,可怕的是我们不去面对它,不去想办法解决它,逃避问题是不可能有任何进步。所以积极想办法应对问题并解决问题,才能不断的进步。 前面讲了,微服务一般都是由单体演进而来,很少有业务从0就开始进行微服务开发。如果能从0就开始用微服务开发,确实是一件很好的事情,前提是你确实考虑清楚了用微服务开发适合当前的业务以及业务的发展需求。 那么问题来了,企业什么时候引入微服务呢? 二、企业什么时候引入微服务? 引入原因: 单体应用无法满足业务增长的需求,业务的交付、业务的可靠性、稳定性要求,随着时间推移问题会越来越多。-- 也就是前面遇到的一些问题 不过也有人,不是从本公司业务发展,开发成本,开发效率来考虑问题,而是什么开发大会上看到很多公司微服务的演讲,或者听说很多公司在用微服务,从这些方面来考虑,也就是随大流,这种思考方式显然不是正确思考方式。不要为了微服务而微服务。采用微服务收益一定要大于单体应用,要能解决遇到的问题。 从单体架构升级到微服务架构,肯定希望提高研发效率,缩短工期,加快产品交付速度。 那他们在具体生产效率上有什么区别? 根据马丁·福勒(Martin Fowler)的这篇文章,揭示了生产率和复杂度的关系。 在复杂度较小时,单体应用的生产率更高,微服务架构反而降低了生产率。但是,当复杂度到了一定规模,无论采用单体应用还是微服务架构,都会降低系统的生产率。区别是:单体应用生产率开始急剧下降,而微服务架构则能缓解生产率下降的程度。如下图: x 轴是系统复杂度,y 轴是开发的生产力 绿色表示单体应用 蓝色表示微服务架构 单体应用和微服务有一个相交的点,这个点是单体应用生产率急剧下降,微服务平缓下降的交叉点,他们的生产效率开始出现不同。 这个点就是把单体应用切换到微服务的时间点。 但问题是,这个时间点,文章并没有具体说明什么时候会出现,怎么衡量这个时间点。 所以只能各个公司具体问题具体分析,技术领导者要考虑判断这个时间点。这也是考虑技术领导力的时候。不过有些要素可以参考: 业务角度 业务需求开发是否经常延迟 产品交付是否能跟上业务发展 研发质量 代码是否因为修改而经常出现bug 代码臃肿庞大 技术人员 有技术,有意愿 团队人数 等等一些考虑因素,在加上前一篇单体应用出现的一些问题。 在考虑清楚之后,决定引入微服务,那么,又会遇到什么问题? 三、组织架构如何变化? 康威定律 康威定律(康威法则 , Conway's Law) 是马尔文·康威1967年提出的: 设计系统的架构受制于产生这些设计的组织的沟通结构。 康威定律告诉我们,如果我们实施了微服务,那么组织架构的变动也要跟着实施微服务架构而做出相应的调整。这样才有可能适应微服务的发展。 单体架构和微服务架构 先看看传统单体架构和微服务架构,如下图: 图片来自:https://martinfowler.com/articles/microservices.html 左半部分的单体架构图: 单体应用将所有功能放到一个进程中 扩展:通过将整个应用复制到多态服务器实现扩展 右半部分的微服务架构图: 微服务架构将功能分离,放到多个不同的进程中 扩展:通过将不同的服务分布于不同的服务器上,并按需要复制方式进行扩展 组织架构 单体应用的组织架构: 图片来自:https://martinfowler.com/articles/microservices.html 它是一个整体式的应用团队,每个团队按照职能来进行划分(图片左半部分),比如:UI团队,中间件团队,DBA团队。 不同职能的人属于不同的团队。做项目的时候就从不同职能部门选出一些人来负责项目。这样的组织架构有一个问题就是:跨职能部门沟通协调问题。这种团队组织形式不能适应微服务架构的特点。 微服务应用组织架构 图片来自:https://martinfowler.com/articles/microservices.html 微服务架构特点:每个微服务是独立的,团队可以独立开发,独立测试,独立部署,服务是自治的。相应的团队组成人员也有产品,技术,测试,团队成员在自己内部就可以完整的进行微服务各种功能开发。 这就要打破原先传统的那种按职能划分的组织团队形式,而要把不同职能的人组织在一个团队内,组成一个跨职能的产品组织架构。这样才能把一个微服务功能架构、设计、开发、测试、部署、上线运行,在一个组织内部完成,从而形成完整的业务、开发、交付闭环。 团队组织的变化 一图胜千言: 图片来自:https://insights.thoughtworks.cn/management-of-microservices-team/ 原先那种职能型的团队,变成了跨职能的小团队,这种团队和微服务架构对齐。 每个团队组织成员多少合适呢? 亚马逊的“两个披萨团队”,6-10人的规模。这个只是一种参考,毕竟每个公司规模、业务、行业、成员等不一样,找到适合自己的团队构成,就是最好的团队组织。 说明: 以上图片侵删,请联系主页邮箱! 四、怎么构建第一个微服务项目 第一种:有新项目,可以从0开始设计微服务架构。 第二种:改造旧有的老项目 这种也可以划分2类: 从项目小范围开始试水,进行改造。 完全重构项目 - 一般不推荐这种方式。因为不仅老项目需要维护,而且来了新需求咋办?是老项目停止需求开发,还是新旧一起加,一起加又浪费人力,不加技术跟不上业务发展。这些风险都是需要思考衡量。 第三种:从边缘不重要的小项目开始。 这种项目需求开发一般不紧迫,项目又小,相对独立,与现有系统耦合较小,可以完全重构。从这种小项目开始实施微服务,一步一步来构建,降低风险。 经验丰富后,在逐步将其他项目进行改造。 这种是折中的办法,不是那种“休克疗法”。 五、参考 https://martinfowler.com/articles/microservices.html Microservices https://martinfowler.com/bliki/MicroservicePremium.html https://insights.thoughtworks.cn/management-of-microservices-team/ 微服务化小团队集群的组织和管理 我的公众号:九卷沉思录

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

[雪峰磁针石博客]pyspark工具机器学习(自然语言处理和推荐系统)2数据处理1

本章介绍数据处理。数据处理是执行Machine Learning所需的关键步骤,因为我们需要清理,过滤,合并和转换我们的所需数据形式。 快速入门 读取 >>> from pyspark.sql import SparkSession >>> spark=SparkSession.builder.appName('data_processing').getOrCreate() >>> df=spark.read.csv('sample_data.csv',inferSchema=True, header=True) >>> df.columns ['ratings', 'age', 'experience', 'family', 'mobile'] >>> len(df.columns) 5 >>> df.count() 33 >>> df.printSchema() root |-- ratings: integer (nullable = true) |-- age: integer (nullable = true) |-- experience: double (nullable = true) |-- family: integer (nullable = true) |-- mobile: string (nullable = true) >>> df.show(3) +-------+---+----------+------+-------+ |ratings|age|experience|family| mobile| +-------+---+----------+------+-------+ | 3| 32| 9.0| 3| Vivo| | 3| 27| 13.0| 3| Apple| | 4| 22| 2.5| 0|Samsung| +-------+---+----------+------+-------+ only showing top 3 rows >>> df.select('age','mobile').show(5) +---+-------+ |age| mobile| +---+-------+ | 32| Vivo| | 27| Apple| | 22|Samsung| | 37| Apple| | 27| MI| +---+-------+ only showing top 5 rows >>> df.describe().show() +-------+------------------+------------------+------------------+------------------+------+ |summary| ratings| age| experience| family|mobile| +-------+------------------+------------------+------------------+------------------+------+ | count| 33| 33| 33| 33| 33| | mean|3.5757575757575757|30.484848484848484|10.303030303030303|1.8181818181818181| null| | stddev|1.1188806636071336| 6.18527087180309| 6.770731351213326|1.8448330794164254| null| | min| 1| 22| 2.5| 0| Apple| | max| 5| 42| 23.0| 5| Vivo| +-------+------------------+------------------+------------------+------------------+------+ 添加列 >>> df.withColumn("age_after_10_yrs",(df["age"]+10)).show(10,False) +-------+---+----------+------+-------+----------------+ |ratings|age|experience|family|mobile |age_after_10_yrs| +-------+---+----------+------+-------+----------------+ |3 |32 |9.0 |3 |Vivo |42 | |3 |27 |13.0 |3 |Apple |37 | |4 |22 |2.5 |0 |Samsung|32 | |4 |37 |16.5 |4 |Apple |47 | |5 |27 |9.0 |1 |MI |37 | |4 |27 |9.0 |0 |Oppo |37 | |5 |37 |23.0 |5 |Vivo |47 | |5 |37 |23.0 |5 |Samsung|47 | |3 |22 |2.5 |0 |Apple |32 | |3 |27 |6.0 |0 |MI |37 | +-------+---+----------+------+-------+----------------+ only showing top 10 rows >>> from pyspark.sql.types import StringType,DoubleType >>> df.withColumn('age_double',df['age'].cast(DoubleType())).show(10,False) +-------+---+----------+------+-------+----------+ |ratings|age|experience|family|mobile |age_double| +-------+---+----------+------+-------+----------+ |3 |32 |9.0 |3 |Vivo |32.0 | |3 |27 |13.0 |3 |Apple |27.0 | |4 |22 |2.5 |0 |Samsung|22.0 | |4 |37 |16.5 |4 |Apple |37.0 | |5 |27 |9.0 |1 |MI |27.0 | |4 |27 |9.0 |0 |Oppo |27.0 | |5 |37 |23.0 |5 |Vivo |37.0 | |5 |37 |23.0 |5 |Samsung|37.0 | |3 |22 |2.5 |0 |Apple |22.0 | |3 |27 |6.0 |0 |MI |27.0 | +-------+---+----------+------+-------+----------+ only showing top 10 rows 上面的False表示超过20个字符也不会截断。 数据过滤 >>> df.filter(df['mobile']=='Vivo').show() +-------+---+----------+------+------+ |ratings|age|experience|family|mobile| +-------+---+----------+------+------+ | 3| 32| 9.0| 3| Vivo| | 5| 37| 23.0| 5| Vivo| | 4| 37| 6.0| 0| Vivo| | 5| 37| 13.0| 1| Vivo| | 4| 37| 6.0| 0| Vivo| +-------+---+----------+------+------+ >>> df.filter(df['mobile']=='Vivo').select('age','ratings', 'mobile').show() +---+-------+------+ |age|ratings|mobile| +---+-------+------+ | 32| 3| Vivo| | 37| 5| Vivo| | 37| 4| Vivo| | 37| 5| Vivo| | 37| 4| Vivo| +---+-------+------+ >>> df.filter(df['mobile']=='Vivo').filter(df['experience']>10).show() +-------+---+----------+------+------+ |ratings|age|experience|family|mobile| +-------+---+----------+------+------+ | 5| 37| 23.0| 5| Vivo| | 5| 37| 13.0| 1| Vivo| +-------+---+----------+------+------+ >>> df.filter((df['mobile']=='Vivo')&(df['experience'] >10)).show() +-------+---+----------+------+------+ |ratings|age|experience|family|mobile| +-------+---+----------+------+------+ | 5| 37| 23.0| 5| Vivo| | 5| 37| 13.0| 1| Vivo| +-------+---+----------+------+------+ 唯一值 >>> df.select('mobile').distinct().show() +-------+ | mobile| +-------+ | MI| | Oppo| |Samsung| | Vivo| | Apple| +-------+ >>> df.select('mobile').distinct().count() 5 分组和排序 >>> df.groupBy('mobile').count().show(5,False) +-------+-----+ |mobile |count| +-------+-----+ |MI |8 | |Oppo |7 | |Samsung|6 | |Vivo |5 | |Apple |7 | +-------+-----+ >>> df.groupBy('mobile').count().orderBy('count',ascending=False).show(5,False) +-------+-----+ |mobile |count| +-------+-----+ |MI |8 | |Oppo |7 | |Apple |7 | |Samsung|6 | |Vivo |5 | +-------+-----+ >>> df.groupBy('mobile').mean().show(5,False) +-------+------------------+------------------+------------------+------------------+ |mobile |avg(ratings) |avg(age) |avg(experience) |avg(family) | +-------+------------------+------------------+------------------+------------------+ |MI |3.5 |30.125 |10.1875 |1.375 | |Oppo |2.857142857142857 |28.428571428571427|10.357142857142858|1.4285714285714286| |Samsung|4.166666666666667 |28.666666666666668|8.666666666666666 |1.8333333333333333| |Vivo |4.2 |36.0 |11.4 |1.8 | |Apple |3.4285714285714284|30.571428571428573|11.0 |2.7142857142857144| +-------+------------------+------------------+------------------+------------------+ >>> df.groupBy('mobile').sum().show(5,False) +-------+------------+--------+---------------+-----------+ |mobile |sum(ratings)|sum(age)|sum(experience)|sum(family)| +-------+------------+--------+---------------+-----------+ |MI |28 |241 |81.5 |11 | |Oppo |20 |199 |72.5 |10 | |Samsung|25 |172 |52.0 |11 | |Vivo |21 |180 |57.0 |9 | |Apple |24 |214 |77.0 |19 | +-------+------------+--------+---------------+-----------+ >>> df.groupBy('mobile').max().show(5,False) +-------+------------+--------+---------------+-----------+ |mobile |max(ratings)|max(age)|max(experience)|max(family)| +-------+------------+--------+---------------+-----------+ |MI |5 |42 |23.0 |5 | |Oppo |4 |42 |23.0 |2 | |Samsung|5 |37 |23.0 |5 | |Vivo |5 |37 |23.0 |5 | |Apple |4 |37 |16.5 |5 | +-------+------------+--------+---------------+-----------+ >>> df.groupBy('mobile').max().show(3,False) +-------+------------+--------+---------------+-----------+ |mobile |max(ratings)|max(age)|max(experience)|max(family)| +-------+------------+--------+---------------+-----------+ |MI |5 |42 |23.0 |5 | |Oppo |4 |42 |23.0 |2 | |Samsung|5 |37 |23.0 |5 | +-------+------------+--------+---------------+-----------+ only showing top 3 rows >>> df.groupBy('mobile').min().show(5,False) +-------+------------+--------+---------------+-----------+ |mobile |min(ratings)|min(age)|min(experience)|min(family)| +-------+------------+--------+---------------+-----------+ |MI |1 |27 |2.5 |0 | |Oppo |2 |22 |6.0 |0 | |Samsung|2 |22 |2.5 |0 | |Vivo |3 |32 |6.0 |0 | |Apple |3 |22 |2.5 |0 | +-------+------------+--------+---------------+-----------+ 聚合 >>> df.groupBy('mobile').agg({'experience':'sum'}).show(5,False) +-------+---------------+ |mobile |sum(experience)| +-------+---------------+ |MI |81.5 | |Oppo |72.5 | |Samsung|52.0 | |Vivo |57.0 | |Apple |77.0 | +-------+---------------+ 参考资料 python测试开发项目实战-目录 python工具书籍下载-持续更新 python 3.7极速入门教程 - 目录 原文地址 本文涉及的python测试开发库 谢谢点赞! [本文相关海量书籍下载](https://github.com/china-testing/python-api-tesing/blob/master/books.md http://spark.apache.org/docs/2.1.0/api/python/pyspark.sql.html

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

[雪峰磁针石博客]pyspark工具机器学习(自然语言处理和推荐系统)2数据处理2

用户定义函数(UDF:User-Defined Functions) UDF广泛用于数据处理,以转换数据帧。 PySpark中有两种类型的UDF:常规UDF和Pandas UDF。 Pandas UDF在速度和处理时间方面更加强大。 传统的Python函数 >>> from pyspark.sql.functions import udf >>> def price_range(brand): ... prices = {"Samsung":'High Price', "Apple":'High Price', "MI":'Mid Price'} ... return prices.get('test',"Low Price") ... >>> brand_udf=udf(price_range,StringType()) >>> df.withColumn('price_range',brand_udf(df['mobile'])).show(10,False) +-------+---+----------+------+-------+-----------+ |ratings|age|experience|family|mobile |price_range| +-------+---+----------+------+-------+-----------+ |3 |32 |9.0 |3 |Vivo |Low Price | |3 |27 |13.0 |3 |Apple |Low Price | |4 |22 |2.5 |0 |Samsung|Low Price | |4 |37 |16.5 |4 |Apple |Low Price | |5 |27 |9.0 |1 |MI |Low Price | |4 |27 |9.0 |0 |Oppo |Low Price | |5 |37 |23.0 |5 |Vivo |Low Price | |5 |37 |23.0 |5 |Samsung|Low Price | |3 |22 |2.5 |0 |Apple |Low Price | |3 |27 |6.0 |0 |MI |Low Price | +-------+---+----------+------+-------+-----------+ only showing top 10 rows >>> Lambda函数 >>> age_udf = udf(lambda age: "young" if age <= 30 else "senior", StringType()) >>> df.withColumn("age_group", age_udf(df.age)).show(10,False) +-------+---+----------+------+-------+---------+ |ratings|age|experience|family|mobile |age_group| +-------+---+----------+------+-------+---------+ |3 |32 |9.0 |3 |Vivo |senior | |3 |27 |13.0 |3 |Apple |young | |4 |22 |2.5 |0 |Samsung|young | |4 |37 |16.5 |4 |Apple |senior | |5 |27 |9.0 |1 |MI |young | |4 |27 |9.0 |0 |Oppo |young | |5 |37 |23.0 |5 |Vivo |senior | |5 |37 |23.0 |5 |Samsung|senior | |3 |22 |2.5 |0 |Apple |young | |3 |27 |6.0 |0 |MI |young | +-------+---+----------+------+-------+---------+ only showing top 10 rows PandasUDF(矢量化UDF) 有两种类型的Pandas UDF:Scalar和GroupedMap。 Pandas UDF与使用基本UDf非常相似。我们必须首先从PySpark导入pandas_udf并将其应用于要转换的任何特定列。 >>> from pyspark.sql.functions import pandas_udf >>> def remaining_yrs(age): ... return (100-age) ... >>> from pyspark.sql.types import IntegerType >>> length_udf = pandas_udf(remaining_yrs, IntegerType()) >>> df.withColumn("yrs_left", length_udf(df['age'])).show(10,False) /opt/anaconda3/lib/python3.6/site-packages/pyarrow/__init__.py:159: UserWarning: pyarrow.open_stream is deprecated, please use pyarrow.ipc.open_stream warnings.warn("pyarrow.open_stream is deprecated, please use " +-------+---+----------+------+-------+--------+ |ratings|age|experience|family|mobile |yrs_left| +-------+---+----------+------+-------+--------+ |3 |32 |9.0 |3 |Vivo |68 | |3 |27 |13.0 |3 |Apple |73 | |4 |22 |2.5 |0 |Samsung|78 | |4 |37 |16.5 |4 |Apple |63 | |5 |27 |9.0 |1 |MI |73 | |4 |27 |9.0 |0 |Oppo |73 | |5 |37 |23.0 |5 |Vivo |63 | |5 |37 |23.0 |5 |Samsung|63 | |3 |22 |2.5 |0 |Apple |78 | |3 |27 |6.0 |0 |MI |73 | +-------+---+----------+------+-------+--------+ only showing top 10 rows PandasUDF(多列) >>> def prod(rating,exp): ... return rating*exp ... >>> prod_udf = pandas_udf(prod, DoubleType()) >>> df.withColumn("product",prod_udf(df['ratings'], df['experience'])).show(10,False) /opt/anaconda3/lib/python3.6/site-packages/pyarrow/__init__.py:159: UserWarning: pyarrow.open_stream is deprecated, please use pyarrow.ipc.open_stream warnings.warn("pyarrow.open_stream is deprecated, please use " +-------+---+----------+------+-------+-------+ |ratings|age|experience|family|mobile |product| +-------+---+----------+------+-------+-------+ |3 |32 |9.0 |3 |Vivo |27.0 | |3 |27 |13.0 |3 |Apple |39.0 | |4 |22 |2.5 |0 |Samsung|10.0 | |4 |37 |16.5 |4 |Apple |66.0 | |5 |27 |9.0 |1 |MI |45.0 | |4 |27 |9.0 |0 |Oppo |36.0 | |5 |37 |23.0 |5 |Vivo |115.0 | |5 |37 |23.0 |5 |Samsung|115.0 | |3 |22 |2.5 |0 |Apple |7.5 | |3 |27 |6.0 |0 |MI |18.0 | +-------+---+----------+------+-------+-------+ only showing top 10 rows 删除重复值 >>> df.count() 33 >>> df=df.dropDuplicates() >>> df.count() 26 删除列 >>> df_new=df.drop('mobile') >>> df_new.show() +-------+---+----------+------+ |ratings|age|experience|family| +-------+---+----------+------+ | 3| 32| 9.0| 3| | 4| 22| 2.5| 0| | 5| 27| 6.0| 0| | 4| 22| 6.0| 1| | 3| 27| 6.0| 0| | 2| 32| 16.5| 2| | 4| 27| 9.0| 0| | 2| 27| 9.0| 2| | 3| 37| 16.5| 5| | 4| 27| 6.0| 1| | 5| 37| 23.0| 5| | 2| 27| 6.0| 2| | 4| 37| 6.0| 0| | 5| 37| 23.0| 5| | 4| 37| 9.0| 2| | 5| 37| 13.0| 1| | 5| 27| 2.5| 0| | 3| 42| 23.0| 5| | 5| 22| 2.5| 0| | 1| 37| 23.0| 5| +-------+---+----------+------+ only showing top 20 rows 参考资料 python测试开发项目实战-目录 python工具书籍下载-持续更新 python 3.7极速入门教程 - 目录 原文地址 本文涉及的python测试开发库 谢谢点赞! [本文相关海量书籍下载](https://github.com/china-testing/python-api-tesing/blob/master/books.md http://spark.apache.org/docs/2.1.0/api/python/pyspark.sql.html 写数据 CSV 如果我们想以原始csv格式将其保存为单个文件,我们可以在spark中使用coalesce函数。 >>> write_uri = '/home/andrew/test.csv' >>> df.coalesce(1).write.format("csv").option("header","true").save(write_uri) Parquet 如果数据集很大且涉及很多列,我们可以选择对其进行压缩并将其转换为Parquet文件格式。它减少了数据的整体大小并在处理数据时优化了性能,因为它可以处理所需列的子集而不是整个数据。我们可以轻松地将数据帧转换并保存为Parquet格式。 注意完整的数据集以及代码可以在本书的GitHub存储库中进行参考,并在onSpark 2.3及更高版本上执行最佳。

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

Python零基础学习代码实践 —— 不使用函数找出三个输入的数中最大的数

第一次写的比较复杂了 #三位数比较大小 num1 = int(input("num1 = ")) num2 = int(input("num2 = ")) num3 = int(input("num3 = ")) if num1 < num2: max = num2 - num3 if max > 0: print("最大的数是", num2) else: print("最大的数是", num3) else: max = num1 - num3 if max > 0: print("最大的数是;", num1) else: print("最大的数是;", num3) 看了视频之后自己又写了一遍 #三位数比较大小 num1 = int(input("num1 = ")) num2 = int(input("num2 = ")) num3 = int(input("num3 = ")) max = num1 if num2 > num1: max = num2 if num3 > max: max = num3 print("最大数为:", max)

资源下载

更多资源
Mario

Mario

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

Nacos

Nacos

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

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

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

用户登录
用户注册