-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 72.7 KB
/
Copy pathcontent.json
File metadata and controls
1 lines (1 loc) · 72.7 KB
1
[{"title":"people.run","date":"2017-05-18T12:00:00.000Z","path":"2017/05/18/feel/life-run/","text":"people.run 还记得我的 people.run不知道啥时候才有精力去弄也许是很多年后的事了","tags":[{"name":"life","slug":"life","permalink":"http://bee.gs/tags/life/"}]},{"title":"自定义功能模块设计","date":"2016-12-19T16:00:00.000Z","path":"2016/12/20/tech/自定义功能模块设计/","text":"自定义功能模块设计项目搭建架构图在线地址点击查看架构图。 数据表设计如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125--自定义数据源表create table CUSTOM_DATA( id primary key VARCHAR(64) not null, name VARCHAR(200), sql CLOB, url varchar(2000), method varchar(10), data_source varchar(30), page char(1); control_type char(1); source_type char(1); description varchar(2000), status CHAR(1), create_id varchar(64), create_time timestamp, modify_id varchar(64), modify_time timestamp)--表 注释comment on table CUSTOM_DATA is '自定义数据源表';--列 注释comment on column CUSTOM_DATA.id is 'uuid主键';comment on column CUSTOM_DATA.name is '名称';comment on column CUSTOM_DATA.sql is 'SQL语句';comment on column CUSTOM_DATA.url is '请求地址';comment on column CUSTOM_DATA.method is '请求方式';comment on column CUSTOM_DATA.data_source is '数据库连接池';comment on column CUSTOM_DATA.page is '0不分页,1分页';comment on column CUSTOM_DATA.control_type is '控件类型:0、table 1、chart';comment on column CUSTOM_DATA.source_type is '数据源类型:0、sql 1、http请求';comment on column CUSTOM_DATA.description is '描述';comment on column CUSTOM_DATA.status is '状态';comment on column CUSTOM_DATA.create_id is '创建人';comment on column CUSTOM_DATA.create_time is '创建时间';comment on column CUSTOM_DATA.modify_id is '修改人';comment on column CUSTOM_DATA.modify_time is '修改时间';-- 注释 自定义页面page表或者组件create table CUSTOM_PAGE( id primary key VARCHAR(64) not null, name VARCHAR(100), html_code CLOB, rest_id VARCHAR(32), query_ids VARCHAR(2000), type char(1), description VARCHAR(2000), create_id varchar(64), create_time timestamp, modify_id varchar(64), modify_time timestamp)-- 表注释 comment on table CUSTOM_PAGE is '自定义页面page表';-- 列注释comment on column CUSTOM_PAGE.id is 'uuid主键';comment on column CUSTOM_PAGE.name is '名称';comment on column CUSTOM_PAGE.html_code is '存储页面所有代码';comment on column CUSTOM_PAGE.rest_id is '此id为32位数字,开放给其它平台用';comment on column CUSTOM_PAGE.query_ids is 'query的id集合, 用分号隔开,方便统计用';comment on column CUSTOM_PAGE.type is '页面类型:0代表页面 主要用于APP中 2代表组件 用于页面局部自定义功能';comment on column CUSTOM_PAGE.description is '页面使用说明';comment on column CUSTOM_PAGE.create_id is '创建人';comment on column CUSTOM_PAGE.create_time is '创建时间';comment on column CUSTOM_PAGE.modify_id is '修改人';comment on column CUSTOM_PAGE.modify_time is '修改时间';-- 自定义APPcreate table CUSTOM_APP( id primary key VARCHAR(64) not null, name VARCHAR2(100), icon VARCHAR2(100), description VARCHAR(2000), status char(1), create_id varchar(64), create_time timestamp, modify_id varchar(64), modify_time timestamp)-- 表注释comment on column CUSTOM_APP.category is '应用标签';-- 字段注释comment on column CUSTOM_APP.id is 'uuid主键';comment on column CUSTOM_APP.name is '名称';comment on column CUSTOM_APP.icon is '图标';comment on column CUSTOM_APP.description is '描述';comment on column CUSTOM_APP.status is '状态0启用 1禁用';comment on column CUSTOM_APP.create_id is '创建人';comment on column CUSTOM_APP.create_time is '创建时间';comment on column CUSTOM_APP.modify_id is '修改人';comment on column CUSTOM_APP.modify_time is '修改时间';--app和页面建立关联 菜单表create table CUSTOM_APP_PAGE( id primary key VARCHAR(64) not null, name VARCHAR2(100), icon VARCHAR2(100), app_id VARCHAR(64) not null, page_id VARCHAR(64) not null, description VARCHAR(2000), status char(1), create_id varchar(64), create_time timestamp, modify_id varchar(64), modify_time timestamp)-- 表注释comment on column CUSTOM_APP_PAGE.category is 'app菜单表';-- 字段注释comment on column CUSTOM_APP_PAGE.id is 'uuid主键';comment on column CUSTOM_APP_PAGE.name is '名称';comment on column CUSTOM_APP_PAGE.icon is '图标';comment on column CUSTOM_APP_PAGE.app_id is 'app_id和APP表建立关联';comment on column CUSTOM_APP_PAGE.page_id is 'page_id和页面表建立关联';comment on column CUSTOM_APP_PAGE.description is '描述';comment on column CUSTOM_APP_PAGE.status is '状态0启用 1禁用';comment on column CUSTOM_APP_PAGE.create_id is '创建人';comment on column CUSTOM_APP_PAGE.create_time is '创建时间';comment on column CUSTOM_APP_PAGE.modify_id is '修改人';comment on column CUSTOM_APP_PAGE.modify_time is '修改时间';","tags":[{"name":"Java","slug":"Java","permalink":"http://bee.gs/tags/Java/"},{"name":"架构图","slug":"架构图","permalink":"http://bee.gs/tags/架构图/"},{"name":"自定义","slug":"自定义","permalink":"http://bee.gs/tags/自定义/"},{"name":"sql","slug":"sql","permalink":"http://bee.gs/tags/sql/"}]},{"title":"Java-springmvc-项目搭建架构图","date":"2016-11-24T16:00:00.000Z","path":"2016/11/25/tech/java-springmvc项目搭建架构图设计/","text":"Java-springmvc-项目搭建架构图项目搭建架构图在线地址点击查看架构图","tags":[{"name":"Java","slug":"Java","permalink":"http://bee.gs/tags/Java/"},{"name":"springmvd","slug":"springmvd","permalink":"http://bee.gs/tags/springmvd/"},{"name":"架构图","slug":"架构图","permalink":"http://bee.gs/tags/架构图/"},{"name":"项目搭建","slug":"项目搭建","permalink":"http://bee.gs/tags/项目搭建/"}]},{"title":"java-springmvc+ehcache缓存实现","date":"2016-11-23T16:00:00.000Z","path":"2016/11/24/tech/java-springmvc+ehcache缓存实现/","text":"ehcache介绍Ehcache是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider(hibernate-ehcache )。当然也可以和mybatis(mybatis-ehcache)结合,它具有内存和磁盘存储,ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便,如果大规模集群还是考虑用memcached或redis。如果是单个应用或者对缓存访问要求很高的应用,用ehcache。 ehcache结构设计图 CacheManager:缓存管理器,一般单例模式,当然也可以多个实例,里面主要存放各个缓存区域CacheCache:所有cache都实现了Ehcache接口,类似一个HashMap,里面存放着各种键值对element,每个cache都可以设置存活时间,访问间歇时间等。element:单条缓存数据的组成单位,由key和value组成 Cache的元素的属性 name:缓存名称。 maxElementsInMemory:缓存最大个数。 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 maxElementsOnDisk:硬盘最大缓存个数。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 spring结合ehcache1、配置所需的相关jar包123456789101112131415161718192021<!--缓存start--><!--ehcache缓存核心包--> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.6.9</version> </dependency> <!--做ehcache监控用的,mvn仓库里没有需要下载ehcache的监控插件ehcache-monitor-kit-1.0.3,从里面提供的lib复制出来,后面会有相关介绍--> <dependency> <groupId>org.terracotta</groupId> <artifactId>ehcache-probe</artifactId> <version>1.0.3</version> </dependency> <!--spring注解对ehcache的操作--> <dependency> <groupId>com.googlecode.ehcache-spring-annotations</groupId> <artifactId>ehcache-spring-annotations</artifactId> <version>1.2.0</version> </dependency> <!--end--> 2、配置ehcache和spring结合时的相关配置文件,ehcache.xsd下载地址 1234567891011121314151617181920212223242526272829303132<?xml version=\"1.0\" encoding=\"UTF-8\"?><ehcache xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"ehcache.xsd\" updateCheck=\"true\" monitoring=\"autodetect\" dynamicConfig=\"true\"> <!-- 默认缓存配置. 自动失效:最后一次访问时间间隔300秒失效,若没有访问过自创建时间600秒失效。--> <defaultCache maxEntriesLocalHeap=\"1000\" eternal=\"false\" timeToIdleSeconds=\"300\" timeToLiveSeconds=\"600\" overflowToDisk=\"true\" statistics=\"true\"/> <!-- 系统缓存 --> <cache name=\"sysCache\" maxEntriesLocalHeap=\"1000\" eternal=\"true\" overflowToDisk=\"true\" statistics=\"true\"/> <!-- 用户缓存 --> <cache name=\"userCache\" maxEntriesLocalHeap=\"1000\" eternal=\"true\" overflowToDisk=\"true\" statistics=\"true\"/><!--配置ehcache缓存监控的相关地址,端口--> <cacheManagerPeerListenerFactory class=\"org.terracotta.ehcachedx.monitor.probe.ProbePeerListenerFactory\" properties=\"monitorAddress=localhost, monitorPort=9889, memoryMeasurement=true\" /></ehcache> springmvc对ehcache的初始化配置 1234567891011121314151617181920212223242526<?xml version=\"1.0\" encoding=\"UTF-8\"?><beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:cache=\"http://www.springframework.org/schema/cache\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd\"><!-- 启用缓存注解功能--> <cache:annotation-driven cache-manager=\"ehcacheManager\"/> <!-- 基于spring实现的缓存管理器--> <bean id=\"ehcacheManager\" class=\"org.springframework.cache.ehcache.EhCacheCacheManager\"> <property name=\"cacheManager\" ref=\"cacheManagerFactory\"/> </bean> <!--加载ehcache的相关配置--> <bean id=\"cacheManagerFactory\" class=\"org.springframework.cache.ehcache.EhCacheManagerFactoryBean\"> <property name=\"configLocation\" value=\"classpath:cache/ehcache.xml\"/> </bean> <!--创建工具类ApplicationUtil,以便普通class根据bean id动态获取spring管理的bean--> <bean id=\"applicationUtil\" class=\"com.personal.core.utils.ApplicationUtil\"></bean></beans> 3、实现动态获取bean的工具类ApplicationUtil 123456789101112131415161718192021222324252627package com.personal.core.utils;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;/** * 注释 * * @author: coding99 * @Date: 16-11-24 * @Time: 下午8:05 */public class ApplicationUtil implements ApplicationContextAware{ private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ApplicationUtil.applicationContext = applicationContext; } public static Object getBean(String name){ return applicationContext.getBean(name); }} 5、实现动态创建cache缓存块的工具类EHCacheUtils 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235package com.personal.core.utils;import net.sf.ehcache.Cache;import net.sf.ehcache.CacheManager;import net.sf.ehcache.Element;import org.springframework.cache.ehcache.EhCacheCacheManager;import java.util.List;/** * 注释 * * @author: coding99 * @Date: 16-11-24 * @Time: 下午8:03 */public class EHCacheUtils { private static CacheManager cacheManager = null; static { EHCacheUtils.initCacheManager(); } /** * 初始化缓存管理容器 * @return */ public static CacheManager initCacheManager() { try{ if(cacheManager == null) { EhCacheCacheManager ehCacheCacheManager = (EhCacheCacheManager)ApplicationUtil.getBean(\"ehcacheManager\"); cacheManager = ehCacheCacheManager.getCacheManager(); } }catch (Exception e) { e.printStackTrace(); } return cacheManager; } /** * 初始化内存块 * @param cacheName * @param maxElementsInMemory * @param overflowToDisk * @param eternal * @param timeToIdleSeconds * @param timeToLiveSeconds * @return */ public static Cache initCache(String cacheName,int maxElementsInMemory,boolean overflowToDisk,boolean eternal,long timeToLiveSeconds,long timeToIdleSeconds) { Cache cache = cacheManager.getCache(cacheName); try { if(null == cache) { cache = new Cache(cacheName,maxElementsInMemory,overflowToDisk,eternal,timeToLiveSeconds,timeToIdleSeconds); cacheManager.addCache(cache); } } catch (Exception e) { e.printStackTrace(); } return cache; } /** * 初始化内存块 * @param cacheName * @param timeToIdleSeconds * @param timeToLiveSeconds * @return */ public static Cache initCache(String cacheName,long timeToLiveSeconds,long timeToIdleSeconds) { return initCache(cacheName, EHCacheConfig.MAX_ELEMENTS_IN_MEMORY, EHCacheConfig.OVER_FLOW_TO_DISK, EHCacheConfig.ETERNAL,timeToLiveSeconds,timeToIdleSeconds); } /** * 移除缓存 * @param cacheName */ public static void removeCache(String cacheName) { checkCacheManager(); Cache cache = cacheManager.getCache(cacheName); if(null != cache) { cacheManager.removeCache(cacheName); } } /** * * 获取所有的cache名称 * * @return */ public static String[] getAllCaches() { checkCacheManager(); return cacheManager.getCacheNames(); } /** * 移除所有cache */ public static void removeAllCache() { checkCacheManager(); cacheManager.removalAll(); } /** * 初始化缓存 * * @param cacheName * @return */ public static Cache initCache(String cacheName) { checkCacheManager(); if(null == cacheManager.getCache(cacheName)) { cacheManager.addCache(cacheName); } return cacheManager.getCache(cacheName); } /** * 添加缓存 * @param cache 缓存块 * @param key 关键字 * @param value 值 */ public static void put(Cache cache,Object key, Object value) { checkCache(cache); Element element = new Element(key,value); cache.put(element); } /** * 获取值 * @param cache 缓存块 * @param key * @return */ public static Object get(Cache cache,Object key) { checkCache(cache); Element element = cache.get(key); if(null == element) { return null; } return element.getObjectValue(); } /** * 移除key * @param cache 缓存块 * @param key */ public static void remove(Cache cache,String key) { checkCache(cache); cache.remove(key); } /** * 移除所有元素 * @param cache 缓存块 */ public static void removeAllKey(Cache cache) { checkCache(cache); cache.removeAll(); } /** * 获取 所有的key * @param cache 缓存块 * @return */ public static List getKeys(Cache cache) { checkCache(cache); return cache.getKeys(); } /** * 检测内存管理器是否初始化 */ private static void checkCacheManager() { if(null == cacheManager) { throw new IllegalArgumentException(\"调用前请先初始化CacheManager值:EHCacheUtil.initCacheManager\"); } } /** * 检查内存块是否存在 * @param cache */ private static void checkCache(Cache cache) { if(null == cache) { throw new IllegalArgumentException(\"调用前请先初始化Cache值:EHCacheUtil.initCache(参数)\"); } } /** * * @param args */ public static void main(String[] args) { Cache cache1 = EHCacheUtils.initCache(\"cache1\", 60, 30); EHCacheUtils.put(cache1, \"A\", \"a\"); Cache cache2 = EHCacheUtils.initCache(\"cache2\", 50, 20); EHCacheUtils.put(cache2, \"A\", \"b\"); System.out.println(EHCacheUtils.cacheManager.getCache(\"cache1\")); System.out.println(EHCacheUtils.cacheManager.getCache(\"cache2\")); System.out.println(EHCacheUtils.cacheManager.getCache(\"sysCache\")); System.out.println(EHCacheUtils.cacheManager.getCache(\"userCache\")); }} 123456789101112131415161718192021222324252627package com.personal.core.utils;/** * 注释 * * @author: coding99 * @Date: 16-11-24 * @Time: 下午8:03 */public class EHCacheConfig { //元素最大数量 public static final int MAX_ELEMENTS_IN_MEMORY = 1000; //是否把溢出数据持久化到硬盘 public static final boolean OVER_FLOW_TO_DISK = true; //是否会死亡 public static boolean ETERNAL = false; //缓存间歇时间 public static final int TIME_TO_IDLE_SECONDS = 300; //缓存存活时间 public static final int TIME_TO_LIVE_SECONDS = 600; //是否需要持久化到硬盘 public static final boolean DISK_PERSISTENT = false; //内存存取策略 public static String MEMORY_STORE_EVICTION_POLICY = \"LRU\";} 6、基于spring注解的的ehcache的用法 使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例 @Cacheable可作用与类或者方法上,主要用于把返回的数据存入相应的缓存块里 @CacheEvict主要用于在方法执行前或者执行后清除指定缓存块里的相应元素 相关属性请百度相应的用法 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051@Cacheable(value=\"userCache\", key=\"#id\") public User find(Integer id) { returnnull; } @Cacheable(value=\"userCache\", key=\"#p0\") public User find(Integer id) { returnnull; } @Cacheable(value=\"userCache\", key=\"#user.id\") public User find(User user) { returnnull; } @Cacheable(value=\"userCache\", key=\"#p0.id\") public User find(User user) { return null; } @Cacheable({\"cache1\", \"cache2\"})//Cache是发生在cache1和cache2上的 public User find(Integer id) { return null; } @CacheEvict(value=\"userCache\", beforeInvocation=true) public void delete(Integer id) { System.out.println(\"delete user by id: \" + id); } ehcache监控监控 ehcache缓存: 1.下载地址 2.解压缩到目录下,复制ehcache-monitor-kit-1.0.0\\lib\\ehcache-probe-1.0.0.jar到项目里面,如上面的通过pom.xml文件引入等方式 3.将以下配置copy的ehcache.xml文件的ehcache标签中 123<cacheManagerPeerListenerFactory class=\"org.terracotta.ehcachedx.monitor.probe.ProbePeerListenerFactory\" properties=\"monitorAddress=localhost, monitorPort=9889\" /> 如果有提示报错,请对properties里面的元素做换行或者空格处理,具体也不知道为啥 4.在\\ehcache-monitor-kit-1.0.0\\etc\\ehcache-monitor.conf中可以配置监控的ip和端口号。如把相应的#去掉 5.删除 startup.bat中的行 -j %PRGDIR%\\etc\\jetty.xml启动被监控的web application和ehcache-monitor-kit-1.0.0\\bin目录下的startup.bat(在windows环境下) 6.在浏览器中输入 http://localhost:9889/monitor/即可开始监控。","tags":[{"name":"Java","slug":"Java","permalink":"http://bee.gs/tags/Java/"},{"name":"springmvc","slug":"springmvc","permalink":"http://bee.gs/tags/springmvc/"},{"name":"ehcache","slug":"ehcache","permalink":"http://bee.gs/tags/ehcache/"},{"name":"缓存","slug":"缓存","permalink":"http://bee.gs/tags/缓存/"}]},{"title":"Ubuntu-Nginx+Tomcat+Redis集群配置","date":"2016-10-24T14:54:00.000Z","path":"2016/10/24/tech/ubuntu-Nginx+Tomcat+redis集群配置/","text":"Nginx 安装及配置 简介:中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。Ngnix是一个负载均衡服务器,其特点是占有内存少,并发能力强nginx一般用来做静态服务器,本身不具备动态解析功能,需要结合其它插件或软件使用,如tomcat,php等,在性能方面:如果,不做集群调优等情况下,tomcat(tomcat是动态服务器,需要依赖于jdk)一般支持并发100个左右,而nginx在静态方面可以达到几万并发。 nginx(下载:http://nginx.org/en/download.html)nginx-1.9.3.tar.gz 模块依赖性gzip模块需要 zlib 库( 下载: http://www.zlib.net/)zlib-1.2.8.tar.gz :gzip可以加快我们网站打开的速度,原理是通过服务器压缩,客户端浏览器快速解压,大大的节省了网站的流量,提高了网页打开的速度,但与此同时也会增加cpu的开销。rewrite模块需要 pcre 库 ( 下载: http://www.pcre.org/) pcre-8.38.tar.gz :rewrite主要时实现url的重写,通过pcre的perl兼容正则表达式进行匹配,通过rewrite规则,可实现规范的URL,或者动态的URL地址需要伪装成静态的HTML,便于搜索引擎抓到,旧URL可以通过rewrite跳转到新URL上。ssl 功能需要openssl库( 下载: http://www.openssl.org/ )openssl-1.1.0.tar.gz:openssl可以生成数字证书,自带功能加密功能,增加nginx的ssl安全性。 #安装:sudo su切换到root用户1、下载完包后,我们把它们移动到一个nginx的文件夹里(mv openssl-1.1.0.tar.gz nginx-1.9.3.tar.gz pcre-8.38.tar.gz zlib-1.2.8.tar.gz /opt/nginx),再解压(tar -zxvf openssl-1.1.0.tar.gz nginx-1.9.3.tar.gz pcre-8.38.tar.gz zlib-1.2.8.tar.gz)2、安装pcre(cd pcre-8.38 ./configure make&make install)3、安装zlib(cd zlib-1.2.8 ./configure make&make install)4、安装openssl(cd openssl-1.1.0 ./config make&make install)5、安装nginx(cd nginx-1.9.3 ./configure make&make install) 执行./configure如果提示找不到上面安装的相关模块时,则可以使用一下命令用–with-openssl=、–with-pcre=、–with-zlib=指定依赖的模块目录。如./configure –width-openssl=/opt/nginx/nginx-1.9.3 在make&make install, 如果编译通过,则默认安装即可。 使用:(注意,nignx安装后的使用目录是/usr/local/nginx/)下面的说明用开始安装的目录也行。 我们切换到这个目录下,通过./nginx启动nginx服务器,浏览器输入http://localhost,显示以下信息,则安装成功。如果要要重启nginx,使用./nginx -s reload即可关闭方式有以下几种 1、直接输入 ./nginx -s stop 2、 查询nginx主进程号 ps -ef | grep nginx 从容停止 kill -QUIT 主进程号 快速停止 kill -TERM 主进程号 强制停止 kill -9 nginx 若nginx.conf配置了pid文件路径,如果没有,则在logs目录下 kill -信号类型 /usr/local/nginx/logs/nginx.pid 判断配置文件是否正确可用./nginx -t 配置文件介绍: 除了nginx.conf,其余配置文件,一般只需要使用默认提供即可。 nginx.conf 在conf文件下的nginx.conf时主要配置文件,其内容如上图:worker_process表示工作进程的数量,一般设置为cpu的核数(上图没截取到)worker_connections表示每个工作进程的最大连接数(上图没截取到) server{}块定义了虚拟主机 listener 监听端口,默认为80 server_name监听的域名 location{}是用来匹配URI进行配置,URI即语法中的’/uri/‘。location{}配置任何查询,因为素有请求都以/开头。root 指定对应uri的资源查找路径,这里html为相对路径,完整路径为/opt/nginx/nginx-1.9.3/htmlindex指定首页index文件的名称,可用配置多个,以空格分开,如有多个,按配置顺序查找。error_page服务器发生错误请求时候,它会自动根据错误状态码跳转到相应的50页面。mime.types 定义的文件扩展名与文件类型映射关系fastcgi_params ginx配置Fastcgi解析时会调用fastcgi_params配置文件来传递服务器变量,这样CGI中可以获取到这些变量的值。默认传递以下变量:uwsgi_params 与fastcgi_params一样,传递哪些服务器变量,只有前缀不一样,以uwsgi_param开始而非fastcgi_param。scgi_params 与fastcgi_params一样,传递哪些服务器变量,只有前缀不一样,以uwsgi_param开始而非fastcgi_param。koi-utf、koi-win、win-utf这三个文件都是与编码转换映射文件,用于在输出内容到客户端时,将一种编码转换到另一种编码。koi-win: charset_map koi8-r < – > windows-1251koi-utf: charset_map koi8-r < – > utf-8win-utf: charset_map windows-1251 < – > utf-8 Redis 的安装 wget http://download.redis.io/releases/redis-2.8.9.tar.gz 下载软件安装包 tar -zxvf redis-2.8.9.tar.gz -c /opt/nginx 解压到指定的目录cd /redis-2.8.9 切换到该目录下sudo make & make install安装redis软件安装完毕,接下来我们可以测试是否安装成功 Tomcat + Redis + Nginx集群配置 1、wget http://mirror.bit.edu.cn/apache/tomcat/tomcat-7/v7.0.70/bin/apache-tomcat-7.0.70.tar.gz 2、tar -zxvf apache-tomcat-7.0.70.tar.gz , mv apache-tomcat-7.0.70 tomcat1 3 、tar -zxvf apache-tomcat-7.0.70.tar.gz ,mv apache-tomcat-7.0.70 tomcat2 4、tar -zxvf apache-tomcat-7.0.70.tar.gz, mv apache-tomcat-7.0.70 tomcat3 5、修改tomcat2里的端口依次为8006 8081 8010 修改tomcat3里的端口依次为8007 8082 8011 6、之后我们创建一个文件夹test(mkdir test) 再创建一个index(vim index.jsp)内容如下 1234567891011121314151617181920212223242526<%@ page language=\"java\" %><html> <head><title>TomcatA</title></head> <body> <table align=\"centre\" border=\"1\"> <tr> <td>Session ID</td> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body></html>sessionID:<%=session.getId()%><br>SessionIP:<%=request.getServerName()%><br>SessionPort:<%=request.getServerPort()%><%//为了区分,第二个可以是222out.println(\"This is Tomcat Server 1111\");%> 7、之后分别修改tomcat1 tomcat2 tomcat3的server.xml,在内为其指定web项目的路径 1<Context path=\"\" reloadable=\"true\" docBase=\"/opt/nginx/test\"/> 8、cd /usr/local/nginx/conf切换到nignx安装后的目录下,修改nginx.conf(vim nginx.conf)配置文件 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192#user nobody; # 进程数,一般这个和cpu核心数相等 worker_processes 2; # 错误日志配置 #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; # pid 进程 #pid logs/nginx.pid; #工作模式及连接数上限events { # work 进程连接数 use epoll; worker_connections 2048; # 并发总数是 worker_processes 和 worker_connections 的乘积 # 即 max_clients = worker_processes * worker_connections # 在设置了反向代理的情况下,max_clients = worker_processes * worker_connections / 4 为什么 # 为什么上面反向代理要除以4,应该说是一个经验值 # 根据以上条件,正常情况下的Nginx Server可以应付的最大连接数为:4 * 8000 = 32000 # worker_connections 值的设置跟物理内存大小有关 # 因为并发受IO约束,max_clients的值须小于系统可以打开的最大文件数 # 而系统可以打开的最大文件数和内存大小成正比,一般1GB内存的机器上可以打开的文件数大约是10万左右 # 我们来看看360M内存的VPS可以打开的文件句柄数是多少: # $ cat /proc/sys/fs/file-max # 输出 34336 # 32000 < 34336,即并发连接总数小于系统可以打开的文件句柄总数,这样就在操作系统可以承受的范围之内 # 所以,worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置 # 使得并发总数小于操作系统可以打开的最大文件数目 # 其实质也就是根据主机的物理CPU和内存进行配置 # 当然,理论上的并发总数可能会和实际有所偏差,因为主机还有其他的工作进程需要消耗系统资源。 # ulimit -SHn 65535} http { #设定mime类型,类型由mime.type文件定义 include mime.types; default_type application/octet-stream; #设定日志格式 #log_format main '$remote_addr - $remote_user[$time_local] \"$request\" ' # '$status $body_bytes_sent\"$http_referer\" ' # '\"$http_user_agent\"\"$http_x_forwarded_for\"'; #access_log logs/access.log main; # 开启高效文件传输模式,调用sendfile 进行文件的传输,但一般为了和网速的io进行平衡, #sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件, #对于普通应用,必须设为 on, #如果用来进行下载等应用磁盘IO重负载应用,可设置为 off, #以平衡磁盘与网络I/O处理速度,降低系统的uptime. sendfile on; #tcp_nopush on; #连接超时时间 #keepalive_timeout 0; keepalive_timeout 65; tcp_nodelay on; # 是否开启gzip压缩输出 #开启zip网页压缩 gzip on; gzip_min_length 1k; gzip_buffers 4 8k; gzip_http_version 1.1; gzip_types text/plain application/x-javascript text/css application/xml; #设定请求缓冲 client_header_buffer_size 128k; large_client_header_buffers 4 128k; #负载均衡配置,nginx 是做反向代理的,访问nginx就是在访问负载均衡配置的这个server,这个可以通过log查看 # 查看的error log 从中可以清晰看到 负载均衡的地址:client: 127.0.0.1, server: localhost,request: \"GET /index.jsp # HTTP/1.1\", upstream: \"http://127.0.0.1:18081/index.jsp\", host: \"localhost\" upstream localhost{ #ip_hash iphash 实现负载均衡,这里使用了轮回的方式实现 # nginx 转发的后台tomcat服务器配置,以及其权重的配置,权重越大要承载的量越大 #ip_hash; #每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题 server localhost:8080 weight=1 max_fails=2 fail_timeout=30s; server localhost:8081 weight=1 max_fails=2 fail_timeout=30s; server localhost:8082 weight=1 max_fails=2 fail_timeout=30s; } # 虚拟主机的配置 server { # 就是nginx服务的端口 listen 80; #这个就是访问域名 server_name localhost; # 字符编码的设置 charset utf-8; #access_log logs/host.access.log main; # 动静分离,动态的反向到负载均衡的tomcat上 location / { # nginx 的根目录,当发送静态请求时会默认到这个目录下去查找 root /opt/nginx/test; #定义首页索引文件的名称 index index.html index.htm index.jsp index.do; } #error_page 404 /404.html; # 定义错误提示页面 /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { #root html; } # 静态页面由 nginx处理 location ~ ^/(images|javascript|js|css|flash|media|static)/ { #过期30天,静态文件不怎么更新,过期可以设大一点, #如果频繁更新,则可以设置得小一点。 #expires 30d; } #jsp,do等页面交由tomcat处理 location ~ \\.(jsp|do)$ { # 代理路径,和负载均衡的 upstream localhost 这个localhost路径是一致的,但http是不能忘! proxy_pass http://localhost; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 10m; client_body_buffer_size 128k; proxy_buffers 32 4k; proxy_connect_timeout 3; proxy_send_timeout 30; proxy_read_timeout 30; } #禁止访问 .htxxx 文件 location ~ /.ht { deny all; } }} 9、cd ../sbin/ 切换到启动命令文件夹下,执行sudo ./nginx -t测试配置文件配置是否有误如果提示成功,我们分别启动tomcat1 tomcat2 tomcat3, 再执行sudo ./nginx 启动nginx,然后访问http://localhost,不断f5刷新,可以看到访问被分配到不同的tomcat,这样就起到负载均衡的作用,如果 我们尝试把tomcat1关掉,访问就被分配到剩下的两个tomcat下,但是有一个问题,就是每次访问不到不同的tomcat,session就发生了变化, 这时后我们可以采用redis来做session共享。 10、Tomcat 结合 Redis做session共享需要jedis-2.8.1.jar commons-pool2-2.4.2.jar tomcat-redis-session-manager-2.0.0.jar这几个包,jedis-2.8.1.jar commons-pool2-2.4.2.jar 可以从http://mvnrepository.com/ 上下载, tomcat-redis-session-manager-2.0.0.jar这个包比较麻烦,需要自己用gradle编译,因为tomcat-redis-session-manager的作者把源码开放到git上,打包方式是gradle,编译方式如下: 1、 如果自己系统上没有gradle,请先安装gradle, sudo apt-get install gradle, 2、git clone https://github.com/jcoleman/tomcat-redis-session-manager.git 3、修改build.gradle,在signing节点中添加required = false以及删掉或注释掉文件中的sonatype的repository代码即可 4、执行gradle build,编译结束后生成build,在里面的libs文件夹下可以找到 tomcat-redis-session-manager-2.0.0.jar这个包 11、分别把jedis-2.8.1.jar commons-pool2-2.4.2.jar tomcat-redis-session-manager-2.0.0.jar复制到 tomcat1 tomct2 tomcat3的libs目录下, 再一次修改tomcat1 tomcat2 tomcat3的conf里的 context.xml 文件,在里头的context节点里增加如下内容 123456789101112<Valve className=\"com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve\" /> <Manager className=\"com.orangefunction.tomcat.redissessions.RedisSessionManager\" host=\"localhost\" port=\"6379\" database=\"10\" maxInactiveInterval=\"60\"/> 11、最后,启动redis,tomcat1,tomcat2,tomcat3,nginx,访问http://localhost,不断按f5刷新,可以看到都是同样内容,除非关闭浏览器再重新启动才能看到session id的变化,说明已经实现了 tomcat集群和session共享。","tags":[{"name":"Ubuntu","slug":"Ubuntu","permalink":"http://bee.gs/tags/Ubuntu/"},{"name":"Linux","slug":"Linux","permalink":"http://bee.gs/tags/Linux/"},{"name":"Nginx","slug":"Nginx","permalink":"http://bee.gs/tags/Nginx/"},{"name":"Tomcat","slug":"Tomcat","permalink":"http://bee.gs/tags/Tomcat/"},{"name":"Redis - 安装","slug":"Redis-安装","permalink":"http://bee.gs/tags/Redis-安装/"}]},{"title":"Ubuntu -Jdk-Mysql安装","date":"2016-10-23T15:23:00.000Z","path":"2016/10/23/tech/ubuntu-jdk-mysql/","text":"ubuntu下的Jdk Mysql安装 软件环境 : 操作系统:Ubuntu 14.04.2 LTS(cat /etc/issue) x86_64(uname -m) JAVA版本:jdk-7u71-linux-x64.tar.gz 软件下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 也可以在线下载(由于oracle下载需要选择确认,所以我们下载时需要设置请求头的Cookie): wget –no-check-certificate –no-cookies –header “Cookie: oraclelicense=accept-securebackup-cookie” http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-linux-x64.tar.gz 安装软件的方法如下: 1、切换到root用户 sudo su 输入密码 2、mkdir /usr/local/Java 创建jdk存放的文件夹叫java 3、cp jdk-7u71-Linux-x64.tar.gz /usr/local/java 复制jdk到指定文件 4、tar -zxvf jdk-7u71-linux-x64.tar.gz 解压jdk 5、chmod -R 777 /usr/local/java 更改文件夹的权限,让任何操作者都可以访问 6、设置环境变量 ,ubuntu设置环境变量的文件有哪些? /etc/profile: 在登录时,操作系统定制用户环境时使用的第一个文件,此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行。/etc/environment在登录时操作系统使用的第二个文件,系统在读取你自己的profile前,设置环境文件的环境变量;/etc/bashrc —— 为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取;~/.profile —— 每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次!默认情况下,它设置一些环境变量,执行用户的.bashrc文件;~/.bashrc —— 该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该文件被读取这时候我们可选择修改/etc/profile文件vim /etc/profile件加入一下环境变量# java enviroment export JAVA_HOME=/usr/local/java/jdk1.7.0_71 export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$CLASSPATH export PATH=$JAVA_HOME/bin:$PATH按下esc 输入wq 7、让环境变量立即生效 source /etc/profile8、设置默认JDKsudo update-alternatives –install “/usr/bin/java” “java” “/usr/local/java/jdk1.7.0_71/bin/java” 300 sudo update-alternatives –install “/usr/bin/javac” “javac” “/usr/local/java/jdk1.7.0_71/bin/javac” 300 sudo update-alternatives –install “/usr/bin/javaws” “javaws” “/usr/local/java/jdk1.7.0_71/bin/javaws” 300 9、测试 java -version显示jdk版本配置成功java version “1.7.0_71” Java(TM) SE Runtime Environment (build 1.7.0_71-b14) Java HotSpot(TM) 64-Bit Server VM (build 24.71-b01, mixed mode)查看环境变量可以echo $JAVA_HOME显示=/usr/local/java/jdk1.7.0_71 ubuntu下的Mysql安装 1、跟新源,sudo apt-get update2、首先检查系统中是否已经安装了MySQL 在终端输入sudo netstat -tap|grep mysql 如果没有反应则未安装,如果已经安装则显示 tcp 0 0 localhost:mysql : LISTEN 3、如果没有安装,则安装MySQL. 在终端输入 sudo apt-get install mysql-server mysql-client 在此安装过程中会让你输入root用户(管理MySQL数据库用户,非Linux系统用户)密码,按照要求输入即可。如下所示 4、如何判断mysql是否安装成功 输入sudo service mysql restart 如果成功则显示mysql启动成功 5、登陆mysql,在终端输入mysql -u root -p 输入刚刚设置的密码即可登陆mysql ubuntu下的Mysql卸载 Mysql卸载一定要卸载干净,避免再次安装时出现莫名奇妙的错误 1、sudo apt-get autoremove mysql* –purge 2、sudo rm /var/lib/mysql/ -R 3、sudo rm /etc/mysql/ -R","tags":[{"name":"Ubuntu","slug":"Ubuntu","permalink":"http://bee.gs/tags/Ubuntu/"},{"name":"Linux","slug":"Linux","permalink":"http://bee.gs/tags/Linux/"},{"name":"Jdk","slug":"Jdk","permalink":"http://bee.gs/tags/Jdk/"},{"name":"Mysql","slug":"Mysql","permalink":"http://bee.gs/tags/Mysql/"},{"name":"安装","slug":"安装","permalink":"http://bee.gs/tags/安装/"}]},{"title":"Ubuntu -Sublime-安装","date":"2016-10-21T17:01:00.000Z","path":"2016/10/22/tech/ubuntu-sublime-install/","text":"web前端的开发神器 WebStrom和Sublime堪称史上最强的web开发神器: 提供了智能的代码提示,智能补全,各种强大的轻量级的插件支持。WebStrom相对智能一点,但是耗内存大,有时会卡顿现象,界面不简洁,Sublime相对灵活轻巧,界面清爽,功能齐全,插件快速安装等特点。 ###ubuntu上安装教程: 1、安装Sublime Text 3 及常用的神器插件首先添加sublime text 3的仓库:sudo add-apt-repository ppa:webupd8team/sublime-text-3根据提示按ENTER 继续,建立信任数据库再添加sublime text 3的仓库:sudo apt update最后安装Sublime Text 3:sudo apt install sublime-text-installer软件安装成功。2、安装Package Control输入命令subl打开Sublime Text 3按快捷键ctrl+`(Esc下面的按键),在弹出的控制台输入一下代码回车import urllib.request,os,hashlib; h = ‘2915d1851351e5ee549c20394736b442’ + ‘8bc59f460fa1548d1514676163dafc88’; pf = ‘Package Control.sublime-package’; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( ‘http://packagecontrol.io/‘ + pf.replace(‘ ‘, ‘%20’)).read(); dh = hashlib.sha256(by).hexdigest(); print(‘Error validating download (got %s instead of %s), please try manual install’ % (dh, h)) if dh != h else open(os.path.join( ipp, pf), ‘wb’ ).write(by)重启软件后可以在Preference菜单下找到Package Control命令,单击运行Packeage Control,输入 ###常用插件 ####安装方法ctrl+shift+p>输入install>按下enter>查找插件>确认12345678910111213141516171819202122232425262728293031323334353637383940414243444546471.Git常用的git社区有csdn、github、开源中国,还有自己搭建的git服务器,在开过程中我们习惯了在软件里直接使用git,而不是去切换到终端运行相应的命令。2.GitGutterGitGutter 更好的帮助开发者查看文件之前的改动和差异,提升开发效率,减少错误的提交,代码冲突。3.EmmetEmmet 项目的前身是前端开发人员熟知的 Zen Coding(快速编写 HTML/CSS 代码的方案)。在 Sublime Text 编辑器中搭配 Emmet 插件真的是让你编码快上加快。4.SublimeREPL这个插件允许你在Sublime里面运行各种语言(如NodeJs,Python,Scala等等),所以编程少不了它5.DocBlockr如果你遵循的编码的风格很严格,这款插件能够使你的任务更容易。DocBlokr 帮助你创造你的代码注释,通过解析功能,参数,变量,并且自动添加基本项目6.CSSComb这是用来给css属性进行排序格式化的插件,如果你想让你的代码看起来简洁明了,清新飘逸,那么这个插件就是一个有效的解决方案,特别是很多人一个开发的时候可以保持编码风格统一7.Alignment这个插件让你能对齐你的代码,包括 PHP、CSS 和 Javascript。代码看起来更简洁和可读,便于编辑。8.Trmmer你知道当你编写代码时,由于错误或别的某些原因,会产生一些不必要的空格。需要注意的是多余的空格有时也会造成错误。这个插件会自动删除这些不必要的空格。9.ColorPicker如果你经常要查看或设置颜色值,这个插件可以很方便地调用你本机的调色板应用。(译者扩充:)这是一个双向的功能,你既可以在调色板中选择一个颜色,然后按“确定”按钮把该值填写到 SublimeText 中活动文档的当前位置,也可以在活动文档中选择一个颜色的值,按此插件的快捷键就会在显示的调色板中定位到该值所对应的颜色10.angularjs在编写angularjs时可以快速提示补全,避免写错11.reactjs我们经常会忘记一些语法,需要去查找,为了避免不必要的麻烦,装上reactjs就可以解决忘记react语法的问题12.SpaceGray一个好的主题可以让人心情愉悦,避免敲错代码,装上它,就可以拥有令人兴奋的主题。 ####等等,还有好多好用的插件,可根据自己的需要安装。###Sublime常用快捷键: 掌握基本的代码编辑器的快捷键,能让你打码更有效率,刚开始可能不大记得住,多敲几次就能熟悉并使用它 精华键 : Ctrl+Shift+P:打开命令面板Ctrl+P:搜索项目中的文件Ctrl+G:跳转到第几行Ctrl+W:关闭当前打开文件Ctrl+Shift+W:关闭所有打开文件Ctrl+Shift+V:粘贴并格式化Ctrl+D:选择单词,重复可增加选择下一个相同的单词Ctrl+L:选择行,重复可依次增加选择下一行Ctrl+Shift+L:选择多行Ctrl+Shift+Enter:在当前行前插入新行Ctrl+X:删除当前行Ctrl+M:跳转到对应括号Ctrl+U:软撤销,撤销光标位置Ctrl+J:选择标签内容Ctrl+F:查找内容Ctrl+Shift+F:查找并替换Ctrl+H:替换Ctrl+R:前往 methodCtrl+N:新建窗口Ctrl+K+B:开关侧栏Ctrl+Shift+M:选中当前括号内容,重复可选着括号本身Ctrl+F2:设置/删除标记Ctrl+/:注释当前行Ctrl+Shift+/:当前位置插入注释Ctrl+Alt+/:块注释,并Focus到首行,写注释说明用的Ctrl+Shift+A:选择当前标签前后,修改标签用的F11:全屏Shift+F11:全屏免打扰模式,只编辑当前文件Alt+F3:选择所有相同的词Alt+.:闭合标签Alt+Shift+数字:分屏显示Alt+数字:切换打开第N个文件Shift+右键拖动:光标多不,用来更改或插入列内容鼠标的前进后退键可切换Tab文件按Ctrl,依次点击或选取,可需要编辑的多个位置按Ctrl+Shift+上下键,可替换行 选择类 : Ctrl+D 选中光标所占的文本,继续操作则会选中下一个相同的文本。Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑。举个栗子:快速选中并更改所有相同的变量名、函数名等。Ctrl+L 选中整行,继续操作则继续选择下一行,效果和 Shift+↓ 效果一样。Ctrl+Shift+L 先选中多行,再按下快捷键,会在每行行尾插入光标,即可同时编辑这些行。Ctrl+Shift+M 选择括号内的内容(继续选择父括号)。举个栗子:快速选中删除函数中的代码,重写函数体代码或重写括号内里的内容。Ctrl+M 光标移动至括号内结束或开始的位置。Ctrl+Enter 在下一行插入新行。举个栗子:即使光标不在行尾,也能快速向下插入一行。Ctrl+Shift+Enter 在上一行插入新行。举个栗子:即使光标不在行首,也能快速向上插入一行。Ctrl+Shift+[ 选中代码,按下快捷键,折叠代码。Ctrl+Shift+] 选中代码,按下快捷键,展开代码。Ctrl+K+0 展开所有折叠代码。Ctrl+← 向左单位性地移动光标,快速移动光标。Ctrl+→ 向右单位性地移动光标,快速移动光标。shift+↑ 向上选中多行。shift+↓ 向下选中多行。Shift+← 向左选中文本。Shift+→ 向右选中文本。Ctrl+Shift+← 向左单位性地选中文本。Ctrl+Shift+→ 向右单位性地选中文本。Ctrl+Shift+↑ 将光标所在行和上一行代码互换(将光标所在行插入到上一行之前)。Ctrl+Shift+↓ 将光标所在行和下一行代码互换(将光标所在行插入到下一行之后)。Ctrl+Alt+↑ 向上添加多行光标,可同时编辑多行。Ctrl+Alt+↓ 向下添加多行光标,可同时编辑多行。 编辑类 :Ctrl+J 合并选中的多行代码为一行。举个栗子:将多行格式的CSS属性合并为一行。Ctrl+Shift+D 复制光标所在整行,插入到下一行。Tab 向右缩进。Shift+Tab 向左缩进。Ctrl+K+K 从光标处开始删除代码至行尾。Ctrl+Shift+K 删除整行。Ctrl+/ 注释单行。Ctrl+Shift+/ 注释多行。Ctrl+K+U 转换大写。Ctrl+K+L 转换小写。Ctrl+Z 撤销。Ctrl+Y 恢复撤销。Ctrl+U 软撤销,感觉和 Gtrl+Z 一样。Ctrl+F2 设置书签Ctrl+T 左右字母互换。F6 单词检测拼写 搜索类 :Ctrl+F 打开底部搜索框,查找关键字。Ctrl+shift+F 在文件夹内查找,与普通编辑器不同的地方是sublime允许添加多个文件夹进行查找,略高端,未研究。Ctrl+P 打开搜索框。举个栗子:1、输入当前项目中的文件名,快速搜索文件,2、输入@和关键字,查找文件中函数名,3、输入:和数字,跳转到文件中该行代码,4、输入#和关键字,查找变量名。Ctrl+G 打开搜索框,自动带:,输入数字跳转到该行代码。举个栗子:在页面代码比较长的文件中快速定位。Ctrl+R 打开搜索框,自动带@,输入关键字,查找文件中的函数名。举个栗子:在函数较多的页面快速查找某个函数。Ctrl+: 打开搜索框,自动带#,输入关键字,查找文件中的变量名、属性名等。Ctrl+Shift+P 打开命令框。场景栗子:打开命名框,输入关键字,调用sublime text或插件的功能,例如使用package安装插件。Esc 退出光标多行选择,退出搜索框,命令框等。显示类Ctrl+Tab 按文件浏览过的顺序,切换当前窗口的标签页。Ctrl+PageDown 向左切换当前窗口的标签页。Ctrl+PageUp 向右切换当前窗口的标签页。Alt+Shift+1 窗口分屏,恢复默认1屏(非小键盘的数字)Alt+Shift+2 左右分屏-2列Alt+Shift+3 左右分屏-3列Alt+Shift+4 左右分屏-4列Alt+Shift+5 等分4屏Alt+Shift+8 垂直分屏-2屏Alt+Shift+9 垂直分屏-3屏Ctrl+K+B 开启/关闭侧边栏。F11 全屏模式Shift+F11 免打扰模式","tags":[{"name":"Ubuntu","slug":"Ubuntu","permalink":"http://bee.gs/tags/Ubuntu/"},{"name":"Linux","slug":"Linux","permalink":"http://bee.gs/tags/Linux/"},{"name":"安装","slug":"安装","permalink":"http://bee.gs/tags/安装/"},{"name":"Sublime","slug":"Sublime","permalink":"http://bee.gs/tags/Sublime/"}]},{"title":"Java-Jsch-教程","date":"2016-10-21T08:48:00.000Z","path":"2016/10/21/tech/java-jsch/","text":"jsch教程 jsch是ssh2的一个纯Java实现。它允许你连接到一个sshd服务器,使用端口转发,X11转发,文件传输等等。你可以将它的功能集成到你自己的程序中。同时该项目也提供一个J2ME版本用来在手机上直连SSHD服务器。 ####一般连接到服务器有两种方式:1、通过用户名和密码连接,缺点(出于安全需要,一般服务器的密码会定期修改,程序部署后将不得不经常更新配置文件中的密 码。)2、通过用户名和ssh private key file连接,缺点(因为Java程序必须和private key file在同一台机器上,将服务器的private key file复制到本地后,本地机器的安全措施可能会使private key file被窃取,威胁服务器安全。) jsch官网地址为http://www.jcraft.com/jsch/,实现jsch功能需要添加一个jsch-0.1.51.jar包,官网有一些例子可直接下载参考。 maven的配置为: 12345<dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.51</version></dependency> jsch连接到Linux的基本原理和用ssh一样,需要ip地址,端口(一般为22),用户名,密码,为了方便配置,可以把静态变量初始化在配置文件中: 123456#jsch配置host.ip=192.168.1.1host.user=roothost.port=22host.password=123456host.connect.timeout=5000 获取配置文件变量的工具类: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293package com.personal.core.constant;import com.personal.core.utils.PropertiesLoader;import org.apache.commons.lang.StringUtils;import org.springframework.core.io.DefaultResourceLoader;import java.io.File;import java.io.IOException;import java.util.HashMap;import java.util.Map;/** * 注释:全局配置类 * * @Author: coding99 * @Date: 16-9-2 * @Time: 下午7:33 */public class Global { /** * 当前对象实例 */ private static Global global = new Global(); /** * 保存全局属性值 */ private static Map<String, String> map = new HashMap<String, String>(); /** * 属性文件加载对象 */ private static PropertiesLoader loader = new PropertiesLoader(\"sysConfig.properties\"); /** * 获取当前对象实例 */ public static Global getInstance() { return global; } /** * 获取配置 * @see {fns:getConfig('adminPath')} */ public static String getConfig(String key) { String value = map.get(key); if (value == null){ value = loader.getProperty(key); map.put(key, value != null ? value : StringUtils.EMPTY); } return value; }/** * 获取工程路径 * @return */ public static String getProjectPath(){ // 如果配置了工程路径,则直接返回,否则自动获取。String projectPath = Global.getConfig(\"projectPath\");if (StringUtils.isNotBlank(projectPath)){return projectPath;}try {File file = new DefaultResourceLoader().getResource(\"\").getFile();if (file != null){while(true){File f = new File(file.getPath() + File.separator + \"src\" + File.separator + \"main\");if (f == null || f.exists()){break;}if (file.getParentFile() != null){file = file.getParentFile();}else{break;}}projectPath = file.toString();}} catch (IOException e) {e.printStackTrace();}return projectPath; }} /** * 获取工程路径 * @return */ public static String getProjectPath(){ // 如果配置了工程路径,则直接返回,否则自动获取。 String projectPath = Global.getConfig(\"projectPath\"); if (StringUtils.isNotBlank(projectPath)){ return projectPath; } try { File file = new DefaultResourceLoader().getResource(\"\").getFile(); if (file != null){ while(true){ File f = new File(file.getPath() + File.separator + \"src\" + File.separator + \"main\"); if (f == null || f.exists()){ break; } if (file.getParentFile() != null){ file = file.getParentFile(); }else{ break; } } projectPath = file.toString(); } } catch (IOException e) { e.printStackTrace(); } return projectPath; }} 获取脚本的方法通过一个工具类读取这个配置文件,用dom4j进行解析,获取相应指定shell name的脚本,例如 123456789101112131415161718192021222324<?xml version=\"1.0\" encoding=\"UTF-8\"?><configuration> <!--开发环境--> <shell name=\"ls\" env=\"dev\"> <description>查看opt文件夹内容</description> <step>ls -ltr /opt</step> </shell> <!--开发环境--> <shell name=\"ls\" env=\"uat\"> <description>查看opt文件夹内容</description> <step>ls -ltr /opt</step> </shell> <!--生产环境--> <shell name=\"ls\" env=\"pro\"> <description>查看opt文件夹内容</description> <step>ls -ltr /opt</step> </shell></configuration> 获取脚本的方法通过一个工具类读取这个配置文件,用dom4j进行解析,获取相应指定shell name的脚本,例如 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384package com.personal.core.utils;import com.personal.core.constant.Global;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.io.File;import java.util.List;/** * 注释 * * @Author: coding99 * @Date: 16-9-2 * @Time: 下午9:31 */public class ShellConfigUtil { private static final String SHELL_ENV = Global.getConfig(\"environment\"); private static final String DEFAULT_SHELL_CONFIG = ShellConfigUtil.class.getResource(\"/\").getPath()+\"shell.xml\"; private ShellConfigUtil() { } /** * 取得脚本 * @param shellName * @return * @throws Exception */ public static String getShell(String shellName)throws Exception{ String shellContent = \"\"; List<Element> shells = getShellEelements(); for(Element shell : shells) { String name = shell.attributeValue(\"name\"); String env = shell.attributeValue(\"env\"); //把shell数组遍历 if(shellName.equals(name) && SHELL_ENV.equals(env)) { shellContent = getShellResult(shell); } } return shellContent; } /** * 解析xml * @return * @throws Exception */ public static List<Element> getShellEelements() throws Exception{ SAXReader saxReader = new SAXReader();//创建读取配置文件的对象 Document document = saxReader.read(new File(DEFAULT_SHELL_CONFIG));//开始读取配置文件 Element element = document.getRootElement(); List<Element> shellElements = element.elements(\"shell\"); return shellElements; } /** * 获取脚本内容 * @param shell * @return */ public static String getShellResult(Element shell) { String result = \"\"; List<Element> steps = shell.elements(\"step\"); for (Element step : steps) { result = step.getText(); } return result; } public static void main(String[] args) throws Exception{ ShellConfigUtil.getShell(\"ls\"); }} #JSCH实现原理:jsch进行连接服务器连接时可以看做时java的jdbc连接,首先我们需要实例化一个jsch对象,再利用这个对象 根据用户名,主机ip,端口获取一个Session对象,设置好相应的参数后,就进行连接,创建连接后这个session是一直可用的,所以不需要关闭。之后我们需要在session上建立channel通道Channel的类型可以为如下类型:shell - ChannelShell exec - ChannelExec direct-tcpip - ChannelDirectTCPIP sftp - ChannelSftp subsystem - ChannelSubsystem 其中,ChannelShell和ChannelExec比较类似,都可以作为执行Shell脚本的Channel类型。它们有一个比较重要的区别:ChannelShell可以看作是执行一个交互式的Shell,而ChannelExec是执行一个Shell脚本。实现远程命令操作我们需要创建ChannelExec对象。实现文件上传下载我们需要实现ChannelSftp对象。ChannelSftp类是JSch实现SFTP核心类,它包含了所有SFTP的方法,如:put(): 文件上传get(): 文件下载cd(): 进入指定目录ls(): 得到指定目录下的文件列表rename(): 重命名指定文件或目录rm(): 删除指定文件mkdir(): 创建目录rmdir(): 删除目录下面有个工具类展示应用实例:JschUtil.java 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353package com.personal.core.utils;import com.jcraft.jsch.*;import com.personal.core.constant.Global;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.*;import java.nio.charset.Charset;import java.util.*;/** * 注释 * * @Author: coding99 * @Date: 16-9-2 * @Time: 下午7:33 */public class JschUtil { private static final Logger logger = LoggerFactory.getLogger(JschUtil.class); private String charset = \"UTF-8\"; // 设置编码格式,可以根据服务器的编码设置相应的编码格式 private JSch jsch; private Session session; Channel channel = null; ChannelSftp chSftp = null; //初始化配置参数 private String jschHost = Global.getConfig(\"host.ip\"); private int jschPort = Integer.parseInt(Global.getConfig(\"host.port\")); private String jschUserName = Global.getConfig(\"host.user\"); private String jschPassWord = Global.getConfig(\"host.password\"); private int jschTimeOut = Integer.parseInt(Global.getConfig(\"host.connect.timeout\")); /** * 静态内部类实现单例模式 */ private static class LazyHolder { private static final JschUtil INSTANCE = new JschUtil(); } private JschUtil() { } /** * 获取实例 * @return */ public static final JschUtil getInstance() { return LazyHolder.INSTANCE; } /** * 连接到指定的服务器 * @return * @throws JSchException */ public boolean connect() throws JSchException { jsch = new JSch();// 创建JSch对象 boolean result = false; try{ long begin = System.currentTimeMillis();//连接前时间 logger.debug(\"Try to connect to jschHost = \" + jschHost + \",as jschUserName = \" + jschUserName + \",as jschPort = \" + jschPort); session = jsch.getSession(jschUserName, jschHost, jschPort);// // 根据用户名,主机ip,端口获取一个Session对象 session.setPassword(jschPassWord); // 设置密码 Properties config = new Properties(); config.put(\"StrictHostKeyChecking\", \"no\"); session.setConfig(config);// 为Session对象设置properties session.setTimeout(jschTimeOut);//设置连接超时时间 session.connect(); logger.debug(\"Connected successfully to jschHost = \" + jschHost + \",as jschUserName = \" + jschUserName + \",as jschPort = \" + jschPort); long end = System.currentTimeMillis();//连接后时间 logger.debug(\"Connected To SA Successful in {} ms\", (end-begin)); result = session.isConnected(); }catch(Exception e){ logger.error(e.getMessage(), e); }finally{ if(result){ logger.debug(\"connect success\"); }else{ logger.debug(\"connect failure\"); } } if(!session.isConnected()) { logger.error(\"获取连接失败\"); } return session.isConnected(); } /** * 关闭连接 */ public void close() { if(channel != null && channel.isConnected()){ channel.disconnect(); channel=null; } if(session!=null && session.isConnected()){ session.disconnect(); session=null; } } /** * 脚本是同步执行的方式 * 执行脚本命令 * @param command * @return */ public Map<String,Object> execCmmmand(String command) throws Exception{ Map<String,Object> mapResult = new HashMap<String,Object>(); logger.debug(command); StringBuffer result = new StringBuffer();//脚本返回结果 BufferedReader reader = null; int returnCode = -2;//脚本执行退出状态码 try { channel = session.openChannel(\"exec\"); ((ChannelExec) channel).setCommand(command); channel.setInputStream(null); ((ChannelExec) channel).setErrStream(System.err); channel.connect();//执行命令 等待执行结束 InputStream in = channel.getInputStream(); reader = new BufferedReader(new InputStreamReader(in, Charset.forName(charset))); String res=\"\"; while((res=reader.readLine()) != null){ result.append(res+\"\\n\"); logger.debug(res); } returnCode = channel.getExitStatus(); mapResult.put(\"returnCode\",returnCode); mapResult.put(\"result\",result.toString()); } catch (IOException e) { logger.error(e.getMessage(),e); } catch (JSchException e) { logger.error(e.getMessage(), e); } finally { try { reader.close(); } catch (IOException e) { logger.error(e.getMessage(), e); } } return mapResult; } /** * 上传文件 * * @param directory 上传的目录,有两种写法 * 1、如/opt,拿到则是默认文件名 * 2、/opt/文件名,则是另起一个名字 * @param uploadFile 要上传的文件 如/opt/xxx.txt */ public void upload(String directory, String uploadFile) { try { logger.debug(\"Opening Channel.\"); channel = session.openChannel(\"sftp\"); // 打开SFTP通道 channel.connect(); // 建立SFTP通道的连接 chSftp = (ChannelSftp) channel; File file = new File(uploadFile); long fileSize = file.length(); /*方法一*/ OutputStream out = chSftp.put(directory, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 使用OVERWRITE模式 byte[] buff = new byte[1024 * 256]; // 设定每次传输的数据块大小为256KB int read; if (out != null) { logger.debug(\"Start to read input stream\"); InputStream is = new FileInputStream(uploadFile); do { read = is.read(buff, 0, buff.length); if (read > 0) { out.write(buff, 0, read); } out.flush(); } while (read >= 0); logger.debug(\"input stream read done.\"); } // chSftp.put(uploadFile, directory, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); //方法二 // chSftp.put(new FileInputStream(src), dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); //方法三 logger.debug(\"成功上传文件至\"+directory); } catch (Exception e) { e.printStackTrace(); }finally { chSftp.quit(); if (channel != null) { channel.disconnect(); logger.debug(\"channel disconnect\"); } } } /** * 下载文件 * * @param directory 下载的目录,有两种写法 * 1、如/opt,拿到则是默认文件名 * 2、/opt/文件名,则是另起一个名字 * @param downloadFile 要下载的文件 如/opt/xxx.txt * */ public void download(String directory, String downloadFile) { try { logger.debug(\"Opening Channel.\"); channel = session.openChannel(\"sftp\"); // 打开SFTP通道 channel.connect(); // 建立SFTP通道的连接 chSftp = (ChannelSftp) channel; SftpATTRS attr = chSftp.stat(downloadFile); long fileSize = attr.getSize(); OutputStream out = new FileOutputStream(directory); InputStream is = chSftp.get(downloadFile, new MyProgressMonitor()); byte[] buff = new byte[1024 * 2]; int read; if (is != null) { logger.debug(\"Start to read input stream\"); do { read = is.read(buff, 0, buff.length); if (read > 0) { out.write(buff, 0, read); } out.flush(); } while (read >= 0); logger.debug(\"input stream read done.\"); } //chSftp.get(downloadFile, directory, new FileProgressMonitor(fileSize)); // 代码段1 //chSftp.get(downloadFile, out, new FileProgressMonitor(fileSize)); // 代码段2 logger.debug(\"成功下载文件至\"+directory); } catch (Exception e) { e.printStackTrace(); } finally { chSftp.quit(); if (channel != null) { channel.disconnect(); logger.debug(\"channel disconnect\"); } } } /** * 删除文件 * @param deleteFile 要删除的文件 */ public void delete(String deleteFile) { try { connect();//建立服务器连接 logger.debug(\"Opening Channel.\"); channel = session.openChannel(\"sftp\"); // 打开SFTP通道 channel.connect(); // 建立SFTP通道的连接 chSftp = (ChannelSftp) channel; chSftp.rm(deleteFile); logger.debug(\"成功删除文件\"+deleteFile); } catch (Exception e) { e.printStackTrace(); } } public String getCharset() { return charset; } public void setCharset(String charset) { this.charset = charset; } public static void main(String[] args) throws Exception{ JschUtil jschUtil = JschUtil.getInstance(); boolean isConnected = false; isConnected = jschUtil.connect(); if(isConnected == true){ /*上传文件*/ jschUtil.upload(\"/opt/123456.png\",\"/home/sky/Desktop/resizeApi.png\"); /*执行命令*/ String command = \"ls -ltr /opt\"; // String command = ShellConfigUtil.getShell(\"ls\"); Map<String,Object> result = jschUtil.execCmmmand(command); System.out.println(result.get(\"result\").toString()); /*下载文件*/ jschUtil.download(\"/opt/123456.png\",\"/opt/123456.png\"); jschUtil.close(); } }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140package com.personal.core.utils;import java.text.DecimalFormat;import java.util.Timer;import java.util.TimerTask;import com.jcraft.jsch.SftpProgressMonitor;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 注释 * * @Author: coding99 * @Date: 16-9-2 * @Time: 下午8:29 */public class FileProgressMonitor extends TimerTask implements SftpProgressMonitor { private static final Logger logger = LoggerFactory.getLogger(FileProgressMonitor.class); private long progressInterval = 5 * 1000; // 默认间隔时间为5秒 private boolean isEnd = false; // 记录传输是否结束 private long transfered; // 记录已传输的数据总大小 private long fileSize; // 记录文件总大小 private Timer timer; // 定时器对象 private boolean isScheduled = false; // 记录是否已启动timer记时器 public FileProgressMonitor(long fileSize) { this.fileSize = fileSize; } @Override public void run() { if (!isEnd()) { // 判断传输是否已结束 logger.debug(\"Transfering is in progress.\"); long transfered = getTransfered(); if (transfered != fileSize) { // 判断当前已传输数据大小是否等于文件总大小 logger.debug(\"Current transfered: \" + transfered + \" bytes\"); sendProgressMessage(transfered); } else { logger.debug(\"File transfering is done.\"); setEnd(true); // 如果当前已传输数据大小等于文件总大小,说明已完成,设置end } } else { logger.debug(\"Transfering done. Cancel timer.\"); stop(); // 如果传输结束,停止timer记时器 return; } } public void stop() { logger.debug(\"Try to stop progress monitor.\"); if (timer != null) { timer.cancel(); timer.purge(); timer = null; isScheduled = false; } logger.debug(\"Progress monitor stoped.\"); } public void start() { logger.debug(\"Try to start progress monitor.\"); if (timer == null) { timer = new Timer(); } timer.schedule(this, 1000, progressInterval); isScheduled = true; logger.debug(\"Progress monitor started.\"); } /** * 打印progress信息 * @param transfered */ private void sendProgressMessage(long transfered) { if (fileSize != 0) { double d = ((double)transfered * 100)/(double)fileSize; DecimalFormat df = new DecimalFormat( \"#.##\"); logger.debug(\"Sending progress message: \" + df.format(d) + \"%\"); } else { logger.debug(\"Sending progress message: \" + transfered); } } /** * 实现了SftpProgressMonitor接口的count方法 */ public boolean count(long count) { if (isEnd()) return false; if (!isScheduled) { start(); } add(count); return true; } /** * 实现了SftpProgressMonitor接口的end方法 */ public void end() { setEnd(true); logger.debug(\"transfering end.\"); } private synchronized void add(long count) { transfered = transfered + count; } private synchronized long getTransfered() { return transfered; } public synchronized void setTransfered(long transfered) { this.transfered = transfered; } private synchronized void setEnd(boolean isEnd) { this.isEnd = isEnd; } private synchronized boolean isEnd() { return isEnd; } public void init(int op, String src, String dest, long max) { // Not used for putting InputStream }} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package com.personal.core.utils;import com.jcraft.jsch.SftpProgressMonitor;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 注释 * * @Author: coding99 * @Date: 16-9-2 * @Time: 下午8:36 */public class MyProgressMonitor implements SftpProgressMonitor { private static final Logger logger = LoggerFactory.getLogger(MyProgressMonitor.class); private long transfered; @Override public boolean count(long count) { transfered = transfered + count; logger.debug(\"Currently transferred total size: \" + transfered + \" bytes\"); return true; } @Override public void end() { logger.debug(\"Transferring done.\"); } @Override public void init(int op, String src, String dest, long max) { logger.debug(\"Transferring begin.\"); }} #注:工具类主要封装了三个方法 1.执行脚本命令2.文件上传(为了做进度监控,需要用到监控类FileProgressMonitor.java)3.文件下载 (为了做进度监控,需要用到监控类MyProgressMonitor.java) JschUtil.java工具类的几个使用步骤如下1.初始化连接参数2.调用connect()方法进行连接3.执行相应的方法,如果是脚本命令,先用ShellConfigUtil.java获取相应的脚本,再执行(jsch执行脚本的方式是同步,就是要等到脚本执行结束才返回结果,如果没有返回就一直等着,应该不支持交互式命令,比如连接到某个数据库,然后执行相应的操作)4.最后关闭连接 例子只针对单台服务器的操作,如果需要同时操作多台服务器,可在原有基础上做修改。","tags":[{"name":"Java","slug":"Java","permalink":"http://bee.gs/tags/Java/"},{"name":"Jsch","slug":"Jsch","permalink":"http://bee.gs/tags/Jsch/"},{"name":"教程","slug":"教程","permalink":"http://bee.gs/tags/教程/"}]}]