简

人生短暂,学海无边,而大道至简。


  • 首页

  • 归档

  • 分类

  • 标签

spring ioc使用解释

发表于 2017-05-09 | 分类于 spring

控制反转( Inversion of Control )
IoC 主要的作用:实例化对象的工作,通过IoC转交给 Spring 负责管理。解除了对象管理和序员之间的耦

创建对象的三种方式

通过构造方法创建

  • 无参构造创建(默认)
  • 有参构造创建(需要明确配置),需要在类中提供有参构造方法。
    在 applicationContext.xml 中设置调用哪个构造方法创建对象;如果设定的条件匹配多个构造方法执行最后的构造方法。
    1
    2
    3
    index : 参数的索引,从 0 开始,
    name: 参数名
    type:类型(区分开关键字和封装类 int 和 Integer)
    1
    2
    3
    4
    5
    <!-- id表示获取到对象的标识,class标识创建哪个对象 -->
    <bean id="student" class="com.hu.pojo.Student">
    <constructor-arg index="0" name="id" type="int" value="12"></constructor-arg>
    <constructor-arg index="1" name="name" type="java.lang.String" value="hu xiao ming"></constructor-arg>
    </bean>

    实例工厂

    需要先创建工厂,再产生对象。 实现步骤:

    先创建实例工厂

    1
    2
    3
    4
    5
     public class StudentFactory {
    public Student newInstance() {
    return new Student(1, "hu xiao xin");
    }
    }

    在 applicationContext.xml 中配置工厂对象和需要创建的对象

    1
    2
    3
    <bean id="factory" class="com.hu.factory.StudentFactory"></bean>
    <!--交给工厂来创建-->
    <bean id="stu" factory-bean="factory" factory-method="newInstance"></bean>
    测试:
    1
    2
    3
    4
    5
    6
    public static void main(String[] args) {
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = ac.getBean("stu",Student.class);
    System.out.println(student);
    System.out.println(ac.getBeanDefinitionNames());
    }

    静态工厂

    不需要创建工厂对象,步骤:
    1
    2
    3
    4
    5
     public class StudentFactory {
    public static Student newInstance() {
    return new Student(1, "hu xiao xin");
    }
    }
    配置:
    1
    <bean id="stu" class="com.hu.factory.StudentFactory" factory-method="newInstance"/>
    输入结果和上面一致,没有任何问题。

    通过注入方式给Bean赋值

    通过构造方法

    1
    2
    3
    4
    <bean id="student" class="com.hu.pojo.Student">
    <constructor-arg index="0" name="id" type="int" value="12"></constructor-arg>
    <constructor-arg index="1" name="name" type="java.lang.String" value="hu xiao ming"></constructor-arg>
    </bean>

    set方法:

    基本数据类型或String,直接赋值。比如以下两种都是等效的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
           <bean id="student" class="com.hu.pojo.Student">
    <property name="id" value="12"></property>
    <property name="name" value="hu xiao ming"></property>
    </bean>
    或:
    <bean id="student2" class="com.hu.pojo.Student">
    <property name="id"><value>12</value></property>
    <property name="name"><value>hu xiao ming</value></property>
    </bean>

    如果是Set或者List

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!-- 此时没有set的构造方法,是通过属性set方法设值 -->
    <bean id="student3" class="com.hu.pojo.Student">
    <property name="id"><value>12</value></property>
    <property name="name"><value>hu xiao ming</value></property>
    <property name="set">
    <set><!-- 若是List,则用<list> -->
    <value>111</value><value>232</value><value>534</value>
    </set>
    </property>
    <!-- 如果集合只有一个值,可以直接如下这样写 -->
    <!-- <property name="set" value="312321"></property> -->
    </bean>
    如果是数组:和集合类似吧改为

Map

1
2
3
4
5
6
7
<bean id="student3" class="com.hu.pojo.Student">
<property name="map">
<map>
<entry key="a" value="b"></entry>
</map>
</property>
</bean>

如果是Properties类型

1
2
3
4
5
6
7
8
<bean id="student3" class="com.hu.pojo.Student">
<property name="demo">
<props>
<prop key="key">value</prop>
<prop key="key1">value1</prop>
</props>
</property>
</bean>

Scope属性

scope 可取值

  • singleton 默认值,单例
  • prototype 多例,每次获取重新实例化
  • request 每次请求重新实例化,web中
  • session 每个会话对象内,对象是单例的.
  • application 在 application 对象内是单例
  • global session spring 推出的一个对象,依赖于spring-webmvc-portlet.jar,类似于 session

自动注入

1、在 Spring 配置文件中对象名和 ref=”id”id 名相同使用自动注入,可以不配置<property/>

2、两种配置办法

局部配置:在<bean>中通过 autowire="" 配置,只对这个\<bean>生效        

全局配置:在<beans>中通过 default-autowire=""配置,表当当前文件中所有<bean>都是全局配置内容

3、autowire取值类型

1
2
3
4
5
default:默认值,根据全局 default-autowire=""值,默认全局和局部都没有配置情况下,相当于 no
no:不自动注入
byName:通过名称自动注入,在 Spring 容器中找类的 Id
byType:根据类型注入(spring 容器中不可以出现两个相同类型的<bean>)
constructor:根据构造方法注入(提供对应参数的构造方法)底层使用 byName,构造方法参数名和其他<bean>的 id相同

加载配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- 加载properties文件  如果加载多个文件,后面用逗号分隔开 -->
<context:property-placeholder location="classpath:db.properties"/>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- org.mybatis.spring.SqlSessionFactoryBean封装了mybatis的操作,为某个包取别名 -->
<property name="typeAliasesPackage" value="com.hu.pojo"></property>
</bean>

<!-- 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 要扫描 mapper接口 -->
<property name="basePackage" value="com.hu.mapper"></property>
<!-- <property name="sqlSessionFactory" ref="factory"></property> -->
<!-- 此时sqlSessionFactory不适用 自动注入只会影响ref=""的情况,不会影响value
factory的id不能为sqlSessionFactory,会自动注入到MapperScannerConfigurer
这里sqlSessionFactory属性不适用,应当用sqlSessionFactoryBeanName
是因为:配置了class="org.mybatis.spring.mapper.MapperScannerConfigurer"
而这句代码会在数据源加载前就执行了,之后就把表达式${jdbc.driverClass}当成字符串执行 -->
<property name="sqlSessionFactoryBeanName" value="factory"/>
</bean>

如果,properties创建值:

1
2
student.name=xiaoming
student.age=22

加载文件:

1
2
<!-- 加载properties文件  如果加载多个文件,后面用逗号分隔开 -->
<context:property-placeholder location="classpath:db.properties,classpath:student.properties"/>

类里边使用properties的值(相当于给属性设置了一个初始值):

1
2
3
4
5
6
7
8
9
@Component
public class Student {
private int id;
@Value("${student.name}")
private String name;
@Value("${student.age}")
private int age;
//......
}

磁盘和内存读写简单原理

发表于 2017-04-23 | 分类于 计算机体系

当程序要读取的数据时传入内存地址(行地址+列地址),如果数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,通过柱面号,磁头号,扇区号定位磁盘位置,找到数据的起始位置并向后连续读取一页或几页载入内存中。

我们在程序中的数据处理主要是操作磁盘和内存,硬盘是磁盘里面最常用的一种.以前有所谓的软盘,也是磁盘的一种,原理和硬盘是一样的.都是利用磁性物质的特性来保存信息.磁盘的原理就是利用电磁转换,学物理时我们知道电可以使物质带上磁性,而金属在磁场运动时切割磁感线时会产生电流.磁盘上有很多微粒的磁粉.当写通过磁头写数据时,磁头中的电流会导致磁粉极化,改变方向.读数据时,导体磁头经过磁粒的区域时会产生电流. 反正大概意思就是这样.

硬盘的存储数据原理

我们知道信息存储在硬盘里,把它拆开也看不见里面有任何东西,只有些盘片。假设,你用显微镜把盘片放大,会看见盘片表面凹凸不平,凸起的地方被磁化,凹的地方是没有被磁化;凸起的地方代表数字1(磁化为1),凹的地方代表数字0。因此硬盘可以以二进制来存储表示文字、图片等信息。

内存存储数据原理

而内存的原理就完全不同,内存是晶体管制作的(CPU也是晶体管做的),而晶体管的特性就是我们平时常说的用开关的开和关来表示1,0.
通过一些门电路的组合可用来表示数字和实现复杂的逻辑功能.而内存主要是用来临时保存数据.CPU就是处理一些逻辑关系.

晶体管由于必须得通电,然后用电流的有无状态来表示信息,充放电后电荷的多少(电势高低)分别对应二进制数据0和1,所以只有通电的时候可以保存数据,电一断内存里的晶体管状态就处未知状态就啥用处也没了.而磁盘断电后磁性物质还会一直保持原样.

我们知道访问磁盘时就通过磁头去指到固定的地方然后读取数据.不过内存就不一样,不需要啥磁头去读取数据,它是有数据总线连接,我们是通过总线去读取内存的数据.

