实践性分布式架构级之个人学习版,无内容&不更新
来源:互联网 发布:sql server 2008 图标 编辑:程序博客网 时间:2024/05/22 14:23
1.使用Dubbo对传统工程进行服务化改造的思路介绍
传统工程的特点:采用的是mvc架构,都是单工程,一般都是把业务逻辑层、dao层以下的改为dubbo服务,由controller层调用业务层,业务层调用数据层。
2.使用Dubbo对传统工程进行服务化改造
改造成Dubbo服务调用方式后的工程结构:Maven父配置、用户服务接口、用户服务实现、服务消费者
部署环境规划:web(consumer)、zookeeper(注册中心)、service(provider)、MySQL(数据库)
如果以往工程不是maven工程,改成maven工程,将jar依赖在pom.xml文件中配置好,依赖jar包名已经版本号跟之前一致即可,但是有可能会jar包冲突,在依赖pom.xml中清除的冲突jar即可,可以打开Dependencies视窗,可以很快速而且清晰定位到冲突的jar,右击excludeDependencies即可,一般都是清除掉低版本jar
工程与工程之间文件的依赖,一般为从上直接依赖下,controller依赖serviceInterface,service依赖dao,而服务之间的依赖使用dubbo配置文件已经配置依赖,同时这个配置文件又是交给spring管理的,controller依赖serviceInterface1、serviceInterface2………..
工具类、公共视图类、公共常量都是放在common模块(而不是父工程,是因为maven工程继承相当于文件复制,能够在一定程度上减少体积)即可
parent—–管理并声明jar包版本号、工程编译版本、子模块管理维护
common—-工具类,公共视图类,公共常量
pojo———-视图类,pojo,po,vo,domain,注意需要实现序列化接口
dao———-数据层,依赖pojo层,common层
serviceInterface——–业务层接口声明,通常要依赖pojo层
service————业务实现层,最终为war包,使用spring管理业务接口与实现类的关系,同时整合dao层(dao层的配合文件放在service,如果为mybatis,dao层的pom.xml文件还需要声明拷贝文件时带上映射配置文件,否则会报映射文件有问题),通常要依赖serviceInterface层、dao层,因为maven工程依赖会有传递性,如果pojo层没有必要直接依赖(可以间接传递)
web————–controller控制层,文件上使用maven依赖serviceInterface模块(层),common层,服务上使用dubbo配置文件,dubbo配置文件交给spring配置管理,依赖已经注册了的服务
3.Zookeeper注册中心安装
架构:
<——-3.notify通知—————-
———2.subscribe订阅—–>Registry注册中心<—–1.register注册者—–
Consumer消费者 —————4.invoke调用/执行——————> Provider提供者<—0.start启动—-Container容器
——–5.count统计调用———->Monitor监控中心<———-5.count统计调用——
节点角色说明:
Provider:暴露服务的服务提供方——service模块工程
Consumer:调用远程服务的服务消费方——web模块工程
Registry: 服务注册于发现的注册中心——–zookeeper(ip,端口,服务接口)
Monitor:统计服务的调用次数和调用时间的监控中心
Container:服务运行容器
调用关系说明:
0.服务容器负责启动,加载,运行服务提供者
1.服务提供者在启动时,向注册中心注册自己提供的服务
2.服务消费者在启动时,向注册中心订阅自己所需的服务
3.注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
4.服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
5.服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
注册中心
可选方案:zookeeper(分布式应用协调的服务)、Redis(高性能的key-value存储以及缓存,常用于解决服务器集群的session共享问题,这里就不重复说这个了,以前写过的)
1.建议使用dubbo-2.3.3以上的版本使用zookeeper注册中心客户端
2.Zookeeper是Apache Hadoop的子项目,强度相对较好,建议生产环境使用该注册中心
3.Dubbo未对Zookeeper服务器端做任何侵入修改,只需安装原生的Zookeeper服务器即可,所有注册中心逻辑适配都在调用Zookeeper客户端时完成
安装Dubbo注册中心
注册中心服务器(192.168.3.71)配置, 安装 Zookeeper:1、 修改操作系统的/etc/hosts 文件中在后面添加:(如果没有权限-->su root,然后输入密码,这里密码不会回显,连*都没有) # vi /etc/hosts 按i键进入插入模式,在已有文件后面添加一下两行内容:ip 组织名称 # zookeeper servers 192.168.3.71 edu-provider-01 按Esc键后跟着输入wq(保存修改/q未编辑状态下退出编辑/q!已编辑状态下退出编辑)2、 到 http://apache.fayea.com/zookeeper/下载 zookeeper-3.4.6: $ wget http://apache.fayea.com/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz 但是有的时候一些其他服务的安装并没有那么简单,我们如何做呢?使用ie浏览器,按上面的正常下载,进入下载文件管理窗口,开始下载后暂停掉下载,右击该文件点击复制下载链接,在linux服务器的本地连接窗口上输入指令: wget `复制的下载链接`,可以简化文件传输以及本地下载时间,如果下载后无法解压,请用命令: file `文件名称`,如果提示中带有html则说明下载文件链接有问题,请用ie浏览器,经过检验有效,哦不,应该是暂时有效3、 解压 zookeeper 安装包: $ tar -zxvf zookeeper-3.4.6.tar.gz4、 在/home/wusc/zookeeper-3.4.6 目录下创建以下目录: $ cd /home/wusc/zookeeper-3.4.6 $ mkdir data $ mkdir logs5、 将 zookeeper-3.4.6/conf 目录下的 zoo_sample.cfg 文件拷贝一份,命名为为zoo.cfg(这里提及一个linux系统下的原则,修改东西之前记得先备份一份,不然总有一天你会后悔没有听我的) $ cp zoo_sample.cfg zoo.cfg6、 修改 zoo.cfg 配置文件: $ vi zoo.cfg # The number of milliseconds of each tick tickTime=2000 # The number of ticks that the initial # synchronization phase can take initLimit=10 # The number of ticks that can pass between # sending a request and getting an acknowledgement syncLimit=5 # the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. dataDir=/home/wusc/zookeeper-3.4.6/data dataLogDir=/home/wusc/zookeeper-3.4.6/logs # the port at which the clients will connect clientPort=2181 #2888,3888 are election port server.1=edu-provider-01:2888:3888 其中, 2888 端口号是 zookeeper 服务之间通信的端口。 3888 是 zookeeper 与其他应用程序通信的端口。 edu-provider-01 是在 hosts 中已映射了 IP 的主机名。 initLimit:这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不 是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。 当已经超过 10 个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没 有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10 秒。 syncLimit:这个配置项标识 Leader 与 Follower 之间发送消息,请求和应答时 间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 2*2000=4秒。 server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器; B 是这个服务 器的 IP 地址或/etc/hosts 文件中映射了 IP 的主机名; C 表示的是这个服务器与 集群中的 Leader 服务器交换信息的端口; D 表示的是万一集群中的 Leader 服务 器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是 用来执行选举时服务器相互通信的端口。如果是伪集群的配置方式,由于 B 都是 一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给它们分配不同 的端口号。7、 在 dataDir=/home/wusc/zookeeper-3.4.6/data 下创建 myid 文件 编辑 myid 文件,并在对应的 IP 的机器上输入对应的编号。如在 zookeeper 上, myid 文件内容就是 1。 如果只在单点上进行安装配置, 那么只有一个 server.1。 $ vi myid 18、 wusc 用户下修改 vi /home/wusc/.bash_profile,就是一个环境变量,方便使用zookeeper的bin目录的环境变量, 增加 zookeeper 配置: # zookeeper env export ZOOKEEPER_HOME=/home/wusc/zookeeper-3.4.6 export PATH=$ZOOKEEPER_HOME/bin:$PATH 使配置文件生效,加载配置文件 $ source /home/wusc/.bash_profile9、 在防火墙中打开要用到的端口 2181、 2888、 3888 切换到 root 用户权限,执行以下命令: # chkconfig iptables on # service iptables start 编辑/etc/sysconfig/iptables # vi /etc/sysconfig/iptables 增加以下 3 行: -A INPUT -m state --state NEW -m tcp -p tcp --dport 2181 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 2888 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 3888 -j ACCEPT 重启防火墙: # service iptables restart 查看防火墙端口状态: # service iptables status Table: filter Chain INPUT (policy ACCEPT) num target prot opt source destination 1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 2 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 3 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 4 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 5 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:2181 6 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:2888 7 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:3888 8 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain FORWARD (policy ACCEPT) num target prot opt source destination 1 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited Chain OUTPUT (policy ACCEPT) num target prot opt source destination10、 启动并测试 zookeeper(要用 wusc 用户启动,不要用 root) : (1) 使用 wusc 用户到/home/wusc/zookeeper-3.4.6/bin 目录中执行: $ zkServer.sh start (2) 输入 jps 命令查看进程: $ jps 1456 QuorumPeerMain 1475 Jps 其中, QuorumPeerMain 是 zookeeper 进程,启动正常 (3) 查看状态: $ zkServer.sh status (4) 查看 zookeeper 服务输出信息: 由于服务信息输出文件在/home/wusc/zookeeper-3.4.6/bin/zookeeper.out $ tail -500f zookeeper.out $ ps -ef|grep java 直接查看进程 tail -f zookeeper.out 监听日志文件11、 停止 zookeeper 进程: $ zkServer.sh stop12、 配置 zookeeper 开机使用 wusc 用户启动: 编辑/etc/rc.local 文件,加入: $ su root 输入密码 # vi /etc/rc.local su - wusc -c '/home/wusc/zookeeper-3.4.6/bin/zkServer.sh start' 切换到指定目录调动指定脚本文件
4.使用Dubbo对传统工程进行服务化改造后的服务调用测试
service模块为war包,启动
web模块为war包,启动
上面的监听指令监听zookeeper
5.使用Dubbo进行规模化前的工程结构优化
一、使用Dubbo是为了实现系统的分布式服务化
二、做成分布式服务架构的项目特点:
1.多个服务
2.多种类型的工程
3.工程间需要相互调用
4.如何实现工程间解耦?(高内聚、低耦合)
5.工程该怎样拆分?
6.如何对大量的工程进行合理有效管理?(持续集成)
使用Dubbo进行规模服务化前的工程结构优化
parent(Maven父配置)
common(公共工程)———utils、baseXXX
common-config(公共配置工程)
common-core(公共core工程)——–dao
common-web(公共web工程)
facade(服务接口)
service(服务实现)
web-consumer(服务消费者)
6.Dubbo管理控制台的安装
1.Dubbo管理控制台的主要作用:服务治理
2.管理控制台主要包含:
路由规则
动态配置
服务降级
访问控制
权重调整
负载均衡等管理功能
3.管理控制台版本
稳定版:dubbo-admin-2.5.3.war
安装Dubbo管理控制台
Dubbo 管控台可以对注册到 zookeeper 注册中心的服务或服务消费者进行管理,但管控台是否正常对Dubbo 服务没有影响,管控台也不需要高可用,因此可以单节点部署。IP部署容器: apache-tomcat-7.0.57端口: 80801、 下载最新版的 Tomcat7: $ wget http://mirrors.hust.edu.cn/apache/tomcat/tomcat-7/v7.0.57/bin/apache-tomcat-7.0.57.tar.gz2、 解压: $ tar -zxvf apache-tomcat-7.0.57.tar.gz $ mv apache-tomcat-7.0.57 dubbo-admin-tomcat3、 移除/home/wusc/dubbo-admin-tomcat/webapps 目录下的所有文件: $ rm -rf *4、 上传 Dubbo 管理控制台程序 dubbo-admin-2.5.3.war到/home/wusc/dubbo-admin-tomcat/webapps5、 解压并把目录命名为 ROOT: $ unzip dubbo-admin-2.5.3.war -d ROOT 把 dubbo-admin-2.5.3.war 移到/home/wusc/tools 目录备份 $ mv dubbo-admin-2.5.3.war /home/wusc/tools6、 配置 dubbo.properties: $ vi ROOT/WEB-INF/dubbo.properties dubbo.registry.address=zookeeper://192.168.3.71:2181 dubbo.admin.root.password=wusc.123 dubbo.admin.guest.password=wusc.123 (以上密码在正式上生产前要修改)7、 防火墙开启 8080 端口, 用 root 用户修改/etc/sysconfig/iptables, # vi /etc/sysconfig/iptables 增加: ## dubbo-admin-tomcat:8080 -A INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT 重启防火墙: # service iptables restart8、 启动 Tomat7 $ /home/wusc/dubbo-admin-tomcat/bin/startup.sh9、 浏览 http://192.168.3.71:8080/10、 配置部署了 Dubbo 管控台的 Tomcat 开机启动: 在虚拟主机中编辑/etc/rc.local 文件,加入: su - wusc -c '/home/wusc/dubbo-admin-tomcat/bin/startup.sh
7.使用maven构建dubbo服务的可执行jar包
Dubbo服务的运行方式:
1.使用Servlet容器运行(Tomcat、Jetty等)——-不可取
缺点:增加复杂性(端口、管理)
浪费资源(内存)
tomcat运行需要端口,dubbo也需要端口,servlet需要配置,内存调优,如果调优不好可能造成内存不足
2.自建Main方法类来运行(Spring容器)——-不建议(本地调试可用)
缺点:Dubbo本身提供的高级特性没用上
自己编写启动类可能会有缺陷
3.使用Dubbo框架提供的Main方法类来运行(Spring容器)—–建议使用
优点:框架本身提供(com.alibaba.dubbo.container.Main)
可实现优雅关机(ShutdownHook)
业务正在运行时另一边申请强行关机,可能造成数据丢失
注意点:
spring-context.xml<import resource="classpath:spring/spring-xxx.xml">
官方文档:
服务容器是一个standalone的启动程序,因为后台不需要Tomcat或JBoss等Web容器的功能,如果硬要用Web容器去加载服务提供方,增加复杂性,也浪费资源
服务容器只是一个简单的Main方法,并加载一个简单的Spring容器,用于暴露服务
服务容器的加载内容可以拓展,内置了spring、jetty、log4j等加载,可通过Containner拓展点进行拓展
Dubbo是通过JDK的ShutdownHook来完成优雅关机的,所以如果用户使用”kill -9 PID”等强制关闭指令,是不会执行优雅关机的,只有通过”kill PID”时,才会执行
原理:
服务提供方
停止时,先标记为不接受新请求,新请求过来时直接报错,让客户端重试其他机器
服务消费方
停止时,不再发起新的调用请求,所有新的调用在客户端及即报错
然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。
设置优雅停机超时时间,缺省超时时间是10秒:(超时则强制关闭)
<dubbo:application ...> <dubbo:parameter key="shutdown.timeout" value="6000" /><!-- 单位毫秒--></dubbo:application>
如果ShutdownHook不能生效,可以自行调用:
ProtocolConfig.destroyAll();
eclipse通过export导出war对于一般工程是可以的,但是分布式项目就并不好
如何通过maven来构建?
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>wusc.edu.common</groupId> <artifactId>edu-common-parent</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../edu-common-parent</relativePath> </parent> <groupId>wusc.edu.service</groupId> <artifactId>edu-service-user</artifactId> <version>${edu-service-user.version}</version> <packaging>jar</packaging> <name>edu-service-user</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <log4j.leve>debug</log4j.leve> <log4j.ale>debug</log4j.ale> </properties> <build> <finalName>edu-service-user</finalName> <!--把resource文件夹下指定规则的文件打包在jar/war包中 --> <resources> <resource> <targetPath>${project.build.directory}/classes</targetPath> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> <!-- 结合com.alibaba.dubbo.container.Main 官方文档中提到Spring Container,自动加载META-INF/spring目录下的所有Spring配置,配置:(配置java命令 -D参数或者dubbo.properties中 dubbo.spring=classpath*:META-INF/spring/*.xml————配置spring配置加载位置) --> <resource> <targetPath>${project.build.directory}/classes/META-INF/spring</targetPath> <directory>src/main/resources/spring</directory> <filtering>true</filtering> <includes> <include>spring-context.xml</include> </includes> </resource> </resources> <!-- maven插件管理 --> <pluginManagement> <plugins> <!-- 解决Maven插件在Eclipse内执行了一系列的生命周期引起冲突 --> <plugin> <groupId>org.eclipse.m2e</groupId> <artifactId>lifecycle-mapping</artifactId> <version>1.0.0</version> <configuration> <lifecycleMappingMetadata> <pluginExecutions> <pluginExecution> <pluginExecutionFilter> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <versionRange>[2.0,)</versionRange> <goals> <goal>copy-dependencies</goal> </goals> </pluginExecutionFilter> <action> <ignore /> </action> </pluginExecution> </pluginExecutions> </lifecycleMappingMetadata> </configuration> </plugin> </plugins> </pluginManagement> <plugins> <!-- 打包jar文件时,配置manifest文件,加入lib包的jar依赖 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <classesDirectory>target/classes/</classesDirectory> <archive> <manifest> <mainClass>com.alibaba.dubbo.container.Main</mainClass> <!-- 打包时 MANIFEST.MF文件不记录的时间戳版本 --> <useUniqueVersions>false</useUniqueVersions> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> </manifest> <manifestEntries> <Class-Path>.</Class-Path> </manifestEntries> </archive> </configuration> </plugin> <!-- 解决依赖问题--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <type>jar</type> <includeTypes>jar</includeTypes> <useUniqueVersions>false</useUniqueVersions> <outputDirectory> ${project.build.directory}/lib </outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>wusc.edu.common</groupId> <artifactId>edu-common</artifactId> <version>${edu-common.version}</version> </dependency> <dependency> <groupId>wusc.edu.common</groupId> <artifactId>edu-common-config</artifactId> <version>${edu-common-config.version}</version> </dependency> <dependency> <groupId>wusc.edu.common</groupId> <artifactId>edu-common-core</artifactId> <version>${edu-common-core.version}</version> </dependency> <dependency> <groupId>wusc.edu.facade</groupId> <artifactId>edu-facade-user</artifactId> <version>${edu-facade-user.version}</version> </dependency> <!-- Common Dependency Begin --> <dependency> <groupId>antlr</groupId> <artifactId>antlr</artifactId> </dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> </dependency> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <classifier>jdk15</classifier> <scope>compile</scope> </dependency> <dependency> <groupId>ognl</groupId> <artifactId>ognl</artifactId> </dependency> <dependency> <groupId>oro</groupId> <artifactId>oro</artifactId> </dependency> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> </dependency> <dependency> <groupId>commons-digester</groupId> <artifactId>commons-digester</artifactId> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </dependency> <dependency> <groupId>commons-validator</groupId> <artifactId>commons-validator</artifactId> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> </dependency> <dependency> <groupId>net.sf.ezmorph</groupId> <artifactId>ezmorph</artifactId> </dependency> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </dependency> <!-- Common Dependency End --> <!-- Spring Dependency Begin --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-instrument</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-instrument-tomcat</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-struts</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc-portlet</artifactId> </dependency> <!-- Spring Dependency End --> <!-- MyBatis Dependency Begin --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> </dependency> <!-- MyBatis Dependency End --> <!-- Struts2 Dependency Begin --> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-json-plugin</artifactId> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-convention-plugin</artifactId> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-spring-plugin</artifactId> </dependency> <dependency> <groupId>org.apache.struts.xwork</groupId> <artifactId>xwork-core</artifactId> </dependency> <!-- Struts2 Dependency End --> <!-- Others Begin --> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>servlet-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>jsp-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> </dependency> <!-- Others End --> <!-- Mysql Driver Begin --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- Mysql Driver End --> <!-- dubbo 需要的jar start --> <dependency> <groupId>org.jboss.netty</groupId> <artifactId>netty</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </dependency> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> </dependency> <!-- dubbo 需要的jar end --> </dependencies></project>
需要先安装到本地私有库,maven install ,构建时各个模块之间相互依赖,所以使用parent进行构建,达到构建其他的效果
8.在linux操作系统上手工部署dubbo服务
1.环境变量配置
Java环境变量----只需要JREvi /etc/profile## java envexport JAVA_HOME=/usr/local/java/jdk1.7.0_72export JRE_HOME=$JAVA_HOME/jreexport CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/li/tools.jar:$JRE_HOME/lib/rt.jarexport PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/binsource /etc/profile
2.部署目录规范
libservice-xxx.shxxx-service-xxx.jar要避免应用迁移时的路径冲突
3.手工维护Dubbo服务
java -jar edu -serrvice-xx.jar&kill PIDkill -9 PID
4.自定义Dubbo服务维护的Shell脚本
脚本命名规范:/home/wusc/edu/service/xxx/service-xxx.sh例如:/home/wusc/edu/service/user/service-user.sh效果:cd /home/wusc/edu/service/user. /service-user.sh start. /service-user.sh stop. /service-user.sh restart
service-user.sh
#!/bin/sh 头定义## java env 配置java环境变量export JAVA_HOME=/usr/local/java/jdk1.7.0_72export JRE_HOME=$JAVA_HOME/jre## service name 定义服务名称,脚本通用APP_NAME=user 服务名称定义为规范目录规范SERVICE_DIR=/home/wusc/edu/service/$APP_NAME 服务存放的目录SERVICE_NAME=edu-service-$APP_NAME 服务的名称,使用占位符进行声明JAR_NAME=$SERVICE_NAME\.jar PID=$SERVICE_NAME\.pidcd $SERVICE_DIR 进入具体服务的目录case "$1" in 需要接受一个参数 start) start则为启动 nohup $JRE_HOME/bin/java -Xms256m -Xmx512m -jar $JAR_NAME >/dev/null 2>&1 & 设置为守护进程,内存调优 echo $! > $SERVICE_DIR/$PID echo "=== start $SERVICE_NAME" ;; stop) stop则为停止 kill `cat $SERVICE_DIR/$PID` rm -rf $SERVICE_DIR/$PID echo "=== stop $SERVICE_NAME" sleep 5 ## ## edu-service-aa.jar ## edu-service-aa-bb.jar P_ID=`ps -ef | grep -w "$SERVICE_NAME" | grep -v "grep" | awk '{print $2}'` 取出进程id if [ "$P_ID" == "" ]; then 如果进程id为空 echo "=== $SERVICE_NAME process not exists or stop success" 输出服务已经关闭 else 干掉进程 echo "=== $SERVICE_NAME process pid is:$P_ID" echo "=== begin kill $SERVICE_NAME process, pid is:$P_ID" kill -9 $P_ID fi ;; restart) $0 stop sleep 2 $0 start echo "=== restart $SERVICE_NAME" ;; *) 相当于其他 ## restart $0 stop sleep 2 $0 start ;;esacexit 0
log4j.properties
#开发日志将在本地输出,并输出SQLlog4j.rootLogger=INFO,A1,DRFlog4j.appender.A1=org.apache.log4j.ConsoleAppenderlog4j.appender.A1.layout=org.apache.log4j.PatternLayout# log4j.appender.A1.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%nlog4j.appender.A1.layout.ConversionPattern=%d %5p [%F:%L] : %m%nlog4j.appender.DRF=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.DRF.Threshold=INFOlog4j.appender.DRF.DatePattern='.'yyyy-MM-ddlog4j.appender.DRF.File=logs/edu-service-user.loglog4j.appender.DRF.Append=truelog4j.appender.DRF.layout=org.apache.log4j.PatternLayoutlog4j.appender.DRF.layout.ConversionPattern=[%-5p][%d{yyyyMMdd HH:mm:ss,SSS}][%C{1}:%L] %m%n###输出SQL log4j.logger.com.ibatis=DEBUGlog4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUGlog4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUGlog4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUGlog4j.logger.java.sql.Connection=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.PreparedStatement=DEBUG
5.服务维护注意事项
(1) 脚本规范---------尽可能通用(2) 守护进程(3) 内存调优设置-----按需调整(4) 日志处理---------只保留应用Log4j输出的日志(5) 避免服务冲突 ----进程名、端口(6) 避免进程误杀-----全匹配脚本配置灵活高深,满足需求则可可参考官方脚本样例
9.构建Dubbo服务消费者web应用的war包并在tomcat中部署
构建Dubbo服务消费者Web应用的war包:
1.打包类型:war
2.包含的配置文件
3.依赖到的jar包(相关工程要先构建)
4.构建war包
Tomcat中部署服务消费者Web应用的war包
1.环境变量配置(Java环境变量全平台统一)
2.部署目录规范
/home/wusc/edu/web/xxx-tomcat
3.Tomcat内存设置
4.设置Tomcat开机启动
IP部署容器: apache-tomcat-7.0.57端口: 8080应用: edu-web-boss.war1、 下载(或上传)Tomcat7: $ wget http://mirrors.hust.edu.cn/apache/tomcat/tomcat-7/v7.0.57/bin/apache-tomcat-7.0.57.tar.gz2、 规范安装目录: /home/wusc/edu/web/xxx-tomcat 如: /home/wusc/edu/web/boss-tomcat3、 解压安装 $ mkdir –p /home/wusc/edu/web $ tar -zxvf apache-tomcat-7.0.57.tar.gz $ mv apache-tomcat-7.0.57 /home/wusc/edu/web/boss-tomcat4、 移除/home/wusc/edu/web/boss-tomcat/webapps 目录下的所有文件: $ rm -rf /home/wusc/edu/web/boss-tomcat/webapps/*5、 上传 Dubbo 服务消费者 Web 应用 war 包 edu-web-boss.war到/home/wusc/edu/web/boss-tomcat/webapps6、 防火墙开启 8080 端口, 用 root 用户修改/etc/sysconfig/iptables, # vi /etc/sysconfig/iptables 增加: ## boss-tomcat:8080 -A INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT 重启防火墙: # service iptables restart7、 Tomcat 内存设置: $ vi /home/wusc/edu/web/boss-tomcat/bin/catalina.sh #!/bin/sh JAVA_OPTS='-Xms128m -Xmx512m -XX:PermSize=128m'8、 启动 Tomat7 $ /home/wusc/edu/web/boss-tomcat/bin/startup.sh9、 浏览 http://192.168.2.61:8080/edu-web-boss10、 配置 Tomcat 开机启动: 在虚拟主机中编辑/etc/rc.local 文件,加入: su - wusc -c '/home/wusc/edu/web/boss-tomcat/bin/startup.sh'
10.dubbo监控中心的介绍与简易监控中心的安装
1.监控中心(Monitor)的作用
Consumer————->Monitor<————Provider
监控中心负责统计个服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
为服务的监控运维采集数据
2.监控中心是可选的(非必需)
1.Monitor可以不安装
2.Monitro挂掉不会影响到Consumer和Provider之间的调用,只是丢失部分采样数据
3.监控中心可自定义拓展开发
1.个性化运维监控的需求
1.服务的健康状况
2.服务的压力和性能状况
3.告警通知以便及时处理等
2.拓展接口
com.alibaba.dubbo.monitor.MonitorFactory
com.alibaba.dubbo.monitor.Monitor
4.简易监控中心的安装
监控中心也是一个标准的Dubbo服务
配置好了之后可以结合admin管理后台使用,可以清晰的看到服务的访问记录、成功次数、失败次数等
dubbo-monitor-simple-2.5.3-assembly.tar.gz
dubbo-monitor-simple-2.5.3-sources.jar
1、 Dubbo 服务提供者和服务消费者中的 spring 配置文件中增加以下配置:<!-- 监控中心配置 --><!-- 监控中心协议,如果为protocol="registry",表示从注册中心发现监控中心地址,否则直连监控中心 --><!-- 直连监控中心服务器地址,如: address="192.168.3.71:7070" --><dubbo:monitor protocol="registry"/>配置截图如下:添加完以上配置后,重新构建部署 Dubbo 服务和服务消费者应用。2、 Dubbo 简易监控中心的配置解释(不需要修改,使用默认配置)操作系统用户为 wusc, 系统用户目录为/home/wuscdubbo-monitor 的安装目录为: /home/wusc/dubbo-monitordubbo-monitor 的配置文件为: /home/wusc/dubbo-monitor/conf/dubbo.propertiesdubbo-monitor 的配置文件内容如下:dubbo.container=log4j,spring,registry,jettydubbo.application.name=simple-monitordubbo.application.owner=dubbo.registry.address=zookeeper://192.168.3.71:2181dubbo.protocol.port=7070dubbo.jetty.port=8090dubbo.jetty.directory=${user.home}/monitordubbo.charts.directory=${dubbo.jetty.directory}/chartsdubbo.statistics.directory=${user.home}/monitor/statisticsdubbo.log4j.file=logs/dubbo-monitor-simple.logdubbo.log4j.level=WARN看上面配置文件中标红的的 3 行内容, 理解${user.home}这个变量的意思则可, ${user.home}指的就是启动 dubbo-monitor 程序的操作系统用户目录。我们这里用的是 wusc 用户,那么就是/home/wusc 目录(如果是 root 用户启动,那就是/root)。此时, 配置中 3 个目录的绝对路径为:dubbo.jetty.directory=/home/wusc/monitordubbo.charts.directory=/home/wusc/monitor/chartsdubbo.statistics.directory=/home/wusc/monitor/statistics3、 重新启动 dubbo-monitor 简易监控中心, 此时就会看到以上 3 个目录会被创新, 并在目录里面有相应的服务接口调用的报表数据。
11.持续集成-搭建敏捷高效的持续集成管理平台
1.持续集成介绍
持续集成是一种软件开发实践
团队开发成员经常集成他们的工作,每次集成都通过自动化的构建(包括自动化编译、测试、发布)来验证,从而尽快地发现集成错误
持续集成的价值、要素、原则…
持续集成的好处的理解
管理大规模的工程的必要性(切合分布式多工程的特性)
解放应用更新部署的生产力
能让团队成员间进行高效的协作开发
能与各种项目管理工具一起整合成一套实用而且灵活项目开发管理平台
实时交付可用、可测试的软件产品
……
2.持续集成管理平台的组成
持续集成管理平台不只是CI服务器,是一系列软件管理工具的组合
源码版本管理:Subversion、Git
项目构建工具:Maven、Ant
代码质量管理:Sonar(Checkstyle、PMD、FindBugs…..)
持续集成引擎:Hudson、Jenkins、Apache Continuum
应用持续部署:操作系统、JDK、Tomcat、JBoss
实际持续集成过程中要到的其他各种工具、各种插件……
3.持续集成实践介绍
持续集成管理平台
Hudson+Maven+Sonar+Subersion
4.持续集成篇内容
1.Subversion源码版本控制系统的安装
Subversion+Apache+jsvnadmin
2.Maven私有库和本地库的安装与配置
Sonatype Nexus +Maven
3.Sonar代码质量管控平台的安装与使用配置
4.Hudsonn持续集成引擎的安装与配置
12.持续集成-svn版本管理系统的安装
不只是单纯的使用Subversion进行版本控制,而是要使得版本控制能灵活方便点的进行管理
方案:Centos+Subversion+Apache+Jsvnadmin
CI 服务器 root 用户操作建议安装前更新操作系统# yum update更新完成后重启# reboot安装 apache# yum install httpd httpd-devel# service httpd start# chkconfig httpd on# vi /etc/httpd/conf/httpd.conf找到 ServerName 并修改成ServerName localhost:80防火墙中打开 80 端口:# vi /etc/sysconfig/iptables-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT# service iptables restarthttp://192.168.4.221/安装 SVN 服务# yum install mod_dav_svn subversion必须安装 mod_dav_svn 模块安装完 svn 后要重启 apache# service httpd restart查看测试是否安装 svn 模块# ls /etc/httpd/modules/ | grep svnmod_authz_svn.somod_dav_svn.so# svn --version创建 svn 库主目录(多库模式, 一份配置文件管理多个库)# mkdir /svn/# cd /etc/httpd/conf.d# ls此时可以看到一个 subversion.conf 配置文件(是在安装 mod_dav_svn 模块时生成的)# vi subversion.conf添加以下内容#Include /svn/httpd.conf<Location /svn/>DAV svnSVNListParentPath onSVNParentPath /svnAuthType BasicAuthName "Subversion repositories"AuthUserFile /svn/passwd.httpAuthzSVNAccessFile /svn/authzRequire valid-user</Location>RedirectMatch ^(/svn)$ $1/创建/svn/passwd.http 和/svn/authz# touch /svn/passwd.http# touch /svn/authz重启 apache# service httpd restart安装 jsvnadminsvnadmin 介绍(在 Google Code 上,需要 FQ 才能下载。 )https://code.google.com/p/jsvnadmin/https://jsvnadmin.googlecode.com/files/svnadmin-3.0.5.zip安装 MySQL(单独安装, 与业务系统的数据库分开)# rpm -qa | grep mysql ## 查看该操作系统上是否已经安装了 mysql 数据库,有的话,可以通过 rpm -e 命令 或者 rpm -e --nodeps 命令来卸载掉# yum install mysql-server mysql mysql-devel# service mysqld start# chkconfig --list | grep mysqldmysqld 0:off 1:off 2:off 3:off 4:off 5:off 6:off用上面的命令查看到 MySQL 并没有设置开机启动,所以需要设置开机启动# chkconfig mysqld on为了方便远程管理, 防火墙中打开 3306 端口# vi /etc/sysconfig/iptables-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT重启防火墙, 使端口配置生效# service iptables restart设置 MySQL 数据库 root 用户的密码:# mysqladmin -u root password 'wusc.123'登录数据库:# mysql -u root -pMySQL 授权远程访问(先用 root 登录 mysql)mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'wusc.321' WITHGRANT OPTION;mysql> FLUSH PRIVILEGES;使用 Tomcat7 部署 svnadmin# cd /root使用 wget 下载最新版的 tomcat7 的 tar.gz 包# tar -zxvf apache-tomcat-7.0.xx.tar.gz# mv apache-tomcat-7.0.xx svnadmin-tomcat修改 Tomcat 的端口为 9000 和容器编码为 UTF-8# vi /root/svnadmin-tomcat/conf/server.xml以下内容请根据实际修改部分关键内容<Server port="9005" shutdown="SHUTDOWN"><Connector port="9000" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" URIEncoding="UTF-8" />防火墙中打开 9000 端口# vi /etc/sysconfig/iptables-A INPUT -m state --state NEW -m tcp -p tcp --dport 9000 -j ACCEPT重启防火墙, 使端口配置生效# service iptables restart# cd /root/svnadmin-tomcat/webapps# rm -rf *上传 svnadmin.war 到/root/svnadmin-tomcat/webapps 目录# cd /root/svnadmin-tomcat/webapps解压# unzip svnadmin.war -d svnadmin备份# mv svnadmin.war /root/tools/# cd svnadmin/WEB-INF# vi jdbc.properties内容改为如下db=MySQL#MySQLMySQL.jdbc.driver=com.mysql.jdbc.DriverMySQL.jdbc.url=jdbc:mysql://127.0.0.1:3306/svnadmin?characterEncoding=utf-8MySQL.jdbc.username=rootMySQL.jdbc.password=wusc.123创建 svnadmin 数据库并导入相应数据(UTF-8 编码)执行 db/mysql5.sql 和 db/lang/en.sql启动 svnadmin-tomcat# /root/svnadmin-tomcat/bin/startup.sh浏览器中打开: http://192.168.4.221:9000/svnadmin/如: 输入 admin, wusc.123创建库此时:/svn/目录下会创建一个 wusc_edu 的 SVN 库目录。同 时 也 会 多 出 一 个 httpd.conf 文 件 , 内 容 与 subversion.conf 一 样 , 在/etc/httpd/conf.d/subversion.conf 中已配置。/svn/authz 授权文件中会多出如下内容:[aliases][groups]wusc_edu_developer=wusc_edu_manager=wusc_edu_tester=[wusc_edu:/]@wusc_edu_manager=rw以上过程其实是调用了 svn 对应的命令做了相应的操作配置库目录权限# cd /svn# chown -R apache.apache wusc_edu# chmod -R 777 wusc_edu(如创建新库,需要对新建的库目录执行以上两步授权操作)关闭 SELinux(Linux 的访问控制)修改/etc/selinux/config 文件# vi /etc/selinux/config将 SELINUX=enforcing 改为 SELINUX=disabled重启机器即可# rebootSVN 版本管理平台(Subversion+Apache+Jsvnadmin) 的使用:先安装 SVN 管理客户端 TortoiseSVN, 方便对 SVN 库的操作和管理http://tortoisesvn.net/downloads.html(注意 64 位系统要安装 64 位版)接下来可以对 wusc_edu 库进行相应的操作(1) 用户组(2) 用户(3) 授权(4) 导入项目
13.持续集成-svn版本管理系统的使用
14.持续集成-maven私有库和本地库的安装与配置
15.持续集成-sonarqube代码质量管理平台的介绍和安装
16.持续集成-sonarqube代码质量管理平台的配置与使用
17.持续集成-hudson持续集成服务器的安装与配置
18.持续集成-hudson持续集成服务器的使用(自动化编译、分析、打包、部署)
19.基于dubbo的分布式系统架构介绍(以第三方支付系统为例)
20.消息中间件在分布式系统中的作用介绍
21.activemq的安装与使用
22.redis的安装与使用
23.fastdfs分布式文件系统安装
24.fastdfs分布式文件系统使用
25.简易版支付系统介绍
26.dubbo服务集群
27.dubbo分布式服务子系统的划分
28.dubbo服务接口的设计原则
29.dubbo服务启动依赖检查
30.dubbo负载均衡检查
31.dubbo线程模型(结合linux线程数量限制配置的实战经验分享)
32.dubbo线程直连提供者
33.dubbo服务只订阅
34.dubbo服务只注册
35.zookeeper集群的安装、配置、高可用测试
36.zookeeper集群的升级、迁移
37.activemq高可用集群(zookeeper+leveldb)安装、配置、高可用测试
38.ActiveMQ高可用+负载均衡的安装、配置、高可用测试
39.activeMQ+负载均衡集群的安装、配置(伪集群)
40.redis集群的安装
41.redis集群的高可用测试
42.redis集群订单扩展测试
43.keepalived+nginx实现高可用web负载均衡
44.fastdfs集群的安装
45.fastdfs集群的配置
46.fastdfs集群的使用(结合简易版支付系统)
47.使用redis集群实现tomcat集群的session共享
48.MySQL源码编译安装
49.MySQL主从复制的配置
50.MyCat在MySQL主从复制基础上实现读写分离
51.MyCat集群部署
52.MyCat高可用负载均衡集群实现
- 实践性分布式架构级之个人学习版,无内容&不更新
- 分布式事务架构实践
- 个人首页不更新
- jeesz分布式架构之 mycat实现mysql读写分离实践
- 分布式架构学习之:000--介绍Dubbo
- 分布式架构学习之:018--Dubbo集群
- 分布式架构学习之:019--分布式服务子系统的划分
- Windows平台分布式架构实践
- 深入浅出ASP.Net 2.0 Ajax学习之旅随笔(三)-更新其他UpdatePanel控件的内容而不更新本身的UpdatePanel控件的内容
- jquery无刷新更新内容
- 架构师之路:Dubbo的分布式系统架构学习
- FPGA个人学习实践
- sonar之个人实践
- 不定时更新-android学习之路
- hadoop-2.7.3学习之分布式集群安装实践
- 分布式架构 -- 学习路线图
- AJAX 学习实践之 表单无刷新提交
- 分布式系统之架构
- python 类(下)
- spring bean循环引用问题
- 51nod 1717 好数
- Java:Redis安装(Windows),以及Spring MVC与Redis整合
- MyEclipse运行项目的内存溢出问题解决方案
- 实践性分布式架构级之个人学习版,无内容&不更新
- android 帧动画,补间动画,属性动画区别
- 20个经典面试题,全部答对月薪10k+
- 三.python threading多线程总结
- 集合框架-7.3
- FMDB 的简单使用
- bzoj2555 -- 后缀自动机+LCT
- mysql5.7跳过错误事务GTID
- 【吐槽】VS2012的安装项目只能用InstallShield Limited Edition activex控件的问题