磁盘的读写原理:(柱面号,磁头号,扇区号)

确定磁盘地址(柱面号,磁头号,扇区号),内存地址(源/目):

当需要从磁盘读取数据时,系统会将数据逻辑地址传给磁盘,磁盘的控制电路按照寻址逻辑将逻辑地址翻译成物理地址,即确定要读的数据在哪个磁道,哪个扇区。

为了读取这个扇区的数据,需要将磁头放到这个扇区上方,为了实现这一点:

1)首先必须找到柱面,即磁头需要移动对准相应磁道,这个过程叫做寻道,所耗费时间叫做寻道时间,
2)然后目标扇区旋转到磁头下,即磁盘旋转将目标扇区旋转到磁头下。这个过程耗费的时间叫做旋转时间。

即一次访盘请求(读/写)完成过程由三个动作组成:

1)寻道(时间):磁头移动定位到指定磁道
2)旋转延迟(时间):等待指定扇区从磁头下旋转经过
3)数据传输(时间):数据在磁盘与内存之间的实际传输

内存的读写原理:(行地址+列地址)

内存地址

内存中的cell按矩阵形排列,每一行和每一列都会有一个对应的行地址线路(正规叫法叫做word line)和列地址线路(正规叫法是bit line),每个具体的cell就挂接在这样的行地址线路和列地址线路上,对应一个唯一的行号和列号,把行号和列号组合在一起,就是内存的地址。

上图是Thaiphoon Burner的一个SPD dump,每个地址是一个字节。不过我们可以把这些数据假设成只有一个bit,当成是一个简单的内存地址表,左边竖着的是行地址,上方横着的是列地址。例如我们要找第七行、倒数第二列(地址为7E)的数据,它就只有一个对应的值:FD。当然了,在内存的cell中,它只能是0或者1。

寻址

数据要写入内存的一个cell,或者从内存中的一个cell读取数据,首先要完成对这个cell的寻址。寻址的过程,首先是将需要操作的cell的对应行地址信号和列地址信号输入行/列地址缓冲器,然后先通过行解码器(Row Decoder)选择特定的行地址线路,以激活特定的行地址。每一条行地址线路会与多条列地址线路和cell相连接,为了侦测列地址线路上微弱的激活信号,还需要一个额外的感应放大器(Sense Amplifier)放大这个信号。当行激活之后,列地址缓冲器中的列地址信号通过列解码器(Column Decoder)确定列地址,并被对应的感应放大器通过连接IO线路,这样cell就被激活,并可供读写操作,寻址完成。从行地址激活,到找到列地址这段时间,就是tRCD。

从内存读取到磁盘预读

由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:

当一个数据被用到时,其附近的数据也通常会马上被使用。
程序运行期间所需要的数据通常比较集中。
由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。

预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。

nexus私服搭建

发表于 2017-04-21 | 分类于 maven

介绍

私服是架设在局域网的一种特殊的远程仓库,目的是代理远程仓库及部署第三方构件。有了私服之后,当 Maven 需要下载构件时,直接请求私服,私服上存在则下载到本地仓库;否则,私服请求外部的远程仓库,将构件下载到私服,再提供给本地仓库下载。

20200421153417

我们可以使用专门的 Maven 仓库管理软件来搭建私服,比如:Apache Archiva,Artifactory,Sonatype Nexus。这里我们使用 Sonatype Nexus。

1、下载

nexus下载地址:http://www.sonatype.org/
开源版 Nexus OSS
解压后会在同级目录中,出现两个文件夹:nexus-oss-webapp-1.8.0和sonatype-work
前者包含了nexus的运行环境和应用程序,后者包含了工作目录,你自己的配置和仓库。

2、安装到Linux

前提是安装了jdk
创建目录:mkdir /usr/local/nexus
拷贝到Linux服务器,解压到指定目录:tar -zxvf nexus-latest-bundle.tar.gz -C /usr/local/nexus

修改配置

nexus-2.14.11-01/conf/nexus.properties
端口默认已经是8081不做更改,${bundleBasedir}是指带版本号那个目录

nexus启动后需要访问Linux文件系统,需要足够的权限,在nexus启动脚本中,可以指定私服应用的访问用户,${bundleBasedir}/bin/nexus脚本文件中修改如下:
在#RUN_AS_USER=这里增加一行:RUN_AS_USER=root   代表nexus使用root用户权限。

开放端口:vim /etc/sysconfig/iptables    增加8081端口,再重启:service iptables restart

启动并测试:
[root@localhost bin]# ./nexus start
****************************************
WARNING - NOT RECOMMENDED TO RUN AS ROOT
****************************************
Starting Nexus OSS...
Removed stale pid file: /usr/local/nexus/nexus-2.14.11-01/bin/../bin/jsw/linux-x86-64/nexus.pid
Started Nexus OSS.
[root@localhost bin]# ./nexus status
****************************************
WARNING - NOT RECOMMENDED TO RUN AS ROOT
****************************************
Nexus OSS is running (2380).

浏览器测试:

20200421153632

3、配置、登录

默认用户名密码为:admin/admin123 点击左侧 Repositories 链接,查看 Nexus 内置的仓库:

20200421153705

Nexus 的仓库分类:

hosted 宿主仓库:主要用于部署无法从公共仓库获取的构件(如 oracle 的 JDBC 驱动)以及自己或第三方的项目构件;
3rd Party: 顾名思义, 第三方库, 你可能会问不是有中央仓库来管理第三方库嘛,没错, 这里的是指可以让你添加自己的第三方库, 比如有些构件在中央仓库是不存在的. 比如你在中央仓库找不到Oracle 的JDBC驱动, 这个时候我们就需要自己添加到3rdparty仓库。
Releases:这里存放我们自己项目中发布的构建, 通常是Release版本的, 比如我们自己做了一个FTP Server的项目, 生成的构件为ftpserver.war, 我们就可以把这个构建发布到Nexus的Releases本地仓库. 关于符合发布后面会有介绍.
Snapshots:这个仓库非常的有用, 它的目的是让我们可以发布那些非release版本, 非稳定版本, 比如我们在trunk下开发一个项目,在正式release之前你可能需要临时发布一个版本给你的同伴使用, 因为你的同伴正在依赖你的模块开发, 那么这个时候我们就可以发布Snapshot版本到这个仓库, 你的同伴就可以通过简单的命令来获取和使用这个临时版本.
proxy 代理仓库:代理公共的远程仓库;
virtual 虚拟仓库:用于适配 Maven 1;
group 仓库组:Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。

20200421153732

一般主要使用hosted和proxy

代理仓库配置信息:

20200421153755

当下载完之后,仓库文件结构就会出来

20200421153819

4、利用搭建的私服,进行上传和下载jar包

1)、下载:

a、配置认证:

setting中配置如下:

1
2
3
4
5
6
7
8
9
10
    <server>
<id>nexus-releases</id>
<username>deployment</username>
<password>deployment123</password>
</server>
<server>
<id>nexus-snapshots</id>
<username>deployment</username>
<password>deployment123</password>
</server>

b、配置仓库信息:

如果就一个项目配置在pom.xml如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 下载 -->
<distributionManagement>
<repository>
<id>nexus-releases</id>
<name>Nexus Release Repository</name>
<url>http://192.168.18.134:8081/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>nexus-snapshots</id>
<name>Nexus Snapshots Repository</name>
<url>http://192.168.18.134:8081/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>

多个项目时候 可以统一配置父项目或者maven的setting文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    <profile>
<id>hys</id>
<!-- 使用私服配置 -->
<repositories>
<repository>
<id>local_nexus</id>
<url>http://192.168.18.134:8081/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>local_nexus</id>
<url>http://192.168.18.134:8081/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>

<!-- 必须激活才能生效 -->
<activeProfiles>
<activeProfile>hys</activeProfile>
</activeProfiles>

c、配置仓库镜像

1
2
3
4
5
6
7
8
<!--  如果本地仓库找不到依赖的构件,这时需要东西时先到Nexus上找,如果发现Nexus服务关闭后,会自动到中央仓库找
   如果我们想覆盖中央仓库的默认地址,强制依赖的东西都到Nexus中去找,即使Nexus关闭也不会到中央工厂去下载: -->
<mirror>
<id>local_nexus</id>
<name>local maven</name>
<url>http://192.168.18.134:8081/nexus/content/groups/public/</url>
<mirrorOf>Central</mirrorOf>
</mirror>
repository:在repositories元素下,可以使用repository子元素声明一个或者多个远程仓库。
id:仓库声明的唯一id,尤其需要注意的是,Maven自带的中央仓库使用的id为central,如果其他仓库声明也使用该id,就会覆盖中央仓库的配置。
name:仓库的名称,让我们直观方便的知道仓库是哪个,暂时没发现其他太大的含义。
url:指向了仓库的地址,一般来说,该地址都基于http协议,Maven用户都可以在浏览器中打开仓库地址浏览构件。
releases和snapshots:用来控制Maven对于发布版构件和快照版构件的下载权限。需要注意的是enabled子元素,该例中releases的enabled值为true,表示开启JBoss仓库的发布版本下载支持,而snapshots的enabled值为false,表示关闭JBoss仓库的快照版本的下载支持。根据该配置,Maven只会从JBoss仓库下载发布版的构件,而不会下载快照版的构件。
layout:元素值default表示仓库的布局是Maven2及Maven3的默认布局,而不是Maven1的布局。基本不会用到Maven1的布局。
其他:对于releases和snapshots来说,除了enabled,它们还包含另外两个子元素updatePolicy和checksumPolicy。
元素updatePolicy用来配置Maven从远处仓库检查更新的频率,默认值是daily,表示Maven每天检查一次。其他可用的值包括:never-从不检查更新;always-每次构建都检查更新;interval:X-每隔X分钟检查一次更新(X为任意整数)。
元素checksumPolicy用来配置Maven检查校验和文件的策略。当构建被部署到Maven仓库中时,会同时部署对应的检验和文件。在下载构件的时候,Maven会验证校验和文件,如果校验和验证失败,当checksumPolicy的值为默认的warn时,Maven会在执行构建时输出警告信息,其他可用的值包括:fail-Maven遇到校验和错误就让构建失败;ignore-使Maven完全忽略校验和错误。

2)、上载包配置

配置pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
</dependencies>
<distributionManagement>
<repository>
<id>nexus-releases</id><!--必须和setting中配置的用户名密码的id一致-->
<name>Nexus Release Repository</name>
<url>http://192.168.18.134:8081/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>nexus-snapshots</id>
<name>Nexus Snapshots Repository</name>
<url>http://192.168.18.134:8081/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
<!-- 上传插件 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-source</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

右键项目–>Maven build,输入deploy就会发布到私服。

20200421155339
查看私服

20200421155403

keepalived+Nginx配置实现负载均衡

发表于 2017-04-21 | 分类于 负载均衡

一、keepalived服务

Keepalived 一方面具有配置管理LVS的功能,同时还具有对LVS下面节点进行健康检查的功能,另一方面也可实现系统网络服务的高可用功能,用来防止单点故障。

keepalived 工作原理

keepalived 是以 VRRP 协议为实现基础,VRRP全称Virtual Router Redundancy Protocol,即虚拟路由冗余协议。

虚拟路由冗余协议,可以认为是实现路由器高可用的协议,即将N台提供相同功能的路由器组成一个路由器组,这个组里面有一个master和多个backup,master上面有一个对外提供服务的vip(该路由器所在局域网内其他机器的默认路由为该vip),master会发组播vrrp包,用于高速backup自己还活着,当backup收不到vrrp包时就认为master宕掉了,这时就需要根据VRRP的优先级来选举一个backup当master。这样的话就可以保证路由器的高可用了。保证业务的连续性,接管速度最快可以小于1秒。

keepalived主要有三个模块,分别是core、check和vrrp。

core模块为keepalived的核心,负责主进程的启动、维护以及全局配置文件的加载和解析。
check负责健康检查,包括常见的各种检查方式。
vrrp模块是来实现VRRP协议的。

keepalived 与 zookeeper 高可用性区别

Keepalived:

优点:简单,基本不需要业务层面做任何事情,就可以实现高可用,主备容灾。而且容灾的宕机时间也比较短。
缺点:也是简单,因为VRRP、主备切换都没有什么复杂的逻辑,所以无法应对某些特殊场景,比如主备通信链路出问题,会导致脑裂。同时keepalived也不容易做负载均衡。

Zookeeper:

优点:可以支持高可用,负载均衡。本身是个分布式的服务。  
缺点:跟业务结合的比较紧密。需要在业务代码中写好ZK使用的逻辑,比如注册名字。拉取名字对应的服务地址等。  

二、搭建配置keepalived+nginx负载均衡

这里使用192.168.18.138作为master,192.168.18.139作为backup

两台Tomcat也是这两个,实际中应该是四台服务器。

之前在两台中一样安装好Tomcat和Nginx,现在只需要安装

yum -y install keepalived
keepalived相关文件:
/etc/keepalived
/etc/keepalived/keepalived.conf     #keepalived服务主配置文件
/etc/rc.d/init.d/keepalived         #服务启动脚本(centos 7 之前的用init.d 脚本启动,之后的systemd启动)
/etc/sysconfig/keepalived
/usr/bin/genhash
/usr/libexec/keepalived
/usr/sbin/keepalived

keepalived配置说明

[root@etcd1 kubernetes]# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs { # 全局配置
  notification_email { # 定义报警邮件地址,收件人,可以定义多个
    acassen@firewall.loc
    failover@firewall.loc
    sysadmin@firewall.loc
  }
  notification_email_from Alexandre.Cassen@firewall.loc # 定义发送邮件的地址
  smtp_server 192.168.200.1 # 邮箱服务器
  smtp_connect_timeout 30   # 定义超时时间
  router_id LVS_DEVEL      # 定义路由器标识信息,相同的局域网唯一,标识本节点的字条串,通常为hostname
}
# 虚拟 IP 配置 vrrp 每一个vrrp_instance就是定义一个虚拟路由器
vrrp_instance VI_1 {        # 定义实例
  state MASTER            # 状态参数 master/backup 仅表示说明,主节点为 MASTER, 对应的备份节点为 BACKUP  
  interface eth0          # 绑定虚拟 IP 的网络接口,与本机 IP 地址所在的网络接口相同,  
  virtual_router_id 51    # 虚拟路由的 ID 号, 两个节点设置必须一样, 可选 IP 最后一段使用, 相同的 VRID 为一个组,他将决定多播的 MAC 地址
  mcast_src_ip 192.168.10.50     ## 本机 IP 地址  
  priority 100            # 优先级决定是主还是备 --> 越大越优先,节点优先级, 值范围 0-254, MASTER 要比 BACKUP 高    
  nopreempt           # 优先级高的设置 nopreempt 解决异常恢复后再次抢占的问题
  advert_int 1            # 主备心跳通讯时间间隔,组播信息发送间隔,两个节点设置必须一样, 默认 1s  
  authentication {        # 认证授权,设置验证信息,两个节点必须一致
      auth_type PASS
      auth_pass 1111    #密码
  }
  ## 将 track_script 块加入 instance 配置块
  track_script {
          chk_nginx  ## 执行 Nginx 监控的服务
    }
  virtual_ipaddress { # vip,设备之间使用的虚拟ip地址,可以定义多个
      192.168.200.16
      192.168.200.17
      192.168.200.18
  }
}

1、配置/etc/keepalived/keepalived.conf

主master:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  ! Configuration File for keepalived
global_defs {
router_id LVS_01
}
## keepalived 会定时执行脚本并对脚本执行的结果进行分析,动态调整 vrrp_instance 的优先级。
# 如果脚本执行结果为 0,并且 weight 配置的值大于 0,则优先级相应的增加。
# 如果脚本执行结果非 0,并且 weight配置的值小于 0,则优先级相应的减少。
# 其他情况,维持原本配置的优先级,即配置文件中 priority 对应的值。
vrrp_script chk_nginx {
script "/etc/keepalived/nginx_check.sh" ## 检测 nginx 状态的脚本路径
interval 2 ## 检测时间间隔
weight -20 ## 如果条件成立,权重-20
}

vrrp_instance VI_1 {
state MASTER
interface eth3
virtual_router_id 51
priority 100
advert_int 1
#nopreempt
authentication {
auth_type PASS
auth_pass 1111
}
#将track_script加入instance配置快
track_script {
chk_nignx ##执行Nginx监控服务
}
virtual_ipaddress {
192.168.18.88
}
}

启动

/etc/init.d/keepalived start 或 service keepalived start
Unexpected argument(s): start
[root@localhost init.d]# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
  link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  inet 127.0.0.1/8 scope host lo
  inet6 ::1/128 scope host
      valid_lft forever preferred_lft forever
2: eth3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
  link/ether 00:0c:29:58:0f:80 brd ff:ff:ff:ff:ff:ff
  inet 192.168.18.138/24 brd 192.168.18.255 scope global eth3
  inet 192.168.18.88/32 scope global eth3
  inet6 fe80::20c:29ff:fe58:f80/64 scope link
      valid_lft forever preferred_lft forever

可以看出增加了虚拟VIP

keepalived正常运行后,会启动3个进程,其中一个是父进程,负责监控其子进程。一个是vrrp子进程,另外一个是checkers子进程。

[root@localhost init.d]# ps -ef | grep keepalived
root      38347      1  0 05:41 ?        00:00:00 keepalived start
root      38348  38347  0 05:41 ?        00:00:00 keepalived start
root      38349  38347  0 05:41 ?        00:00:00 keepalived start
root      38501  16757  0 05:53 pts/0    00:00:00 grep keepalived

备份服务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
! Configuration File for keepalived
glbal_defs {
router_id LVS_02
}

## keepalived 会定时执行脚本并对脚本执行的结果进行分析,动态调整 vrrp_instance 的优先级。
# 如果脚本执行结果为 0,并且 weight 配置的值大于 0,则优先级相应的增加。
# 如果脚本执行结果非 0,并且 weight配置的值小于 0,则优先级相应的减少。
# 其他情况,维持原本配置的优先级,即配置文件中 priority 对应的值。
vrrp_script chk_nginx {
script "/etc/keepalived/nginx_check.sh" ## 检测 nginx 状态的脚本路径
interval 3 ## 检测时间间隔
weight -20 ## 如果条件成立,权重-20
}

vrrp_instance VI_1 {
state BACKUP
interface eth3
virtual_router_id 51
priority 80
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
#将track_script加入instance配置快
track_script {
chk_nignx ##执行Nginx监控服务
}
virtual_ipaddress {
192.168.18.88
}
}

同样的启动

2、编写 nginx 检测脚本

在所有的节点上面编写 Nginx 状态检测脚本 /etc/keepalived/nginx_check.sh (已在 keepalived.conf 中配置)脚本要求:如果 nginx 停止运行,尝试启动,如果无法启动则杀死本机的 keepalived 进程, keepalied将虚拟 ip 绑定到 BACKUP 机器上。

#!/bin/bash
set -x
A=`ps -C nginx --no-header |wc -l`
if [ $A -eq 0 ];then
  echo `date`':  nginx is not healthy, try to killall keepalived' >> /etc/keepalived/keepalived.log
  killall keepalived
fi

把文件拷贝到192.168.18.139:/etc/keepalived/

[root@localhost keepalived]# scp nginx_check.sh 192.168.18.139:/etc/keepalived/
Warning: Permanently added '192.168.18.139' (RSA) to the list of known hosts.
root@192.168.18.139's password:
nginx_check.sh                                                                      100%  206     0.2KB/s   00:00    

由于之前只配置了138的Nginx,现在把139的Nginx也和138一样配置过去。
在重启两台的keepalived和Nginx

3、测试

两台机子启动keepalived
同时看两台的ip,可以看到虚拟IP被master调动。

20200421131236

当master停止后:backup担任起调动工作。

20200421131300

当master再次启动后:master再度抢占虚拟IP

20200421131315

如果要使master再次起来时,不会抢占,在master /etc/keepalived/keepalived.conf 配置:

20200421131327

浏览器对应测试结果:

20200421131352

注:为了保证以上测试没有问题,需关闭防火墙。

keepalieved.conf详细配置说明

参考:https://www.jianshu.com/p/b050d8861fc1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
! Configuration File for keepalived

global_defs {
notification_email { #发送报警邮件收件地址
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
notification_email_from Alexandre.Cassen@firewall.loc #指明报警邮件的发送地址
smtp_server 192.168.200.1 #邮件服务器地址
smtp_connect_timeout 30 #smtp的超时时间
router_id LVS_DEVEL #物理服务器的主机名
vrrp_mcast_group4 #定义一个组播地址
static_ipaddress
{
192.168.1.1/24 dev eth0 scope global
}
static_routes
{
192.168.2.0/24 via 192.168.1.100 dev eth0
}

}
vrrp_sync_group VG_1 { #定义一个故障组,组内有一个虚拟路由出现故障另一个也会一起跟着转移,适用于LVS的NAT模型。
group {
VI1 # name of vrrp_instance (below)
VI2 # One for each moveable IP

}
}


vrrp_instance VI_1 { #定义一个虚拟路由
state MASTER|BACKUP #当前节点在此虚拟路由器上的初始状态;只能有一个是MASTER,余下的都应该为BACKUP;
interface eth0 #绑定为当前虚拟路由器使用的物理接口;
virtual_router_id 51 #当前虚拟路由器的惟一标识,范围是0-255;
priority 100 #当前主机在此虚拟路径器中的优先级;范围1-254;
advert_int 1 #通告发送间隔,包含主机优先级、心跳等。
authentication { #认证配置
auth_type PASS #认证类型,PASS表示简单字符串认证
auth_pass 1111 #密码,PASS密码最长为8位

virtual_ipaddress {
192.168.200.16 #虚拟路由IP地址,以辅助地址方式设置
192.168.200.18/24 dev eth2 label eth2:1 #以别名的方式设置
}

track_interface {
eth0
eth1

} #配置要监控的网络接口,一旦接口出现故障,则转为FAULT状态;
nopreempt #定义工作模式为非抢占模式;
preempt_delay 300 #抢占式模式下,节点上线后触发新选举操作的延迟时长;
virtual_routes { #配置路由信息,可选项
# src <IPADDR> [to] <IPADDR>/<MASK> via|gw <IPADDR> [or <IPADDR>] dev <STRING> scope
<SCOPE> tab
src 192.168.100.1 to 192.168.109.0/24 via 192.168.200.254 dev eth1
192.168.112.0/24 via 192.168.100.254 192.168.113.0/24 via 192.168.200.254 or 192.168.100.254 dev eth1 blackhole 192.168.114.0/24
}


notify_master <STRING>|<QUOTED-STRING> #当前节点成为主节点时触发的脚本。
notify_backup <STRING>|<QUOTED-STRING> #当前节点转为备节点时触发的脚本。
notify_fault <STRING>|<QUOTED-STRING> #当前节点转为"失败"状态时触发的脚本。
notify <STRING>|<QUOTED-STRING> #通用格式的通知触发机制,一个脚本可完成以上三种状态的转换时的通知。
smtp_alert #如果加入这个选项,将调用前面设置的邮件设置,并自动根据状态发送信息
}
virtual_server 192.168.200.100 443 { #LVS配置段 ,设置LVS的VIP地址和端口
delay_loop #服务轮询的时间间隔;检测RS服务器的状态。
lb_algo rr #调度算法,可选rr|wrr|lc|wlc|lblc|sh|dh。
lb_kind NAT #集群类型。
nat_mask 255.255.255.0 #子网掩码,可选项。
persistence_timeout 50 #是否启用持久连接,连接保存时长
protocol TCP #协议,只支持TCP
sorry_server <IPADDR> <PORT> #备用服务器地址,可选项。

real_server 192.168.201.100 443 { #配置RS服务器的地址和端口
weight 1 #权重
SSL_GET { #检测RS服务器的状态,发送请求报文
url {
path / #请求的URL
digest ff20ad2481f97b1754ef3e12ecd3a9cc #对请求的页面进行hash运算,然后和这个hash码进行比对,如果hash码一样就表示状态正常
status_code <INT> #判断上述检测机制为健康状态的响应码,和digest二选一即可。

} #这个hash码可以使用genhash命令请求这个页面生成
connect_timeout 3 #连接超时时间
nb_get_retry 3 #超时重试次数
delay_before_retry 3 #每次超时过后多久再进行连接
connect_ip <IP ADDRESS> #向当前RS的哪个IP地址发起健康状态检测请求
connect_port <PORT> #向当前RS的哪个PORT发起健康状态检测请求
bindto <IP ADDRESS> #发出健康状态检测请求时使用的源地址;
bind_port <PORT> #发出健康状态检测请求时使用的源端口;
}
}
}

nginx使用和实现代理

发表于 2017-04-21 | 分类于 nginx

一、Nginx环境搭建

1、下载 nginx-1.8.1.tar.gz

通过wget命令下载Nginx包,或者在Windows下载后拷贝到Linux中。
wget http://nginx.org/download/nginx-1.8.1.tar.gz

2、解压到/usr/local/

tar -zxvf nginx-1.8.1.tar.gz -C /usr/local/

3、下载Nginx编译需要的4个依赖库

yum install pcre/yum install pcre-devel/yum install zlib/yum install zlib-devel

4、配置configure检查是否报错

cd nginx-1.8.1 && ./configure –prefix=/usr/local/nginx

5、编译安装make && make install

编译安装完成后,在/usr/local/nginx/下有四个目录;
conf  html  logs  sbin
conf  配置文件
html  网页文件
logs  日志记录
sbin   主要的二进制程序

6、启动

两种方式启动/停止/重启:
[root@localhost sbin]# /usr/local/nginx/sbin/nginx
[root@localhost sbin]# /usr/local/nginx/sbin/nginx -s stop
[root@localhost sbin]# /usr/local/nginx/sbin/nginx -s reload
或 :
[root@localhost sbin]# ./nginx
[root@localhost sbin]# ./nginx -s stop
[root@localhost sbin]# ./nginx -s reload

查看进程是否启动:
[root@localhost sbin]# ps -ef | grep nginx
root       5277      1  0 11:32 ?        00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nobody     5280   5277  0 11:33 ?        00:00:00 nginx: worker process      
root       5290   2660  0 11:43 pts/0    00:00:00 grep nginx

启动看端口:
[root@localhost sbin]# lsof -i:80
COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx   5264   root    6u  IPv4  27214      0t0  TCP *:http (LISTEN)
nginx   5265 nobody    6u  IPv4  27214      0t0  TCP *:http (LISTEN)

发现浏览器访问不了,设置防火墙开放80端口
[root@localhost sbin]# vim /etc/sysconfig/iptables
[root@localhost sbin]# service iptables restart
iptables: Setting chains to policy ACCEPT: filter          [  OK  ]
iptables: Flushing firewall rules:                         [  OK  ]
iptables: Unloading modules:                               [  OK  ]
iptables: Applying firewall rules:                         [  OK  ]

再次访问,ok

二、虚拟主机配置

比如在nginx.conf中新增一个server:
server {
listen 81;
server_name hu.com;

location / {
    root hu.com;
    index index.html;
}
access_log logs/hu.com.access.log main;
#添加日志文件
}

保存后重启Nginx

本机设置下hosts:
192.168.18.138 hu.com

测试,OK

三、日志处理

nginx下的logs是日志目录。

access.log  error.log  hu.com.access.log  nginx.pid
nginx.pid是Nginx运行进程日志。

开启日志格式:

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                 '$status $body_bytes_sent "$http_referer" '
                 '"$http_user_agent" "$http_x_forwarded_for"';

为每个虚拟服务设置日志:

server {
    listen       80;
    server_name  localhost;
    location / {
        root   html;
        index  index.html index.htm;
    }
    access_log  logs/access.log  main;
}
    server {
        listen 81;
        server_name hu.com;
        location / {
            root hu.com;
            index index.html;
        }

access_log logs/hu.com.access.log main;#81的日志

四、日志切分备份

日常中,要对日志分析处理,通常由运维去进行对日志的切割分析,比如实现一个定时任务去处理Nginx日志。

日志切分用shell脚本(log.sh放到sbin中):

#!/bin/sh
#定义变量,当前Nginx目录
BASE_DIR=/usr/local/nginx
#文件名
BASE_FILE_NAME=access.log
#日志目录
CURRENT_PATH=$BASE_DIR/logs
#备份目录
BAK_PATH=$BASE_DIR/datalogs

#需要备份的文件名和路径
CURRENT_FILE=$CURRENT_PATH/$BASE_FILE_NAME

BAK_TIME=`/bin/date -d yesterday +%Y%m%d%H%M`
BAK_FILE=$BAK_PATH/$BAK_TIME-$BASE_FILE_NAME
echo $BAK_FILE #备份文件名和路径

#停了Nginx
$BASE_DIR/sbin/nginx -s stop
#移动文件
mv $CURRENT_FILE $BAK_FILE
#启动Nginx
$BASE_DIR/sbin/nginx

赋权:给文件权限

root@localhost sbin]# chmod 777 log.sh
[root@localhost sbin]# ll
total 3184
-rwxrwxrwx. 1 root root     521 Feb 17 13:52 log.sh
-rwxr-xr-x. 1 root root 3254901 Feb 17 10:39 nginx

定时任务对脚本进行定时调度:

crontab -e
使用crontab你可以在指定的时间执行一个shell脚本或者一系列Linux命令。
使用crontab把下面的代码加进去
*/1 * * * * sh /usr/local/nginx/sbin/log.sh
这是一分钟执行一次脚本。

可以看到备份文件已经生成

[root@localhost nginx]# ll datalogs/
total 8
-rw-r--r--. 1 root root  592 Feb 17 13:30 201902161401-access.log
-rw-r--r--. 1 root root 1504 Feb 17 14:01 201902161402-access.log

五、反向代理

20200421123057

比如配置Nginx在访问有.jsp文件时转到Tomcat,其他在本机访问。配置如下;

server {
listen       80;
server_name  localhost;
location / {
    root   html;
    index  index.html index.htm;
}
#配置反向代理tomcat服务器:拦截.jsp结尾的请求转向到tomcat
location ~ \.jsp$ {
    proxy_pass http://192.168.18.138:8080;
}
access_log  logs/access.log  main;

创建一个jsp文件,写入内容:

<body>
Test1 Page!!!<br/>
remote ip :  <%=request.getHeader("X-real-ip") %> <br/>
nginx server ip : <%=request.getRemoteAddr()%>
</body>

测试:

访问:http://192.168.18.138/

访问:http://192.168.18.138/test.jsp

发现ip并不是客户端ip地址,这是因为,请求Nginx后,由于jsp文件Nginx会去Tomcat服务请求在发回客户端。所以当Tomcat接收请求时接收到的是Nginx的ip,并不是客户端请求IP。

解决方式在配置Nginx中相关处做如下处理:

location ~ \.jsp$ {
    #设置客户端真实ip地址
    #proxy_set_header X-real-ip $remote_addr;
    proxy_pass http://192.168.18.138:8080;
}

再次测试:
访问:http://192.168.18.138/test.jsp

得到客户端IP,OK!!

六、负载均衡

负载均衡主要通过配置upstream来实现

#设定负载均衡的服务器列表
#upstream myproject {
        #weigth参数表示权值,权值越高被分配到的几率越大
        #max_fails 当有#max_fails个请求失败,就表示后端的服务器不可用,默认为1,将其设置为0可以关闭检查(允许请求失败的次数)
        #fail_timeout 在以后的#fail_timeout时间内nginx不会再把请求发往已检查出标记为不可用的服务器
#}

状态有:

(1)down:表示单前的server暂时不参与负载
(2)weight:权重,默认为1, weight越大,负载的权重就越大。
(3)max_fails:允许请求失败的次数默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误。
(4)fail_timeout:max_fails次失败后,暂停的时间。
(5)backup:备用服务器, 其它所有的非backup机器down或者忙的时候,请求backup机器,所以这台机器压力会最轻。

upstream四中配置方式:

1、轮询(weight) 最普通方式

指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。默认当weight不指定时,各服务器weight相同,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
upstream bakend {
server 192.168.1.10 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.1.11 weight=1 max_fails=2 fail_timeout=30s
}

2、ip_hash

每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session不能跨服务器的问题。如果后端服务器down掉,要手工down掉。

upstream resinserver{
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}

3、fair(第三方插件)

按后端服务器的响应时间来分配请求,响应时间短的优先分配。

upstream resinserver{
server 192.168.1.10:8080;
server 192.168.1.11:8080;
fair;
}

4、url_hash(第三方插件)

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存服务器时比较有效。 在upstream中加入hash语句,hash_method是使用的hash算法。

upstream resinserver{
server 192.168.1.10:8080;
server 192.168.1.11:8080;
hash $request_uri;
hash_method crc32;
}

注意,当upstream中只有一个 server 时,max_fails 和 fail_timeout 参数可能不会起作用。

weight\backup 不能和 ip_hash 关键字一起使用。 最后在需要使用负载均衡的server中增加proxy_pass http://tel_img_stream/,对应upstream的名字。

例子,负载均衡配置如下:

upstream myapp {
        server 192.168.18.138:8080 weight=1 max_fails=2 fail_timeout=30s;  
        server 192.168.18.139:8080 weight=1 max_fails=2 fail_timeout=30s;
}

server {
#.........................
    location ~ \.jsp$ {
        #设置客户端真实ip地址
        proxy_set_header X-real-ip $remote_addr;
        proxy_pass http://myapp;
    }

把上面使用的test.jsp拷贝到139的Tomcat,各自单独测试如下:

20200421123613

使用负载均衡测试如下:

20200421123625

20200421123716

四次测试,两个Tomcat交替代理,因为weight=1设置的权重都一样,公平出现。

七、nginx.conf配置详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#user  nobody;
#开启进程数数
worker_processes 1;
#错误日志保存位置
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#进程号保存文件
#pid logs/nginx.pid;
#每个进程最大连接数(最大连接=连接数x进程数)每个worker允许同时产生多少个链接,默认1024
events {
worker_connections 1024;
}
http {
#文件扩展名与文件类型映射表
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 on;
#tcp_nopush on;


#keepalive_timeout 0;
#连接超时时间
keepalive_timeout 65;


#打开gzip压缩
#gzip on;

#设定请求缓冲
#client_header_buffer_size
#large_client_header_buffers

#设定负载均衡的服务器列表
#upstream myproject {
#weigth参数表示权值,权值越高被分配到的几率越大
#max_fails 当有#max_fails个请求失败,就表示后端的服务器不可用,默认为1,将其设置为0可以关闭检查
#fail_timeout 在以后的#fail_timeout时间内nginx不会再把请求发往已检查出标记为不可用的服务器
#}

#webapp
#upstream myapp {
# server 192.168.1.171:8080 weight=1 max_fails=2 fail_timeout=30s;
# server 192.168.1.172:8080 weight=1 max_fails=2 fail_timeout=30s;
#}


#配置虚拟主机,基于域名、ip和端口
server {
#监听端口
listen 80;
#监听域名
server_name localhost;


#charset koi8-r;

#nginx访问日志放在logs/host.access.log下,并且使用main格式(还可以自定义格式)
#access_log logs/host.access.log main;


#返回的相应文件地址
location / {
#设置客户端真实ip地址
#proxy_set_header X-real-ip $remote_addr;
#负载均衡反向代理
#proxy_pass http://myapp;

#返回根路径地址(相对路径:相对于/usr/local/nginx/)
root html;
#默认访问文件
index index.html index.htm;
}


#配置反向代理tomcat服务器:拦截.jsp结尾的请求转向到tomcat
#location ~ \.jsp$ {
# proxy_pass http://192.168.1.171:8080;
#}

#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#

#错误页面及其返回地址
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}


# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}


# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}


# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

#虚拟主机配置:
server {
listen 1234;
server_name bhz.com;
location / {
#正则表达式匹配uri方式:在/usr/local/nginx/bhz.com下 建立一个test123.html 然后使用正则匹配
#location ~ test {
## 重写语法:if return (条件 = ~ ~*)
#if ($remote_addr = 192.168.1.200) {
# return 401;
#}

#if ($http_user_agent ~* firefox) {
# rewrite ^.*$ /firefox.html;
# break;
#}

root bhz.com;
index index.html;
}

#location /goods {
# rewrite "goods-(\d{1,5})\.html" /goods-ctrl.html;
# root bhz.com;
# index index.html;
#}

#配置访问日志
access_log logs/bhz.com.access.log main;
}





# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;


# location / {
# root html;
# index index.html index.htm;
# }
#}




# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;


# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;


# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;


# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;


# location / {
# root html;
# index index.html index.htm;
# }
#}


}

八、使用keepalived+Nginx配置实现负载均衡。

详见:keepalived+Nginx配置实现负载均衡

ActiveMQ安装配置及其简单使用

发表于 2017-04-13 | 分类于 java

ActiveMQ在windows上安装配置

1、安装 下载地址:http://activemq.apache.org/download.html

2、配置(conf目录下)

修改账号密码
credentials.properties
credentials-enc.properties
可以配置用户名密码
activemq.username=system
activemq.password=manager
guest.password=password

开启jmx监控

1
2
3
4
activemq.xml中进行如下修改
<managementContext>
<managementContext createConnector="true"/>
</managementContext>

注:这里的配置不是必须,根据需要自行配置

3、启动运行

cmd,bin目录下:activemq.bat start

4、浏览

浏览器中:http://localhost:8161/admin/index.jsp

用户名,密码在:jetty-realm.properties中设置

20200413113806

ActiveMQ在Linux上安装配置

1、下载软件,拷贝到Linux目录/usr/local

1
2
3
[root@localhost local]# ll
总用量 65912
-rw-r--r--. 1 root root 58588039 4月 14 20:50 apache-activemq-5.15.9-bin.tar.gz

2、解压安装

1
2
3
[root@localhost local]# tar -zxf apache-activemq-5.15.9-bin.tar.gz
[root@localhost local]# ll
drwxr-xr-x. 10 root root 193 3月 15 20:04 apache-activemq-5.15.9

3、安全配置

开启jmx监控:
activemq.xml中进行如下修改
    <managementContext>
        <managementContext createConnector="true"/>
    </managementContext>
注:这里的配置不是必须,根据需要自行配置

配置用户:[root@localhost apache-activemq-5.15.9]# ./bin/activemq

基于Tomcat + JNDI + ActiveMQ实现JMS的点对点消息传送

写了一个简单的JMS例子,之所以使用JNDI 是出于通用性考虑,该例子使用JMS规范提供的通用接口,没有使用具体JMS提供者的接口,这样可以保证我们编写的程序适用于任何一种JMS实现(ActiveMQ、HornetQ…)。

JNDI(Java Naming and Directory Interface)是一个标准规范,类似于JDBC,JMS等规范,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口。J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现,因此Tomcat就实现了JNDI 规范。

使用Tomcat配置JNDI

Tomcat安装路径下的conf文件夹,打开context.xml,作如下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 配置JNDI start-->
<Resource name="queue/connectionFactory"
auth="Container"
type="org.apache.activemq.ActiveMQConnectionFactory"
description="JMS Connection Factory"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
brokerURL="tcp://localhost:61616"
brokerName="LocalActiveMQBroker" />

<Resource name="queue/queue0"
auth="Container"
type="org.apache.activemq.command.ActiveMQQueue"
description="My Test Queue"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
physicalName="TomcatQueue" />
<!-- 配置JNDI end-->

但是,一般不建议直接更改Tomcat的全局配置,在web项目下的WebContent/META-INF 里创建context.xml在里面配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="queue/connectionFactory"
auth="Container"
type="org.apache.activemq.ActiveMQConnectionFactory"
description="JMS Connection Factory"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
brokerURL="tcp://localhost:61616"
brokerName="LocalActiveMQBroker" />

<Resource name="queue/queue0"
auth="Container"
type="org.apache.activemq.command.ActiveMQQueue"
description="My Test Queue"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
physicalName="TomcatQueue" />
</Context>

创建web项目,建两个servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@WebServlet("/send")
public class Send extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
try {
InitialContext context = new InitialContext();
Queue queue = (Queue) context.lookup("java:comp/env/queue/queue0");
QueueConnectionFactory conFactory = (QueueConnectionFactory) context.lookup("java:comp/env/queue/connectionFactory");
QueueConnection queConn = conFactory.createQueueConnection();
QueueSession queSession = queConn.createQueueSession(false, Session.DUPS_OK_ACKNOWLEDGE);
QueueSender queSender = queSession.createSender(queue);
queSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//发送消息
TextMessage message = queSession.createTextMessage("how are you 123");
queSender.send(message);
out.write("send message: " + message.getText());
queConn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

@WebServlet("/receive")
public class Receive extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
response.setContentType("text/html;charset=utf-8");
try {
InitialContext context = new InitialContext();
Queue queue = (Queue) context.lookup("java:comp/env/queue/queue0");
QueueConnectionFactory conFactory = (QueueConnectionFactory) context.lookup("java:comp/env/queue/connectionFactory");

QueueConnection queConn = conFactory.createQueueConnection();
QueueSession queSession = queConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
QueueReceiver queReceiver = queSession.createReceiver(queue);
queConn.start();
//接收消息
TextMessage message = (TextMessage) queReceiver.receive();
out.write("receive message: " + message.getText());
queConn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

运行web程序,访问发送端。

20200413114353

发布/订阅消息传送例子

和上面相似,配置context.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    <Resource name="topic/connectionFactory" auth="Container"
type="org.apache.activemq.ActiveMQConnectionFactory"
description="JMS Connection Factory"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
brokerURL="failover:(tcp://localhost:61616)?initialReconnectDelay=100&amp;maxReconnectAttempts=5"
brokerName="LocalActiveMQBroker"
useEmbeddedBroker="false" />

<Resource name="topic/topic0"
auth="Container"
type="org.apache.activemq.command.ActiveMQTopic"
description="My Test Topic"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
physicalName="TestTopic" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@WebServlet("/publish")
public class Publisher extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();

try {
InitialContext ctx = new InitialContext();
Topic topic = (Topic) ctx.lookup("java:comp/env/topic/topic0");
TopicConnectionFactory connFactory = (TopicConnectionFactory) ctx.lookup("java:comp/env/topic/connectionFactory");

TopicConnection topicConn = connFactory.createTopicConnection();
TopicSession topicSession = topicConn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
TopicPublisher topicPublisher = topicSession.createPublisher(topic);
topicPublisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
TextMessage message = topicSession.createTextMessage();
message.setText("how are you XXX");
topicPublisher.publish(message);
out.write("published: " + message.getText());
topicConn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

@WebServlet("/subscribe")
public class Subscriber extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();

try {
InitialContext ctx = new InitialContext();
Topic topic = (Topic) ctx.lookup("java:comp/env/topic/topic0");
TopicConnectionFactory connFactory = (TopicConnectionFactory) ctx.lookup("java:comp/env/topic/connectionFactory");
TopicConnection topicConn = connFactory.createTopicConnection();
TopicSession topicSession = topicConn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
TopicSubscriber topicSubscriber = topicSession.createSubscriber(topic);
topicConn.start();
TextMessage message = (TextMessage) topicSubscriber.receive();
out.write("received: " + message.getText());
topicConn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

多运行几个接收,一开始没有显示内容,只是订阅,在activemq中可以看到有两个消费者

20200413114528

JMS消息服务

发表于 2017-04-13 | 分类于 java

什么是Java消息服务

Java消息服务指的是两个应用程序之间进行异步通信的API,它为标准消息协议和消息服务提供了一组通用接口,包括创建、发送、读取消息等,用于支持JAVA应用程序开发。在J2EE中,当两个应用程序使用JMS进行通信时,它们之间并不是直接相连的,而是通过一个共同的消息收发服务连接起来,可以达到解耦的效果。

JMS的重要性

在JAVA中,如果两个应用程序之间对各自都不了解,甚至这两个程序可能部署在不同的大洲上,那么它们之间如何发送消息呢?举个例子,一个应用程序A部署在印度,另一个应用程序部署在美国,然后每当A触发某件事后,B想从A获取一些更新信息。当然,也有可能不止一个B对A的更新信息感兴趣,可能会有N个类似B的应用程序想从A中获取更新的信息。

在这种情况下,JAVA提供了最佳的解决方案-JMS,完美解决了上面讨论的问题。

JMS同样适用于基于事件的应用程序,如聊天服务,它需要一种发布事件机制向所有与服务器连接的客户端发送消息。JMS与RMI不同,发送消息的时候,接收者不需要在线。服务器发送了消息,然后就不管了;等到客户端上线的时候,能保证接收到服务器发送的消息。这是一个很强大的解决方案,能处理当今世界很多普遍问题。

JMS消息传送模型

在JMS API出现之前,大部分产品使用”点对点”和”发布/订阅”中的任一方式来进行消息通讯。JMS定义了这两种消息发送模型的规范,它们相互独立。任何JMS的提供者可以实现其中的一种或两种模型,这是它们自己的选择。JMS规范提供了通用接口保证我们基于JMS API编写的程序适用于任何一种模型。

点对点消息传送模型

20200413112504

在点对点消息传送模型中,应用程序由消息队列,发送者,接收者组成。每一个消息发送给一个特殊的消息队列,该队列保存了所有发送给它的消息(除了被接收者消费掉的和过期的消息)。点对点消息模型有一些特性,如下:

每个消息只有一个接收者;
消息发送者和接收者并没有时间依赖性;
当消息发送者发送消息的时候,无论接收者程序在不在运行,都能获取到消息;
当接收者收到消息的时候,会发送确认收到通知(acknowledgement)。

发布/订阅消息传递模型

20200413112636

在发布/订阅消息模型中,发布者发布一个消息,该消息通过topic传递给所有的客户端。在这种模型中,发布者和订阅者彼此不知道对方,是匿名的且可以动态发布和订阅topic。topic主要用于保存和传递消息,且会一直保存消息直到消息被传递给客户端。
发布/订阅消息模型特性如下:

一个消息可以传递给多个订阅者
发布者和订阅者有时间依赖性,只有当客户端创建订阅后才能接受消息,且订阅者需一直保持活动状态以接收消息。
为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。

接收消息

同步:使用同步方式接收消息的话,消息订阅者调用receive()方法。在receive()中,消息未到达或在到达指定时间之前,方法会阻塞,直到消息可用。

异步:使用异步方式接收消息的话,消息订阅者需注册一个消息监听者,类似于事件监听器,只要消息到达,JMS服务提供者会通过调用监听器的onMessage()递送消息。

JMS相关接口

  • 管理对象(Administered objects)-连接工厂(Connection Factories)和目的地(Destination)
  • 连接对象(Connections)
  • 会话(Sessions)
  • 消息生产者(Message Producers)
  • 消息消费者(Message Consumers)
  • 消息监听者(Message Listeners)

JMS管理对象

管理对象(Administered objects)是预先配置的JMS对象,由系统管理员为使用JMS的客户端创建,主要有两个被管理的对象:连接工厂(ConnectionFactory)和目的地(Destination)

这两个管理对象由JMS系统管理员通过使用Application Server管理控制台创建,存储在应用程序服务器的JNDI名字空间或JNDI注册表。

连接工厂(ConnectionFactory)

客户端使用一个连接工厂对象连接到JMS服务提供者,它创建了JMS服务提供者和客户端之间的连接。JMS客户端(如发送者或接受者)会在JNDI名字空间中搜索并获取该连接。使用该连接,客户端能够与目的地通讯,往队列或话题发送/接收消息。让我们用一个例子来理解如何发送消息:

1
2
3
QueueConnectionFactory queueConnFactory = (QueueConnectionFactory) initialCtx.lookup ("primaryQCF");
Queue purchaseQueue = (Queue) initialCtx.lookup ("Purchase_Queue");
Queue returnQueue = (Queue) initialCtx.lookup ("Return_Queue");

目的地(Destination)

目的地指明消息被发送的目的地以及客户端接收消息的来源。JMS使用两种目的地,队列和话题。如下代码指定了一个队列和话题。

创建一个队列Session

1
2
3
QueueSession ses = con.createQueueSession (false, Session.AUTO_ACKNOWLEDGE);  //get the Queue object  
Queue t = (Queue) ctx.lookup ("myQueue"); //create QueueReceiver
QueueReceiver receiver = ses.createReceiver(t);

创建一个话题Session

1
2
3
TopicSession ses = con.createTopicSession (false, Session.AUTO_ACKNOWLEDGE); // get the Topic object  
Topic t = (Topic) ctx.lookup ("myTopic"); //create TopicSubscriber
TopicSubscriber receiver = ses.createSubscriber(t);

JMS连接

连接对象封装了与JMS提供者之间的虚拟连接,如果我们有一个ConnectionFactory对象,可以使用它来创建一个连接。

Connection connection = connectionFactory.createConnection();

创建完连接后,需要在程序使用结束后关闭它:connection.close();

JMS 会话(Session)

Session是一个单线程上下文,用于生产和消费消息,可以创建出消息生产者和消息消费者。
Session对象实现了Session接口,在创建完连接后,我们可以使用它创建Session。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

JMS消息生产者

消息生产者由Session创建,用于往目的地发送消息。生产者实现MessageProducer接口,我们可以为目的地、队列或话题创建生产者;

1
2
3
MessageProducer producer = session.createProducer(dest);
MessageProducer producer = session.createProducer(queue);
MessageProducer producer = session.createProducer(topic);

创建完消息生产者后,可以使用send方法发送消息:producer.send(message);

JMS消息消费者

消息消费者由Session创建,用于接受目的地发送的消息。消费者实现MessageConsumer接口,,我们可以为目的地、队列或话题创建消费者;

1
2
3
MessageConsumer consumer = session.createConsumer(dest);
MessageConsumer consumer = session.createConsumer(queue);
MessageConsumer consumer = session.createConsumer(topic);

JMS消息监听器

JMS消息监听器是消息的默认事件处理者,他实现了MessageListener接口,该接口包含一个onMessage方法,在该方法中需要定义消息达到后的具体动作。通过调用setMessageListener方法我们给指定消费者定义了消息监听器

1
2
Listener myListener = new Listener();
consumer.setMessageListener(myListener);

JMS消息结构

JMS消息由三部分组成:

消息头
JMS消息头预定义了若干字段用于客户端与JMS提供者之间识别和发送消息,预编译头如下:

 - JMSDestination
 - JMSDeliveryMode
 - JMSMessageID
 - JMSTimestamp
 - JMSCorrelationID
 - JMSReplyTo
 - JMSRedelivered
 - JMSType
 - JMSExpiration
 - JMSPriority

消息属性

我们可以给消息设置自定义属性,这些属性主要是提供给应用程序的。对于实现消息过滤功能,消息属性非常有用,JMS API定义了一些标准属性,JMS服务提供者可以选择性的提供部分标准属性。

消息体

在消息体中,JMS API定义了五种类型的消息格式,让我们可以以不同的形式发送和接受消息,并提供了对已有消息格式的兼容。不同的消息类型如下:

Text message : javax.jms.TextMessage,表示一个文本对象。
Object message : javax.jms.ObjectMessage,表示一个JAVA对象。
Bytes message : javax.jms.BytesMessage,表示字节数据。
Stream message :javax.jms.StreamMessage,表示java原始值数据流。
Map message : javax.jms.MapMessage,表示键值对。

参考:https://www.cnblogs.com/chenpi/p/5559349.html

spring初步整合mybatis例子

发表于 2017-04-09 | 分类于 spring

配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 配置数据源:获取数据库连接,数据源封装类在spring-jdbc.jar 中 -->
<bean id="dataSouce" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ssm"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>


<!-- 创建Mybatis工厂 通过Spring 创建 SqlSessionFactory 对象 需要导入包mybatis-spring-1.2.3.jar 里边包含创建session的类SqlSessionFactoryBean-->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接信息来源于 dataSource -->
<property name="dataSource" ref="dataSouce"></property>
</bean>

<!-- 扫描器 相当于 mybatis.xml 中 mappers 下 package 标 签,扫描 com.hu.mapper 包后会给对应接口创建对象 不需要被引用,id可以不写-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 要扫描哪个包 -->
<property name="basePackage" value="com.hu.mapper"></property>
<!-- 和 factory 产生关系 -->
<property name="sqlSessionFactory" ref="factory"></property>
</bean>

</beans>

测试:

1
2
3
4
5
6
7
8
9
public class Test01 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
String[] strings = ac.getBeanDefinitionNames();
for (int i = 0; i < strings.length; i++) {
System.out.println(strings[i]);
}
}
}

结果:

1
2
3
4
5
6
7
8
9
10
11
信息: Loaded JDBC driver: com.mysql.jdbc.Driver
dataSouce
factory
org.mybatis.spring.mapper.MapperScannerConfigurer#0
studentMapper
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor

查询数据库测试

1
2
3
4
5
6
7
8
9
10
11
12
public interface StudentMapper {
@Select("select * from student")
List<Student> findStudentAll();
@Select("select * from student where id=#{0}")
Student findStudentById(int id);
}
测试:
StudentMapper sm = ac.getBean("studentMapper",StudentMapper.class);
//StudentMapper sm = ac.getBean(StudentMapper.class);
System.out.println(sm.findStudentById(1));
输出:
Student [id=1, name=学生1, age=5, tid=1, teacher=null]

spring框架介绍和环境搭建

发表于 2017-04-07 | 分类于 spring

介绍

Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由d Rod Johnson在其著作 Expert One-On-One J2EE Development and Design 中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring 使用基本的 JavaBean来完成以前只可能由 EJB 完成的事情。然而,Spring 的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何 Java 应用都可以从 Spring 中受益。Spring 的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring 是一个分层的 JavaSE/EEfull-stack( 一站式) 轻量级开源框架。

Spring核心模块
20200407094232

Core Container(核心容器)

  • Beans:负责Bean工厂中Bean的装配,所谓Bean工厂即是创建对象的工厂,Bean的装配也就是对象的创建工作;
  • Core:这个模块即是负责IOC(控制反转)最基本的实现;
  • Context:Spring的IOC容器,因大量调用Spring Core中的函数,整合了Spring的大部分功能。Bean创建好对象后,由Context负责建立Bean与Bean之间的关系并维护。所以也可以把Context看成是Bean关系的集合;
  • SpEl:即Spring Expression Language(Spring表达式语言);

Data Access/Integration(数据访问/集成)

  • JDBC:对JDBC的简单封装;
  • ORM:支持数据集成框架的封装(如Mybatis,Hibernate);
  • OXM:即Object XML Mapper,它的作用是在Java对象和XML文档之间来回转换;
  • JMS:生产者和消费者的消息功能的实现;
  • Transations:事务管理,声明式事务;

Web

  • WebSocket:提供Socket通信,web端的的推送功能;
  • Servlet:Spring MVC框架的实现;
  • Web:包含web应用开发用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类,Struts集成类、文件上传的支持类、Filter类和大量辅助工具类;
  • Portlet:实现web模块功能的聚合(如网站首页(Port)下面可能会有不同的子窗口(Portlet));

AOP

面向切面;

Aspects

同样是面向切面的一个重要的组成部分,提供对AspectJ框架的整合;

Instrumentation(设备)

相当于一个检测器,提供对JVM以及对Tomcat的检测;

Messaging(消息)

Spring提供的对消息处理的功能;

Test(测试)

我们在做单元测试时,Spring会帮我们初始化一些测试过程当中需要用到的资源对象;

环境搭建

建一个项目,导入包,新建配置文件,注:也可以不用配置文件,使用java config方式。

引入约束:

1
2
3
4
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

spring 配置文件是基于 schema,schema 文件扩展名.xsd,相当于是 DTD 的升级版,比 DTD 具备更好的扩展性。每次引入一个 xsd 文件是一个 namespace(xmlns)
配置文件中只需要引入基本 schema通过<bean/> 创建对象。默认配置文件被加载时创建对象。

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- id表示获取到对象的标识,class标识创建哪个对象 -->
<bean id="student" class="com.hu.pojo.Student"></bean>

</beans>

测试:

1
2
3
4
5
6
7
8
9
10
public class Test01 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// getBean("<bean>标签 id 值",返回值类型);如果没有第二个参数,默认是 Object
// getBeanDefinitionNames(),获取Spring 容器中目前所有管理的所有对象
Student student = ac.getBean("student",Student.class);
System.out.println(student);
System.out.println(ac.getBeanDefinitionNames());
}
}

Spring常用注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1、 @Component 创建类对象,相当于配置<bean/>。
2、@Service 与@Component 功能相同,写在 ServiceImpl 类上。
3、@Repository 与@Component 功能相同,写在数据访问层类上。
4、@Controller 与@Component 功能相同,写在控制器类上。
5、@Resource(用于对象属性自动注入,不需要写对象的 get/set)
这个注解是 java 中的注解,默认按照 byName 注入,如果没有名称对象,按照 byType 注入。
6、@Autowired(和@Resource完全一致),是spring 的注解,默认按照 byType 注入。
7、 @Value() 获取 properties 文件中内容。
8、@Pointcut() 定义切点。
9、@Aspect() 定义切面类
10、@Before() 前置通知
11、@After 后置通知
12、@AfterReturning 后置通知,必须切点正确执行
13、@AfterThrowing 异常通知
14、@Arround 环绕通知

maven安装使用

发表于 2017-04-01 | 分类于 maven

一、maven 简介

Maven项目对象模型(POM)( Project Object Mode),可以通过一小段描述信息来管理项目的构建,报告和文档的项目管理工具软件。
基于 Ant 的构建工具,Ant 有的功能 Maven 都有,额外添加了其他功能。

运行过程:

20200421155621

通常中央仓库在国外,下载速度慢,一般都是配置国内镜像。

坐标:

每一 jar 文件都有一个唯一坐标.通过坐标可以精确确定是哪个 jar
坐标组成:
Group ID : 公司名.公司网址倒写
Artifact ID : 项目名
Version : 版本

POM项目对象模型,就是把项目看做一个对象处理,通过 maven 构建工具可以让对象(项目)和对象(项目)之间产生关系。

二、安装

1、下载包
2、解压到目录
3、配置环境变量

三、配置

1、修改 JDK 版本

1
2
3
4
5
6
7
8
9
10
11
12
<profile>
<id>jdk-1.7</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.7</jdk>
</activation>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.compilerVersion>1.7</maven.compiler.compilerVersion>
</properties>
</profile>

2、 修改镜像地址(不使用 nexus 时配置)

1
2
3
4
5
6
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>

四、Maven本地资源库更改/更新

1、更改本地资源库

Maven的本地资源库是用来存储所有项目的依赖关系(插件jar和其他文件,这些文件被Maven下载)到本地文件夹。很简单,当你建立一个Maven项目,所有相关文件将被存储在你的Maven本地仓库。 默认情况下,Maven的本地资源库默认为 .m2

目录文件夹:

Unix/Mac OS X - ~/.m2
Windows - C:\Documents and Settings\{your-username}\.m2

修改默认位置:打开{M2_HOME}\conf\setting.xml修改maven配置,找到:

<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository
<localRepository>/path/to/local/repo</localRepository>
-->

修改为新的位置:如下

<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository  
-->
<localRepository>G:\Maven\repository</localRepository>

2、更新本地资源库

通过命令更新新设置的资源库:mvn help:system

mvn archetype:generate

Maven项目管理

步骤:
1、创建 maven project 时选择 packaging 为 war
2、在 webapp 文件夹下新建 META-INF 和 WEB-INF/web.xml
3、引入 java ee 相关的三个 jar包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
       <dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<scope> jar有效范围 provided 表示编译期生效,不会打包发布到 tomcat 中

因为tomcat中已经有sevlet包,如果不加编译期生效这个,就会在tomcat启动时出现了Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]

4、配置tomcat插件,运行时和本地安装的Tomcat没一点关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 控制 tomcat 端口号 -->
<port>801</port>
<!-- 项目发布到 tomcat 后的名称 -->
<path>/mavenwebappdemo</path>
</configuration>
</plugin>
</plugins>
</build>

右键项目–> run as –> maven build–>Goals 中输入 clean tomcat7:run 发布,之后可以在浏览器测试。

5、资源拷贝插件

1、maven 默认只把 src/main/resources 里面的资源文件进行编译到classes 中。
2、如果希望 src/main/java 下的资源文件也被编译到 classes 中,在 pom.xml中的<build>配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 配置编译文件 -->
<resources>
<!-- src/main/java 里面的资源文件进行编译到classes 中 -->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<!-- 上面修改了默认配置,打破了规则,也要配置把resources中的资源文件让其被编译到class -->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>

二、热部署

所谓热部署,就是在应用正在运行的时候升级软件,却不需要重新启动应用。

Tomcat的热部署实现:

1、修改Tomcat配置文件/conf/tomcat-users.xml,在里边添加如下内容

<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<user username="tomcat" password="tomcat" roles="manager-gui,manager-script"/>

2、 在 maven 项目的 pom.xml 中 tomcat 插件的里配置。

<username>tomcat</username>
<password>tomcat</password>
    <url>http://远程路径/manager/text</url>

3、发布

右键项目-->run as --> maven build(选择第二个) -->输入
tomcat7:deploy 第一次发布
tomcat7:redeploy 不是第一次发布
上一页1…22232425下一页
初晨

初晨

永远不要说你知道本质,更别说真相了。

249 日志
46 分类
109 标签
近期文章
  • WebSocket、Socket、TCP、HTTP区别
  • Springboot项目的接口防刷
  • 深入理解Volatile关键字及其实现原理
  • 使用vscode搭建个人笔记环境
  • HBase介绍安装与操作
© 2018 — 2020 Copyright
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4